Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tls cert mount appset controller #985

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/v1beta1/argocd_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ type ArgoCDApplicationSet struct {
LogLevel string `json:"logLevel,omitempty"`

WebhookServer WebhookServerSpec `json:"webhookServer,omitempty"`

// SCMRootCAConfigMap is the name of the config map that stores the Gitlab SCM Provider's TLS certificate which will be mounted on the ApplicationSet Controller (optional).
SCMRootCAConfigMap string `json:"scmRootCAConfigMap,omitempty"`
}

// ArgoCDCASpec defines the CA options for ArgCD.
Expand Down
5 changes: 5 additions & 0 deletions bundle/manifests/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6653,6 +6653,11 @@ spec:
to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
scmRootCAConfigMap:
description: SCMRootCAConfigMap is the name of the config map
that stores the Gitlab SCM Provider's TLS certificate which
will be mounted on the ApplicationSet Controller (optional).
type: string
version:
description: Version is the Argo CD ApplicationSet image tag.
(optional)
Expand Down
3 changes: 3 additions & 0 deletions common/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ const (
// ArgoCDTLSCertsConfigMapName is the upstream hard-coded TLS certificate data ConfigMap name.
ArgoCDTLSCertsConfigMapName = "argocd-tls-certs-cm"

// ArgoCDAppSetGitlabSCMTLSCertsConfigMapName is the hard-coded ApplicationSet Gitlab SCM TLS certificate data ConfigMap name.
ArgoCDAppSetGitlabSCMTLSCertsConfigMapName = "argocd-appset-gitlab-scm-tls-certs-cm"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason for going with config map and not a secret ?

Copy link
Collaborator Author

@ishitasequeira ishitasequeira Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No specific reason, followed what we have used for TLS certs.


// ArgoCDRedisServerTLSSecretName is the name of the TLS secret for the redis-server
ArgoCDRedisServerTLSSecretName = "argocd-operator-redis-tls"

Expand Down
5 changes: 5 additions & 0 deletions config/crd/bases/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6644,6 +6644,11 @@ spec:
to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
scmRootCAConfigMap:
description: SCMRootCAConfigMap is the name of the config map
that stores the Gitlab SCM Provider's TLS certificate which
will be mounted on the ApplicationSet Controller (optional).
type: string
version:
description: Version is the Argo CD ApplicationSet image tag.
(optional)
Expand Down
39 changes: 36 additions & 3 deletions controllers/argocd/applicationset.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ import (
"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
)

const (
ApplicationSetGitlabSCMTlsCertPath = "/app/tls/scm/cert"
)

// getArgoApplicationSetCommand will return the command for the ArgoCD ApplicationSet component.
func getArgoApplicationSetCommand(cr *argoproj.ArgoCD) []string {
cmd := make([]string, 0)
Expand All @@ -46,6 +50,11 @@ func getArgoApplicationSetCommand(cr *argoproj.ArgoCD) []string {
cmd = append(cmd, "--loglevel")
cmd = append(cmd, getLogLevel(cr.Spec.ApplicationSet.LogLevel))

if cr.Spec.ApplicationSet.SCMRootCAConfigMap != "" {
cmd = append(cmd, "--scm-root-ca-path")
cmd = append(cmd, ApplicationSetGitlabSCMTlsCertPath)
}

// ApplicationSet command arguments provided by the user
extraArgs := cr.Spec.ApplicationSet.ExtraCommandArgs
err := isMergable(extraArgs, cmd)
Expand Down Expand Up @@ -144,9 +153,26 @@ func (r *ReconcileArgoCD) reconcileApplicationSetDeployment(cr *argoproj.ArgoCD,
},
},
}
addSCMGitlabVolumeMount := false
if scmRootCAConfigMapName := getSCMRootCAConfigMapName(cr); scmRootCAConfigMapName != "" {
cm := newConfigMapWithName(scmRootCAConfigMapName, cr)
if argoutil.IsObjectFound(r.Client, cr.Namespace, cr.Spec.ApplicationSet.SCMRootCAConfigMap, cm) {
addSCMGitlabVolumeMount = true
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{
Name: "appset-gitlab-scm-tls-cert",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName,
},
},
},
})
}
}

podSpec.Containers = []corev1.Container{
applicationSetContainer(cr),
applicationSetContainer(cr, addSCMGitlabVolumeMount),
}
AddSeccompProfileForOpenShift(r.Client, podSpec)

Expand Down Expand Up @@ -185,7 +211,7 @@ func (r *ReconcileArgoCD) reconcileApplicationSetDeployment(cr *argoproj.ArgoCD,

}

func applicationSetContainer(cr *argoproj.ArgoCD) corev1.Container {
func applicationSetContainer(cr *argoproj.ArgoCD, addSCMGitlabVolumeMount bool) corev1.Container {
// Global proxy env vars go first
appSetEnv := []corev1.EnvVar{{
Name: "NAMESPACE",
Expand All @@ -202,7 +228,7 @@ func applicationSetContainer(cr *argoproj.ArgoCD) corev1.Container {
// Environment specified in the CR take precedence over everything else
appSetEnv = argoutil.EnvMerge(appSetEnv, proxyEnvVars(), false)

return corev1.Container{
container := corev1.Container{
Command: getArgoApplicationSetCommand(cr),
Env: appSetEnv,
Image: getApplicationSetContainerImage(cr),
Expand Down Expand Up @@ -252,6 +278,13 @@ func applicationSetContainer(cr *argoproj.ArgoCD) corev1.Container {
RunAsNonRoot: boolPtr(true),
},
}
if addSCMGitlabVolumeMount {
container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{
Name: "appset-gitlab-scm-tls-cert",
MountPath: ApplicationSetGitlabSCMTlsCertPath,
})
}
return container
}

func (r *ReconcileArgoCD) reconcileApplicationSetServiceAccount(cr *argoproj.ArgoCD) (*corev1.ServiceAccount, error) {
Expand Down
36 changes: 30 additions & 6 deletions controllers/argocd/applicationset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"

argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
Expand Down Expand Up @@ -92,14 +93,14 @@ func TestReconcileApplicationSet_CreateDeployments(t *testing.T) {
deployment))

// Ensure the created Deployment has the expected properties
checkExpectedDeploymentValues(t, deployment, &sa, a)
checkExpectedDeploymentValues(t, r, deployment, &sa, a)
}

func checkExpectedDeploymentValues(t *testing.T, deployment *appsv1.Deployment, sa *corev1.ServiceAccount, a *argoproj.ArgoCD) {
func checkExpectedDeploymentValues(t *testing.T, r *ReconcileArgoCD, deployment *appsv1.Deployment, sa *corev1.ServiceAccount, a *argoproj.ArgoCD) {
assert.Equal(t, deployment.Spec.Template.Spec.ServiceAccountName, sa.ObjectMeta.Name)
appsetAssertExpectedLabels(t, &deployment.ObjectMeta)

want := []corev1.Container{applicationSetContainer(a)}
want := []corev1.Container{applicationSetContainer(a, false)}

if diff := cmp.Diff(want, deployment.Spec.Template.Spec.Containers); diff != "" {
t.Fatalf("failed to reconcile applicationset-controller deployment containers:\n%s", diff)
Expand Down Expand Up @@ -150,6 +151,19 @@ func checkExpectedDeploymentValues(t *testing.T, deployment *appsv1.Deployment,
},
}

if a.Spec.ApplicationSet.SCMRootCAConfigMap != "" && argoutil.IsObjectFound(r.Client, a.Namespace, common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName, a) {
volumes = append(volumes, corev1.Volume{
Name: "appset-gitlab-scm-tls-cert",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName,
},
},
},
})
}

if diff := cmp.Diff(volumes, deployment.Spec.Template.Spec.Volumes); diff != "" {
t.Fatalf("failed to reconcile applicationset-controller deployment volumes:\n%s", diff)
}
Expand Down Expand Up @@ -261,7 +275,7 @@ func TestReconcileApplicationSet_UpdateExistingDeployments(t *testing.T) {
deployment))

// Ensure the updated Deployment has the expected properties
checkExpectedDeploymentValues(t, deployment, &sa, a)
checkExpectedDeploymentValues(t, r, deployment, &sa, a)

}

Expand All @@ -287,7 +301,7 @@ func TestReconcileApplicationSet_Deployments_resourceRequirements(t *testing.T)
assert.Equal(t, deployment.Spec.Template.Spec.ServiceAccountName, sa.ObjectMeta.Name)
appsetAssertExpectedLabels(t, &deployment.ObjectMeta)

containerWant := []corev1.Container{applicationSetContainer(a)}
containerWant := []corev1.Container{applicationSetContainer(a, false)}

if diff := cmp.Diff(containerWant, deployment.Spec.Template.Spec.Containers); diff != "" {
t.Fatalf("failed to reconcile argocd-server deployment:\n%s", diff)
Expand Down Expand Up @@ -346,6 +360,14 @@ func TestReconcileApplicationSet_Deployments_SpecOverride(t *testing.T) {
envVars: map[string]string{common.ArgoCDImageEnvName: "custom-env-image"},
expectedContainerImage: "custom-image:custom-version",
},
{
name: "ensure scm tls cert mount is present",
appSetField: &argoproj.ArgoCDApplicationSet{
SCMRootCAConfigMap: "test-scm-tls-mount",
},
envVars: map[string]string{common.ArgoCDImageEnvName: "custom-env-image"},
expectedContainerImage: "custom-env-image",
},
}

for _, test := range tests {
Expand All @@ -357,6 +379,8 @@ func TestReconcileApplicationSet_Deployments_SpecOverride(t *testing.T) {

a := makeTestArgoCD()
r := makeTestReconciler(t, a)
cm := newConfigMapWithName(getCAConfigMapName(a), a)
r.Client.Create(context.Background(), cm, &client.CreateOptions{})

a.Spec.ApplicationSet = test.appSetField

Expand All @@ -374,7 +398,7 @@ func TestReconcileApplicationSet_Deployments_SpecOverride(t *testing.T) {

specImage := deployment.Spec.Template.Spec.Containers[0].Image
assert.Equal(t, test.expectedContainerImage, specImage)

checkExpectedDeploymentValues(t, r, deployment, &sa, a)
})
}

Expand Down
2 changes: 1 addition & 1 deletion controllers/argocd/argocd_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,6 @@ func (r *ReconcileArgoCD) Reconcile(ctx context.Context, request ctrl.Request) (
// SetupWithManager sets up the controller with the Manager.
func (r *ReconcileArgoCD) SetupWithManager(mgr ctrl.Manager) error {
bldr := ctrl.NewControllerManagedBy(mgr)
r.setResourceWatches(bldr, r.clusterResourceMapper, r.tlsSecretMapper, r.namespaceResourceMapper, r.clusterSecretResourceMapper)
r.setResourceWatches(bldr, r.clusterResourceMapper, r.tlsSecretMapper, r.namespaceResourceMapper, r.clusterSecretResourceMapper, r.applicationSetSCMTLSConfigMapMapper)
return bldr.Complete(r)
}
8 changes: 8 additions & 0 deletions controllers/argocd/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ func getCAConfigMapName(cr *argoproj.ArgoCD) string {
return nameWithSuffix(common.ArgoCDCASuffix, cr)
}

// getSCMRootCAConfigMapName will return the SCMRootCA ConfigMap name for the given ArgoCD ApplicationSet Controller.
func getSCMRootCAConfigMapName(cr *argoproj.ArgoCD) string {
if cr.Spec.ApplicationSet.SCMRootCAConfigMap != "" && len(cr.Spec.ApplicationSet.SCMRootCAConfigMap) > 0 {
return cr.Spec.ApplicationSet.SCMRootCAConfigMap
}
return ""
}

// getConfigManagementPlugins will return the config management plugins for the given ArgoCD.
func getConfigManagementPlugins(cr *argoproj.ArgoCD) string {
plugins := common.ArgoCDDefaultConfigManagementPlugins
Expand Down
28 changes: 28 additions & 0 deletions controllers/argocd/custommapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,31 @@ func (r *ReconcileArgoCD) clusterSecretResourceMapper(o client.Object) []reconci

return result
}

// applicationSetSCMTLSConfigMapMapper maps a watch event on a configmap with name "argocd-appset-gitlab-scm-tls-certs-cm",
// back to the ArgoCD object that we want to reconcile.
func (r *ReconcileArgoCD) applicationSetSCMTLSConfigMapMapper(o client.Object) []reconcile.Request {
var result = []reconcile.Request{}

if o.GetName() == common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName {
argocds := &argoproj.ArgoCDList{}
if err := r.Client.List(context.TODO(), argocds, &client.ListOptions{Namespace: o.GetNamespace()}); err != nil {
return result
}

if len(argocds.Items) != 1 {
return result
}

argocd := argocds.Items[0]
namespacedName := client.ObjectKey{
Name: argocd.Name,
Namespace: argocd.Namespace,
}
result = []reconcile.Request{
{NamespacedName: namespacedName},
}
}

return result
}
8 changes: 7 additions & 1 deletion controllers/argocd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ func removeString(slice []string, s string) []string {
}

// setResourceWatches will register Watches for each of the supported Resources.
func (r *ReconcileArgoCD) setResourceWatches(bldr *builder.Builder, clusterResourceMapper, tlsSecretMapper, namespaceResourceMapper, clusterSecretResourceMapper handler.MapFunc) *builder.Builder {
func (r *ReconcileArgoCD) setResourceWatches(bldr *builder.Builder, clusterResourceMapper, tlsSecretMapper, namespaceResourceMapper, clusterSecretResourceMapper, applicationSetGitlabSCMTLSConfigMapMapper handler.MapFunc) *builder.Builder {

deploymentConfigPred := predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
Expand Down Expand Up @@ -1046,12 +1046,18 @@ func (r *ReconcileArgoCD) setResourceWatches(bldr *builder.Builder, clusterResou

clusterSecretResourceHandler := handler.EnqueueRequestsFromMapFunc(clusterSecretResourceMapper)

appSetGitlabSCMTLSConfigMapHandler := handler.EnqueueRequestsFromMapFunc(applicationSetGitlabSCMTLSConfigMapMapper)

tlsSecretHandler := handler.EnqueueRequestsFromMapFunc(tlsSecretMapper)

bldr.Watches(&source.Kind{Type: &v1.ClusterRoleBinding{}}, clusterResourceHandler)

bldr.Watches(&source.Kind{Type: &v1.ClusterRole{}}, clusterResourceHandler)

bldr.Watches(&source.Kind{Type: &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName,
}}}, appSetGitlabSCMTLSConfigMapHandler)

// Watch for secrets of type TLS that might be created by external processes
bldr.Watches(&source.Kind{Type: &corev1.Secret{Type: corev1.SecretTypeTLS}}, tlsSecretHandler)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6653,6 +6653,11 @@ spec:
to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
scmRootCAConfigMap:
description: SCMRootCAConfigMap is the name of the config map
that stores the Gitlab SCM Provider's TLS certificate which
will be mounted on the ApplicationSet Controller (optional).
type: string
version:
description: Version is the Argo CD ApplicationSet image tag.
(optional)
Expand Down
19 changes: 19 additions & 0 deletions docs/reference/argocd.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Resources | [Empty] | The container compute resources.
LogLevel | info | The log level to be used by the ArgoCD Application Controller component. Valid options are debug, info, error, and warn.
LogFormat | text | The log format to be used by the ArgoCD Application Controller component. Valid options are text or json.
ParallelismLimit | 10 | The kubectl parallelism limit to set for the controller (`--kubectl-parallelism-limit` flag)
SCMRootCAConfigMap (#add-tls-certificate-for-gitlab-scm-provider-to-applicationsets-controller) | [Empty] | The name of the config map that stores the Gitlab SCM Provider's TLS certificate which will be mounted on the ApplicationSet Controller at `"/app/tls/scm/cert"` path.

### ApplicationSet Controller Example

Expand Down Expand Up @@ -119,6 +120,24 @@ spec:
- bar
```

### Add Self signed TLS Certificate for Gitlab SCM Provider to ApplicationSets Controller

ApplicationSetController added a new option `--scm-root-ca-path` and expects the self-signed TLS certificate to be mounted on the path specified and to be used for Gitlab SCM Provider and Gitlab Pull Request Provider. To set this option, you can store the certificate in the config map and specify the config map name using `spec.applicationSet.SCMRootCAConfigMap` in ArgoCD CR. When the parameter `spec.applicationSet.SCMRootCAConfigMap` is set in ArgoCD CR, the operator checks for ConfigMap in the same namespace as the ArgoCD instance and mounts the Certificate stored in ConfigMap to ApplicationSet Controller pods at the path `/app/tls/scm/cert`.

Below example shows how a user can add scmRootCaPath to the ApplicationSet controller.
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
name: example-argocd
labels:
example: applicationset
spec:
applicationSet:
SCMRootCAConfigMap: example-gitlab-scm-tls-cert
```


## Config Management Plugins

Configuration to add a config management plugin. This property maps directly to the `configManagementPlugins` field in the `argocd-cm` ConfigMap.
Expand Down
Loading
Loading