From f127b2e083371cbde50890647f925dce5d48e7fc Mon Sep 17 00:00:00 2001 From: rishabh625 <43094970+rishabh625@users.noreply.github.com> Date: Wed, 17 Aug 2022 17:51:01 +0530 Subject: [PATCH] feat: Added routes,ingress,svc code for applicationset (#677) * Added routes,ingress,svc code Signed-off-by: rishabh625 * Added svc ingress route logic Signed-off-by: rishabh625 * Added enable ingress in test Signed-off-by: rishabh625 * Added correct version of operator Signed-off-by: rishabh625 * ran make bundle * Applied reviewer suggestions, removed ingress for metrics * Applied reviewer suggestions, removed ingress for metrics * Removed unwanted code * fixed make generate * Add kuttl test for applicationset * Renamed kuttl test file * corrected names * fix kuttl test * Update api/v1alpha1/argocd_types.go changed json from webhookSpec to webhookServer Co-authored-by: jannfis * applied reviewers suggestion Signed-off-by: rishabh625 * ran make bundle * Added test case to disable ingress * Added test case to disable ingress * fixed patch command * fixed removed TestAssert from errors kuttl file * corrected filename and patch * corrected test * corrected text for applicationset webhook * ran make bundle * merged master branch and resolved conflicts Signed-off-by: rishabh625 * ran make bundle Signed-off-by: rishabh625 Co-authored-by: jannfis --- api/v1alpha1/argocd_types.go | 16 +++ api/v1alpha1/zz_generated.deepcopy.go | 18 +++ ...argocd-operator.clusterserviceversion.yaml | 22 +++ bundle/manifests/argoproj.io_argocds.yaml | 136 ++++++++++++++++++ common/values.go | 3 + config/crd/bases/argoproj.io_argocds.yaml | 136 ++++++++++++++++++ ...argocd-operator.clusterserviceversion.yaml | 22 +++ controllers/argocd/applicationset.go | 53 +++++++ controllers/argocd/applicationset_test.go | 11 ++ controllers/argocd/ingress.go | 69 +++++++++ controllers/argocd/ingress_test.go | 25 +++- controllers/argocd/route.go | 82 +++++++++++ controllers/argocd/util.go | 9 ++ deploy/catalog_source.yaml | 9 -- ...operator.v0.4.0.clusterserviceversion.yaml | 22 +++ .../0.4.0/argoproj.io_argocds.yaml | 133 +++++++++++++++++ ...operator.v0.5.0.clusterserviceversion.yaml | 22 +++ .../0.5.0/argoproj.io_argocds.yaml | 136 ++++++++++++++++++ .../05-applicationset.yaml | 20 +++ tests/k8s/1-001_validate_basic/05-assert.yaml | 42 ++++++ .../06-disable-applicationset-ingress.yaml | 7 + tests/k8s/1-001_validate_basic/06-errors.yaml | 4 + 22 files changed, 984 insertions(+), 13 deletions(-) delete mode 100644 deploy/catalog_source.yaml create mode 100644 tests/k8s/1-001_validate_basic/05-applicationset.yaml create mode 100644 tests/k8s/1-001_validate_basic/05-assert.yaml create mode 100644 tests/k8s/1-001_validate_basic/06-disable-applicationset-ingress.yaml create mode 100644 tests/k8s/1-001_validate_basic/06-errors.yaml diff --git a/api/v1alpha1/argocd_types.go b/api/v1alpha1/argocd_types.go index c84d9790c..c9ac7e14a 100644 --- a/api/v1alpha1/argocd_types.go +++ b/api/v1alpha1/argocd_types.go @@ -131,6 +131,8 @@ type ArgoCDApplicationSet struct { // LogLevel describes the log level that should be used by the ApplicationSet controller. Defaults to ArgoCDDefaultLogLevel if not set. Valid options are debug,info, error, and warn. LogLevel string `json:"logLevel,omitempty"` + + WebhookServer WebhookServerSpec `json:"webhookServer,omitempty"` } // ArgoCDCASpec defines the CA options for ArgCD. @@ -830,6 +832,20 @@ type SSHHostsSpec struct { Keys string `json:"keys,omitempty"` } +// WebhookServerSpec defines the options for the ApplicationSet Webhook Server component. +type WebhookServerSpec struct { + + // Host is the hostname to use for Ingress/Route resources. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Host",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server","urn:alm:descriptor:com.tectonic.ui:text"} + Host string `json:"host,omitempty"` + + // Ingress defines the desired state for an Ingress for the Application set webhook component. + Ingress ArgoCDIngressSpec `json:"ingress,omitempty"` + + // Route defines the desired state for an OpenShift Route for the Application set webhook component. + Route ArgoCDRouteSpec `json:"route,omitempty"` +} + // IsDeletionFinalizerPresent checks if the instance has deletion finalizer func (argocd *ArgoCD) IsDeletionFinalizerPresent() bool { for _, finalizer := range argocd.GetFinalizers() { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index fd62289cc..35d5d89c0 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -129,6 +129,7 @@ func (in *ArgoCDApplicationSet) DeepCopyInto(out *ArgoCDApplicationSet) { *out = new(v1.ResourceRequirements) (*in).DeepCopyInto(*out) } + in.WebhookServer.DeepCopyInto(&out.WebhookServer) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoCDApplicationSet. @@ -996,3 +997,20 @@ func (in *SSHHostsSpec) DeepCopy() *SSHHostsSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookServerSpec) DeepCopyInto(out *WebhookServerSpec) { + *out = *in + in.Ingress.DeepCopyInto(&out.Ingress) + in.Route.DeepCopyInto(&out.Route) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookServerSpec. +func (in *WebhookServerSpec) DeepCopy() *WebhookServerSpec { + if in == nil { + return nil + } + out := new(WebhookServerSpec) + in.DeepCopyInto(out) + return out +} diff --git a/bundle/manifests/argocd-operator.clusterserviceversion.yaml b/bundle/manifests/argocd-operator.clusterserviceversion.yaml index 431ac9546..59fb1b2a1 100644 --- a/bundle/manifests/argocd-operator.clusterserviceversion.yaml +++ b/bundle/manifests/argocd-operator.clusterserviceversion.yaml @@ -301,6 +301,28 @@ spec: x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text - urn:alm:descriptor:com.tectonic.ui:advanced + - description: Host is the hostname to use for Ingress/Route resources. + displayName: Host + path: applicationSet.webhookServer.host + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:text + - description: Enabled will toggle the creation of the Ingress. + displayName: Ingress Enabled' + path: applicationSet.webhookServer.ingress.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Enabled will toggle the creation of the OpenShift Route. + displayName: Route Enabled' + path: applicationSet.webhookServer.route.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: ConfigManagementPlugins is used to specify additional config management plugins. displayName: Config Management Plugins' diff --git a/bundle/manifests/argoproj.io_argocds.yaml b/bundle/manifests/argoproj.io_argocds.yaml index 0e6e16189..ff9379d6b 100644 --- a/bundle/manifests/argoproj.io_argocds.yaml +++ b/bundle/manifests/argoproj.io_argocds.yaml @@ -81,6 +81,142 @@ spec: description: Version is the Argo CD ApplicationSet image tag. (optional) type: string + webhookServer: + description: WebhookServerSpec defines the options for the ApplicationSet + Webhook Server component. + properties: + host: + description: Host is the hostname to use for Ingress/Route + resources. + type: string + ingress: + description: Ingress defines the desired state for an Ingress + for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + apply to the Ingress. + type: object + enabled: + description: Enabled will toggle the creation of the Ingress. + type: boolean + ingressClassName: + description: IngressClassName for the Ingress resource. + type: string + path: + description: Path used for the Ingress resource. + type: string + tls: + description: TLS configuration. Currently the Ingress + only supports a single TLS port, 443. If multiple members + of this list specify different hosts, they will be multiplexed + on the same port according to the hostname specified + through the SNI TLS extension, if the ingress controller + fulfilling the ingress supports SNI. + items: + description: IngressTLS describes the transport layer + security associated with an Ingress. + properties: + hosts: + description: Hosts are a list of hosts included + in the TLS certificate. The values in this list + must match the name/s used in the tlsSecret. Defaults + to the wildcard host setting for the loadbalancer + controller fulfilling this Ingress, if left unspecified. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: SecretName is the name of the secret + used to terminate TLS traffic on port 443. Field + is left optional to allow TLS routing based on + SNI hostname alone. If the SNI host in a listener + conflicts with the "Host" header field used by + an IngressRule, the SNI host is used for termination + and value of the Host header is used for routing. + type: string + type: object + type: array + required: + - enabled + type: object + route: + description: Route defines the desired state for an OpenShift + Route for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + use for the Route resource. + type: object + enabled: + description: Enabled will toggle the creation of the OpenShift + Route. + type: boolean + labels: + additionalProperties: + type: string + description: Labels is the map of labels to use for the + Route resource + type: object + path: + description: Path the router watches for, to route traffic + for to the service. + type: string + tls: + description: TLS provides the ability to configure certificates + and termination for the Route. + properties: + caCertificate: + description: caCertificate provides the cert authority + certificate contents + type: string + certificate: + description: certificate provides certificate contents + type: string + destinationCACertificate: + description: destinationCACertificate provides the + contents of the ca certificate of the final destination. When + using reencrypt termination this file should be + provided in order to have routers use it for health + checks on the secure connection. If this field is + not specified, the router may provide its own destination + CA and perform hostname validation using the short + service name (service.namespace.svc), which allows + infrastructure generated certificates to automatically + verify. + type: string + insecureEdgeTerminationPolicy: + description: "insecureEdgeTerminationPolicy indicates + the desired behavior for insecure connections to + a route. While each router may make its own decisions + on which ports to expose, this is normally port + 80. \n * Allow - traffic is sent to the server on + the insecure port (default) * Disable - no traffic + is allowed on the insecure port. * Redirect - clients + are redirected to the secure port." + type: string + key: + description: key provides key file contents + type: string + termination: + description: termination indicates termination type. + type: string + required: + - termination + type: object + wildcardPolicy: + description: WildcardPolicy if any for the route. Currently + only 'Subdomain' or 'None' is allowed. + type: string + required: + - enabled + type: object + type: object type: object banner: description: Banner defines an additional banner to be displayed in diff --git a/common/values.go b/common/values.go index 1613c2551..da6707919 100644 --- a/common/values.go +++ b/common/values.go @@ -85,4 +85,7 @@ const ( // ArgoCDServerTLSSecretName is the name of the TLS secret for the argocd-server ArgoCDServerTLSSecretName = "argocd-server-tls" + + //ApplicationSetServiceNameSuffix is the suffix for Apllication Set Controller Service + ApplicationSetServiceNameSuffix = "applicationset-controller" ) diff --git a/config/crd/bases/argoproj.io_argocds.yaml b/config/crd/bases/argoproj.io_argocds.yaml index 22da49a7f..03f2eddc7 100644 --- a/config/crd/bases/argoproj.io_argocds.yaml +++ b/config/crd/bases/argoproj.io_argocds.yaml @@ -83,6 +83,142 @@ spec: description: Version is the Argo CD ApplicationSet image tag. (optional) type: string + webhookServer: + description: WebhookServerSpec defines the options for the ApplicationSet + Webhook Server component. + properties: + host: + description: Host is the hostname to use for Ingress/Route + resources. + type: string + ingress: + description: Ingress defines the desired state for an Ingress + for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + apply to the Ingress. + type: object + enabled: + description: Enabled will toggle the creation of the Ingress. + type: boolean + ingressClassName: + description: IngressClassName for the Ingress resource. + type: string + path: + description: Path used for the Ingress resource. + type: string + tls: + description: TLS configuration. Currently the Ingress + only supports a single TLS port, 443. If multiple members + of this list specify different hosts, they will be multiplexed + on the same port according to the hostname specified + through the SNI TLS extension, if the ingress controller + fulfilling the ingress supports SNI. + items: + description: IngressTLS describes the transport layer + security associated with an Ingress. + properties: + hosts: + description: Hosts are a list of hosts included + in the TLS certificate. The values in this list + must match the name/s used in the tlsSecret. Defaults + to the wildcard host setting for the loadbalancer + controller fulfilling this Ingress, if left unspecified. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: SecretName is the name of the secret + used to terminate TLS traffic on port 443. Field + is left optional to allow TLS routing based on + SNI hostname alone. If the SNI host in a listener + conflicts with the "Host" header field used by + an IngressRule, the SNI host is used for termination + and value of the Host header is used for routing. + type: string + type: object + type: array + required: + - enabled + type: object + route: + description: Route defines the desired state for an OpenShift + Route for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + use for the Route resource. + type: object + enabled: + description: Enabled will toggle the creation of the OpenShift + Route. + type: boolean + labels: + additionalProperties: + type: string + description: Labels is the map of labels to use for the + Route resource + type: object + path: + description: Path the router watches for, to route traffic + for to the service. + type: string + tls: + description: TLS provides the ability to configure certificates + and termination for the Route. + properties: + caCertificate: + description: caCertificate provides the cert authority + certificate contents + type: string + certificate: + description: certificate provides certificate contents + type: string + destinationCACertificate: + description: destinationCACertificate provides the + contents of the ca certificate of the final destination. When + using reencrypt termination this file should be + provided in order to have routers use it for health + checks on the secure connection. If this field is + not specified, the router may provide its own destination + CA and perform hostname validation using the short + service name (service.namespace.svc), which allows + infrastructure generated certificates to automatically + verify. + type: string + insecureEdgeTerminationPolicy: + description: "insecureEdgeTerminationPolicy indicates + the desired behavior for insecure connections to + a route. While each router may make its own decisions + on which ports to expose, this is normally port + 80. \n * Allow - traffic is sent to the server on + the insecure port (default) * Disable - no traffic + is allowed on the insecure port. * Redirect - clients + are redirected to the secure port." + type: string + key: + description: key provides key file contents + type: string + termination: + description: termination indicates termination type. + type: string + required: + - termination + type: object + wildcardPolicy: + description: WildcardPolicy if any for the route. Currently + only 'Subdomain' or 'None' is allowed. + type: string + required: + - enabled + type: object + type: object type: object banner: description: Banner defines an additional banner to be displayed in diff --git a/config/manifests/bases/argocd-operator.clusterserviceversion.yaml b/config/manifests/bases/argocd-operator.clusterserviceversion.yaml index 34034767e..88db44a00 100644 --- a/config/manifests/bases/argocd-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/argocd-operator.clusterserviceversion.yaml @@ -175,6 +175,28 @@ spec: x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text - urn:alm:descriptor:com.tectonic.ui:advanced + - description: Host is the hostname to use for Ingress/Route resources. + displayName: Host + path: applicationSet.webhookServer.host + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:text + - description: Enabled will toggle the creation of the Ingress. + displayName: Ingress Enabled' + path: applicationSet.webhookServer.ingress.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Enabled will toggle the creation of the OpenShift Route. + displayName: Route Enabled' + path: applicationSet.webhookServer.route.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: ConfigManagementPlugins is used to specify additional config management plugins. displayName: Config Management Plugins' diff --git a/controllers/argocd/applicationset.go b/controllers/argocd/applicationset.go index f1daa4fea..a9beecf5b 100644 --- a/controllers/argocd/applicationset.go +++ b/controllers/argocd/applicationset.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" argoprojv1a1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1" @@ -72,6 +73,11 @@ func (r *ReconcileArgoCD) reconcileApplicationSetController(cr *argoprojv1a1.Arg return err } + log.Info("reconciling applicationset service") + if err := r.reconcileApplicationSetService(cr); err != nil { + return err + } + return nil } @@ -455,3 +461,50 @@ func setAppSetLabels(obj *metav1.ObjectMeta) { obj.Labels["app.kubernetes.io/part-of"] = "argocd-applicationset" obj.Labels["app.kubernetes.io/component"] = "controller" } + +// reconcileApplicationSetService will ensure that the Service is present for the ApplicationSet webhook and metrics component. +func (r *ReconcileArgoCD) reconcileApplicationSetService(cr *argoprojv1a1.ArgoCD) error { + log.Info("reconciling applicationset service") + + svc := newServiceWithSuffix(common.ApplicationSetServiceNameSuffix, common.ApplicationSetServiceNameSuffix, cr) + if cr.Spec.ApplicationSet == nil { + + if argoutil.IsObjectFound(r.Client, cr.Namespace, svc.Name, svc) { + err := argoutil.FetchObject(r.Client, cr.Namespace, svc.Name, svc) + if err != nil { + return err + } + log.Info(fmt.Sprintf("Deleting applicationset controller service %s as applicationset is disabled", svc.Name)) + err = r.Delete(context.TODO(), svc) + if err != nil { + return err + } + } + } else { + if argoutil.IsObjectFound(r.Client, cr.Namespace, svc.Name, svc) { + return nil // Service found, do nothing + } + } + svc.Spec.Ports = []corev1.ServicePort{ + { + Name: "webhook", + Port: 7000, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt(7000), + }, { + Name: "metrics", + Port: 8080, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt(8080), + }, + } + + svc.Spec.Selector = map[string]string{ + common.ArgoCDKeyName: nameWithSuffix(common.ApplicationSetServiceNameSuffix, cr), + } + + if err := controllerutil.SetControllerReference(cr, svc, r.Scheme); err != nil { + return err + } + return r.Client.Create(context.TODO(), svc) +} diff --git a/controllers/argocd/applicationset_test.go b/controllers/argocd/applicationset_test.go index 9b1e9d12d..b99bba014 100644 --- a/controllers/argocd/applicationset_test.go +++ b/controllers/argocd/applicationset_test.go @@ -486,3 +486,14 @@ func setProxyEnvVars(t *testing.T) { t.Setenv("HTTP_PROXY", "http://example.com") t.Setenv("NO_PROXY", ".cluster.local") } + +func TestReconcileApplicationSet_Service(t *testing.T) { + logf.SetLogger(ZapLogger(true)) + a := makeTestArgoCD() + r := makeTestReconciler(t, a) + + s := newServiceWithSuffix(common.ApplicationSetServiceNameSuffix, common.ApplicationSetServiceNameSuffix, a) + + assert.NoError(t, r.reconcileApplicationSetService(a)) + assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Namespace: s.Namespace, Name: s.Name}, s)) +} diff --git a/controllers/argocd/ingress.go b/controllers/argocd/ingress.go index 903454ae0..f28596b36 100644 --- a/controllers/argocd/ingress.go +++ b/controllers/argocd/ingress.go @@ -81,6 +81,11 @@ func (r *ReconcileArgoCD) reconcileIngresses(cr *argoprojv1a1.ArgoCD) error { if err := r.reconcilePrometheusIngress(cr); err != nil { return err } + + if err := r.reconcileApplicationSetControllerIngress(cr); err != nil { + return err + } + return nil } @@ -385,3 +390,67 @@ func (r *ReconcileArgoCD) reconcilePrometheusIngress(cr *argoprojv1a1.ArgoCD) er } return r.Client.Create(context.TODO(), ingress) } + +// reconcileApplicationSetControllerIngress will ensure that the ApplicationSetController Ingress is present. +func (r *ReconcileArgoCD) reconcileApplicationSetControllerIngress(cr *argoprojv1a1.ArgoCD) error { + ingress := newIngressWithSuffix(common.ApplicationSetServiceNameSuffix, cr) + if argoutil.IsObjectFound(r.Client, cr.Namespace, ingress.Name, ingress) { + if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Ingress.Enabled { + return r.Client.Delete(context.TODO(), ingress) + } + return nil // Ingress found and enabled, do nothing + } + + if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Ingress.Enabled { + log.Info("not enabled") + return nil // Ingress not enabled, move along... + } + + // Add annotations + atns := make(map[string]string) + atns[common.ArgoCDKeyIngressSSLRedirect] = "true" + atns[common.ArgoCDKeyIngressBackendProtocol] = "HTTP" + + // Override default annotations if specified + if len(cr.Spec.ApplicationSet.WebhookServer.Ingress.Annotations) > 0 { + atns = cr.Spec.ApplicationSet.WebhookServer.Ingress.Annotations + } + + ingress.ObjectMeta.Annotations = atns + + pathType := networkingv1.PathTypeImplementationSpecific + // Add rules + ingress.Spec.Rules = []networkingv1.IngressRule{ + { + Host: getApplicationSetHTTPServerHost(cr), + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/api/webhook", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: nameWithSuffix(common.ApplicationSetServiceNameSuffix, cr), + Port: networkingv1.ServiceBackendPort{ + Name: "webhook", + }, + }, + }, + PathType: &pathType, + }, + }, + }, + }, + }, + } + + // Allow override of TLS options if specified + if len(cr.Spec.ApplicationSet.WebhookServer.Ingress.TLS) > 0 { + ingress.Spec.TLS = cr.Spec.ApplicationSet.WebhookServer.Ingress.TLS + } + + if err := controllerutil.SetControllerReference(cr, ingress, r.Scheme); err != nil { + return err + } + return r.Client.Create(context.TODO(), ingress) +} diff --git a/controllers/argocd/ingress_test.go b/controllers/argocd/ingress_test.go index 9141ce33c..bc32ca75c 100644 --- a/controllers/argocd/ingress_test.go +++ b/controllers/argocd/ingress_test.go @@ -4,13 +4,13 @@ import ( "context" "testing" + "github.com/argoproj-labs/argocd-operator/api/v1alpha1" + argoprojv1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1" + "github.com/argoproj-labs/argocd-operator/common" + "github.com/stretchr/testify/assert" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/types" logf "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/stretchr/testify/assert" - - argoprojv1alpha1 "github.com/argoproj-labs/argocd-operator/api/v1alpha1" ) func TestReconcileArgoCD_reconcile_ServerIngress_ingressClassName(t *testing.T) { @@ -182,3 +182,20 @@ func TestReconcileArgoCD_reconcile_PrometheusIngress_ingressClassName(t *testing }) } } + +func TestReconcileApplicationSetService_Ingress(t *testing.T) { + logf.SetLogger(ZapLogger(true)) + a := makeTestArgoCD() + obj := v1alpha1.ArgoCDApplicationSet{ + WebhookServer: v1alpha1.WebhookServerSpec{ + Ingress: v1alpha1.ArgoCDIngressSpec{ + Enabled: true, + }, + }, + } + a.Spec.ApplicationSet = &obj + r := makeTestReconciler(t, a) + ingress := newIngressWithSuffix(common.ApplicationSetServiceNameSuffix, a) + assert.NoError(t, r.reconcileApplicationSetControllerIngress(a)) + assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Namespace: ingress.Namespace, Name: ingress.Name}, ingress)) +} diff --git a/controllers/argocd/route.go b/controllers/argocd/route.go index 2a5ec9574..70daa62e2 100644 --- a/controllers/argocd/route.go +++ b/controllers/argocd/route.go @@ -86,6 +86,11 @@ func (r *ReconcileArgoCD) reconcileRoutes(cr *argoprojv1a1.ArgoCD) error { if err := r.reconcileServerRoute(cr); err != nil { return err } + + if err := r.reconcileApplicationSetControllerWebhookRoute(cr); err != nil { + return err + } + return nil } @@ -284,3 +289,80 @@ func (r *ReconcileArgoCD) reconcileServerRoute(cr *argoprojv1a1.ArgoCD) error { } return r.Client.Update(context.TODO(), route) } + +// reconcileApplicationSetControllerWebhookRoute will ensure that the ArgoCD Server Route is present. +func (r *ReconcileArgoCD) reconcileApplicationSetControllerWebhookRoute(cr *argoprojv1a1.ArgoCD) error { + name := fmt.Sprintf("%s-%s", common.ApplicationSetServiceNameSuffix, "webhook") + route := newRouteWithSuffix(name, cr) + found := argoutil.IsObjectFound(r.Client, cr.Namespace, route.Name, route) + if found { + if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Route.Enabled { + // Route exists but enabled flag has been set to false, delete the Route + return r.Client.Delete(context.TODO(), route) + } + } + + if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Route.Enabled { + return nil // Route not enabled, move along... + } + + // Allow override of the Annotations for the Route. + if len(cr.Spec.ApplicationSet.WebhookServer.Route.Annotations) > 0 { + route.Annotations = cr.Spec.Server.Route.Annotations + } + + // Allow override of the Labels for the Route. + if len(cr.Spec.ApplicationSet.WebhookServer.Route.Labels) > 0 { + labels := route.Labels + for key, val := range cr.Spec.Server.Route.Labels { + labels[key] = val + } + route.Labels = labels + } + + // Allow override of the Host for the Route. + if len(cr.Spec.Server.Host) > 0 { + route.Spec.Host = cr.Spec.ApplicationSet.WebhookServer.Host + } + + if cr.Spec.Server.Insecure { + // Disable TLS and rely on the cluster certificate. + route.Spec.Port = &routev1.RoutePort{ + TargetPort: intstr.FromString("webhook"), + } + route.Spec.TLS = &routev1.TLSConfig{ + InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect, + Termination: routev1.TLSTerminationEdge, + } + } else { + // Server is using TLS configure passthrough. + route.Spec.Port = &routev1.RoutePort{ + TargetPort: intstr.FromString("webhook"), + } + route.Spec.TLS = &routev1.TLSConfig{ + InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect, + Termination: routev1.TLSTerminationPassthrough, + } + } + + // Allow override of TLS options for the Route + if cr.Spec.Server.Route.TLS != nil { + route.Spec.TLS = cr.Spec.Server.Route.TLS + } + + route.Spec.To.Kind = "Service" + route.Spec.To.Name = nameWithSuffix(common.ApplicationSetServiceNameSuffix, cr) + + // Allow override of the WildcardPolicy for the Route + if cr.Spec.Server.Route.WildcardPolicy != nil && len(*cr.Spec.Server.Route.WildcardPolicy) > 0 { + route.Spec.WildcardPolicy = *cr.Spec.Server.Route.WildcardPolicy + } + + if err := controllerutil.SetControllerReference(cr, route, r.Scheme); err != nil { + return err + } + if !found { + return r.Client.Create(context.TODO(), route) + } + return r.Client.Update(context.TODO(), route) +} diff --git a/controllers/argocd/util.go b/controllers/argocd/util.go index 044be379a..b49a434e3 100644 --- a/controllers/argocd/util.go +++ b/controllers/argocd/util.go @@ -1476,3 +1476,12 @@ func contains(s []string, g string) bool { } return false } + +// getApplicationSetHTTPServerHost will return the host for the given ArgoCD. +func getApplicationSetHTTPServerHost(cr *argoprojv1a1.ArgoCD) string { + host := cr.Name + if len(cr.Spec.ApplicationSet.WebhookServer.Host) > 0 { + host = cr.Spec.ApplicationSet.WebhookServer.Host + } + return host +} diff --git a/deploy/catalog_source.yaml b/deploy/catalog_source.yaml deleted file mode 100644 index 59538e2f1..000000000 --- a/deploy/catalog_source.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: CatalogSource -metadata: - name: argocd-catalog -spec: - sourceType: grpc - image: quay.io/argoprojlabs/argocd-operator-registry@sha256:becf932b398c1b89b6abb486b18725c3d408f7cce136d0d2784fd305b6ea41e2 - displayName: Argo CD Operators - publisher: Argo CD Community diff --git a/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml b/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml index c5536cda3..92f0ab6be 100644 --- a/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml +++ b/deploy/olm-catalog/argocd-operator/0.4.0/argocd-operator.v0.4.0.clusterserviceversion.yaml @@ -301,6 +301,28 @@ spec: x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text - urn:alm:descriptor:com.tectonic.ui:advanced + - description: Host is the hostname to use for Ingress/Route resources. + displayName: Host + path: applicationSet.webhookServer.host + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:text + - description: Enabled will toggle the creation of the Ingress. + displayName: Ingress Enabled' + path: applicationSet.webhookServer.ingress.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Enabled will toggle the creation of the OpenShift Route. + displayName: Route Enabled' + path: applicationSet.webhookServer.route.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: ConfigManagementPlugins is used to specify additional config management plugins. displayName: Config Management Plugins' diff --git a/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml b/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml index 0e6e16189..d869c2ec8 100644 --- a/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml +++ b/deploy/olm-catalog/argocd-operator/0.4.0/argoproj.io_argocds.yaml @@ -81,6 +81,139 @@ spec: description: Version is the Argo CD ApplicationSet image tag. (optional) type: string + webhookServer: + description: WebhookServerSpec defines the options for the ApplicationSet + Webhook Server component. + properties: + host: + description: Host is the hostname to use for Ingress/Route + resources. + type: string + ingress: + description: Ingress defines the desired state for an Ingress + for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + apply to the Ingress. + type: object + enabled: + description: Enabled will toggle the creation of the Ingress. + type: boolean + path: + description: Path used for the Ingress resource. + type: string + tls: + description: TLS configuration. Currently the Ingress + only supports a single TLS port, 443. If multiple members + of this list specify different hosts, they will be multiplexed + on the same port according to the hostname specified + through the SNI TLS extension, if the ingress controller + fulfilling the ingress supports SNI. + items: + description: IngressTLS describes the transport layer + security associated with an Ingress. + properties: + hosts: + description: Hosts are a list of hosts included + in the TLS certificate. The values in this list + must match the name/s used in the tlsSecret. Defaults + to the wildcard host setting for the loadbalancer + controller fulfilling this Ingress, if left unspecified. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: SecretName is the name of the secret + used to terminate TLS traffic on port 443. Field + is left optional to allow TLS routing based on + SNI hostname alone. If the SNI host in a listener + conflicts with the "Host" header field used by + an IngressRule, the SNI host is used for termination + and value of the Host header is used for routing. + type: string + type: object + type: array + required: + - enabled + type: object + route: + description: Route defines the desired state for an OpenShift + Route for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + use for the Route resource. + type: object + enabled: + description: Enabled will toggle the creation of the OpenShift + Route. + type: boolean + labels: + additionalProperties: + type: string + description: Labels is the map of labels to use for the + Route resource + type: object + path: + description: Path the router watches for, to route traffic + for to the service. + type: string + tls: + description: TLS provides the ability to configure certificates + and termination for the Route. + properties: + caCertificate: + description: caCertificate provides the cert authority + certificate contents + type: string + certificate: + description: certificate provides certificate contents + type: string + destinationCACertificate: + description: destinationCACertificate provides the + contents of the ca certificate of the final destination. When + using reencrypt termination this file should be + provided in order to have routers use it for health + checks on the secure connection. If this field is + not specified, the router may provide its own destination + CA and perform hostname validation using the short + service name (service.namespace.svc), which allows + infrastructure generated certificates to automatically + verify. + type: string + insecureEdgeTerminationPolicy: + description: "insecureEdgeTerminationPolicy indicates + the desired behavior for insecure connections to + a route. While each router may make its own decisions + on which ports to expose, this is normally port + 80. \n * Allow - traffic is sent to the server on + the insecure port (default) * Disable - no traffic + is allowed on the insecure port. * Redirect - clients + are redirected to the secure port." + type: string + key: + description: key provides key file contents + type: string + termination: + description: termination indicates termination type. + type: string + required: + - termination + type: object + wildcardPolicy: + description: WildcardPolicy if any for the route. Currently + only 'Subdomain' or 'None' is allowed. + type: string + required: + - enabled + type: object + type: object type: object banner: description: Banner defines an additional banner to be displayed in diff --git a/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml b/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml index 431ac9546..59fb1b2a1 100644 --- a/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml +++ b/deploy/olm-catalog/argocd-operator/0.5.0/argocd-operator.v0.5.0.clusterserviceversion.yaml @@ -301,6 +301,28 @@ spec: x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text - urn:alm:descriptor:com.tectonic.ui:advanced + - description: Host is the hostname to use for Ingress/Route resources. + displayName: Host + path: applicationSet.webhookServer.host + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:text + - description: Enabled will toggle the creation of the Ingress. + displayName: Ingress Enabled' + path: applicationSet.webhookServer.ingress.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Enabled will toggle the creation of the OpenShift Route. + displayName: Route Enabled' + path: applicationSet.webhookServer.route.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Grafana + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Prometheus + - urn:alm:descriptor:com.tectonic.ui:fieldGroup:Server + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: ConfigManagementPlugins is used to specify additional config management plugins. displayName: Config Management Plugins' diff --git a/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml b/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml index 0e6e16189..ff9379d6b 100644 --- a/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml +++ b/deploy/olm-catalog/argocd-operator/0.5.0/argoproj.io_argocds.yaml @@ -81,6 +81,142 @@ spec: description: Version is the Argo CD ApplicationSet image tag. (optional) type: string + webhookServer: + description: WebhookServerSpec defines the options for the ApplicationSet + Webhook Server component. + properties: + host: + description: Host is the hostname to use for Ingress/Route + resources. + type: string + ingress: + description: Ingress defines the desired state for an Ingress + for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + apply to the Ingress. + type: object + enabled: + description: Enabled will toggle the creation of the Ingress. + type: boolean + ingressClassName: + description: IngressClassName for the Ingress resource. + type: string + path: + description: Path used for the Ingress resource. + type: string + tls: + description: TLS configuration. Currently the Ingress + only supports a single TLS port, 443. If multiple members + of this list specify different hosts, they will be multiplexed + on the same port according to the hostname specified + through the SNI TLS extension, if the ingress controller + fulfilling the ingress supports SNI. + items: + description: IngressTLS describes the transport layer + security associated with an Ingress. + properties: + hosts: + description: Hosts are a list of hosts included + in the TLS certificate. The values in this list + must match the name/s used in the tlsSecret. Defaults + to the wildcard host setting for the loadbalancer + controller fulfilling this Ingress, if left unspecified. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: SecretName is the name of the secret + used to terminate TLS traffic on port 443. Field + is left optional to allow TLS routing based on + SNI hostname alone. If the SNI host in a listener + conflicts with the "Host" header field used by + an IngressRule, the SNI host is used for termination + and value of the Host header is used for routing. + type: string + type: object + type: array + required: + - enabled + type: object + route: + description: Route defines the desired state for an OpenShift + Route for the Application set webhook component. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is the map of annotations to + use for the Route resource. + type: object + enabled: + description: Enabled will toggle the creation of the OpenShift + Route. + type: boolean + labels: + additionalProperties: + type: string + description: Labels is the map of labels to use for the + Route resource + type: object + path: + description: Path the router watches for, to route traffic + for to the service. + type: string + tls: + description: TLS provides the ability to configure certificates + and termination for the Route. + properties: + caCertificate: + description: caCertificate provides the cert authority + certificate contents + type: string + certificate: + description: certificate provides certificate contents + type: string + destinationCACertificate: + description: destinationCACertificate provides the + contents of the ca certificate of the final destination. When + using reencrypt termination this file should be + provided in order to have routers use it for health + checks on the secure connection. If this field is + not specified, the router may provide its own destination + CA and perform hostname validation using the short + service name (service.namespace.svc), which allows + infrastructure generated certificates to automatically + verify. + type: string + insecureEdgeTerminationPolicy: + description: "insecureEdgeTerminationPolicy indicates + the desired behavior for insecure connections to + a route. While each router may make its own decisions + on which ports to expose, this is normally port + 80. \n * Allow - traffic is sent to the server on + the insecure port (default) * Disable - no traffic + is allowed on the insecure port. * Redirect - clients + are redirected to the secure port." + type: string + key: + description: key provides key file contents + type: string + termination: + description: termination indicates termination type. + type: string + required: + - termination + type: object + wildcardPolicy: + description: WildcardPolicy if any for the route. Currently + only 'Subdomain' or 'None' is allowed. + type: string + required: + - enabled + type: object + type: object type: object banner: description: Banner defines an additional banner to be displayed in diff --git a/tests/k8s/1-001_validate_basic/05-applicationset.yaml b/tests/k8s/1-001_validate_basic/05-applicationset.yaml new file mode 100644 index 000000000..f6582332d --- /dev/null +++ b/tests/k8s/1-001_validate_basic/05-applicationset.yaml @@ -0,0 +1,20 @@ +# Delete previous cluster +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: +- apiVersion: argoproj.io/v1alpha1 + kind: ArgoCD + name: example-argocd +commands: + # Sleep to allow resources to be completely deleted + - command: sleep 30s +--- +apiVersion: argoproj.io/v1alpha1 +kind: ArgoCD +metadata: + name: example-argocd +spec: + applicationSet: + webhookServer: + ingress: + enabled: true \ No newline at end of file diff --git a/tests/k8s/1-001_validate_basic/05-assert.yaml b/tests/k8s/1-001_validate_basic/05-assert.yaml new file mode 100644 index 000000000..871040c62 --- /dev/null +++ b/tests/k8s/1-001_validate_basic/05-assert.yaml @@ -0,0 +1,42 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 720 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example-argocd-applicationset-controller +status: + readyReplicas: 1 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: example-argocd-applicationset-controller +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: example-argocd-applicationset-controller +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: example-argocd-applicationset-controller +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: example-argocd-applicationset-controller +spec: + rules: + - host: example-argocd + http: + paths: + - backend: + service: + name: example-argocd-applicationset-controller + port: + name: webhook + path: /api/webhook + pathType: ImplementationSpecific diff --git a/tests/k8s/1-001_validate_basic/06-disable-applicationset-ingress.yaml b/tests/k8s/1-001_validate_basic/06-disable-applicationset-ingress.yaml new file mode 100644 index 000000000..a8685cc5f --- /dev/null +++ b/tests/k8s/1-001_validate_basic/06-disable-applicationset-ingress.yaml @@ -0,0 +1,7 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +# patches to disable applicationset +- script: | + kubectl patch --type=merge -n $NAMESPACE argocd/example-argocd --patch '{"spec":{"applicationSet":{"webhookServer":{"ingress":{"enabled":false}}}}}' +- script: sleep 10 \ No newline at end of file diff --git a/tests/k8s/1-001_validate_basic/06-errors.yaml b/tests/k8s/1-001_validate_basic/06-errors.yaml new file mode 100644 index 000000000..923a2e826 --- /dev/null +++ b/tests/k8s/1-001_validate_basic/06-errors.yaml @@ -0,0 +1,4 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: example-argocd-applicationset-controller \ No newline at end of file