From 2f040baaf93380db722ee1b6743fb2f01929c91e Mon Sep 17 00:00:00 2001 From: Daniel Clark Date: Wed, 22 Sep 2021 00:20:49 +0000 Subject: [PATCH 1/4] OperatorConfig CRD supports more of alertmanager_config --- cmd/operator/deploy/operator/clusterrole.yaml | 1 + cmd/rule-evaluator/main.go | 3 + pkg/operator/operator.go | 2 +- pkg/operator/operator_config.go | 328 +++++++++++++++++- 4 files changed, 325 insertions(+), 9 deletions(-) diff --git a/cmd/operator/deploy/operator/clusterrole.yaml b/cmd/operator/deploy/operator/clusterrole.yaml index 2dd13bc7a8..0707aba0b7 100644 --- a/cmd/operator/deploy/operator/clusterrole.yaml +++ b/cmd/operator/deploy/operator/clusterrole.yaml @@ -48,6 +48,7 @@ rules: - certificatesigningrequests - rules - operatorconfigs + - secrets verbs: ["create", "update", "get", "list", "watch", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/cmd/rule-evaluator/main.go b/cmd/rule-evaluator/main.go index 116de13e38..f90e9f2070 100644 --- a/cmd/rule-evaluator/main.go +++ b/cmd/rule-evaluator/main.go @@ -43,6 +43,9 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/discovery" + + // Import to enable 'kubernetes_sd_configs' to SD config register. + _ "github.com/prometheus/prometheus/discovery/kubernetes" "github.com/prometheus/prometheus/notifier" "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/promql" diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index 95cec382e4..d9bf288a97 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -169,7 +169,7 @@ func New(logger logr.Logger, clientConfig *rest.Config, registry prometheus.Regi return nil, errors.Wrap(err, "build Kubernetes clientset") } // Create temporary directory to store webhook serving cert files. - certDir, err := ioutil.TempDir("", "prometheus-engine-operator-certs") + certDir, err := ioutil.TempDir("", "operator-cert") if err != nil { return nil, errors.Wrap(err, "create temporary certificate dir") } diff --git a/pkg/operator/operator_config.go b/pkg/operator/operator_config.go index fa53464dd6..347a6f7d4e 100644 --- a/pkg/operator/operator_config.go +++ b/pkg/operator/operator_config.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "path" + "strings" monitoringv1alpha1 "github.com/GoogleCloudPlatform/prometheus-engine/pkg/operator/apis/monitoring/v1alpha1" "github.com/go-logr/logr" @@ -11,8 +12,10 @@ import ( "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -20,13 +23,26 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +// TODO(pintohutch): move these into the operator Options and pass in. const ( NameRuleEvaluator = "rule-evaluator" rulesVolumeName = "rules" + secretVolumeName = "rules-secret" + RulesSecretName = "rules" rulesDir = "/etc/rules" + secretsDir = "/etc/secrets" RuleEvaluatorPort = 19092 ) +var ( + rulesLabels = map[string]string{ + LabelAppName: NameRuleEvaluator, + } + rulesAnnotations = map[string]string{ + AnnotationMetricName: componentName, + } +) + // setupOperatorConfigControllers ensures a rule-evaluator // deployment as part of managed collection. func setupOperatorConfigControllers(op *Operator) error { @@ -58,8 +74,9 @@ func setupOperatorConfigControllers(op *Operator) error { // operatorConfigReconciler reconciles the OperatorConfig CRD. type operatorConfigReconciler struct { - client client.Client - opts Options + client client.Client + opts Options + secretData map[string][]byte } // newOperatorConfigReconciler creates a new operatorConfigReconciler. @@ -82,6 +99,11 @@ func (r *operatorConfigReconciler) Reconcile(ctx context.Context, req reconcile. if err := r.ensureRuleEvaluatorConfig(ctx, config); err != nil { return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator config") } + + if err := r.ensureRuleEvaluatorSecrets(ctx); err != nil { + return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator secrets") + } + if err := r.ensureRuleEvaluatorDeployment(ctx, &config.Rules); err != nil { return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator deploy") } @@ -91,7 +113,7 @@ func (r *operatorConfigReconciler) Reconcile(ctx context.Context, req reconcile. // ensureRuleEvaluatorConfig reconciles the ConfigMap for rule-evaluator. func (r *operatorConfigReconciler) ensureRuleEvaluatorConfig(ctx context.Context, config *monitoringv1alpha1.OperatorConfig) error { - amConfigs, err := makeAlertManagerConfigs(&config.Rules.Alerting) + amConfigs, err := r.makeAlertManagerConfigs(ctx, &config.Rules.Alerting) if err != nil { return errors.Wrap(err, "make alertmanager config") } @@ -156,6 +178,31 @@ func makeRuleEvaluatorConfigMap(amConfigs []yaml.MapSlice, name, namespace, file return cm, nil } +// ensureRuleEvaluatorSecrets reconciles the Secrets for rule-evaluator. +func (r *operatorConfigReconciler) ensureRuleEvaluatorSecrets(ctx context.Context) error { + var secret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: RulesSecretName, + Namespace: r.opts.OperatorNamespace, + Annotations: rulesAnnotations, + Labels: rulesLabels, + }, + Data: make(map[string][]byte), + } + for f, b := range r.secretData { + secret.Data[f] = b + } + + if err := r.client.Update(ctx, secret); apierrors.IsNotFound(err) { + if err := r.client.Create(ctx, secret); err != nil { + return errors.Wrap(err, "create rule-evaluator secrets") + } + } else if err != nil { + return errors.Wrap(err, "update rule-evaluator secrets") + } + return nil +} + // ensureRuleEvaluatorDeployment reconciles the Deployment for rule-evaluator. func (r *operatorConfigReconciler) ensureRuleEvaluatorDeployment(ctx context.Context, rules *monitoringv1alpha1.RuleEvaluatorSpec) error { deploy := r.makeRuleEvaluatorDeployment(rules) @@ -194,12 +241,12 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring } spec := appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ - MatchLabels: podLabels, + MatchLabels: rulesLabels, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: podLabels, - Annotations: podAnnotations, + Labels: rulesLabels, + Annotations: rulesAnnotations, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -275,6 +322,11 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring MountPath: rulesDir, ReadOnly: true, }, + { + Name: secretVolumeName, + MountPath: secretsDir, + ReadOnly: true, + }, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ @@ -322,6 +374,13 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring }, }, }, + }, { + Name: secretVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: RulesSecretName, + }, + }, }, }, // Collector service account used for K8s endpoints-based SD. @@ -345,6 +404,8 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring ObjectMeta: metav1.ObjectMeta{ Namespace: r.opts.OperatorNamespace, Name: NameRuleEvaluator, + // TODO(pintohutch): OwnerReferences should tear down rule-evaluator + // when gmp-operator is deleted. }, Spec: spec, } @@ -355,7 +416,7 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring // https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config. // TODO(pintohutch): change function signature to use native Promethues go structs // over []yaml.MapSlice. -func makeAlertManagerConfigs(spec *monitoringv1alpha1.AlertingSpec) ([]yaml.MapSlice, error) { +func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, spec *monitoringv1alpha1.AlertingSpec) ([]yaml.MapSlice, error) { var configs []yaml.MapSlice for _, am := range spec.Alertmanagers { var cfg yaml.MapSlice @@ -375,9 +436,260 @@ func makeAlertManagerConfigs(spec *monitoringv1alpha1.AlertingSpec) ([]yaml.MapS if am.Scheme != "" { cfg = append(cfg, yaml.MapItem{Key: "scheme", Value: am.Scheme}) } - // TODO(pintohutch): fill the rest out. + // Authorization. + // Default to Bearer for authorization type. + if am.Authorization != nil { + authCfg := yaml.MapSlice{} + if t := am.Authorization.Type; t != "" { + authCfg = append(authCfg, yaml.MapItem{Key: "type", Value: strings.TrimSpace(t)}) + } + if c := am.Authorization.Credentials; c != nil { + b, err := getSecretKeyBytes(ctx, r.client, c) + if err != nil { + return configs, err + } + authCfg = append(authCfg, yaml.MapItem{Key: "credentials", Value: string(b)}) + } + cfg = append(cfg, yaml.MapItem{Key: "authorization", Value: authCfg}) + } + + // TLS config. + if tls := am.TLSConfig; tls != nil { + cfg = append(cfg, yaml.MapItem{Key: "tls_config", Value: tlsConfigYAML(secretsDir, tls)}) + // Populate secretData cache to act on (i.e. upsert dedicated Secret) later. + secretData, err := getTLSSecretData(ctx, r.client, tls) + if err != nil { + return configs, err + } + r.secretData = secretData + } + + // Kubernetes SD configs. + cfg = append(cfg, yaml.MapItem{Key: "kubernetes_sd_configs", Value: k8sSDConfigYAML(am.Namespace)}) + + // Relabel configs. + cfg = append(cfg, yaml.MapItem{Key: "relabel_configs", Value: relabelConfigsYAML(&am)}) + + // TODO(pintohutch): Unsure if these make sense within K8s endpoints SD... + // basic_auth support. + // oauth2 support. + // proxy_url support. + // follow_redirects support. + + // Append to alertmanagers config array. configs = append(configs, cfg) } + + // Allow for separate alertmanager_configs to be used by rule-evaluator + // in cases where the CRDs don't provide enough configuration. + // Note: any configs that specify filepaths are unlikely to work as this + // requires manually mounting volumes and files to the deployment. + for _, amc := range spec.AlertmanagerConfigs { + b, err := getSecretOrConfigMapBytes(ctx, r.client, &amc) + if err != nil { + return configs, err + } + var cfgs []yaml.MapSlice + err = yaml.Unmarshal(b, &cfgs) + if err != nil { + return nil, errors.Wrap(err, "unmarshalling alert manager configs") + } + configs = append(configs, cfgs...) + } + return configs, nil } + +// tlsConfigYAML creates a yaml.MapSlice compatible with https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config +// from the provided TLSConfig and mount path `dir`. +func tlsConfigYAML(dir string, tls *monitoringv1alpha1.TLSConfig) yaml.MapSlice { + var ( + filepath string + tlsConfig = yaml.MapSlice{ + {Key: "insecure_skip_verify", Value: tls.InsecureSkipVerify}, + } + ) + if tls.CA.Secret != nil || tls.CA.ConfigMap != nil { + filepath = path.Join(dir, pathForSelector(&tls.CA)) + tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: filepath}) + } + if tls.Cert.Secret != nil || tls.Cert.ConfigMap != nil { + filepath = path.Join(dir, pathForSelector(&tls.Cert)) + tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: filepath}) + } + if tls.KeySecret != nil { + scm := &monitoringv1alpha1.NamespacedSecretOrConfigMap{Secret: tls.KeySecret} + filepath = path.Join(dir, pathForSelector(scm)) + tlsConfig = append(tlsConfig, yaml.MapItem{Key: "key_file", Value: filepath}) + } + if tls.ServerName != "" { + tlsConfig = append(tlsConfig, yaml.MapItem{Key: "server_name", Value: tls.ServerName}) + } + return tlsConfig +} + +// k8sSDConfigYAML returns the kubernetes_sd_config YAML spec +// from the provided namespace. +func k8sSDConfigYAML(namespace string) []yaml.MapSlice { + k8sSDConfig := yaml.MapSlice{ + { + Key: "role", + Value: "endpoints", + }, + } + + k8sSDConfig = append(k8sSDConfig, yaml.MapItem{ + Key: "namespaces", + Value: yaml.MapSlice{ + { + Key: "names", + Value: []string{namespace}, + }, + }, + }) + + return []yaml.MapSlice{ + k8sSDConfig, + } +} + +// relabelConfigsYAML returns the relabel_configs YAML spec +// from the provided AlertmanagerEndpoints. +func relabelConfigsYAML(am *monitoringv1alpha1.AlertmanagerEndpoints) []yaml.MapSlice { + var relabelings []yaml.MapSlice + + relabelings = append(relabelings, yaml.MapSlice{ + {Key: "action", Value: "keep"}, + {Key: "source_labels", Value: []string{"__meta_kubernetes_service_name"}}, + {Key: "regex", Value: am.Name}, + }) + + if am.Port.StrVal != "" { + relabelings = append(relabelings, yaml.MapSlice{ + {Key: "action", Value: "keep"}, + {Key: "source_labels", Value: []string{"__meta_kubernetes_endpoint_port_name"}}, + {Key: "regex", Value: am.Port.String()}, + }) + } else if am.Port.IntVal != 0 { + relabelings = append(relabelings, yaml.MapSlice{ + {Key: "action", Value: "keep"}, + {Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_port_number"}}, + {Key: "regex", Value: am.Port.String()}, + }) + } + + return relabelings +} + +// getTLSSecretData parses the provided TLSConfig and fetches the secret key bytes +// and returns them in a map, keyed by unique filenames. +func getTLSSecretData(ctx context.Context, kClient client.Reader, tls *monitoringv1alpha1.TLSConfig) (map[string][]byte, error) { + var m = make(map[string][]byte) + // Fetch CA cert bytes. + b, err := getSecretOrConfigMapBytes(ctx, kClient, &tls.CA) + if err != nil { + return m, err + } + m[pathForSelector(&tls.CA)] = b + + // Fetch client cert bytes. + b, err = getSecretOrConfigMapBytes(ctx, kClient, &tls.Cert) + if err != nil { + return m, err + } + m[pathForSelector(&tls.Cert)] = b + + // Fetch secret client key bytes. + if secret := tls.KeySecret; secret != nil { + b, err := getSecretKeyBytes(ctx, kClient, secret) + if err != nil { + return m, err + } + m[pathForSelector(&monitoringv1alpha1.NamespacedSecretOrConfigMap{Secret: tls.KeySecret})] = b + } + return m, nil +} + +// getSecretOrConfigMapBytes is a helper function to conditionally fetch +// the secret or configmap selector payloads. +func getSecretOrConfigMapBytes(ctx context.Context, kClient client.Reader, scm *monitoringv1alpha1.NamespacedSecretOrConfigMap) ([]byte, error) { + var ( + b []byte + err error + ) + if secret := scm.Secret; secret != nil { + b, err = getSecretKeyBytes(ctx, kClient, secret) + if err != nil { + return b, err + } + } else if cm := scm.ConfigMap; cm != nil { + b, err = getConfigMapKeyBytes(ctx, kClient, cm) + if err != nil { + return b, err + } + } + return b, nil +} + +// getSecretKeyBytes processes the given NamespacedSecretKeySelector and returns the referenced data. +func getSecretKeyBytes(ctx context.Context, kClient client.Reader, sel *monitoringv1alpha1.NamespacedSecretKeySelector) ([]byte, error) { + var ( + secret = &corev1.Secret{} + nn = types.NamespacedName{ + Namespace: sel.Namespace, + Name: sel.Name, + } + bytes []byte + ) + err := kClient.Get(ctx, nn, secret) + if err != nil { + return bytes, errors.Wrapf(err, "unable to get secret %q", sel.Name) + } + bytes, ok := secret.Data[sel.Key] + if !ok { + return bytes, errors.Errorf("key %q in secret %q not found", sel.Key, sel.Name) + } + + return bytes, nil +} + +// getConfigMapKeyBytes processes the given NamespacedConfigMapKeySelector and returns the referenced data. +func getConfigMapKeyBytes(ctx context.Context, kClient client.Reader, sel *monitoringv1alpha1.NamespacedConfigMapKeySelector) ([]byte, error) { + var ( + cm = &corev1.ConfigMap{} + nn = types.NamespacedName{ + Namespace: sel.Namespace, + Name: sel.Name, + } + b []byte + ) + err := kClient.Get(ctx, nn, cm) + if err != nil { + return b, errors.Wrapf(err, "unable to get secret %q", sel.Name) + } + + // Check 'data' first, then 'binaryData'. + if s, ok := cm.Data[sel.Key]; ok { + return []byte(s), nil + } else if b, ok := cm.BinaryData[sel.Key]; ok { + return b, nil + } else { + return b, errors.Errorf("key %q in secret %q not found", sel.Key, sel.Name) + } +} + +// pathForSelector cretes the filepath for the provided NamespacedSecretOrConfigMap. +// This can be used to avoid naming collisions of like-keys across K8s resources. +func pathForSelector(scm *monitoringv1alpha1.NamespacedSecretOrConfigMap) string { + if scm == nil { + return "" + } + if scm.ConfigMap != nil { + return fmt.Sprintf("%s_%s_%s_%s", "configmap", scm.ConfigMap.Namespace, scm.ConfigMap.Name, scm.ConfigMap.Key) + } + if scm.Secret != nil { + return fmt.Sprintf("%s_%s_%s_%s", "secret", scm.Secret.Namespace, scm.Secret.Name, scm.Secret.Key) + } + return "" +} From 76354f03ee5ffa4edc25a8a3cbca62ecf889516e Mon Sep 17 00:00:00 2001 From: Daniel Clark Date: Mon, 11 Oct 2021 22:15:16 +0000 Subject: [PATCH 2/4] review feedback - 1 --- cmd/operator/deploy/operator/clusterrole.yaml | 7 +- cmd/operator/deploy/operator/crds.yaml | 47 +--------- cmd/operator/deploy/operator/operator.yaml | 14 +++ cmd/operator/deploy/operator/role.yaml | 26 ++++++ .../apis/monitoring/v1alpha1/types.go | 5 - .../v1alpha1/zz_generated.deepcopy.go | 49 +++++----- pkg/operator/operator_config.go | 92 ++++++++----------- 7 files changed, 103 insertions(+), 137 deletions(-) create mode 100644 cmd/operator/deploy/operator/role.yaml diff --git a/cmd/operator/deploy/operator/clusterrole.yaml b/cmd/operator/deploy/operator/clusterrole.yaml index 0707aba0b7..d8d9142d8f 100644 --- a/cmd/operator/deploy/operator/clusterrole.yaml +++ b/cmd/operator/deploy/operator/clusterrole.yaml @@ -39,16 +39,13 @@ metadata: rules: - apiGroups: ["", "apps", "admissionregistration.k8s.io", "monitoring.googleapis.com", "certificates.k8s.io"] resources: - - deployments - podmonitorings - podmonitorings/status + - rules + - operatorconfigs - configmaps - - daemonsets - validatingwebhookconfigurations - certificatesigningrequests - - rules - - operatorconfigs - - secrets verbs: ["create", "update", "get", "list", "watch", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/cmd/operator/deploy/operator/crds.yaml b/cmd/operator/deploy/operator/crds.yaml index b678b21c68..0ab158c5f9 100644 --- a/cmd/operator/deploy/operator/crds.yaml +++ b/cmd/operator/deploy/operator/crds.yaml @@ -284,49 +284,6 @@ spec: description: Alerting contains how the rule-evaluator configures alerting. type: object properties: - alertmanagerConfigs: - description: 'AlertmanagerConfigs contains a list of secret or configmap selectors that can be used to insert complete Alertmanager config specs. Each selector should return a payload that can be unmarshalled to a list of alertmanager_config: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config.' - type: array - items: - description: 'NamespacedSecretOrConfigMap allows to specify data as a Secret or ConfigMap. Fields are mutually exclusive. Taking inspiration from prometheus-operator: https://github.com/prometheus-operator/prometheus-operator/blob/2c81b0cf6a5673e08057499a08ddce396b19dda4/Documentation/api.md#secretorconfigmap' - type: object - properties: - configMap: - description: ConfigMap containing data to use for the targets. - type: object - required: - - key - - namespace - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - namespace: - type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - secret: - description: Secret containing data to use for the targets. - type: object - required: - - key - - namespace - properties: - key: - description: The key of the secret to select from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - namespace: - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean alertmanagers: description: Alertmanagers contains endpoint configuration for designated Alertmanagers. type: array @@ -496,10 +453,10 @@ spec: description: Used to verify the hostname for the targets. type: string labelLocation: - description: LabelLocation is the `location` label value on exported time series generated from recording rules. If left blank, the rule-evaluator will try and fetch the location from the GCE metadata server. + description: 'LabelLocation is the `location` label value on exported time series generated from recording rules. If left blank, the rule-evaluator will try and fetch the location from the GCE metadata server. TODO(pintohutch): promote LabelLocation to OperatorConfig to permit configuration of collectors as well.' type: string labelProjectID: - description: LabelProjectID is the `project_id` label value on exported time series generated from recording rules. If left blank, the rule-evaluator will try and fetch the project ID from the GCE metadata server. + description: 'LabelProjectID is the `project_id` label value on exported time series generated from recording rules. If left blank, the rule-evaluator will try and fetch the project ID from the GCE metadata server. TODO(pintohutch): promote LabelProjectID to OperatorConfig to permit configuration of collectors as well.' type: string projectID: description: ProjectID is the GCP project ID to evaluate rules against. If left blank, the rule-evaluator will try and fetch the project ID from the GCE metadata server. diff --git a/cmd/operator/deploy/operator/operator.yaml b/cmd/operator/deploy/operator/operator.yaml index 30d0537d77..2ee64203b8 100644 --- a/cmd/operator/deploy/operator/operator.yaml +++ b/cmd/operator/deploy/operator/operator.yaml @@ -50,6 +50,20 @@ subjects: namespace: gmp-system name: operator --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: gmp-system:operator + namespace: gmp-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: gmp-system:operator +subjects: +- kind: ServiceAccount + namespace: gmp-system + name: operator +--- apiVersion: apps/v1 kind: Deployment metadata: diff --git a/cmd/operator/deploy/operator/role.yaml b/cmd/operator/deploy/operator/role.yaml new file mode 100644 index 0000000000..6927660090 --- /dev/null +++ b/cmd/operator/deploy/operator/role.yaml @@ -0,0 +1,26 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: gmp-system:operator + namespace: gmp-system +rules: +- apiGroups: [""] + resources: + - secrets + - deployments + - daemonsets + verbs: ["get", "watch", "create", "delete", "list", "update"] \ No newline at end of file diff --git a/pkg/operator/apis/monitoring/v1alpha1/types.go b/pkg/operator/apis/monitoring/v1alpha1/types.go index f6b6b957e1..774d7ed312 100644 --- a/pkg/operator/apis/monitoring/v1alpha1/types.go +++ b/pkg/operator/apis/monitoring/v1alpha1/types.go @@ -84,11 +84,6 @@ type RuleEvaluatorSpec struct { type AlertingSpec struct { // Alertmanagers contains endpoint configuration for designated Alertmanagers. Alertmanagers []AlertmanagerEndpoints `json:"alertmanagers,omitempty"` - // AlertmanagerConfigs contains a list of secret or configmap selectors - // that can be used to insert complete Alertmanager config specs. - // Each selector should return a payload that can be unmarshalled to - // a list of alertmanager_config: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config. - AlertmanagerConfigs []NamespacedSecretOrConfigMap `json:"alertmanagerConfigs,omitempty"` } // AlertmanagerEndpoints defines a selection of a single Endpoints object diff --git a/pkg/operator/apis/monitoring/v1alpha1/zz_generated.deepcopy.go b/pkg/operator/apis/monitoring/v1alpha1/zz_generated.deepcopy.go index 8b9432295b..4c6bfa73a0 100644 --- a/pkg/operator/apis/monitoring/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/operator/apis/monitoring/v1alpha1/zz_generated.deepcopy.go @@ -33,13 +33,6 @@ func (in *AlertingSpec) DeepCopyInto(out *AlertingSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.AlertmanagerConfigs != nil { - in, out := &in.AlertmanagerConfigs, &out.AlertmanagerConfigs - *out = make([]NamespacedSecretOrConfigMap, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } return } @@ -80,6 +73,27 @@ func (in *AlertmanagerEndpoints) DeepCopy() *AlertmanagerEndpoints { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Authorization) DeepCopyInto(out *Authorization) { + *out = *in + if in.Credentials != nil { + in, out := &in.Credentials, &out.Credentials + *out = new(NamespacedSecretKeySelector) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authorization. +func (in *Authorization) DeepCopy() *Authorization { + if in == nil { + return nil + } + out := new(Authorization) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LabelMapping) DeepCopyInto(out *LabelMapping) { *out = *in @@ -511,27 +525,6 @@ func (in *RulesStatus) DeepCopy() *RulesStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Authorization) DeepCopyInto(out *Authorization) { - *out = *in - if in.Credentials != nil { - in, out := &in.Credentials, &out.Credentials - *out = new(NamespacedSecretKeySelector) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SafeAuthorization. -func (in *Authorization) DeepCopy() *Authorization { - if in == nil { - return nil - } - out := new(Authorization) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ScrapeEndpoint) DeepCopyInto(out *ScrapeEndpoint) { *out = *in diff --git a/pkg/operator/operator_config.go b/pkg/operator/operator_config.go index 347a6f7d4e..8feba5a67b 100644 --- a/pkg/operator/operator_config.go +++ b/pkg/operator/operator_config.go @@ -34,14 +34,17 @@ const ( RuleEvaluatorPort = 19092 ) -var ( - rulesLabels = map[string]string{ +func rulesLabels() map[string]string { + return map[string]string{ LabelAppName: NameRuleEvaluator, } - rulesAnnotations = map[string]string{ +} + +func rulesAnnotations() map[string]string { + return map[string]string{ AnnotationMetricName: componentName, } -) +} // setupOperatorConfigControllers ensures a rule-evaluator // deployment as part of managed collection. @@ -92,15 +95,19 @@ func (r *operatorConfigReconciler) Reconcile(ctx context.Context, req reconcile. logger := logr.FromContext(ctx).WithValues("operatorconfig", req.NamespacedName) logger.Info("reconciling operatorconfig") - var config = &monitoringv1alpha1.OperatorConfig{} + var ( + config = &monitoringv1alpha1.OperatorConfig{} + secretData map[string][]byte + ) if err := r.client.Get(ctx, req.NamespacedName, config); err != nil { return reconcile.Result{}, errors.Wrap(err, "get operatorconfig") } - if err := r.ensureRuleEvaluatorConfig(ctx, config); err != nil { + secretData, err := r.ensureRuleEvaluatorConfig(ctx, config) + if err != nil { return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator config") } - if err := r.ensureRuleEvaluatorSecrets(ctx); err != nil { + if err := r.ensureRuleEvaluatorSecrets(ctx, secretData); err != nil { return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator secrets") } @@ -112,25 +119,25 @@ func (r *operatorConfigReconciler) Reconcile(ctx context.Context, req reconcile. } // ensureRuleEvaluatorConfig reconciles the ConfigMap for rule-evaluator. -func (r *operatorConfigReconciler) ensureRuleEvaluatorConfig(ctx context.Context, config *monitoringv1alpha1.OperatorConfig) error { - amConfigs, err := r.makeAlertManagerConfigs(ctx, &config.Rules.Alerting) +func (r *operatorConfigReconciler) ensureRuleEvaluatorConfig(ctx context.Context, config *monitoringv1alpha1.OperatorConfig) (map[string][]byte, error) { + amConfigs, secretData, err := r.makeAlertManagerConfigs(ctx, &config.Rules.Alerting) if err != nil { - return errors.Wrap(err, "make alertmanager config") + return secretData, errors.Wrap(err, "make alertmanager config") } cm, err := makeRuleEvaluatorConfigMap(amConfigs, NameRuleEvaluator, r.opts.OperatorNamespace, "config.yaml") if err != nil { - return errors.Wrap(err, "make rule-evaluator configmap") + return secretData, errors.Wrap(err, "make rule-evaluator configmap") } // Upsert rule-evaluator ConfigMap. if err := r.client.Update(ctx, cm); err != nil { if err := r.client.Create(ctx, cm); err != nil { - return errors.Wrap(err, "create rule-evaluator config") + return secretData, errors.Wrap(err, "create rule-evaluator config") } } else if err != nil { - return errors.Wrap(err, "update rule-evaluator config") + return secretData, errors.Wrap(err, "update rule-evaluator config") } - return nil + return secretData, nil } // makeRuleEvaluatorConfigMap creates the ConfigMap for rule-evaluator. @@ -179,17 +186,17 @@ func makeRuleEvaluatorConfigMap(amConfigs []yaml.MapSlice, name, namespace, file } // ensureRuleEvaluatorSecrets reconciles the Secrets for rule-evaluator. -func (r *operatorConfigReconciler) ensureRuleEvaluatorSecrets(ctx context.Context) error { - var secret = &corev1.Secret{ +func (r *operatorConfigReconciler) ensureRuleEvaluatorSecrets(ctx context.Context, data map[string][]byte) error { + secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: RulesSecretName, Namespace: r.opts.OperatorNamespace, - Annotations: rulesAnnotations, - Labels: rulesLabels, + Annotations: rulesAnnotations(), + Labels: rulesLabels(), }, Data: make(map[string][]byte), } - for f, b := range r.secretData { + for f, b := range data { secret.Data[f] = b } @@ -220,12 +227,6 @@ func (r *operatorConfigReconciler) ensureRuleEvaluatorDeployment(ctx context.Con // makeRuleEvaluatorDeployment creates the Deployment for rule-evaluator. func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoringv1alpha1.RuleEvaluatorSpec) *appsv1.Deployment { - podLabels := map[string]string{ - LabelAppName: NameRuleEvaluator, - } - podAnnotations := map[string]string{ - AnnotationMetricName: componentName, - } evaluatorArgs := []string{ fmt.Sprintf("--config.file=%s", path.Join(configOutDir, configFilename)), fmt.Sprintf("--web.listen-address=:%d", RuleEvaluatorPort), @@ -241,12 +242,12 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring } spec := appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ - MatchLabels: rulesLabels, + MatchLabels: rulesLabels(), }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: rulesLabels, - Annotations: rulesAnnotations, + Labels: rulesLabels(), + Annotations: rulesAnnotations(), }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -404,8 +405,6 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring ObjectMeta: metav1.ObjectMeta{ Namespace: r.opts.OperatorNamespace, Name: NameRuleEvaluator, - // TODO(pintohutch): OwnerReferences should tear down rule-evaluator - // when gmp-operator is deleted. }, Spec: spec, } @@ -416,8 +415,11 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring // https://prometheus.io/docs/prometheus/latest/configuration/configuration/#alertmanager_config. // TODO(pintohutch): change function signature to use native Promethues go structs // over []yaml.MapSlice. -func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, spec *monitoringv1alpha1.AlertingSpec) ([]yaml.MapSlice, error) { - var configs []yaml.MapSlice +func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, spec *monitoringv1alpha1.AlertingSpec) ([]yaml.MapSlice, map[string][]byte, error) { + var ( + configs []yaml.MapSlice + secretData = make(map[string][]byte) + ) for _, am := range spec.Alertmanagers { var cfg yaml.MapSlice // Timeout, APIVersion, PathPrefix, and Scheme all resort to defaults if left unspecified. @@ -438,8 +440,8 @@ func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, } // Authorization. - // Default to Bearer for authorization type. if am.Authorization != nil { + // TODO(pintohutch): use native Prometheus structs here. authCfg := yaml.MapSlice{} if t := am.Authorization.Type; t != "" { authCfg = append(authCfg, yaml.MapItem{Key: "type", Value: strings.TrimSpace(t)}) @@ -447,7 +449,7 @@ func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, if c := am.Authorization.Credentials; c != nil { b, err := getSecretKeyBytes(ctx, r.client, c) if err != nil { - return configs, err + return configs, secretData, err } authCfg = append(authCfg, yaml.MapItem{Key: "credentials", Value: string(b)}) } @@ -460,9 +462,8 @@ func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, // Populate secretData cache to act on (i.e. upsert dedicated Secret) later. secretData, err := getTLSSecretData(ctx, r.client, tls) if err != nil { - return configs, err + return configs, secretData, err } - r.secretData = secretData } // Kubernetes SD configs. @@ -481,24 +482,7 @@ func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, configs = append(configs, cfg) } - // Allow for separate alertmanager_configs to be used by rule-evaluator - // in cases where the CRDs don't provide enough configuration. - // Note: any configs that specify filepaths are unlikely to work as this - // requires manually mounting volumes and files to the deployment. - for _, amc := range spec.AlertmanagerConfigs { - b, err := getSecretOrConfigMapBytes(ctx, r.client, &amc) - if err != nil { - return configs, err - } - var cfgs []yaml.MapSlice - err = yaml.Unmarshal(b, &cfgs) - if err != nil { - return nil, errors.Wrap(err, "unmarshalling alert manager configs") - } - configs = append(configs, cfgs...) - } - - return configs, nil + return configs, secretData, nil } // tlsConfigYAML creates a yaml.MapSlice compatible with https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config From d7ea3f2ee58b3b66b2d3268f65fe2ae7270a5539 Mon Sep 17 00:00:00 2001 From: Daniel Clark Date: Wed, 13 Oct 2021 00:19:18 +0000 Subject: [PATCH 3/4] review feedback - 2 --- cmd/operator/deploy/operator/clusterrole.yaml | 11 +++- cmd/operator/deploy/operator/operator.yaml | 14 ----- cmd/operator/deploy/operator/role.yaml | 26 --------- pkg/operator/operator_config.go | 58 ++++++++++--------- 4 files changed, 41 insertions(+), 68 deletions(-) delete mode 100644 cmd/operator/deploy/operator/role.yaml diff --git a/cmd/operator/deploy/operator/clusterrole.yaml b/cmd/operator/deploy/operator/clusterrole.yaml index d8d9142d8f..533d685992 100644 --- a/cmd/operator/deploy/operator/clusterrole.yaml +++ b/cmd/operator/deploy/operator/clusterrole.yaml @@ -39,13 +39,20 @@ metadata: rules: - apiGroups: ["", "apps", "admissionregistration.k8s.io", "monitoring.googleapis.com", "certificates.k8s.io"] resources: +# TODO(pintohutch): restrict access to non-CRD resources to select namespaces only. +# This can also be enforced via watch/list filters of controllers/managers. +# Ultimately we'll want that enforced here by the kube-apiserver and configured +# properly at the controller/manager level. + - deployments - podmonitorings - podmonitorings/status - - rules - - operatorconfigs - configmaps + - daemonsets - validatingwebhookconfigurations - certificatesigningrequests + - rules + - operatorconfigs + - secrets verbs: ["create", "update", "get", "list", "watch", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/cmd/operator/deploy/operator/operator.yaml b/cmd/operator/deploy/operator/operator.yaml index 2ee64203b8..30d0537d77 100644 --- a/cmd/operator/deploy/operator/operator.yaml +++ b/cmd/operator/deploy/operator/operator.yaml @@ -50,20 +50,6 @@ subjects: namespace: gmp-system name: operator --- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: gmp-system:operator - namespace: gmp-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: gmp-system:operator -subjects: -- kind: ServiceAccount - namespace: gmp-system - name: operator ---- apiVersion: apps/v1 kind: Deployment metadata: diff --git a/cmd/operator/deploy/operator/role.yaml b/cmd/operator/deploy/operator/role.yaml deleted file mode 100644 index 6927660090..0000000000 --- a/cmd/operator/deploy/operator/role.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: gmp-system:operator - namespace: gmp-system -rules: -- apiGroups: [""] - resources: - - secrets - - deployments - - daemonsets - verbs: ["get", "watch", "create", "delete", "list", "update"] \ No newline at end of file diff --git a/pkg/operator/operator_config.go b/pkg/operator/operator_config.go index 8feba5a67b..e3c9af972a 100644 --- a/pkg/operator/operator_config.go +++ b/pkg/operator/operator_config.go @@ -62,7 +62,7 @@ func setupOperatorConfigControllers(op *Operator) error { &monitoringv1alpha1.OperatorConfig{}, ). Owns( - &corev1.ConfigMap{}, + &corev1.Secret{}, builder.WithPredicates(objFilter)). Owns( &appsv1.Deployment{}, @@ -99,18 +99,26 @@ func (r *operatorConfigReconciler) Reconcile(ctx context.Context, req reconcile. config = &monitoringv1alpha1.OperatorConfig{} secretData map[string][]byte ) + + // Fetch OperatorConfig. if err := r.client.Get(ctx, req.NamespacedName, config); err != nil { return reconcile.Result{}, errors.Wrap(err, "get operatorconfig") } + + // Ensure the rule-evaluator config and grab any to-be-mirrored + // secret data on the way. secretData, err := r.ensureRuleEvaluatorConfig(ctx, config) if err != nil { return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator config") } + // Mirror the fetched secret data to where the rule-evaluator can + // mount and access. if err := r.ensureRuleEvaluatorSecrets(ctx, secretData); err != nil { return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator secrets") } + // Ensure the rule-evaluator deployment and volume mounts. if err := r.ensureRuleEvaluatorDeployment(ctx, &config.Rules); err != nil { return reconcile.Result{}, errors.Wrap(err, "ensure rule-evaluator deploy") } @@ -118,20 +126,20 @@ func (r *operatorConfigReconciler) Reconcile(ctx context.Context, req reconcile. return reconcile.Result{}, nil } -// ensureRuleEvaluatorConfig reconciles the ConfigMap for rule-evaluator. -func (r *operatorConfigReconciler) ensureRuleEvaluatorConfig(ctx context.Context, config *monitoringv1alpha1.OperatorConfig) (map[string][]byte, error) { - amConfigs, secretData, err := r.makeAlertManagerConfigs(ctx, &config.Rules.Alerting) +// ensureRuleEvaluatorConfig reconciles the config for rule-evaluator. +func (r *operatorConfigReconciler) ensureRuleEvaluatorConfig(ctx context.Context, oc *monitoringv1alpha1.OperatorConfig) (map[string][]byte, error) { + amConfigs, secretData, err := r.makeAlertManagerConfigs(ctx, &oc.Rules.Alerting) if err != nil { return secretData, errors.Wrap(err, "make alertmanager config") } - cm, err := makeRuleEvaluatorConfigMap(amConfigs, NameRuleEvaluator, r.opts.OperatorNamespace, "config.yaml") + cfg, err := makeRuleEvaluatorConfig(amConfigs, NameRuleEvaluator, r.opts.OperatorNamespace, configFilename) if err != nil { return secretData, errors.Wrap(err, "make rule-evaluator configmap") } - // Upsert rule-evaluator ConfigMap. - if err := r.client.Update(ctx, cm); err != nil { - if err := r.client.Create(ctx, cm); err != nil { + // Upsert rule-evaluator config. + if err := r.client.Update(ctx, cfg); err != nil { + if err := r.client.Create(ctx, cfg); err != nil { return secretData, errors.Wrap(err, "create rule-evaluator config") } } else if err != nil { @@ -140,10 +148,12 @@ func (r *operatorConfigReconciler) ensureRuleEvaluatorConfig(ctx context.Context return secretData, nil } -// makeRuleEvaluatorConfigMap creates the ConfigMap for rule-evaluator. +// makeRuleEvaluatorConfig creates the config for rule-evaluator. +// This is stored as a Secret rather than a ConfigMap as it could contain +// sensitive configuration information. // TODO(pintohutch): change function signature to use native Promethues go structs // over k8s configmap. -func makeRuleEvaluatorConfigMap(amConfigs []yaml.MapSlice, name, namespace, filename string) (*corev1.ConfigMap, error) { +func makeRuleEvaluatorConfig(amConfigs []yaml.MapSlice, name, namespace, filename string) (*corev1.Secret, error) { // Prepare and encode the Prometheus config used in rule-evaluator. pmConfig := yaml.MapSlice{} @@ -172,17 +182,17 @@ func makeRuleEvaluatorConfigMap(amConfigs []yaml.MapSlice, name, namespace, file return nil, errors.Wrap(err, "marshal Prometheus config") } - // Create rule-evaluator ConfigMap. - cm := &corev1.ConfigMap{ + // Create rule-evaluator Secret. + s := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Data: map[string]string{ - filename: string(cfgEncoded), + Data: map[string][]byte{ + filename: cfgEncoded, }, } - return cm, nil + return s, nil } // ensureRuleEvaluatorSecrets reconciles the Secrets for rule-evaluator. @@ -346,10 +356,8 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring // Rule-evaluator input Prometheus config. Name: configVolumeName, VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: NameRuleEvaluator, - }, + Secret: &corev1.SecretVolumeSource{ + SecretName: NameRuleEvaluator, }, }, }, { @@ -376,6 +384,7 @@ func (r *operatorConfigReconciler) makeRuleEvaluatorDeployment(rules *monitoring }, }, }, { + // Mirrored config secrets (config specified as filepaths). Name: secretVolumeName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ @@ -460,10 +469,11 @@ func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, if tls := am.TLSConfig; tls != nil { cfg = append(cfg, yaml.MapItem{Key: "tls_config", Value: tlsConfigYAML(secretsDir, tls)}) // Populate secretData cache to act on (i.e. upsert dedicated Secret) later. - secretData, err := getTLSSecretData(ctx, r.client, tls) + sd, err := getTLSSecretData(ctx, r.client, tls) if err != nil { - return configs, secretData, err + return configs, sd, err } + secretData = sd } // Kubernetes SD configs. @@ -472,11 +482,7 @@ func (r *operatorConfigReconciler) makeAlertManagerConfigs(ctx context.Context, // Relabel configs. cfg = append(cfg, yaml.MapItem{Key: "relabel_configs", Value: relabelConfigsYAML(&am)}) - // TODO(pintohutch): Unsure if these make sense within K8s endpoints SD... - // basic_auth support. - // oauth2 support. - // proxy_url support. - // follow_redirects support. + // TODO(pintohutch): add support for basic_auth, oauth2, proxy_url, follow_redirects. // Append to alertmanagers config array. configs = append(configs, cfg) From a59558d7d6faf1f97a60db20b94b83482b934065 Mon Sep 17 00:00:00 2001 From: Daniel Clark Date: Wed, 13 Oct 2021 00:23:39 +0000 Subject: [PATCH 4/4] review feedback - 2a --- pkg/operator/operator_config.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/operator/operator_config.go b/pkg/operator/operator_config.go index e3c9af972a..dbd6f3afae 100644 --- a/pkg/operator/operator_config.go +++ b/pkg/operator/operator_config.go @@ -77,9 +77,8 @@ func setupOperatorConfigControllers(op *Operator) error { // operatorConfigReconciler reconciles the OperatorConfig CRD. type operatorConfigReconciler struct { - client client.Client - opts Options - secretData map[string][]byte + client client.Client + opts Options } // newOperatorConfigReconciler creates a new operatorConfigReconciler.