As a developer, when modifications are pushed to a feature branch, you and your team want to test this new feature. If you have the chance to work with a stateless application, you can deploy another instance of the application with modifications from the feature branch.
An interesting feature of ArgoCD is the Pull Request Generator. It's a generator for ApplicationSet. An ApplicationSet is a template of ArgoCD Application associated with a generator. Generator can be a directory: an application will be created for every sub-folder. There is also the Cluster generator that deploy the same Application but in every cluster managed by ArgoCD.
The syntax for the Pull Request generator is quite simple:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapps
spec:
generators:
- pullRequest:
github:
owner: camptocamp
repo: myrepository
tokenRef:
secretName: github-token
key: token
labels:
- deploy
template:
...
Before testing this feature what is expected ?
- A new ArgoCD Application is created when a pull request is created with a "deploy" label
- In the template of the Application we can use metadata of the pull request: ID, title, description, labels, source and target branch name, commit ref, …
- A comment is added to the PR when the Application is deployed and Synced with the available URLs
Now we will see how to implement this ;-)
Workflow before Implementation
Source Code Repository
Let's start with a simple application repository: git@github.com:camptocamp/frontend.git
Let's consider that this repository has a github action that builds a container image when a pull request is opened. The image is tagged with the short commit hash and the concatenation of the branch name and the short commit hash. For example, if the feature branch is name update_lib
with the last commit: 4a9b29e
, the following tags will be generated:
4a9b29e
update_lib-4a9b29e
Deployment Repository
There is another git repository: git@github.com:camptocamp/argocd-project-foo-apps.git
to describe what needs to be deployed in each kubernetes cluster (dev, int, …). In this repository, we have one folder per environment:
apps
├── dev
│ ├── backend
│ └── frontend
├── int
│ ├── backend
│ └── frontend
└── prod
├── backend
└── frontend
We are using an ApplicationSet to deploy every component defined in this git repository using the directory generator. For example, for "dev" env:
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: foo-dev
namespace: argocd
spec:
generators:
- git:
directories:
- path: apps/dev/*
repoURL: git@github.com:camptocamp/argocd-project-foo-apps.git
revision: main
template:
metadata:
name: foo-dev-{{path.basename}} # sub folder name
spec:
source:
path: '{{path}}' # full path
repoURL: git@github.com:camptocamp/argocd-project-foo-apps.git
targetRevision: main
project: default
destination:
namespace: foo-dev
server: https://kubernetes.default.svc
This ApplicationSet will create one ArgoCD Application per folder in apps/dev/
. Imagine that we have the following structure in git :
apps
└── dev
├── backend
│ ├── Chart.yaml
│ └── values.yaml
├── database
│ ├── Chart.yaml
│ └── values.yaml
└── frontend
├── Chart.yaml
└── values.yaml
This will generate 3 Argocd Applications :
- foo-dev-database
- foo-dev-backend
- foo-dev-frontend
Review App Workflow
When a pull request is opened for a specific application, for example the frontend, we want to deploy a new instance of this specific component. We will monitor pull requests on the frontend repository.
So we want to create an additional ApplicationSet to deploy the frontend if a pull request is created with the label deploy
in the frontend git repository.
Implementation
Create a token to access GitHub API
First step is to create a token to access the frontend repository.
Then we need to deploy this token as a kubernetes secret in the cluster :
export GITHUB_TOKEN=ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
kubectl create secret generic github-token \
--from-literal=token=$GITHUB_TOKEN \
-o yaml --dry-run=client > github-token-secret.yaml
If you follow the GitOps principles to deploy manifests in the cluster, you can commit this file.
Create a webhook
For a better user experience, we can setup a webhook that will notify ArgoCD when something changes on Github.
This webhook needs to be defined in the source code repository, where pull requests are created.
Go to the repository "Settings" and then "Webhooks". Click on the "Add webhook" button.
- The Payload URL is the URL to access ArgoCD with the path
/api/webhook
. - The Content Type should be set to
application/json
. - It's a good idea to set a "Secret", just use a random string, we will use this random string in the next step.
- Regarding events, we need to individually selects events and choose "Pull requests" events.
Deploy the new ApplicationSet
This new ApplicationSet will use the "Pull request" generator, this "generator" will monitor pull requests on the source code repository. Just create and commit a file with the following ApplicationSet manifest and deploy this new manifest.
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: foo-dev-review-frontend
namespace: argocd
spec:
generators:
- pullRequest:
github:
owner: camptocamp
repo: frontend # This is the application source code repo
tokenRef:
secretName: github-token
key: token
labels:
- deploy # label on PR that trigger review app
template:
metadata:
name: 'foo-dev-frontend-{{branch}}-{{number}}'
spec:
source:
repoURL: git@github.com:camptocamp/argocd-project-foo-apps.git
targetRevision: main
path: apps/dev/frontend
helm:
parameters:
- name: "image.tag"
value: "{{branch}}-{{head_sha}}" # override of the image tag
- name: "ingress.prefix"
value: "{{branch}}" # add a prefix to the URL
project: default
destination:
server: https://kubernetes.default.svc
namespace: foo-dev
This ApplicationSet will deploy a new instance of the frontend for each pull request with the deploy
label. The image tag will be overridden with the branch name and the short commit hash and a unique prefix will be added to the URL to avoid conflicts with other instances. Finally, the name of the release is also unique as it's the same as the Application name: foo-dev-frontend-{{branch}}-{{number}}
, this should also avoid conflicts on object names.
Configure ArgoCD to use secret for webhook
The last step is to protect the ArgoCD webhook with a password. The ArgoCD Helm chart allows setting a secret for the github webhook endpoint : configs.secret.githubSecret
.
Testing
Now it's time to test. For this step, you just need to make a modification in the source repository and create a pull request for this. Please wait until the container image is built. Then by adding the deploy
label a new ArgoCD app should be created :-)
Conclusion
This new ArgoCD feature is very interesting. Maybe it can help to have feedback on the pull requests to see the status of the review app. It can also be interesting to have the list of URLs available. ArgoCD already shows this information in the webUI. So maybe just a link to the ArgocD application is enough.
Camptocamp will participate in the KubeCon at Amsterdam from 18 to 21 april. Maybe we can meet there and discuss development workflows that really help developers.
Top comments (3)
One key thing is to use dynamic namespace (probably based on the name of repo and branch name) for the deployment. In scenarios multiple PR's on the same repo.
Can you shown in your helm chart example how to pass in like the
{{ .Release.Namespace }}
for each PR? ThanksHow did you handle posting the comment on the PR letting the user know the app was deployed?
Unfortunately it's not implemented in argocd. Argocd has rights to inspect le PR and it should be able to post comment. From my point of view it can be a nice improvement.