github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/ingress.go (about)

     1  // Copyright 2019 ArgoCD Operator Developers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // 	http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package argocd
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	networkingv1 "k8s.io/api/networking/v1"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    24  
    25  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    26  	"github.com/argoproj-labs/argocd-operator/common"
    27  	"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
    28  )
    29  
    30  // getArgoServerPath will return the Ingress Path for the Argo CD component.
    31  func getPathOrDefault(path string) string {
    32  	result := common.ArgoCDDefaultIngressPath
    33  	if len(path) > 0 {
    34  		result = path
    35  	}
    36  	return result
    37  }
    38  
    39  // newIngress returns a new Ingress instance for the given ArgoCD.
    40  func newIngress(cr *argoproj.ArgoCD) *networkingv1.Ingress {
    41  	return &networkingv1.Ingress{
    42  		ObjectMeta: metav1.ObjectMeta{
    43  			Name:      cr.Name,
    44  			Namespace: cr.Namespace,
    45  			Labels:    argoutil.LabelsForCluster(cr),
    46  		},
    47  	}
    48  }
    49  
    50  // newIngressWithName returns a new Ingress with the given name and ArgoCD.
    51  func newIngressWithName(name string, cr *argoproj.ArgoCD) *networkingv1.Ingress {
    52  	ingress := newIngress(cr)
    53  	ingress.ObjectMeta.Name = name
    54  
    55  	lbls := ingress.ObjectMeta.Labels
    56  	lbls[common.ArgoCDKeyName] = name
    57  	ingress.ObjectMeta.Labels = lbls
    58  
    59  	return ingress
    60  }
    61  
    62  // newIngressWithSuffix returns a new Ingress with the given name suffix for the ArgoCD.
    63  func newIngressWithSuffix(suffix string, cr *argoproj.ArgoCD) *networkingv1.Ingress {
    64  	return newIngressWithName(fmt.Sprintf("%s-%s", cr.Name, suffix), cr)
    65  }
    66  
    67  // reconcileIngresses will ensure that all ArgoCD Ingress resources are present.
    68  func (r *ReconcileArgoCD) reconcileIngresses(cr *argoproj.ArgoCD) error {
    69  	if err := r.reconcileArgoServerIngress(cr); err != nil {
    70  		return err
    71  	}
    72  
    73  	if err := r.reconcileArgoServerGRPCIngress(cr); err != nil {
    74  		return err
    75  	}
    76  
    77  	if err := r.reconcileGrafanaIngress(cr); err != nil {
    78  		return err
    79  	}
    80  
    81  	if err := r.reconcilePrometheusIngress(cr); err != nil {
    82  		return err
    83  	}
    84  
    85  	if err := r.reconcileApplicationSetControllerIngress(cr); err != nil {
    86  		return err
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  // reconcileArgoServerIngress will ensure that the ArgoCD Server Ingress is present.
    93  func (r *ReconcileArgoCD) reconcileArgoServerIngress(cr *argoproj.ArgoCD) error {
    94  	ingress := newIngressWithSuffix("server", cr)
    95  	if argoutil.IsObjectFound(r.Client, cr.Namespace, ingress.Name, ingress) {
    96  		if !cr.Spec.Server.Ingress.Enabled {
    97  			// Ingress exists but enabled flag has been set to false, delete the Ingress
    98  			return r.Client.Delete(context.TODO(), ingress)
    99  		}
   100  		return nil // Ingress found and enabled, do nothing
   101  	}
   102  
   103  	if !cr.Spec.Server.Ingress.Enabled {
   104  		return nil // Ingress not enabled, move along...
   105  	}
   106  
   107  	// Add default annotations
   108  	atns := make(map[string]string)
   109  	atns[common.ArgoCDKeyIngressSSLRedirect] = "true"
   110  	atns[common.ArgoCDKeyIngressBackendProtocol] = "HTTP"
   111  
   112  	// Override default annotations if specified
   113  	if len(cr.Spec.Server.Ingress.Annotations) > 0 {
   114  		atns = cr.Spec.Server.Ingress.Annotations
   115  	}
   116  
   117  	ingress.ObjectMeta.Annotations = atns
   118  
   119  	ingress.Spec.IngressClassName = cr.Spec.Server.Ingress.IngressClassName
   120  
   121  	pathType := networkingv1.PathTypeImplementationSpecific
   122  	// Add rules
   123  	ingress.Spec.Rules = []networkingv1.IngressRule{
   124  		{
   125  			Host: getArgoServerHost(cr),
   126  			IngressRuleValue: networkingv1.IngressRuleValue{
   127  				HTTP: &networkingv1.HTTPIngressRuleValue{
   128  					Paths: []networkingv1.HTTPIngressPath{
   129  						{
   130  							Path: getPathOrDefault(cr.Spec.Server.Ingress.Path),
   131  							Backend: networkingv1.IngressBackend{
   132  								Service: &networkingv1.IngressServiceBackend{
   133  									Name: nameWithSuffix("server", cr),
   134  									Port: networkingv1.ServiceBackendPort{
   135  										Name: "http",
   136  									},
   137  								},
   138  							},
   139  							PathType: &pathType,
   140  						},
   141  					},
   142  				},
   143  			},
   144  		},
   145  	}
   146  
   147  	// Add default TLS options
   148  	ingress.Spec.TLS = []networkingv1.IngressTLS{
   149  		{
   150  			Hosts: []string{
   151  				getArgoServerHost(cr),
   152  			},
   153  			SecretName: common.ArgoCDSecretName,
   154  		},
   155  	}
   156  
   157  	// Allow override of TLS options if specified
   158  	if len(cr.Spec.Server.Ingress.TLS) > 0 {
   159  		ingress.Spec.TLS = cr.Spec.Server.Ingress.TLS
   160  	}
   161  
   162  	if err := controllerutil.SetControllerReference(cr, ingress, r.Scheme); err != nil {
   163  		return err
   164  	}
   165  	return r.Client.Create(context.TODO(), ingress)
   166  }
   167  
   168  // reconcileArgoServerGRPCIngress will ensure that the ArgoCD Server GRPC Ingress is present.
   169  func (r *ReconcileArgoCD) reconcileArgoServerGRPCIngress(cr *argoproj.ArgoCD) error {
   170  	ingress := newIngressWithSuffix("grpc", cr)
   171  	if argoutil.IsObjectFound(r.Client, cr.Namespace, ingress.Name, ingress) {
   172  		if !cr.Spec.Server.GRPC.Ingress.Enabled {
   173  			// Ingress exists but enabled flag has been set to false, delete the Ingress
   174  			return r.Client.Delete(context.TODO(), ingress)
   175  		}
   176  		return nil // Ingress found and enabled, do nothing
   177  	}
   178  
   179  	if !cr.Spec.Server.GRPC.Ingress.Enabled {
   180  		return nil // Ingress not enabled, move along...
   181  	}
   182  
   183  	// Add default annotations
   184  	atns := make(map[string]string)
   185  	atns[common.ArgoCDKeyIngressBackendProtocol] = "GRPC"
   186  
   187  	// Override default annotations if specified
   188  	if len(cr.Spec.Server.GRPC.Ingress.Annotations) > 0 {
   189  		atns = cr.Spec.Server.GRPC.Ingress.Annotations
   190  	}
   191  
   192  	ingress.ObjectMeta.Annotations = atns
   193  
   194  	ingress.Spec.IngressClassName = cr.Spec.Server.GRPC.Ingress.IngressClassName
   195  
   196  	pathType := networkingv1.PathTypeImplementationSpecific
   197  	// Add rules
   198  	ingress.Spec.Rules = []networkingv1.IngressRule{
   199  		{
   200  			Host: getArgoServerGRPCHost(cr),
   201  			IngressRuleValue: networkingv1.IngressRuleValue{
   202  				HTTP: &networkingv1.HTTPIngressRuleValue{
   203  					Paths: []networkingv1.HTTPIngressPath{
   204  						{
   205  							Path: getPathOrDefault(cr.Spec.Server.GRPC.Ingress.Path),
   206  							Backend: networkingv1.IngressBackend{
   207  								Service: &networkingv1.IngressServiceBackend{
   208  									Name: nameWithSuffix("server", cr),
   209  									Port: networkingv1.ServiceBackendPort{
   210  										Name: "https",
   211  									},
   212  								},
   213  							},
   214  							PathType: &pathType,
   215  						},
   216  					},
   217  				},
   218  			},
   219  		},
   220  	}
   221  
   222  	// Add TLS options
   223  	ingress.Spec.TLS = []networkingv1.IngressTLS{
   224  		{
   225  			Hosts: []string{
   226  				getArgoServerGRPCHost(cr),
   227  			},
   228  			SecretName: common.ArgoCDSecretName,
   229  		},
   230  	}
   231  
   232  	// Allow override of TLS options if specified
   233  	if len(cr.Spec.Server.GRPC.Ingress.TLS) > 0 {
   234  		ingress.Spec.TLS = cr.Spec.Server.GRPC.Ingress.TLS
   235  	}
   236  
   237  	if err := controllerutil.SetControllerReference(cr, ingress, r.Scheme); err != nil {
   238  		return err
   239  	}
   240  	return r.Client.Create(context.TODO(), ingress)
   241  }
   242  
   243  // reconcileGrafanaIngress will ensure that the ArgoCD Server GRPC Ingress is present.
   244  func (r *ReconcileArgoCD) reconcileGrafanaIngress(cr *argoproj.ArgoCD) error {
   245  	ingress := newIngressWithSuffix("grafana", cr)
   246  	if argoutil.IsObjectFound(r.Client, cr.Namespace, ingress.Name, ingress) {
   247  		if !cr.Spec.Grafana.Enabled || !cr.Spec.Grafana.Ingress.Enabled {
   248  			// Ingress exists but enabled flag has been set to false, delete the Ingress
   249  			return r.Client.Delete(context.TODO(), ingress)
   250  		}
   251  		log.Info(grafanaDeprecatedWarning)
   252  		return nil // Ingress found and enabled, do nothing
   253  	}
   254  
   255  	if !cr.Spec.Grafana.Enabled || !cr.Spec.Grafana.Ingress.Enabled {
   256  		return nil // Grafana itself or Ingress not enabled, move along...
   257  	}
   258  
   259  	log.Info(grafanaDeprecatedWarning)
   260  
   261  	return nil
   262  }
   263  
   264  // reconcilePrometheusIngress will ensure that the Prometheus Ingress is present.
   265  func (r *ReconcileArgoCD) reconcilePrometheusIngress(cr *argoproj.ArgoCD) error {
   266  	ingress := newIngressWithSuffix("prometheus", cr)
   267  	if argoutil.IsObjectFound(r.Client, cr.Namespace, ingress.Name, ingress) {
   268  		if !cr.Spec.Prometheus.Enabled || !cr.Spec.Prometheus.Ingress.Enabled {
   269  			// Ingress exists but enabled flag has been set to false, delete the Ingress
   270  			return r.Client.Delete(context.TODO(), ingress)
   271  		}
   272  		return nil // Ingress found and enabled, do nothing
   273  	}
   274  
   275  	if !cr.Spec.Prometheus.Enabled || !cr.Spec.Prometheus.Ingress.Enabled {
   276  		return nil // Prometheus itself or Ingress not enabled, move along...
   277  	}
   278  
   279  	// Add default annotations
   280  	atns := make(map[string]string)
   281  	atns[common.ArgoCDKeyIngressSSLRedirect] = "true"
   282  	atns[common.ArgoCDKeyIngressBackendProtocol] = "HTTP"
   283  
   284  	// Override default annotations if specified
   285  	if len(cr.Spec.Prometheus.Ingress.Annotations) > 0 {
   286  		atns = cr.Spec.Prometheus.Ingress.Annotations
   287  	}
   288  
   289  	ingress.ObjectMeta.Annotations = atns
   290  
   291  	ingress.Spec.IngressClassName = cr.Spec.Prometheus.Ingress.IngressClassName
   292  
   293  	pathType := networkingv1.PathTypeImplementationSpecific
   294  	// Add rules
   295  	ingress.Spec.Rules = []networkingv1.IngressRule{
   296  		{
   297  			Host: getPrometheusHost(cr),
   298  			IngressRuleValue: networkingv1.IngressRuleValue{
   299  				HTTP: &networkingv1.HTTPIngressRuleValue{
   300  					Paths: []networkingv1.HTTPIngressPath{
   301  						{
   302  							Path: getPathOrDefault(cr.Spec.Prometheus.Ingress.Path),
   303  							Backend: networkingv1.IngressBackend{
   304  								Service: &networkingv1.IngressServiceBackend{
   305  									Name: "prometheus-operated",
   306  									Port: networkingv1.ServiceBackendPort{
   307  										Name: "web",
   308  									},
   309  								},
   310  							},
   311  							PathType: &pathType,
   312  						},
   313  					},
   314  				},
   315  			},
   316  		},
   317  	}
   318  
   319  	// Add TLS options
   320  	ingress.Spec.TLS = []networkingv1.IngressTLS{
   321  		{
   322  			Hosts:      []string{cr.Name},
   323  			SecretName: common.ArgoCDSecretName,
   324  		},
   325  	}
   326  
   327  	// Allow override of TLS options if specified
   328  	if len(cr.Spec.Prometheus.Ingress.TLS) > 0 {
   329  		ingress.Spec.TLS = cr.Spec.Prometheus.Ingress.TLS
   330  	}
   331  
   332  	if err := controllerutil.SetControllerReference(cr, ingress, r.Scheme); err != nil {
   333  		return err
   334  	}
   335  	return r.Client.Create(context.TODO(), ingress)
   336  }
   337  
   338  // reconcileApplicationSetControllerIngress will ensure that the ApplicationSetController Ingress is present.
   339  func (r *ReconcileArgoCD) reconcileApplicationSetControllerIngress(cr *argoproj.ArgoCD) error {
   340  	ingress := newIngressWithSuffix(common.ApplicationSetServiceNameSuffix, cr)
   341  	if argoutil.IsObjectFound(r.Client, cr.Namespace, ingress.Name, ingress) {
   342  		if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Ingress.Enabled {
   343  			return r.Client.Delete(context.TODO(), ingress)
   344  		}
   345  		return nil // Ingress found and enabled, do nothing
   346  	}
   347  
   348  	if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Ingress.Enabled {
   349  		log.Info("not enabled")
   350  		return nil // Ingress not enabled, move along...
   351  	}
   352  
   353  	// Add annotations
   354  	atns := make(map[string]string)
   355  	atns[common.ArgoCDKeyIngressSSLRedirect] = "true"
   356  	atns[common.ArgoCDKeyIngressBackendProtocol] = "HTTP"
   357  
   358  	// Override default annotations if specified
   359  	if len(cr.Spec.ApplicationSet.WebhookServer.Ingress.Annotations) > 0 {
   360  		atns = cr.Spec.ApplicationSet.WebhookServer.Ingress.Annotations
   361  	}
   362  
   363  	ingress.ObjectMeta.Annotations = atns
   364  
   365  	pathType := networkingv1.PathTypeImplementationSpecific
   366  	httpServerHost, err := getApplicationSetHTTPServerHost(cr)
   367  	if err != nil {
   368  		return err
   369  	}
   370  
   371  	// Add rules
   372  	ingress.Spec.Rules = []networkingv1.IngressRule{
   373  		{
   374  			Host: httpServerHost,
   375  			IngressRuleValue: networkingv1.IngressRuleValue{
   376  				HTTP: &networkingv1.HTTPIngressRuleValue{
   377  					Paths: []networkingv1.HTTPIngressPath{
   378  						{
   379  							Path: "/api/webhook",
   380  							Backend: networkingv1.IngressBackend{
   381  								Service: &networkingv1.IngressServiceBackend{
   382  									Name: nameWithSuffix(common.ApplicationSetServiceNameSuffix, cr),
   383  									Port: networkingv1.ServiceBackendPort{
   384  										Name: "webhook",
   385  									},
   386  								},
   387  							},
   388  							PathType: &pathType,
   389  						},
   390  					},
   391  				},
   392  			},
   393  		},
   394  	}
   395  
   396  	// Allow override of TLS options if specified
   397  	if len(cr.Spec.ApplicationSet.WebhookServer.Ingress.TLS) > 0 {
   398  		ingress.Spec.TLS = cr.Spec.ApplicationSet.WebhookServer.Ingress.TLS
   399  	}
   400  
   401  	if err := controllerutil.SetControllerReference(cr, ingress, r.Scheme); err != nil {
   402  		return err
   403  	}
   404  	return r.Client.Create(context.TODO(), ingress)
   405  }