argo ci/cd初体验

argo ci/cd

从一个demo开始,由于一些网络问题,镜像拉取不下来,可以看我之前的 blog 解决。

注意

项目里有一些应用是 kustomization 和 helm 安装的,由于网络原因,安装会失败。我使用下面的命令,生成了最终的 yaml。

kustomize build . > output.yaml
helm template . > output.yaml

开始

整个项目目录结构是这样。入口文件是 setup.sh。

├── argo.yaml
├── assets
│   └── images
├── bootstrap
│   ├── applications
│   ├── applications-rollouts
│   ├── app-of-apps
│   ├── app-of-apps-rollouts
│   ├── argocd
│   ├── argo-rollouts
│   ├── argo-workflows
│   ├── final-application
│   ├── ingress-nginx
│   ├── k3d.conf
│   ├── minio
│   ├── nfs-server-provisioner
│   ├── output.yaml
│   └── workflow-templates
├── CI
│   ├── Dockerfile
│   └── index.html
├── hera
│   └── nfs
├── LICENSE
├── output.yaml
├── README.md
├── renovate.json
├── rollouts-workflow-2.yml
├── rollouts-workflow-s3-2.yml
├── rollouts-workflow-s3.yml
├── rollouts-workflow.yml
├── setup.sh
├── workflow-s3.yml
└── workflow.yml

看一下 setup.sh 的内容。

#!/usr/bin/env bash

k3d cluster delete workflows-ci || true
k3d cluster create --config bootstrap/k3d.conf

# Prevent users from accidentally deploying to the wrong cluster.
currentContext=$(kubectl config current-context)
if [ "$currentContext" == "k3d-workflows-ci" ]; then
    echo "Starting deployment to cluster..."
else
    echo "The kubectl context is not what we expected. Exiting for safety. Perhaps the k3d cluster failed to create?"
    exit 1
fi

# Deploy Argo CD, which in turn deploys everything else
kubectl -n kube-system rollout status deployment/metrics-server
kubectl apply -k bootstrap/argocd
kubectl -n argocd rollout status statefulset/argocd-application-controller
kubectl -n argocd rollout status deployment/argocd-repo-server
kubectl -n argocd apply -f bootstrap/app-of-apps"${1}"

# Create 'final-application' namespace for the final application
kubectl create namespace final-application

# Wait for Argo CD to start syncing its new-found applications
sleep 30
kubectl -n nfs-server-provisioner rollout status statefulset/nfs-server-provisioner
kubectl -n ingress-nginx rollout status deployment/nginx-ingress-nginx-controller
kubectl -n ingress-nginx rollout status daemonset/svclb-nginx-ingress-nginx-controller
kubectl -n argo rollout status deployment/workflow-controller
kubectl -n argo rollout status deployment/argo-server
kubectl -n minio rollout status deployment/minio

echo "Complete. You should be able to navigate to https://localhost:8443/argo/workflows/argo in your browser now. (Remember to accept the self-signed SSL cert)."

setup.sh 会部署一个 k3s 的集群,然后部署 kubectl apply -k bootstrap/argocd,会把 argocd 部署起来,

再通过kubectl -n argocd apply -f bootstrap/app-of-apps部署一个 Application,

这个 Application 会 bootstrap/applications-rollouts 下的 6 个 Application 启动起来。

❯ tree -L 2 bootstrap/applications-rollouts
bootstrap/applications-rollouts
├── argocd.yml
├── argo-rollouts.yml
├── argo-workflows-templates.yml
├── argo-workflows.yml
├── minio.yml
└── nginx.yml

应用分别对应下面的文件的内容,就是定义了这些 Application。原来的仓库是有 nfs 的,我部署不成功,然后手动安装了nfs,所以会比官方少一个。

bootstrap/argocd
bootstrap/argo-rollouts
bootstrap/workflow-templates
bootstrap/argo-workflows
bootstrap/minio
bootstrap/ingress-nginx

你也可以使用自己的集群,直接去执行然后下面两个命令。

kubectl apply -k bootstrap/argocd
kubectl -n argocd apply -f bootstrap/app-of-apps

通过 port-forward 转发就可以在浏览器看到页面了。

k port-forward services/argocd-server --address 0.0.0.0 8443:443
k port-forward services/argo-server --address 0.0.0.0 8843:2746

初始化完成应该可以看到这几个 application

image.png

也可以点卡片进去看到详情。

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

手动触发

使用kubectl -n argo create -f workflow.yml创建流水线。

我们简单看下 workflow.yaml 的内容,引用的是 ci-workflow,然后指定了一些参数,包括 app_repo、git_branch、target_branch、container_tag、dockerfile、path。

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: ci-workflow-
  namespace: argo
  labels:
    workflows.argoproj.io/workflow-template: ci-workflow
spec:
  arguments:
    parameters:
      - name: app_repo
        value: "argo-workflows-ci-example"
      - name: git_branch
        value: "main"
      - name: target_branch
        value: "main"
      - name: container_tag
        value: "stable"
      - name: container_image
        value: "192.168.1.94:443/my_test/hello-world"
      - name: dockerfile
        value: Dockerfile
      - name: path
        value: "/CI"
  workflowTemplateRef:
    name: ci-workflow

再来看看 ci-workflow 是啥?文件位置:bootstrap/workflow-templates/nfs/ci-workflow.yml

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: ci-workflow
  annotations:
    workflows.argoproj.io/description: >-
      A basic CI leveraging Argo Workflows.

      The Workflow...

      * pulls a repo from git. Specifically pulling a branch based on a pull request; * merges the target branch into it; * modifies the html that will be copied into the container to inject the unique name of the running workflow; * builds a container from a Dockerfile and pushes to a registry; * deploys an Argo CD application that uses the newly-built container to deploy a static website.

      It does not pretend to be a definitive example, but it aims to inspire. In order to make this a semi-usable example, we have cut a number of security corners. Please don't just blindly run this in production.
    workflows.argoproj.io/maintainer: "Pipekit Inc"
    workflows.argoproj.io/maintainer_url: "http://192.168.80.36/zhangcheng/argo-workflows-ci-example.git"
    workflows.argoproj.io/version: ">= 3.3.6"
spec:
  # This is entirely optional. It allows us to use Prometheus to scrape the metrics from the argo Workflow Controller, and to measure the success of our CI.
  metrics:
    prometheus:
      # Writes a prometheus metric stating the length of time it took the workflow to complete. Grouped by workflow status and 'type'.
      - name: exec_duration_gauge
        labels:
          - key: name
            value: "ci-workflow"
          - key: type
            value: "pull-request"
          - key: status
            value: "{{status}}"
        help: "Duration gauge by name"
        gauge:
          value: "{{workflow.duration}}"
          realtime: false
      # If the workflow fails, we increase the Prometheus failure counter by 1.
      - name: result_counter
        help: "Count of step execution by result status"
        labels:
          - key: status
            value: Failed
          - key: name
            value: "ci-workflow"
          - key: type
            value: "pull-request"
        when: "{{status}} == Failed"
        counter:
          value: "1"
      # If the workflow succeeds, we increase the Prometheus succeeded counter by 1.
      - name: result_counter
        help: "Count of step execution by result status"
        labels:
          - key: status
            value: Succeeded
          - key: name
            value: "ci-workflow"
          - key: type
            value: "pull-request"
        when: "{{status}} == Succeeded"
        counter:
          value: "1"
  entrypoint: main
  # We request an NFS share (called 'workdir') so that we can pull the code, and manipulate it across multiple nodes.
  # By the nature of the share, we would be able to access it from multiple nodes at the same time, allowing for parallel nodes.
  # We use nfs-server-provisioner for this, which uses disk on the Kubernetes node to provision the share. The PVCs are cleaned up when the workflow finishes.
  volumeClaimTemplates:
    - metadata:
        name: workdir
      spec:
        accessModes: ["ReadWriteMany"]
        storageClassName: nfs-csi
        resources:
          requests:
            storage: 1Gi
  # The container build step copies the code from the NFS share to this ephemeral volume, and then runs the build.
  # We do this a) because NFS shares are slow and b) to allow us to run another workflow step alongside the build if we wish.
  volumes:
    - name: harbor-cert
      secret:
        secretName: harbor-ca-cert
    - name: docker-config
      secret:
        secretName: registry-auth
        items:
          - key: .dockerconfigjson
            path: config.json
    - name: container-build
      emptyDir: {}
      # You can set default parameters here if you prefer. If you simply don't inject them when calling this template, the defaults will come through.
      # We default 'container_tag' to 'stable' here.
  arguments:
    parameters:
      - name: app_repo
        value: ""
      - name: git_branch
        value: ""
      - name: target_branch
        value: ""
      - name: container_tag
        value: "stable"
      - name: container_image
        value: ""
      - name: dockerfile
        value: ""
      - name: path
        value: ""
        # All the steps in this DAG are referencing external templates.
        # This allows us to re-use those templates in other workflows, and also makes this CI workflow quite tidy.
        # For reference we have also included a 'local' template (delete-application) to show that it's possible to mix-and-match local and external templates.
  templates:
    - name: main
      dag:
        tasks:
          - name: git-checkout-pr
            templateRef:
              name: git-checkout-pr
              template: main
          - name: html-modifier
            templateRef:
              name: html-modifier
              template: main
            depends: git-checkout-pr
          - name: container-build
            templateRef:
              name: container-build
              template: main
            depends: html-modifier
          - name: deploy-application
            templateRef:
              name: deploy-application
              template: main
            depends: container-build
          - name: delete-application
            template: delete-application
            depends: (deploy-application.Failed)
    # This only runs if deploy-application fails. This allows us to ensure that we have a tidy environment for the next Workflow run.
    - name: delete-application
      resource:
        action: delete
        manifest: |
          apiVersion: argoproj.io/v1alpha1
          kind: Application
          metadata:
            name: final-application
            namespace: argocd

注意:相比官方的文件,我增加了 docker-config、harbor-cert 两个 volumes,这个很重要,后面再讲。

可以看到 workflow 的定义有参数的定义,正好对应我们运行流水线时,传入的参数。

重点看一下 tasks 部署,应用了一些其他的 template。这些 template 也是在 bootstrap/workflow-templates/nfs 下。

通过 depends 的定义,可以看到流水线的执行过程,

git-checkout-pr → html-modifier → container-build → deploy-application

tasks:
  - name: git-checkout-pr
    templateRef:
      name: git-checkout-pr
      template: main
  - name: html-modifier
    templateRef:
      name: html-modifier
      template: main
    depends: git-checkout-pr
  - name: container-build
    templateRef:
      name: container-build
      template: main
    depends: html-modifier
  - name: deploy-application
    templateRef:
      name: deploy-application
      template: main
    depends: container-build
  - name: delete-application
    template: delete-application
    depends: (deploy-application.Failed)

git-checkout-pr

由于我的服务器连不上 github,我 clone 了一个,放到自建的 gitlab,然后创建 token 下载。

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: git-checkout-pr
  annotations:
    workflows.argoproj.io/description: >-
      Clones a git repository and then performs a git checkout of a branch defined in the workflow workflow.parameters. Then merges in a defined target branch.
    workflows.argoproj.io/maintainer: "Pipekit Inc"
    workflows.argoproj.io/maintainer_url: "https://github.com/pipekit/argo-workflows-ci-example"
    workflows.argoproj.io/version: ">= 3.3.6"
spec:
  entrypoint: main
  templates:
    - name: main
      dag:
        tasks:
          - name: git-checkout-pr
            template: git-checkout-pr
    - name: git-checkout-pr
      container:
        image: alpine:latest
        command:
          - sh
          - -c
          - |
            apk --update add git
            cd /workdir
            echo "Start Clone of source branch"
            git clone http://gitlab-token:[email protected]/xxxxx/{{workflow.parameters.app_repo}}.git
            cd {{workflow.parameters.app_repo}}
            ## These lines are a hack just for the example.
            git config --global --add safe.directory /workdir/{{workflow.parameters.app_repo}}
            git config --global user.email "[email protected]"
            git config --global user.name "Tim Collins"
            git checkout {{workflow.parameters.git_branch}}
            echo "Merge in target branch"
            git merge origin/{{workflow.parameters.target_branch}}
            echo "Complete."
        volumeMounts:
          - name: "workdir"
            mountPath: /workdir
        resources:
          requests:
            memory: 250Mi
            cpu: 4m
      #20 minutes
      activeDeadlineSeconds: 1200

html-modifier

这个步骤是模拟修改、提交。

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: html-modifier
  annotations:
    workflows.argoproj.io/description: >-
      Performs a sed command to inject the current running Workflow name into a given file.
    workflows.argoproj.io/maintainer: "Pipekit Inc"
    workflows.argoproj.io/maintainer_url: "https://github.com/pipekit/argo-workflows-ci-example"
    workflows.argoproj.io/version: ">= 3.3.6"
spec:
  entrypoint: main
  templates:
    - name: main
      dag:
        tasks:
          - name: html-modifier
            template: html-modifier

    - name: html-modifier
      container:
        image: ubuntu:latest
        command:
          - /bin/bash
          - -c
          - |
            cd /workdir/{{workflow.parameters.app_repo}}/CI

            if grep -q CHANGEMEPLEASE index.html; then
              cat index.html | sed -E 's/CHANGEMEPLEASE/{{workflow.name}} and it used nfs-server-provisioner for artifact passing./g' > tmp_index.html
              mv tmp_index.html index.html
            else
              echo "CHANGEMEPLEASE was not found in index.html. Exiting"
              exit 1
            fi

            cat index.html

        volumeMounts:
          - name: workdir
            mountPath: /workdir
        resources:
          requests:
            memory: 256Mi
            cpu: 100m
      #20 minutes
      activeDeadlineSeconds: 1200

container-build

当我再尝试 jenkins 打包镜像时,也遇到 buildkit:v0.18.2-rootless,打包失败的问题和上传镜像失败的问题。因为我的 harbor 的证书是自签的,才会遇到这个。如果你的都是标准的,不会遇到这些问题。

经过千辛万苦的尝试,最终还是解决了。通过增加 harbor-ca.crt 和 config.json,

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: container-build
  annotations:
    workflows.argoproj.io/description: >-
      Uses Buildkit to build a container image within Kubernetes.
    workflows.argoproj.io/maintainer: "Pipekit Inc"
    workflows.argoproj.io/maintainer_url: "https://github.com/pipekit/argo-workflows-ci-example"
    workflows.argoproj.io/version: ">= 3.3.6"
spec:
  entrypoint: main
  templates:
    - name: main
      dag:
        tasks:
          - name: container-build
            template: container-build

    - name: container-build
      container:
        image: moby/buildkit:v0.18.2-rootless
        command:
          - sh
          - -c
          - |
            echo "Retrieving git clone..." && cp -R /workdir/{{workflow.parameters.app_repo}} /container-build

            mkdir -p /home/user/.docker

            buildctl-daemonless.sh build \
            --frontend \
            dockerfile.v0 \
            --local \
            context=/container-build/{{workflow.parameters.app_repo}}{{workflow.parameters.path}} \
            --local \
            dockerfile=/container-build/{{workflow.parameters.app_repo}}{{workflow.parameters.path}} \
            --opt filename={{workflow.parameters.dockerfile}} \
            --output \
            type=image,name={{workflow.parameters.container_image}}:{{workflow.parameters.container_tag}},push=true,registry.insecure=true
        env:
          - name: BUILDKITD_FLAGS
            value: --oci-worker-no-process-sandbox
        volumeMounts:
          - name: container-build
            mountPath: /container-build
          - name: workdir
            mountPath: /workdir
          - name: docker-config
            mountPath: /home/user/.docker/config.json
            subPath: config.json
          - name: harbor-cert
            mountPath: /etc/ssl/certs/harbor-ca.crt
            subPath: harbor-ca.crt
        securityContext:
          seccompProfile:
            type: Unconfined
          runAsUser: 1000
          runAsGroup: 1000
        resources:
          requests:
            memory: 1Gi
            cpu: 1
      #20 minutes
      activeDeadlineSeconds: 1200

harbor-ca.crt 创建

## download ca
openssl s_client -showcerts -connect 192.168.1.94:443 </dev/null 2>/dev/null | openssl x509 -outform PEM > harbor-ca.crt

kubectl create secret generic harbor-ca-cert --from-file=harbor-ca.crt=/etc/ssl/certs/harbor-ca.crt

config.json 创建

kubectl create secret docker-registry registry-auth --docker-server=https://192.168.1.94:443 --docker-username=admin --docker-password=Harbor12345

deploy-application

最后创建了一个 argo 的 application,用于部署。通过 application 的 status.health.status,判断是否部署成功。部署失败,会删除这个应用,这个功能是在

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: deploy-application
  annotations:
    workflows.argoproj.io/description: >-
      Leverages Argo Workflows' ability to interact directly with Kubernetes to deploy an Argo CD Application.
      It monitors the health status of the application and is only considered 'done' once the Argo CD
      Application reports itself as healthy.
    workflows.argoproj.io/maintainer: "Pipekit Inc"
    workflows.argoproj.io/maintainer_url: "https://github.com/pipekit/argo-workflows-ci-example"
    workflows.argoproj.io/version: ">= 3.3.6"
spec:
  entrypoint: main
  templates:
    - name: main
      dag:
        tasks:
          - name: deploy-application
            template: deploy-application

    - name: deploy-application
      resource:
        action: create
        successCondition: status.health.status == Healthy
        failureCondition: status.health.status == Degraded
        manifest: |
          apiVersion: argoproj.io/v1alpha1
          kind: Application
          metadata:
            name: final-application
            finalizers:
              - resources-finalizer.argocd.argoproj.io
            namespace: argocd
          spec:
            destination:
              namespace: final-application
              server: 'https://kubernetes.default.svc'
            project: default
            source:
              path: bootstrap/final-application
              repoURL: 'http://192.168.80.36/zhangcheng/argo-workflows-ci-example.git'
              targetRevision: HEAD
            syncPolicy:
              automated:
                prune: true
                selfHeal: true
              syncOptions:
                - PrunePropagationPolicy=background
                - PruneLast=true
                - CreateNamespace=true

delete-application

是在 ci-workflow.yml 里定义的。

    - name: delete-application
      resource:
        action: delete
        manifest: |
          apiVersion: argoproj.io/v1alpha1
          kind: Application
          metadata:
            name: final-application
            namespace: argocd

一切顺利的话,可以看到下面的执行的流水线和详细的执行过程。

image.png

image.png

发生错误,可以点开该节点,看到详细的报错信息。

image.png

workflow template 的定义

image.png

最终结果,final-application 是一个 argo appliction,所以也能在页面上看到

image.png

代码触发

项目在 hera 文件下提供了 python 代码调用的方式,python hera/nfs/workflow.py, 入口函数是在 workflow.py 里。

制定了参数和引用的 workflow,引用的 workflow 是 ci_workflow.py 里的 ci_workflow

from ci_workflow import ci_workflow

with Workflow(
    generate_name="hera-ci-workflow-",
    namespace="argo",
    labels={
        'argoworkflows.argoproj.io/workflow-template': 'hera-ci-workflow',
    },
    arguments=[
        Parameter(name="app_repo", value="argo-workflows-ci-example"),
        Parameter(name="git_branch", value="main"),
        Parameter(name="target_branch", value="main"),
        Parameter(name="container_tag", value="stable"),
        Parameter(name="container_image", value="k3d-registry.localhost:5000/hello-world"),
        Parameter(name="dockerfile", value="Dockerfile"),
        Parameter(name="path", value="/CI"),
    ],
    workflow_template_ref=ci_workflow
) as workflow:
    workflow.create()
    print(workflow.get_workflow_link())   # get a link for monitoring
    print("Waiting for workflow to complete")
    workflow.wait()

再来看看 ci_workflow.py。从其他 4 个 py 文件里,引入了 5 个 workflowtemplate,和上面 yaml 的定义过程是一样的。

from git_checkout_pr import git_checkout_pr
from html_modifier import html_modifier
from container_build import container_build
from deploy_application import deploy_application, deploy_application_manifest

with WorkflowTemplate(
    name="hera-ci-workflow",
    entrypoint="main",
    volume_claim_templates=[
        m.PersistentVolumeClaim(
            metadata=m.ObjectMeta(name="workdir"),
            spec=m.PersistentVolumeClaimSpec(
                access_modes=['ReadWriteMany'],
                storage_class_name='nfs',
                resources=m.ResourceRequirements(
                    requests={
                        "storage": m.Quantity(__root__="1Gi"),
                    }
                ),
            ),
        )
    ],
        # The container build step copies the code from the NFS share to this ephemeral volume, and then runs the build.
        # We do this a) because NFS shares are slow and b) to allow us to run another workflow step alongside the build if we wish.
    volumes=[EmptyDirVolume(name="container-build")],
    arguments=[
        Parameter(name="app_repo", value=""),
        Parameter(name="git_branch", value=""),
        Parameter(name="target_branch", value=""),
        Parameter(name="container_tag", value=""),
        Parameter(name="container_image", value=""),
        Parameter(name="dockerfile", value=""),
        Parameter(name="path", value=""),
    ],
    annotations={
        'workflows.argoproj.io/description': '''A basic CI leveraging Argo Workflows.

The Workflow...

* pulls a repo from git. Specifically pulling a branch based on a pull request;
* merges the target branch into it;
* modifies the html that will be copied into the container to inject the unique name of the running workflow;
* builds a container from a Dockerfile and pushes to a registry;
* deploys an Argo CD application that uses the newly-built container to deploy a static website.

It does not pretend to be a definitive example, but it aims to inspire. In order to make this a semi-usable example, we have cut a number of security corners. Please don't just blindly run this in production.
''',
    },
) as ci_workflow:
    delete_application_resource = Resource(name="delete-application",
                  action="delete",
                  manifest=deploy_application_manifest
                  )
    with DAG(name="main") as main:
        git_checkout_pr_task = git_checkout_pr(name="git-checkout-pr")
        html_modifier_task = html_modifier(name="html-modifier")
        container_build_task = container_build(name="container-build")
        deploy_application_task = deploy_application(name="deploy-application")
        delete_application_task = delete_application_resource(name="delete-application", depends="deploy-application.Failed")
        git_checkout_pr_task >> html_modifier_task >> container_build_task >> deploy_application_task

ci_workflow.to_file(".")
try:
    ci_workflow.create()
except AlreadyExists:
    ci_workflow.update()

重点看一下,流程的定义,使用 DAG 定义的有向无环图。git_checkout_pr_task >> html_modifier_task >> container_build_task >> deploy_application_task,hera 通过重载rshift 实现了类似 pipeline 的调用。

with DAG(name="main") as main:
        git_checkout_pr_task = git_checkout_pr(name="git-checkout-pr")
        html_modifier_task = html_modifier(name="html-modifier")
        container_build_task = container_build(name="container-build")
        deploy_application_task = deploy_application(name="deploy-application")
        delete_application_task = delete_application_resource(name="delete-application", depends="deploy-application.Failed")
        git_checkout_pr_task >> html_modifier_task >> container_build_task >> deploy_application_task

git_checkout_pr

git_checkout_script = '''apk --update add git

cd /workdir
echo "Start Clone of source branch"
git clone https://github.com/pipekit/{{workflow.parameters.app_repo}}.git
cd {{workflow.parameters.app_repo}}

## These lines are a hack just for the example.
git config --global --add safe.directory /workdir/{{workflow.parameters.app_repo}}
git config --global user.email "[email protected]"
git config --global user.name "Tim Collins"

git checkout {{workflow.parameters.git_branch}}

echo "Merge in target branch"
git merge origin/{{workflow.parameters.target_branch}}

echo "Complete."
'''

git_checkout_pr = Container(name="git-checkout-pr",
               image="alpine:latest",
               command=["sh", "-c", git_checkout_script],
               volume_mounts=[
                   m.VolumeMount(name="workdir",
                                 mount_path="/workdir")],
               resources=Resources(memory_request="250Mi", cpu_request="4m"),
               active_deadline_seconds=1200)

html_modifier

html_modifier_script = '''cd /workdir/{{workflow.parameters.app_repo}}/CI

if grep -q CHANGEMEPLEASE index.html; then
  cat index.html | sed -E 's/CHANGEMEPLEASE/{{workflow.name}} and it used nfs-server-provisioner for artifact passing./g' > tmp_index.html
  mv tmp_index.html index.html
else
  echo "CHANGEMEPLEASE was not found in index.html. Exiting"
  exit 1
fi

cat index.html
'''

html_modifier = Container(name="html-modifier",
                               image="ubuntu:latest",
                               command=["sh", "-c", html_modifier_script],
                               volume_mounts=[
                                   m.VolumeMount(name="workdir",
                                                 mount_path="/workdir")],
                               resources=Resources(memory_request="256Mi", cpu_request="100m"),
                               active_deadline_seconds=1200)

container_build

container_build_script = '''echo "Retrieving git clone..." && cp -R /workdir/{{workflow.parameters.app_repo}} /container-build && \
buildctl-daemonless.sh build \
--frontend \
dockerfile.v0 \
--local \
context=/container-build/{{workflow.parameters.app_repo}}{{workflow.parameters.path}} \
--local \
dockerfile=/container-build/{{workflow.parameters.app_repo}}{{workflow.parameters.path}} \
--opt filename={{workflow.parameters.dockerfile}} \
--output \
type=image,name={{workflow.parameters.container_image}}:{{workflow.parameters.container_tag}},push=true,registry.insecure=true
'''

# Uses Buildkit to build a container image within Kubernetes.
container_build = Container(name="container-build",
                            image="moby/buildkit:v0.12.3-rootless",
                            command=["sh", "-c", container_build_script],
                            env=[
                                Env(name="BUILDKITD_FLAGS",
                                    value="--oci-worker-no-process-sandbox")],
                            volume_mounts=[
                                m.VolumeMount(name="container-build",
                                              mount_path="/container-build"),
                                m.VolumeMount(name="workdir",
                                              mount_path="/workdir")],
                            resources=Resources(memory_request="1Gi", cpu_request="1"),
                            security_context=m.SecurityContext(seccomp_profile=m.SeccompProfile(type="Unconfined"),
                                                               run_as_user=1000,
                                                               run_as_group=1000,),
                            active_deadline_seconds=1200)

deploy_application

deploy_application_manifest = '''apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: final-application
  finalizers:
    - resources-finalizer.argocd.argoproj.io
  namespace: argocd
spec:
  destination:
    namespace: final-application
    server: 'https://kubernetes.default.svc'
  project: default
  source:
    path: bootstrap/final-application
    repoURL: 'http://192.168.80.36/zhangcheng/argo-workflows-ci-example.git'
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - PrunePropagationPolicy=background
      - PruneLast=true
      - CreateNamespace=true
'''

deploy_application = Resource(name="deploy-application",
                              action="create",
                              success_condition="status.health.status == Healthy",
                              failure_condition="status.health.status == Degraded",
                              manifest=deploy_application_manifest
                              )

delete_application_resource

  delete_application_resource = Resource(name="delete-application",
                action="delete",
                manifest=deploy_application_manifest
                )
© 2025 · Built with Gatsby