maidn-cli/internal/assets/templates/delivery-pipeline.yaml.tmpl

327 lines
10 KiB
Cheetah

apiVersion: tekton.dev/v1
kind: Task
metadata:
name: {{ .AppName }}-update-manifest
namespace: tekton-pipelines
spec:
stepTemplate:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
seccompProfile:
type: RuntimeDefault
params:
- name: manifests-url
- name: manifests-branch
- name: app-name
- name: app-repository
- name: image
- name: tag
- name: environment
- name: pr-number
default: ""
- name: app-url
default: ""
- name: app-revision
default: ""
steps:
- name: update
image: alpine/git:2.47.2
env:
- name: MANIFESTS_URL
value: $(params.manifests-url)
- name: MANIFESTS_BRANCH
value: $(params.manifests-branch)
- name: APP_NAME
value: $(params.app-name)
- name: APP_REPOSITORY
value: $(params.app-repository)
- name: IMAGE
value: $(params.image)
- name: TAG
value: $(params.tag)
- name: ENVIRONMENT
value: $(params.environment)
- name: PR_NUMBER
value: $(params.pr-number)
- name: APP_URL
value: $(params.app-url)
- name: APP_REVISION
value: $(params.app-revision)
script: |
#!/bin/sh
set -eu
fail() { exit 1; }
valid_name() { case "$1" in ''|*[!a-z0-9-]*|-*|*-) fail ;; esac; }
valid_revision() { case "$1" in [A-Za-z0-9]*) ;; *) fail ;; esac; case "$1" in *[!A-Za-z0-9._/-]*|*..*|*//*|*/) fail ;; esac; }
valid_commit() { [ "${#1}" -eq 40 ] || fail; case "$1" in *[!0-9a-fA-F]*) fail ;; esac; }
valid_url() { case "$1" in https://*/*.git) ;; *) fail ;; esac; case "$1" in *[@?#]*) fail ;; esac; }
valid_repository() { case "$1" in */*) ;; *) fail ;; esac; case "$1" in *..*|*//*|/*|*/) fail ;; esac; }
valid_pr_number() { case "$1" in [1-9]*) ;; *) fail ;; esac; case "$1" in *[!0-9]*) fail ;; esac; [ $((${#APP_NAME} + ${#1} + 4)) -le 63 ] || fail; }
valid_name "$APP_NAME"
valid_url "$MANIFESTS_URL"
valid_revision "$MANIFESTS_BRANCH"
valid_repository "$APP_REPOSITORY"
valid_commit "$TAG"
git clone --branch "$MANIFESTS_BRANCH" "$MANIFESTS_URL" /tmp/manifests
cd /tmp/manifests
if [ "$ENVIRONMENT" = preview ]; then
valid_pr_number "$PR_NUMBER"
valid_url "$APP_URL"
valid_revision "$APP_REVISION"
app_dir="apps/previews/$APP_NAME-pr-$PR_NUMBER"
marker="$app_dir/ownership.yaml"
if [ -e "$app_dir" ]; then
[ -d "$app_dir" ] && [ ! -L "$app_dir" ] && [ -f "$marker" ] && [ ! -L "$marker" ] || fail
expected_marker=$(mktemp)
trap 'rm -f "$expected_marker"' EXIT
cat > "$expected_marker" <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: maidn-preview-owner
namespace: $APP_NAME-pr-$PR_NUMBER
labels:
maidn.io/preview-owner: "true"
maidn.io/preview-app: "$APP_NAME"
maidn.io/preview-pr: "$PR_NUMBER"
annotations:
maidn.io/preview-repository: "$APP_REPOSITORY"
data:
app: "$APP_NAME"
repository: "$APP_REPOSITORY"
pr-number: "$PR_NUMBER"
EOF
cmp -s "$expected_marker" "$marker" || fail
fi
git clone "$APP_URL" /tmp/app
git -C /tmp/app checkout "$APP_REVISION"
[ -f /tmp/app/preview/values.yaml ] || fail
mkdir -p "$app_dir"
cp /tmp/app/preview/values.yaml "$app_dir/values.yaml"
sed -i "s/PLACEHOLDER_PR/$PR_NUMBER/g" "$app_dir/values.yaml"
cat > "$marker" <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: maidn-preview-owner
namespace: $APP_NAME-pr-$PR_NUMBER
labels:
maidn.io/preview-owner: "true"
maidn.io/preview-app: "$APP_NAME"
maidn.io/preview-pr: "$PR_NUMBER"
annotations:
maidn.io/preview-repository: "$APP_REPOSITORY"
data:
app: "$APP_NAME"
repository: "$APP_REPOSITORY"
pr-number: "$PR_NUMBER"
EOF
cat > "$app_dir/namespace.yaml" <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: $APP_NAME-pr-$PR_NUMBER
EOF
cat > "$app_dir/release.yaml" <<EOF
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: $APP_NAME
namespace: $APP_NAME-pr-$PR_NUMBER
spec:
interval: 5m
chart:
spec:
chart: ./charts/$APP_NAME
sourceRef:
kind: GitRepository
name: $APP_NAME
namespace: flux-system
values:
image:
repository: $IMAGE
tag: $TAG
EOF
cat > "$app_dir/kustomization.yaml" <<EOF
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: $APP_NAME-pr-$PR_NUMBER
resources:
- namespace.yaml
- ownership.yaml
- release.yaml
EOF
root=apps/previews/kustomization.yaml
grep -q '^resources:' "$root" || fail
grep -qxF " - $APP_NAME-pr-$PR_NUMBER" "$root" 2>/dev/null || printf ' - %s-pr-%s\n' "$APP_NAME" "$PR_NUMBER" >> "$root"
elif [ "$ENVIRONMENT" = staging ] || [ "$ENVIRONMENT" = production ]; then
app_dir="apps/$ENVIRONMENT/$APP_NAME"
[ -f "$app_dir/release.yaml" ] || fail
sed -i -E "s|^([[:space:]]*tag:).*|\1 $TAG|" "$app_dir/release.yaml"
else
fail
fi
git config user.name Maidn
git config user.email maidn@free-maidn.com
git add apps
git diff --cached --quiet || git commit -m "chore: deploy $APP_NAME $TAG"
git push origin "$MANIFESTS_BRANCH"
---
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: {{ .AppName }}
namespace: tekton-pipelines
spec:
params:
- name: git-url
default: {{ quote .AppRepoURL }}
- name: git-revision
default: {{ quote .AppRepoRef }}
- name: branch
default: {{ quote .AppRepoRef }}
- name: event-type
default: push
- name: event-action
default: ""
- name: pr-number
default: ""
- name: image
default: {{ quote .ImageRepository }}
- name: manifests-url
default: {{ quote .ManifestsURL }}
- name: manifests-branch
default: {{ quote .ManifestsBranch }}
workspaces:
- name: source
taskRunTemplate:
serviceAccountName: tekton-delivery
tasks:
- name: build-and-push
when:
- input: $(params.event-action)
operator: notin
values: [closed]
taskRef:
name: maidn-node-static-image
params:
- name: url
value: {{ quote .AppRepoURL }}
- name: revision
value: $(params.git-revision)
- name: image
value: $(params.image)
- name: output-directory
value: {{ quote .BuildOutputDirectory }}
- name: build-configuration
value: {{ quote .BuildConfiguration }}
workspaces:
- name: source
workspace: source
- name: update-preview
runAfter: [build-and-push]
when:
- input: $(params.event-type)
operator: in
values: [pull_request]
- input: $(params.event-action)
operator: in
values: [opened, reopened, synchronize]
taskRef:
name: {{ .AppName }}-update-manifest
params:
- name: manifests-url
value: $(params.manifests-url)
- name: manifests-branch
value: $(params.manifests-branch)
- name: app-name
value: {{ quote .AppName }}
- name: app-repository
value: {{ quote .AppRepository }}
- name: image
value: $(params.image)
- name: tag
value: $(params.git-revision)
- name: environment
value: preview
- name: pr-number
value: $(params.pr-number)
- name: app-url
value: {{ quote .AppRepoURL }}
- name: app-revision
value: $(params.git-revision)
- name: update-staging
runAfter: [build-and-push]
when:
- input: $(params.event-type)
operator: in
values: [push]
- input: $(params.branch)
operator: in
values: [{{ quote .AppRepoRef }}]
taskRef:
name: {{ .AppName }}-update-manifest
params:
- name: manifests-url
value: $(params.manifests-url)
- name: manifests-branch
value: $(params.manifests-branch)
- name: app-name
value: {{ quote .AppName }}
- name: app-repository
value: {{ quote .AppRepository }}
- name: image
value: $(params.image)
- name: tag
value: $(params.git-revision)
- name: environment
value: staging
- name: promote-production
runAfter: [build-and-push]
when:
- input: $(params.event-type)
operator: in
values: [promotion]
- input: $(params.branch)
operator: in
values: [{{ quote .ProductionBranch }}]
taskRef:
name: {{ .AppName }}-update-manifest
params:
- name: manifests-url
value: $(params.manifests-url)
- name: manifests-branch
value: $(params.manifests-branch)
- name: app-name
value: {{ quote .AppName }}
- name: app-repository
value: {{ quote .AppRepository }}
- name: image
value: $(params.image)
- name: tag
value: $(params.git-revision)
- name: environment
value: production
- name: cleanup-preview
when:
- input: $(params.event-type)
operator: in
values: [pull_request]
- input: $(params.event-action)
operator: in
values: [closed]
taskRef:
name: maidn-preview-orphan-reconciler
params:
- name: app-name
value: {{ quote .AppName }}
- name: pr-number
value: $(params.pr-number)
- name: app-repository
value: {{ quote .AppRepository }}