Manage permitted destination of an ArgoCD Project
This post talks about an ArgoCD feature that allows to select which namespaces are allowed to host ArgoCD Applications belonging to the same project.
At time of writing this post, ArgoCD latest version is 2.7.4.
What is an AppProject in ArgoCD
AppProject is a custom resource of ArgoCD that allows to group Apps logically. As you can read on the official documentation, three out of four features provided by AppProjects start with “restict where/what …”. It is not hard to understand that they are used to restrict the App “scope”, particularly when ArgoCD is used by many teams or tenants. For instance, Team A shouldn’t create Apps on namespaces of Team B.
Create an AppProject
I took this simple AppProject from official documentation that allows to create ArgoCD apps only on guestbook
namespace:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: guestbook-project
namespace: argocd
# Finalizer that ensures that project is not deleted until it is not referenced by any application
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
description: Example Project
# Allow manifests to deploy from any Git repos
sourceRepos:
- '*'
# Only permit applications to deploy to the guestbook namespace in the same cluster
destinations:
- namespace: guestbook
server: https://kubernetes.default.svc
Create the project from the manifest uploaded in my repository:
$ kubectl apply -f https://raw.githubusercontent.com/andregri/argocd-example-apps/master/01-project-permitted-destination/guestbook-project.yaml
appproject.argoproj.io/guestbook-project created
List the projects:
$ kubectl get appprojects -n argocd
NAME AGE
default 26d
guestbook-project 64s
Try to create an App in a namespace not permitted
Again, I took a simple App from the documentation, but this time I changed two attributes:
.spec.project: my-project
to create the App in the AppProjectmy-project
..spec.destination.namespace: default
to create the resource in thedefault
namespace, that it is different from the permitted namespaceguestbook
!
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: my-project
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: default
Create the App:
$ kubectl apply -f https://raw.githubusercontent.com/andregri/argocd-example-apps/master/01-project-permitted-destination/not-permitted-app.yaml
application.argoproj.io/guestbook created
List the apps:
$ kubectl get app -n argocd
NAME SYNC STATUS HEALTH STATUS
guestbook Unknown Unknown
The application is not synced because of an error. Get the status
of the resource:
$ kubectl get app guestbook -n argocd -o jsonpath="{.status}" | jq
{
"conditions": [
{
"lastTransitionTime": "2023-06-20T15:50:24Z",
"message": "application destination {https://kubernetes.default.svc default} is not permitted in project 'guestbook-project'",
"type": "InvalidSpecError"
}
],
"health": {
"status": "Unknown"
},
"sync": {
"status": "Unknown"
}
}
The App is not created because “application destination {https://kubernetes.default.svc default} is not permitted in project ‘guestbook-project’”.
Change the App destination namespace to a permitted namespace
Update the destination namespace in the App manifest with a permitted namespace:
.spec.destination.namespace: default
that is permitted by AppProjectguestbook-project
Create the namespace guestbook
if it doesn’t exist:
$ kubectl create ns guestbook
namespace/guestbook created
Apply the changes:
$ kubectl apply -f https://raw.githubusercontent.com/andregri/argocd-example-apps/master/01-project-permitted-destination/permitted-app.yaml
application.argoproj.io/guestbook configured
Check the App:
$ kubectl get app guestbook -n argocd
NAME SYNC STATUS HEALTH STATUS
guestbook OutOfSync Missing
This time the App was created because the SYNC STATUS
is OutOfSync
.
Overriding the destination namespace
Create two Apps in the guestbook
Project. The destination namespace of these Apps is default
, which is the permitted namespace by the project.
App 1 points to path guestbook/dev
of repo argocd-example-apps. However, the deployment provisioned by this App is specifying the namespace as well: namespace: guestbook-dev
.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook-dev
namespace: argocd
spec:
project: guestbook-project
source:
repoURL: https://github.com/andregri/argocd-example-apps.git
targetRevision: HEAD
path: guestbook/dev
destination:
server: https://kubernetes.default.svc
namespace: guestbook
App 2 points to path guestbook/prod
of repo argocd-example-apps. However, the deployment provisioned by this App is specifying the namespace as well: namespace: guestbook-prod
.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook-prod
namespace: argocd
spec:
project: guestbook-project
source:
repoURL: https://github.com/andregri/argocd-example-apps.git
targetRevision: HEAD
path: guestbook/prod
destination:
server: https://kubernetes.default.svc
namespace: guestbook
According to ArgoCD issue, the destination namespace defined in the App should be overridden by the namespace defined in the resource.
Create the namespaces defined in the Deployments:
$ kubectl create ns guestbook-dev
namespace/guestbook-dev created
$ kubectl create ns guestbook-prod
namespace/guestbook-prod created
Create the Apps:
$ kubectl apply -f https://raw.githubusercontent.com/andregri/argocd-example-apps/master/02-project-override-namespace/app-dev.yaml
application.argoproj.io/guestbook-dev created
$ kubectl apply -f https://raw.githubusercontent.com/andregri/argocd-example-apps/master/02-project-override-namespace/app-prod.yaml
application.argoproj.io/guestbook-prod created
Sync the apps with argocdcli:
$ argocd app sync guestbook-dev
...
GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
apps Deployment guestbook-dev guestbook-ui OutOfSync Missing namespace guestbook-dev is not permitted in project 'guestbook-project'
FATA[0001] Operation has completed with phase: Failed
The app sync failed because the deployment is overriding the destination namespace with a namespace that is not permitted by the project.
Update the project permitted namespaces:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: guestbook-project
namespace: argocd
# Finalizer that ensures that project is not deleted until it is not referenced by any application
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
description: Example Project
# Allow manifests to deploy from any Git repos
sourceRepos:
- '*'
# Only permit applications to deploy to the guestbook-dev namespace in the same cluster
destinations:
- namespace: guestbook-dev
server: https://kubernetes.default.svc
- namespace: guestbook-prod
server: https://kubernetes.default.svc
- namespace: guestbook
server: https://kubernetes.default.svc
Update the project:
$ kubectl apply -f https://raw.githubusercontent.com/andregri/argocd-example-apps/master/02-project-override-namespace/guestbook-project.yaml
appproject.argoproj.io/guestbook-project configured
Sync the app again:
$ argocd app sync guestbook-dev
$ argocd app sync guestbook-prod
Check that the deployment was created in the namespace override by the deployment:
$ kubectl get pods -n guestbook-dev
NAME READY STATUS RESTARTS AGE
guestbook-ui-b848d5d9d-fzdtf 1/1 Running 0 11m
$ kubectl get pods -n guestbook-prod
NAME READY STATUS RESTARTS AGE
guestbook-ui-b848d5d9d-srmq4 1/1 Running 0 41s
Conclusion
- Applications in ArgoCD allows to define a destination namespace for the Kubernetes resources to deploy
- ApplicationProject in ArgoCD permits to deploy App resources only in the namespace defined in
.destination.namespace
- If the kubernetes resource defines explicitly a namespace, then it overrides the Application destination namespace.
Resources
- https://argo-cd.readthedocs.io/en/stable/user-guide/projects/#managing-projects
- https://github.com/argoproj/argo-cd/pull/8383/files → # The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace
Leave a comment