diff --git a/pkg/apis/agent/v1alpha1/agent_types.go b/pkg/apis/agent/v1alpha1/agent_types.go index a81088c158..ec84fd780a 100644 --- a/pkg/apis/agent/v1alpha1/agent_types.go +++ b/pkg/apis/agent/v1alpha1/agent_types.go @@ -339,16 +339,8 @@ func (aea *AgentESAssociation) AssociationConfAnnotationName() string { return commonv1.ElasticsearchConfigAnnotationName(aea.ref) } -func (aea *AgentESAssociation) AssociationConf() *commonv1.AssociationConf { - if aea.esAssocConfs == nil { - return nil - } - assocConf, found := aea.esAssocConfs[aea.ref] - if !found { - return nil - } - - return &assocConf +func (aea *AgentESAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConfByRef(aea, aea.ref, aea.esAssocConfs) } func (aea *AgentESAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { @@ -370,8 +362,8 @@ func (a *AgentKibanaAssociation) ElasticServiceAccount() (commonv1.ServiceAccoun return "", nil } -func (a *AgentKibanaAssociation) AssociationConf() *commonv1.AssociationConf { - return a.kbAssocConf +func (a *AgentKibanaAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(a, a.kbAssocConf) } func (a *AgentKibanaAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { @@ -414,8 +406,8 @@ func (a *AgentFleetServerAssociation) ElasticServiceAccount() (commonv1.ServiceA return "", nil } -func (a *AgentFleetServerAssociation) AssociationConf() *commonv1.AssociationConf { - return a.fsAssocConf +func (a *AgentFleetServerAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(a, a.fsAssocConf) } func (a *AgentFleetServerAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { diff --git a/pkg/apis/apm/v1/apmserver_types.go b/pkg/apis/apm/v1/apmserver_types.go index fd0ee51f01..bdd8573259 100644 --- a/pkg/apis/apm/v1/apmserver_types.go +++ b/pkg/apis/apm/v1/apmserver_types.go @@ -219,8 +219,8 @@ func (aes *ApmEsAssociation) AssociationRef() commonv1.ObjectSelector { return aes.Spec.ElasticsearchRef.WithDefaultNamespace(aes.Namespace) } -func (aes *ApmEsAssociation) AssociationConf() *commonv1.AssociationConf { - return aes.esAssocConf +func (aes *ApmEsAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(aes, aes.esAssocConf) } func (aes *ApmEsAssociation) SetAssociationConf(assocConf *commonv1.AssociationConf) { @@ -268,8 +268,8 @@ func (akb *ApmKibanaAssociation) RequiresAssociation() bool { return akb.Spec.KibanaRef.Name != "" } -func (akb *ApmKibanaAssociation) AssociationConf() *commonv1.AssociationConf { - return akb.kibanaAssocConf +func (akb *ApmKibanaAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(akb, akb.kibanaAssocConf) } func (akb *ApmKibanaAssociation) SetAssociationConf(assocConf *commonv1.AssociationConf) { diff --git a/pkg/apis/beat/v1beta1/beat_types.go b/pkg/apis/beat/v1beta1/beat_types.go index b034ebbe36..359a17bf62 100644 --- a/pkg/apis/beat/v1beta1/beat_types.go +++ b/pkg/apis/beat/v1beta1/beat_types.go @@ -253,8 +253,8 @@ func (b *BeatESAssociation) AssociationConfAnnotationName() string { return commonv1.ElasticsearchConfigAnnotationNameBase } -func (b *BeatESAssociation) AssociationConf() *commonv1.AssociationConf { - return b.esAssocConf +func (b *BeatESAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(b, b.esAssocConf) } func (b *BeatESAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { @@ -271,8 +271,8 @@ type BeatKibanaAssociation struct { var _ commonv1.Association = &BeatKibanaAssociation{} -func (b *BeatKibanaAssociation) AssociationConf() *commonv1.AssociationConf { - return b.kbAssocConf +func (b *BeatKibanaAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(b, b.kbAssocConf) } func (b *BeatKibanaAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { diff --git a/pkg/apis/common/v1/association.go b/pkg/apis/common/v1/association.go index 1dace9e8e6..f2fb3a200a 100644 --- a/pkg/apis/common/v1/association.go +++ b/pkg/apis/common/v1/association.go @@ -166,7 +166,7 @@ type Association interface { AssociationConfAnnotationName() string // AssociationConf is the configuration of the Association allowing to connect to the Association resource. - AssociationConf() *AssociationConf + AssociationConf() (*AssociationConf, error) SetAssociationConf(*AssociationConf) // AssociationID uniquely identifies this Association among all Associations of the same type belonging to Associated() diff --git a/pkg/apis/common/v1/conf.go b/pkg/apis/common/v1/conf.go new file mode 100644 index 0000000000..c1f292fcae --- /dev/null +++ b/pkg/apis/common/v1/conf.go @@ -0,0 +1,70 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License 2.0; +// you may not use this file except in compliance with the Elastic License 2.0. + +package v1 + +import ( + "encoding/json" + "reflect" + "unsafe" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/types" +) + +// GetAndSetAssociationConf returns the association configuration if it is not nil, else the association configured is +// read from the annotation and put back in the given association. +func GetAndSetAssociationConf(assoc Association, assocConf *AssociationConf) (*AssociationConf, error) { + if assocConf == nil { + return setAssocConfFromAnnotation(assoc) + } + return assocConf, nil +} + +// GetAndSetAssociationConfByRef returns the association configuration corresponding to the namespaced name of the +// referenced resource if it is found in the given map of association configurations. +// The association configurations map is not persisted and can be cleared by an update of the parent resource +// (see https://github.com/elastic/cloud-on-k8s/issues/4709#issuecomment-1042898108), hence we check if this map is empty, +// in which case we try to populate it again from the annotation. +func GetAndSetAssociationConfByRef(assoc Association, ref types.NamespacedName, assocConfs map[types.NamespacedName]AssociationConf) (*AssociationConf, error) { + assocConf, found := assocConfs[ref] + if !found { + return setAssocConfFromAnnotation(assoc) + } + return &assocConf, nil +} + +// setAssocConfFromAnnotation sets the association configuration extracted from the annotations in the given association. +func setAssocConfFromAnnotation(assoc Association) (*AssociationConf, error) { + assocConf, err := extractAssocConfFromAnnotation(assoc.Associated().GetAnnotations(), assoc.AssociationConfAnnotationName()) + if err != nil { + return nil, err + } + assoc.SetAssociationConf(assocConf) + return assocConf, nil +} + +// extractAssocConfFromAnnotation extracts the association configuration from annotations and an annotation name. +func extractAssocConfFromAnnotation(annotations map[string]string, annotationName string) (*AssociationConf, error) { + var assocConf AssociationConf + serializedConf, exists := annotations[annotationName] + if !exists || serializedConf == "" { + return nil, nil + } + + if err := json.Unmarshal(unsafeStringToBytes(serializedConf), &assocConf); err != nil { + return nil, errors.Wrapf(err, "failed to extract association configuration") + } + + return &assocConf, nil +} + +func unsafeStringToBytes(s string) []byte { + hdr := *(*reflect.StringHeader)(unsafe.Pointer(&s)) //nolint:govet + return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ //nolint:govet + Data: hdr.Data, + Len: hdr.Len, + Cap: hdr.Len, + })) +} diff --git a/pkg/apis/elasticsearch/v1/elasticsearch_types.go b/pkg/apis/elasticsearch/v1/elasticsearch_types.go index 72a97483a1..da0676f5c5 100644 --- a/pkg/apis/elasticsearch/v1/elasticsearch_types.go +++ b/pkg/apis/elasticsearch/v1/elasticsearch_types.go @@ -546,16 +546,8 @@ func (ema *EsMonitoringAssociation) AssociationRef() commonv1.ObjectSelector { } } -func (ema *EsMonitoringAssociation) AssociationConf() *commonv1.AssociationConf { - if ema.AssocConfs == nil { - return nil - } - assocConf, found := ema.AssocConfs[ema.ref] - if !found { - return nil - } - - return &assocConf +func (ema *EsMonitoringAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConfByRef(ema, ema.ref, ema.AssocConfs) } func (ema *EsMonitoringAssociation) SetAssociationConf(assocConf *commonv1.AssociationConf) { diff --git a/pkg/apis/elasticsearch/v1/elasticsearch_types_test.go b/pkg/apis/elasticsearch/v1/elasticsearch_types_test.go index 6aa876d13c..1f1114d034 100644 --- a/pkg/apis/elasticsearch/v1/elasticsearch_types_test.go +++ b/pkg/apis/elasticsearch/v1/elasticsearch_types_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1" "github.com/elastic/cloud-on-k8s/pkg/utils/pointer" "github.com/elastic/cloud-on-k8s/pkg/utils/set" ) @@ -314,3 +315,84 @@ func TestElasticsearch_DisabledPredicates(t *testing.T) { }) } } + +// Test_AssociationConfs tests that if the association configuration map in an associated object is cleared, then +// AssociationConf() is rebuilt from the annotation. +func Test_AssociationConfs(t *testing.T) { + // simple es without associations + es := &Elasticsearch{ + ObjectMeta: metav1.ObjectMeta{ + Name: "es", + Namespace: "default", + }, + } + assert.Equal(t, 0, len(es.GetAssociations())) + assert.Equal(t, 0, len(es.AssocConfs)) + + // es with associations + metricsEsRef := commonv1.ObjectSelector{ + Name: "metrics", + Namespace: "default", + } + esMon := &Elasticsearch{ + ObjectMeta: metav1.ObjectMeta{ + Name: "esmon", + Namespace: "default", + Annotations: map[string]string{ + "association.k8s.elastic.co/es-conf-864518565": `{"authSecretName":"es-default-metrics-beat-es-mon-user","authSecretKey":"default-es-default-esmon-beat-es-mon-user","caCertProvided":true,"caSecretName":"es-es-monitoring-default-metrics-ca","url":"https://metrics-es-http.default.svc:9200","version":"8.0.0"}`, + "association.k8s.elastic.co/es-conf-1654136115": `{"authSecretName":"es-default-logs-beat-es-mon-user","authSecretKey":"default-es-default-esmon-beat-es-mon-user","caCertProvided":true,"caSecretName":"es-es-monitoring-default-logs-ca","url":"https://logs-es-http.default.svc:9200","version":"8.0.0"}`, + }, + }, + Spec: ElasticsearchSpec{ + Monitoring: Monitoring{ + Metrics: MetricsMonitoring{ + ElasticsearchRefs: []commonv1.ObjectSelector{metricsEsRef}, + }, + Logs: LogsMonitoring{ + ElasticsearchRefs: []commonv1.ObjectSelector{{ + Name: "logs", + Namespace: "default"}, + }, + }, + }, + }, + } + assert.Equal(t, 2, len(esMon.GetAssociations())) + + // map should be initially empty + assert.Equal(t, 0, len(esMon.AssocConfs)) + + // get and set assoc conf + for _, assoc := range esMon.GetAssociations() { + assocConf, err := assoc.AssociationConf() + assert.NotNil(t, assocConf) + assert.NoError(t, err) + } + // map should have been populated by the call to AssociationConf() + assert.Equal(t, 2, len(esMon.AssocConfs)) + + // simulate the case where the assocConfs map is reset, which can happen if the resource is updated + esMon.AssocConfs = nil + assert.Equal(t, 0, len(esMon.AssocConfs)) + + // get and set assoc conf + for _, assoc := range esMon.GetAssociations() { + assocConf, err := assoc.AssociationConf() + assert.NotNil(t, assocConf) + assert.NoError(t, err) + } + // checks that all map entries are set again + assert.Equal(t, 2, len(esMon.AssocConfs)) + + // delete just one entry in the map + delete(esMon.AssocConfs, metricsEsRef.NamespacedName()) + assert.Equal(t, 1, len(esMon.AssocConfs)) + + // checks that the missing entry is set again + for _, assoc := range esMon.GetAssociations() { + assocConf, err := assoc.AssociationConf() + assert.NotNil(t, assocConf) + assert.NoError(t, err) + } + assert.Equal(t, 2, len(esMon.AssocConfs)) +} diff --git a/pkg/apis/enterprisesearch/v1/enterprisesearch_types.go b/pkg/apis/enterprisesearch/v1/enterprisesearch_types.go index d47e177dd1..d0a9c18231 100644 --- a/pkg/apis/enterprisesearch/v1/enterprisesearch_types.go +++ b/pkg/apis/enterprisesearch/v1/enterprisesearch_types.go @@ -99,8 +99,8 @@ func (ent *EnterpriseSearch) AssociationRef() commonv1.ObjectSelector { return ent.Spec.ElasticsearchRef.WithDefaultNamespace(ent.Namespace) } -func (ent *EnterpriseSearch) AssociationConf() *commonv1.AssociationConf { - return ent.assocConf +func (ent *EnterpriseSearch) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(ent, ent.assocConf) } func (ent *EnterpriseSearch) SetAssociationConf(assocConf *commonv1.AssociationConf) { diff --git a/pkg/apis/enterprisesearch/v1beta1/enterprisesearch_types.go b/pkg/apis/enterprisesearch/v1beta1/enterprisesearch_types.go index 0eba517e09..f3a8506534 100644 --- a/pkg/apis/enterprisesearch/v1beta1/enterprisesearch_types.go +++ b/pkg/apis/enterprisesearch/v1beta1/enterprisesearch_types.go @@ -95,8 +95,8 @@ func (ent *EnterpriseSearch) AssociationRef() commonv1.ObjectSelector { return ent.Spec.ElasticsearchRef.WithDefaultNamespace(ent.Namespace) } -func (ent *EnterpriseSearch) AssociationConf() *commonv1.AssociationConf { - return ent.assocConf +func (ent *EnterpriseSearch) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(ent, ent.assocConf) } func (ent *EnterpriseSearch) SetAssociationConf(assocConf *commonv1.AssociationConf) { diff --git a/pkg/apis/kibana/v1/kibana_types.go b/pkg/apis/kibana/v1/kibana_types.go index b5497ddfa9..07fd2088c9 100644 --- a/pkg/apis/kibana/v1/kibana_types.go +++ b/pkg/apis/kibana/v1/kibana_types.go @@ -316,8 +316,8 @@ func (kbes *KibanaEsAssociation) AssociationRef() commonv1.ObjectSelector { return kbes.Spec.ElasticsearchRef.WithDefaultNamespace(kbes.Namespace) } -func (kbes *KibanaEsAssociation) AssociationConf() *commonv1.AssociationConf { - return kbes.assocConf +func (kbes *KibanaEsAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(kbes, kbes.assocConf) } func (kbes *KibanaEsAssociation) SetAssociationConf(assocConf *commonv1.AssociationConf) { @@ -367,8 +367,8 @@ func (kbent *KibanaEntAssociation) AssociationRef() commonv1.ObjectSelector { return kbent.Spec.EnterpriseSearchRef.WithDefaultNamespace(kbent.Namespace) } -func (kbent *KibanaEntAssociation) AssociationConf() *commonv1.AssociationConf { - return kbent.entAssocConf +func (kbent *KibanaEntAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(kbent, kbent.entAssocConf) } func (kbent *KibanaEntAssociation) SetAssociationConf(assocConf *commonv1.AssociationConf) { @@ -420,15 +420,8 @@ func (kbmon *KbMonitoringAssociation) AssociationRef() commonv1.ObjectSelector { } } -func (kbmon *KbMonitoringAssociation) AssociationConf() *commonv1.AssociationConf { - if kbmon.monitoringAssocConfs == nil { - return nil - } - assocConf, found := kbmon.monitoringAssocConfs[kbmon.ref] - if !found { - return nil - } - return &assocConf +func (kbmon *KbMonitoringAssociation) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConfByRef(kbmon, kbmon.ref, kbmon.monitoringAssocConfs) } func (kbmon *KbMonitoringAssociation) SetAssociationConf(assocConf *commonv1.AssociationConf) { diff --git a/pkg/apis/kibana/v1/kibana_types_test.go b/pkg/apis/kibana/v1/kibana_types_test.go index 8dd2962a2a..d7c050fdfb 100644 --- a/pkg/apis/kibana/v1/kibana_types_test.go +++ b/pkg/apis/kibana/v1/kibana_types_test.go @@ -6,10 +6,40 @@ package v1 import ( "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1" ) func TestApmEsAssociation_AssociationConfAnnotationName(t *testing.T) { k := Kibana{} require.Equal(t, "association.k8s.elastic.co/es-conf", k.EsAssociation().AssociationConfAnnotationName()) } + +// Test_AssociationConf tests that AssociationConf reads the conf from the annotation. +func Test_AssociationConf(t *testing.T) { + kb := &Kibana{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kb", + Namespace: "default", + Annotations: map[string]string{ + "association.k8s.elastic.co/es-conf": `{"authSecretName":"es-default-es-beat-es-mon-user","authSecretKey":"default-es-default-esmon-beat-es-mon-user","caCertProvided":true,"caSecretName":"es-es-monitoring-default-metrics-ca","url":"https://metrics-es-http.default.svc:9200","version":"8.0.0"}`, + }, + }, + Spec: KibanaSpec{ + ElasticsearchRef: commonv1.ObjectSelector{ + Name: "es", + Namespace: "default"}, + }, + } + + entAssocConf, err := kb.EntAssociation().AssociationConf() + assert.NoError(t, err) + assert.Nil(t, entAssocConf) + esAssocConf, err := kb.EsAssociation().AssociationConf() + assert.NoError(t, err) + assert.NotNil(t, esAssocConf) + assert.Equal(t, "https://metrics-es-http.default.svc:9200", esAssocConf.URL) +} diff --git a/pkg/apis/maps/v1alpha1/maps_types.go b/pkg/apis/maps/v1alpha1/maps_types.go index 6ed0bf2728..c0d86c8636 100644 --- a/pkg/apis/maps/v1alpha1/maps_types.go +++ b/pkg/apis/maps/v1alpha1/maps_types.go @@ -88,8 +88,8 @@ func (m *ElasticMapsServer) ServiceAccountName() string { return m.Spec.ServiceAccountName } -func (m *ElasticMapsServer) AssociationConf() *commonv1.AssociationConf { - return m.assocConf +func (m *ElasticMapsServer) AssociationConf() (*commonv1.AssociationConf, error) { + return commonv1.GetAndSetAssociationConf(m, m.assocConf) } func (m *ElasticMapsServer) SetAssociationConf(assocConf *commonv1.AssociationConf) { diff --git a/pkg/controller/agent/config.go b/pkg/controller/agent/config.go index 0bfd5eb229..cada681dae 100644 --- a/pkg/controller/agent/config.go +++ b/pkg/controller/agent/config.go @@ -91,14 +91,16 @@ func buildOutputConfig(params Params) (*settings.CanonicalConfig, error) { } } - for _, assoc := range esAssociations { - if !assoc.AssociationConf().IsConfigured() { + outputs := map[string]interface{}{} + for i, assoc := range esAssociations { + assocConf, err := assoc.AssociationConf() + if err != nil { + return settings.NewCanonicalConfig(), err + } + if !assocConf.IsConfigured() { return settings.NewCanonicalConfig(), nil } - } - outputs := map[string]interface{}{} - for i, assoc := range esAssociations { credentials, err := association.ElasticsearchAuthSettings(params.Client, assoc) if err != nil { return settings.NewCanonicalConfig(), err @@ -108,9 +110,9 @@ func buildOutputConfig(params Params) (*settings.CanonicalConfig, error) { "type": "elasticsearch", "username": credentials.Username, "password": credentials.Password, - "hosts": []string{assoc.AssociationConf().GetURL()}, + "hosts": []string{assocConf.GetURL()}, } - if assoc.AssociationConf().GetCACertProvided() { + if assocConf.GetCACertProvided() { output["ssl.certificate_authorities"] = []string{path.Join(certificatesDir(assoc), CAFileName)} } @@ -158,13 +160,18 @@ func extractConnectionSettings( return connectionSettings{}, err } + assocConf, err := assoc.AssociationConf() + if err != nil { + return connectionSettings{}, err + } + ca := "" - if assoc.AssociationConf().GetCACertProvided() { + if assocConf.GetCACertProvided() { ca = path.Join(certificatesDir(assoc), CAFileName) } return connectionSettings{ - host: assoc.AssociationConf().GetURL(), + host: assocConf.GetURL(), ca: ca, credentials: credentials, }, err diff --git a/pkg/controller/agent/controller.go b/pkg/controller/agent/controller.go index 48f238454e..5409805991 100644 --- a/pkg/controller/agent/controller.go +++ b/pkg/controller/agent/controller.go @@ -127,7 +127,7 @@ func (r *ReconcileAgent) Reconcile(ctx context.Context, request reconcile.Reques defer tracing.EndContextTransaction(ctx) var agent agentv1alpha1.Agent - if err := association.FetchWithAssociations(ctx, r.Client, request, &agent); err != nil { + if err := r.Client.Get(ctx, request.NamespacedName, &agent); err != nil { if apierrors.IsNotFound(err) { r.onDelete(request.NamespacedName) return reconcile.Result{}, nil @@ -153,7 +153,11 @@ func (r *ReconcileAgent) Reconcile(ctx context.Context, request reconcile.Reques func (r *ReconcileAgent) doReconcile(ctx context.Context, agent agentv1alpha1.Agent) *reconciler.Results { defer tracing.Span(&ctx)() results := reconciler.NewResult(ctx) - if !association.AreConfiguredIfSet(agent.GetAssociations(), r.recorder) { + areAssocsConfigured, err := association.AreConfiguredIfSet(agent.GetAssociations(), r.recorder) + if err != nil { + return results.WithError(err) + } + if !areAssocsConfigured { return results } diff --git a/pkg/controller/agent/driver.go b/pkg/controller/agent/driver.go index 00d1117ce1..fe6611caae 100644 --- a/pkg/controller/agent/driver.go +++ b/pkg/controller/agent/driver.go @@ -78,7 +78,11 @@ func internalReconcile(params Params) *reconciler.Results { if err != nil { return results.WithError(err) } - if !association.AllowVersion(agentVersion, ¶ms.Agent, params.Logger(), params.EventRecorder) { + assocAllowed, err := association.AllowVersion(agentVersion, ¶ms.Agent, params.Logger(), params.EventRecorder) + if err != nil { + return results.WithError(err) + } + if !assocAllowed { return results // will eventually retry } diff --git a/pkg/controller/agent/health.go b/pkg/controller/agent/health.go index f5c9ace8ea..4e69051561 100644 --- a/pkg/controller/agent/health.go +++ b/pkg/controller/agent/health.go @@ -10,24 +10,28 @@ import ( ) // CalculateHealth returns health of the Agent based on association status, desired count and ready count. -func CalculateHealth(associations []v1.Association, ready, desired int32) agentv1alpha1.AgentHealth { +func CalculateHealth(associations []v1.Association, ready, desired int32) (agentv1alpha1.AgentHealth, error) { for _, assoc := range associations { - if assoc.AssociationConf().IsConfigured() { + assocConf, err := assoc.AssociationConf() + if err != nil { + return "", err + } + if assocConf.IsConfigured() { statusMap := assoc.AssociationStatusMap(assoc.AssociationType()) if !statusMap.AllEstablished() { - return agentv1alpha1.AgentRedHealth + return agentv1alpha1.AgentRedHealth, nil } } } switch { case ready == 0: - return agentv1alpha1.AgentRedHealth + return agentv1alpha1.AgentRedHealth, nil case ready == desired: - return agentv1alpha1.AgentGreenHealth + return agentv1alpha1.AgentGreenHealth, nil case ready > 0: - return agentv1alpha1.AgentYellowHealth + return agentv1alpha1.AgentYellowHealth, nil default: - return agentv1alpha1.AgentRedHealth + return agentv1alpha1.AgentRedHealth, nil } } diff --git a/pkg/controller/agent/pod.go b/pkg/controller/agent/pod.go index d4c6b7ba9d..70250d4b19 100644 --- a/pkg/controller/agent/pod.go +++ b/pkg/controller/agent/pod.go @@ -153,7 +153,11 @@ func buildPodTemplate(params Params, fleetCerts *certificates.CertificatesSecret } // all volumes with CAs of direct associations - vols = append(vols, getVolumesFromAssociations(params.Agent.GetAssociations())...) + caAssocVols, err := getVolumesFromAssociations(params.Agent.GetAssociations()) + if err != nil { + return corev1.PodTemplateSpec{}, err + } + vols = append(vols, caAssocVols...) labels := maps.Merge(NewLabels(params.Agent), map[string]string{ VersionLabelName: spec.Version}) @@ -314,8 +318,12 @@ func applyRelatedEsAssoc(agent agentv1alpha1.Agent, esAssociation commonv1.Assoc ) } + assocConf, err := esAssociation.AssociationConf() + if err != nil { + return nil, err + } builder = builder.WithVolumeLikes(volume.NewSecretVolumeWithMountPath( - esAssociation.AssociationConf().GetCASecretName(), + assocConf.GetCASecretName(), fmt.Sprintf("%s-certs", esAssociation.AssociationType()), certificatesDir(esAssociation), )) @@ -346,21 +354,25 @@ func writeEsAssocToConfigHash(params Params, esAssociation commonv1.Association, ) } -func getVolumesFromAssociations(associations []commonv1.Association) []volume.VolumeLike { +func getVolumesFromAssociations(associations []commonv1.Association) ([]volume.VolumeLike, error) { var vols []volume.VolumeLike //nolint:prealloc for i, assoc := range associations { - if !assoc.AssociationConf().CAIsConfigured() { + assocConf, err := assoc.AssociationConf() + if err != nil { + return nil, err + } + if !assocConf.CAIsConfigured() { // skip as there is no volume to mount if association has no CA configured continue } - caSecretName := assoc.AssociationConf().GetCASecretName() + caSecretName := assocConf.GetCASecretName() vols = append(vols, volume.NewSecretVolumeWithMountPath( caSecretName, fmt.Sprintf("%s-certs-%d", assoc.AssociationType(), i), certificatesDir(assoc), )) } - return vols + return vols, nil } func getAssociatedFleetServer(params Params) (commonv1.Associated, error) { @@ -374,13 +386,8 @@ func getAssociatedFleetServer(params Params) (commonv1.Associated, error) { fsRef := assoc.AssociationRef() fs := agentv1alpha1.Agent{} - - if err := association.FetchWithAssociations( - params.Context, - params.Client, - reconcile.Request{NamespacedName: fsRef.NamespacedName()}, - &fs, - ); err != nil { + request := reconcile.Request{NamespacedName: fsRef.NamespacedName()} + if err = params.Client.Get(params.Context, request.NamespacedName, &fs); err != nil { return nil, err } @@ -496,9 +503,12 @@ func getFleetSetupFleetEnvVars(agent agentv1alpha1.Agent, client k8s.Client) (ma if assoc == nil { return fleetCfg, nil } - - fleetCfg[FleetURL] = assoc.AssociationConf().GetURL() - if assoc.AssociationConf().GetCACertProvided() { + assocConf, err := assoc.AssociationConf() + if err != nil { + return nil, err + } + fleetCfg[FleetURL] = assocConf.GetURL() + if assocConf.GetCACertProvided() { fleetCfg[FleetCA] = path.Join(certificatesDir(assoc), CAFileName) } } diff --git a/pkg/controller/agent/pod_test.go b/pkg/controller/agent/pod_test.go index a64ca9f747..71b57475d9 100644 --- a/pkg/controller/agent/pod_test.go +++ b/pkg/controller/agent/pod_test.go @@ -472,7 +472,8 @@ func Test_getVolumesFromAssociations(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assocs := tt.params.Agent.GetAssociations() tt.setAssocConfs(assocs) - associations := getVolumesFromAssociations(assocs) + associations, err := getVolumesFromAssociations(assocs) + require.NoError(t, err) require.Equal(t, tt.wantAssociationsLength, len(associations)) }) } diff --git a/pkg/controller/agent/reconcile.go b/pkg/controller/agent/reconcile.go index cfebadd439..7d99771fcc 100644 --- a/pkg/controller/agent/reconcile.go +++ b/pkg/controller/agent/reconcile.go @@ -143,7 +143,11 @@ func updateStatus(params Params, ready, desired int32) error { } agent.Status.AvailableNodes = ready agent.Status.ExpectedNodes = desired - agent.Status.Health = CalculateHealth(agent.GetAssociations(), ready, desired) + health, err := CalculateHealth(agent.GetAssociations(), ready, desired) + if err != nil { + return err + } + agent.Status.Health = health agent.Status.Version = common.LowestVersionFromPods(agent.Status.Version, pods, VersionLabelName) return params.Client.Status().Update(context.Background(), &agent) diff --git a/pkg/controller/apmserver/config.go b/pkg/controller/apmserver/config.go index b7e9b898d6..3fead53c74 100644 --- a/pkg/controller/apmserver/config.go +++ b/pkg/controller/apmserver/config.go @@ -105,7 +105,11 @@ func newConfigFromSpec(c k8s.Client, as *apmv1.ApmServer) (*settings.CanonicalCo } func newElasticsearchConfigFromSpec(c k8s.Client, esAssociation apmv1.ApmEsAssociation) (*settings.CanonicalConfig, error) { - if !esAssociation.AssociationConf().IsConfigured() { + esAssocConf, err := esAssociation.AssociationConf() + if err != nil { + return nil, err + } + if !esAssocConf.IsConfigured() { return settings.NewCanonicalConfig(), nil } @@ -116,11 +120,11 @@ func newElasticsearchConfigFromSpec(c k8s.Client, esAssociation apmv1.ApmEsAssoc } tmpOutputCfg := map[string]interface{}{ - "output.elasticsearch.hosts": []string{esAssociation.AssociationConf().GetURL()}, + "output.elasticsearch.hosts": []string{esAssocConf.GetURL()}, "output.elasticsearch.username": credentials.Username, "output.elasticsearch.password": credentials.Password, } - if esAssociation.AssociationConf().GetCACertProvided() { + if esAssocConf.GetCACertProvided() { tmpOutputCfg["output.elasticsearch.ssl.certificate_authorities"] = []string{filepath.Join(certificatesDir(esAssociation.AssociationType()), certificates.CAFileName)} } @@ -128,7 +132,11 @@ func newElasticsearchConfigFromSpec(c k8s.Client, esAssociation apmv1.ApmEsAssoc } func newKibanaConfigFromSpec(c k8s.Client, kibanaAssociation apmv1.ApmKibanaAssociation) (*settings.CanonicalConfig, error) { - if !kibanaAssociation.AssociationConf().IsConfigured() { + kbAssocConf, err := kibanaAssociation.AssociationConf() + if err != nil { + return nil, err + } + if !kbAssocConf.IsConfigured() { return settings.NewCanonicalConfig(), nil } @@ -140,11 +148,11 @@ func newKibanaConfigFromSpec(c k8s.Client, kibanaAssociation apmv1.ApmKibanaAsso tmpOutputCfg := map[string]interface{}{ "apm-server.kibana.enabled": true, - "apm-server.kibana.host": kibanaAssociation.AssociationConf().GetURL(), + "apm-server.kibana.host": kbAssocConf.GetURL(), "apm-server.kibana.username": credentials.Username, "apm-server.kibana.password": credentials.Password, } - if kibanaAssociation.AssociationConf().GetCACertProvided() { + if kbAssocConf.GetCACertProvided() { tmpOutputCfg["apm-server.kibana.ssl.certificate_authorities"] = []string{filepath.Join(certificatesDir(kibanaAssociation.AssociationType()), certificates.CAFileName)} } diff --git a/pkg/controller/apmserver/controller.go b/pkg/controller/apmserver/controller.go index f1f765c20c..11306eb3af 100644 --- a/pkg/controller/apmserver/controller.go +++ b/pkg/controller/apmserver/controller.go @@ -174,7 +174,7 @@ func (r *ReconcileApmServer) Reconcile(ctx context.Context, request reconcile.Re defer tracing.EndTransaction(tx) var as apmv1.ApmServer - if err := association.FetchWithAssociations(ctx, r.Client, request, &as); err != nil { + if err := r.Client.Get(ctx, request.NamespacedName, &as); err != nil { if apierrors.IsNotFound(err) { return reconcile.Result{}, r.onDelete(types.NamespacedName{ Namespace: request.Namespace, @@ -199,7 +199,11 @@ func (r *ReconcileApmServer) Reconcile(ctx context.Context, request reconcile.Re return reconcile.Result{}, r.onDelete(k8s.ExtractNamespacedName(&as)) } - if !association.AreConfiguredIfSet(as.GetAssociations(), r.recorder) { + areAssocsConfigured, err := association.AreConfiguredIfSet(as.GetAssociations(), r.recorder) + if err != nil { + return reconcile.Result{}, tracing.CaptureError(ctx, err) + } + if !areAssocsConfigured { return reconcile.Result{}, nil } @@ -241,7 +245,11 @@ func (r *ReconcileApmServer) doReconcile(ctx context.Context, request reconcile. return reconcile.Result{}, err } logger := log.WithValues("namespace", as.Namespace, "as_name", as.Name) - if !association.AllowVersion(asVersion, as, logger, r.recorder) { + assocAllowed, err := association.AllowVersion(asVersion, as, logger, r.recorder) + if err != nil { + return reconcile.Result{}, tracing.CaptureError(ctx, err) + } + if !assocAllowed { return reconcile.Result{}, nil // will eventually retry } diff --git a/pkg/controller/apmserver/deployment.go b/pkg/controller/apmserver/deployment.go index 8680d8fa6f..8bcd55f460 100644 --- a/pkg/controller/apmserver/deployment.go +++ b/pkg/controller/apmserver/deployment.go @@ -128,9 +128,13 @@ func buildConfigHash(c k8s.Client, as *apmv1.ApmServer, params PodSpecParams) (s // - in the CA certificates of the referenced resources in associations for _, association := range as.GetAssociations() { - if association.AssociationConf().CAIsConfigured() { + assocConf, err := association.AssociationConf() + if err != nil { + return "", err + } + if assocConf.CAIsConfigured() { var publicCASecret corev1.Secret - key := types.NamespacedName{Namespace: as.Namespace, Name: association.AssociationConf().GetCASecretName()} + key := types.NamespacedName{Namespace: as.Namespace, Name: assocConf.GetCASecretName()} if err := c.Get(context.Background(), key, &publicCASecret); err != nil { return "", err } diff --git a/pkg/controller/apmserver/pod.go b/pkg/controller/apmserver/pod.go index 1de0a52328..3db26709d5 100644 --- a/pkg/controller/apmserver/pod.go +++ b/pkg/controller/apmserver/pod.go @@ -143,7 +143,10 @@ func newPodSpec(c k8s.Client, as *apmv1.ApmServer, p PodSpecParams) (corev1.PodT WithVolumeMounts(volumeMounts...). WithInitContainers(initContainers...) - builder = withAssociationCACertsVolumes(builder, *as) + builder, err = withAssociationCACertsVolumes(builder, *as) + if err != nil { + return corev1.PodTemplateSpec{}, err + } builder = withHTTPCertsVolume(builder, *as) return builder.WithInitContainerDefaults().PodTemplate, nil @@ -161,19 +164,23 @@ func withHTTPCertsVolume(builder *defaults.PodTemplateBuilder, as apmv1.ApmServe return builder.WithVolumes(vol.Volume()).WithVolumeMounts(vol.VolumeMount()) } -func withAssociationCACertsVolumes(builder *defaults.PodTemplateBuilder, as apmv1.ApmServer) *defaults.PodTemplateBuilder { +func withAssociationCACertsVolumes(builder *defaults.PodTemplateBuilder, as apmv1.ApmServer) (*defaults.PodTemplateBuilder, error) { for _, association := range as.GetAssociations() { - if !association.AssociationConf().CAIsConfigured() { + assocConf, err := association.AssociationConf() + if err != nil { + return nil, err + } + if !assocConf.CAIsConfigured() { continue } vol := volume.NewSecretVolumeWithMountPath( - association.AssociationConf().GetCASecretName(), + assocConf.GetCASecretName(), fmt.Sprintf("%s-certs", association.AssociationType()), filepath.Join(ApmBaseDir, certificatesDir(association.AssociationType())), ) builder.WithVolumes(vol.Volume()).WithVolumeMounts(vol.VolumeMount()) } - return builder + return builder, nil } diff --git a/pkg/controller/association/conf.go b/pkg/controller/association/conf.go index 4cd55a8c8f..fbaaf35657 100644 --- a/pkg/controller/association/conf.go +++ b/pkg/controller/association/conf.go @@ -8,64 +8,43 @@ import ( "context" "encoding/json" "fmt" - "reflect" "strings" "unsafe" "github.com/go-logr/logr" "github.com/pkg/errors" - "go.elastic.co/apm" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/reconcile" commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1" "github.com/elastic/cloud-on-k8s/pkg/controller/common/events" - "github.com/elastic/cloud-on-k8s/pkg/controller/common/tracing" "github.com/elastic/cloud-on-k8s/pkg/controller/common/version" "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) -// FetchWithAssociations retrieves an object and extracts its association configurations. -func FetchWithAssociations( - ctx context.Context, - client k8s.Client, - request reconcile.Request, - associated commonv1.Associated, -) error { - span, _ := apm.StartSpan(ctx, "fetch_associations", tracing.SpanTypeApp) - defer span.End() - - if err := client.Get(context.Background(), request.NamespacedName, associated); err != nil { - return err - } - - for _, association := range associated.GetAssociations() { - assocConf, err := GetAssociationConf(association) - if err != nil { - return err - } - association.SetAssociationConf(assocConf) - } - - return nil -} - -func AreConfiguredIfSet(associations []commonv1.Association, r record.EventRecorder) bool { +func AreConfiguredIfSet(associations []commonv1.Association, r record.EventRecorder) (bool, error) { allAssociationsConfigured := true for _, association := range associations { - allAssociationsConfigured = allAssociationsConfigured && IsConfiguredIfSet(association, r) + isAssocConfigured, err := IsConfiguredIfSet(association, r) + if err != nil { + return false, err + } + allAssociationsConfigured = allAssociationsConfigured && isAssocConfigured } - return allAssociationsConfigured + return allAssociationsConfigured, nil } // IsConfiguredIfSet checks if an association is set in the spec and if it has been configured by an association controller. // This is used to prevent the deployment of an associated resource while the association is not yet fully configured. -func IsConfiguredIfSet(association commonv1.Association, r record.EventRecorder) bool { +func IsConfiguredIfSet(association commonv1.Association, r record.EventRecorder) (bool, error) { ref := association.AssociationRef() - if (&ref).IsDefined() && !association.AssociationConf().IsConfigured() { + assocConf, err := association.AssociationConf() + if err != nil { + return false, err + } + if (&ref).IsDefined() && !assocConf.IsConfigured() { r.Event( association, corev1.EventTypeWarning, @@ -79,9 +58,9 @@ func IsConfiguredIfSet(association commonv1.Association, r record.EventRecorder) "ref_namespace", ref.Namespace, "ref_name", ref.Name, ) - return false + return false, nil } - return true + return true, nil } type Credentials struct { @@ -96,7 +75,10 @@ func (c Credentials) HasServiceAccountToken() bool { // against an Elasticsearch cluster. // This is also used for transitive authentication that relies on Elasticsearch native realm (eg. APMServer -> Kibana) func ElasticsearchAuthSettings(c k8s.Client, association commonv1.Association) (Credentials, error) { - assocConf := association.AssociationConf() + assocConf, err := association.AssociationConf() + if err != nil { + return Credentials{}, err + } if !assocConf.AuthIsConfigured() { return Credentials{}, nil } @@ -123,23 +105,27 @@ func ElasticsearchAuthSettings(c k8s.Client, association commonv1.Association) ( // For example: Kibana in version 7.8.0 cannot be deployed if its Elasticsearch association reports version 7.7.0. // A difference in the patch version is ignored: Kibana 7.8.1+ can be deployed alongside Elasticsearch 7.8.0. // Referenced resources version is parsed from the association conf annotation. -func AllowVersion(resourceVersion version.Version, associated commonv1.Associated, logger logr.Logger, recorder record.EventRecorder) bool { +func AllowVersion(resourceVersion version.Version, associated commonv1.Associated, logger logr.Logger, recorder record.EventRecorder) (bool, error) { for _, assoc := range associated.GetAssociations() { assocRef := assoc.AssociationRef() if !assocRef.IsDefined() { // no association specified, move on continue } - if assoc.AssociationConf() == nil || assoc.AssociationConf().Version == "" { + assocConf, err := assoc.AssociationConf() + if err != nil { + return false, err + } + if assocConf == nil || assocConf.Version == "" { // no conf reported yet, this may be the initial resource creation logger.Info("Delaying version deployment since the version of an associated resource is not reported yet", "version", resourceVersion, "ref_namespace", assocRef.Namespace, "ref_name", assocRef.Name) - return false + return false, nil } - refVer, err := version.Parse(assoc.AssociationConf().Version) + refVer, err := version.Parse(assocConf.Version) if err != nil { - logger.Error(err, "Invalid version found in association configuration", "association_version", assoc.AssociationConf().Version) - return false + logger.Error(err, "Invalid version found in association configuration", "association_version", assocConf.Version) + return false, nil } compatibleVersions := refVer.GTE(resourceVersion) || ((refVer.Major == resourceVersion.Major) && (refVer.Minor == resourceVersion.Minor)) @@ -151,21 +137,10 @@ func AllowVersion(resourceVersion version.Version, associated commonv1.Associate "ref_type", assoc.AssociationType(), "ref_namespace", assocRef.Namespace, "ref_name", assocRef.Name) recorder.Event(associated, corev1.EventTypeWarning, events.EventReasonDelayed, fmt.Sprintf("Delaying deployment of version %s since the referenced %s is not upgraded yet", resourceVersion, assoc.AssociationType())) - return false + return false, nil } } - return true -} - -// GetAssociationConf extracts the association configuration from the given object by reading the annotations. -func GetAssociationConf(association commonv1.Association) (*commonv1.AssociationConf, error) { - accessor := meta.NewAccessor() - annotations, err := accessor.Annotations(association) - if err != nil { - return nil, err - } - - return extractAssociationConf(annotations, association.AssociationConfAnnotationName()) + return true, nil } // SingleAssociationOfType returns single association from the provided slice that matches provided type. Returns @@ -188,24 +163,6 @@ func SingleAssociationOfType( return result, nil } -func extractAssociationConf(annotations map[string]string, annotationName string) (*commonv1.AssociationConf, error) { - if len(annotations) == 0 { - return nil, nil - } - - var assocConf commonv1.AssociationConf - serializedConf, exists := annotations[annotationName] - if !exists || serializedConf == "" { - return nil, nil - } - - if err := json.Unmarshal(unsafeStringToBytes(serializedConf), &assocConf); err != nil { - return nil, errors.Wrapf(err, "failed to extract association configuration") - } - - return &assocConf, nil -} - // RemoveObsoleteAssociationConfs removes all no longer needed annotations on `associated` matching // `associationConfAnnotationNameBase` prefix. func RemoveObsoleteAssociationConfs( @@ -303,18 +260,6 @@ func UpdateAssociationConf( return client.Update(context.Background(), obj) } -// unsafeStringToBytes converts a string to a byte array without making extra allocations. -// since we read potentially large strings from annotations on every reconcile loop, this should help -// reduce the amount of garbage created. -func unsafeStringToBytes(s string) []byte { - hdr := *(*reflect.StringHeader)(unsafe.Pointer(&s)) //nolint:govet - return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ //nolint:govet - Data: hdr.Data, - Len: hdr.Len, - Cap: hdr.Len, - })) -} - // unsafeBytesToString converts a byte array to string without making extra allocations. func unsafeBytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) diff --git a/pkg/controller/association/conf_test.go b/pkg/controller/association/conf_test.go index 6059d07899..2ff25b0725 100644 --- a/pkg/controller/association/conf_test.go +++ b/pkg/controller/association/conf_test.go @@ -84,7 +84,7 @@ func testFetchAPMServer(t *testing.T) { client := k8s.NewFakeClient(tc.apmServer) var got apmv1.ApmServer - err := FetchWithAssociations(context.Background(), client, tc.request, &got) + err := client.Get(context.Background(), tc.request.NamespacedName, &got) if tc.wantErr { require.Error(t, err) @@ -96,11 +96,13 @@ func testFetchAPMServer(t *testing.T) { require.Equal(t, "test-image", got.Spec.Image) require.EqualValues(t, 1, got.Spec.Count) for _, assoc := range got.GetAssociations() { + assocConf, err := assoc.AssociationConf() + require.NoError(t, err) switch assoc.AssociationType() { case "elasticsearch": - require.Equal(t, tc.wantEsAssocConf, assoc.AssociationConf()) + require.Equal(t, tc.wantEsAssocConf, assocConf) case "kibana": - require.Equal(t, tc.wantKibanaAssocConf, assoc.AssociationConf()) + require.Equal(t, tc.wantKibanaAssocConf, assocConf) default: t.Fatalf("unknown association type: %s", assoc.AssociationType()) } @@ -146,7 +148,7 @@ func testFetchKibana(t *testing.T) { client := k8s.NewFakeClient(tc.kibana) var got kbv1.Kibana - err := FetchWithAssociations(context.Background(), client, tc.request, &got) + err := client.Get(context.Background(), tc.request.NamespacedName, &got) if tc.wantErr { require.Error(t, err) @@ -157,7 +159,9 @@ func testFetchKibana(t *testing.T) { require.Equal(t, "kb-ns", got.Namespace) require.Equal(t, "test-image", got.Spec.Image) require.EqualValues(t, 1, got.Spec.Count) - require.Equal(t, tc.wantAssocConf, got.EsAssociation().AssociationConf()) + assocConf, err := got.EsAssociation().AssociationConf() + require.NoError(t, err) + require.Equal(t, tc.wantAssocConf, assocConf) }) } } @@ -220,7 +224,8 @@ func TestAreConfiguredIfSet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := AreConfiguredIfSet(tt.associations, tt.recorder) + got, err := AreConfiguredIfSet(tt.associations, tt.recorder) + require.NoError(t, err) if got != tt.want { t.Errorf("AreConfiguredIfSet() got = %v, want %v", got, tt.want) } @@ -346,7 +351,7 @@ func TestUpdateAssociationConf(t *testing.T) { request := reconcile.Request{NamespacedName: types.NamespacedName{Name: "kb-test", Namespace: "kb-ns"}} client := k8s.NewFakeClient(kb) - assocConf := &commonv1.AssociationConf{ + expectedAssocConf := &commonv1.AssociationConf{ AuthSecretName: "auth-secret", AuthSecretKey: "kb-user", CASecretName: "ca-secret", @@ -355,13 +360,15 @@ func TestUpdateAssociationConf(t *testing.T) { // check the existing values var got kbv1.Kibana - err := FetchWithAssociations(context.Background(), client, request, &got) + err := client.Get(context.Background(), request.NamespacedName, &got) require.NoError(t, err) require.Equal(t, "kb-test", got.Name) require.Equal(t, "kb-ns", got.Namespace) require.Equal(t, "test-image", got.Spec.Image) require.EqualValues(t, 1, got.Spec.Count) - require.Equal(t, assocConf, got.EsAssociation().AssociationConf()) + assocConf, err := got.EsAssociation().AssociationConf() + require.NoError(t, err) + require.Equal(t, expectedAssocConf, assocConf) // update and check the new values newAssocConf := &commonv1.AssociationConf{ @@ -374,13 +381,15 @@ func TestUpdateAssociationConf(t *testing.T) { err = UpdateAssociationConf(client, got.EsAssociation(), newAssocConf) require.NoError(t, err) - err = FetchWithAssociations(context.Background(), client, request, &got) + err = client.Get(context.Background(), request.NamespacedName, &got) require.NoError(t, err) require.Equal(t, "kb-test", got.Name) require.Equal(t, "kb-ns", got.Namespace) require.Equal(t, "test-image", got.Spec.Image) require.EqualValues(t, 1, got.Spec.Count) - require.Equal(t, newAssocConf, got.EsAssociation().AssociationConf()) + assocConf, err = got.EsAssociation().AssociationConf() + require.NoError(t, err) + require.Equal(t, newAssocConf, assocConf) } func TestRemoveAssociationConf(t *testing.T) { @@ -388,7 +397,7 @@ func TestRemoveAssociationConf(t *testing.T) { request := reconcile.Request{NamespacedName: types.NamespacedName{Name: "kb-test", Namespace: "kb-ns"}} client := k8s.NewFakeClient(kb) - assocConf := &commonv1.AssociationConf{ + expectedAssocConf := &commonv1.AssociationConf{ AuthSecretName: "auth-secret", AuthSecretKey: "kb-user", CASecretName: "ca-secret", @@ -397,25 +406,29 @@ func TestRemoveAssociationConf(t *testing.T) { // check the existing values var got kbv1.Kibana - err := FetchWithAssociations(context.Background(), client, request, &got) + err := client.Get(context.Background(), request.NamespacedName, &got) require.NoError(t, err) require.Equal(t, "kb-test", got.Name) require.Equal(t, "kb-ns", got.Namespace) require.Equal(t, "test-image", got.Spec.Image) require.EqualValues(t, 1, got.Spec.Count) - require.Equal(t, assocConf, got.EsAssociation().AssociationConf()) + assocConf, err := got.EsAssociation().AssociationConf() + require.NoError(t, err) + require.Equal(t, expectedAssocConf, assocConf) // remove and check the new values err = RemoveAssociationConf(client, got.EsAssociation()) require.NoError(t, err) - err = FetchWithAssociations(context.Background(), client, request, &got) + err = client.Get(context.Background(), request.NamespacedName, &got) require.NoError(t, err) require.Equal(t, "kb-test", got.Name) require.Equal(t, "kb-ns", got.Namespace) require.Equal(t, "test-image", got.Spec.Image) require.EqualValues(t, 1, got.Spec.Count) - require.Nil(t, got.EsAssociation().AssociationConf()) + assocConf, err = got.EsAssociation().AssociationConf() + require.NoError(t, err) + require.Nil(t, assocConf) } func TestAllowVersion(t *testing.T) { @@ -501,7 +514,7 @@ func TestAllowVersion(t *testing.T) { logger := log.WithValues("a", "b") recorder := record.NewFakeRecorder(10) t.Run(tt.name, func(t *testing.T) { - if got := AllowVersion(tt.args.resourceVersion, tt.args.associated, logger, recorder); got != tt.want { + if got, err := AllowVersion(tt.args.resourceVersion, tt.args.associated, logger, recorder); err != nil && got != tt.want { t.Errorf("AllowVersion() = %v, want %v", got, tt.want) } }) diff --git a/pkg/controller/association/reconciler.go b/pkg/controller/association/reconciler.go index 704a070a13..5f97a6b8be 100644 --- a/pkg/controller/association/reconciler.go +++ b/pkg/controller/association/reconciler.go @@ -150,7 +150,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) ( defer tracing.EndTransaction(tx) associated := r.AssociatedObjTemplate() - if err := FetchWithAssociations(ctx, r.Client, request, associated); err != nil { + if err := r.Client.Get(ctx, request.NamespacedName, associated); err != nil { if apierrors.IsNotFound(err) { // object resource has been deleted, remove artifacts related to the association. r.onDelete(ctx, types.NamespacedName{ @@ -398,7 +398,11 @@ func (r *Reconciler) updateAssocConf( span, _ := apm.StartSpan(ctx, "update_assoc_conf", tracing.SpanTypeApp) defer span.End() - if !reflect.DeepEqual(expectedAssocConf, association.AssociationConf()) { + assocConf, err := association.AssociationConf() + if err != nil { + return "", err + } + if !reflect.DeepEqual(expectedAssocConf, assocConf) { r.log(k8s.ExtractNamespacedName(association)).Info("Updating association configuration") if err := UpdateAssociationConf(r.Client, association, expectedAssocConf); err != nil { if apierrors.IsConflict(err) { diff --git a/pkg/controller/beat/common/config.go b/pkg/controller/beat/common/config.go index e36b899b02..d9d55127ff 100644 --- a/pkg/controller/beat/common/config.go +++ b/pkg/controller/beat/common/config.go @@ -21,7 +21,11 @@ import ( // buildOutputConfig will create the output section in Beat config according to the association configuration. func buildOutputConfig(client k8s.Client, associated beatv1beta1.BeatESAssociation) (*settings.CanonicalConfig, error) { - if !associated.AssociationConf().IsConfigured() { + esAssocConf, err := associated.AssociationConf() + if err != nil { + return nil, err + } + if !esAssocConf.IsConfigured() { return settings.NewCanonicalConfig(), nil } @@ -32,13 +36,13 @@ func buildOutputConfig(client k8s.Client, associated beatv1beta1.BeatESAssociati esOutput := map[string]interface{}{ "output.elasticsearch": map[string]interface{}{ - "hosts": []string{associated.AssociationConf().GetURL()}, + "hosts": []string{esAssocConf.GetURL()}, "username": credentials.Username, "password": credentials.Password, }, } - if associated.AssociationConf().GetCACertProvided() { + if esAssocConf.GetCACertProvided() { esOutput["output.elasticsearch.ssl.certificate_authorities"] = []string{path.Join(certificatesDir(&associated), CAFileName)} } @@ -47,7 +51,11 @@ func buildOutputConfig(client k8s.Client, associated beatv1beta1.BeatESAssociati // BuildKibanaConfig builds on optional Kibana configuration for dashboard setup and visualizations. func BuildKibanaConfig(client k8s.Client, associated beatv1beta1.BeatKibanaAssociation) (*settings.CanonicalConfig, error) { - if !associated.AssociationConf().IsConfigured() { + kbAssocConf, err := associated.AssociationConf() + if err != nil { + return nil, err + } + if !kbAssocConf.IsConfigured() { return settings.NewCanonicalConfig(), nil } @@ -59,13 +67,13 @@ func BuildKibanaConfig(client k8s.Client, associated beatv1beta1.BeatKibanaAssoc kibanaCfg := map[string]interface{}{ "setup.dashboards.enabled": true, "setup.kibana": map[string]interface{}{ - "host": associated.AssociationConf().GetURL(), + "host": kbAssocConf.GetURL(), "username": credentials.Username, "password": credentials.Password, }, } - if associated.AssociationConf().GetCACertProvided() { + if kbAssocConf.GetCACertProvided() { kibanaCfg["setup.kibana.ssl.certificate_authorities"] = []string{path.Join(certificatesDir(&associated), CAFileName)} } return settings.NewCanonicalConfigFrom(kibanaCfg) diff --git a/pkg/controller/beat/common/config_test.go b/pkg/controller/beat/common/config_test.go index a626f25511..c59786ad17 100644 --- a/pkg/controller/beat/common/config_test.go +++ b/pkg/controller/beat/common/config_test.go @@ -76,7 +76,9 @@ func Test_buildBeatConfig(t *testing.T) { withAssocWithCA := *withAssoc.DeepCopy() esAssocWithCA := beatv1beta1.BeatESAssociation{Beat: &withAssocWithCA} - esAssocWithCA.AssociationConf().CACertProvided = true + assocConf, err := esAssocWithCA.AssociationConf() + require.NoError(t, err) + assocConf.CACertProvided = true withAssocWithCAWithonfig := *withAssocWithCA.DeepCopy() withAssocWithCAWithonfig.Spec.Config = userCfg diff --git a/pkg/controller/beat/common/driver.go b/pkg/controller/beat/common/driver.go index 0b57652756..f5e3a589e9 100644 --- a/pkg/controller/beat/common/driver.go +++ b/pkg/controller/beat/common/driver.go @@ -78,7 +78,11 @@ func Reconcile( if err != nil { return results.WithError(err) } - if !association.AllowVersion(beatVersion, ¶ms.Beat, params.Logger, params.Recorder()) { + assocAllowed, err := association.AllowVersion(beatVersion, ¶ms.Beat, params.Logger, params.Recorder()) + if err != nil { + return results.WithError(err) + } + if !assocAllowed { return results // will eventually retry } diff --git a/pkg/controller/beat/common/health.go b/pkg/controller/beat/common/health.go index a325db16ee..1cafcf3986 100644 --- a/pkg/controller/beat/common/health.go +++ b/pkg/controller/beat/common/health.go @@ -10,24 +10,28 @@ import ( ) // CalculateHealth returns health of the Beat based on association status, desired count and ready count. -func CalculateHealth(associations []v1.Association, ready, desired int32) beatv1beta1.BeatHealth { +func CalculateHealth(associations []v1.Association, ready, desired int32) (beatv1beta1.BeatHealth, error) { for _, assoc := range associations { - if assoc.AssociationConf().IsConfigured() { + assocConf, err := assoc.AssociationConf() + if err != nil { + return "", err + } + if assocConf.IsConfigured() { statusMap := assoc.AssociationStatusMap(assoc.AssociationType()) if !statusMap.AllEstablished() { - return beatv1beta1.BeatRedHealth + return beatv1beta1.BeatRedHealth, nil } } } switch { case ready == 0: - return beatv1beta1.BeatRedHealth + return beatv1beta1.BeatRedHealth, nil case ready == desired: - return beatv1beta1.BeatGreenHealth + return beatv1beta1.BeatGreenHealth, nil case ready > 0: - return beatv1beta1.BeatYellowHealth + return beatv1beta1.BeatYellowHealth, nil default: - return beatv1beta1.BeatRedHealth + return beatv1beta1.BeatRedHealth, nil } } diff --git a/pkg/controller/beat/common/health_test.go b/pkg/controller/beat/common/health_test.go index b5fcb7143a..b2d26bb4d0 100644 --- a/pkg/controller/beat/common/health_test.go +++ b/pkg/controller/beat/common/health_test.go @@ -158,7 +158,8 @@ func Test_CalculateHealth(t *testing.T) { }, } { t.Run(tt.name, func(t *testing.T) { - got := beatcommon.CalculateHealth(tt.associations, tt.ready, tt.desired) + got, err := beatcommon.CalculateHealth(tt.associations, tt.ready, tt.desired) + require.NoError(t, err) require.Equal(t, tt.want, got) }) } diff --git a/pkg/controller/beat/common/pod.go b/pkg/controller/beat/common/pod.go index 1ef3fc25d0..2215a79ee3 100644 --- a/pkg/controller/beat/common/pod.go +++ b/pkg/controller/beat/common/pod.go @@ -97,15 +97,19 @@ func buildPodTemplate( dataVolume, } - for _, association := range params.Beat.GetAssociations() { - if !association.AssociationConf().CAIsConfigured() { + for _, assoc := range params.Beat.GetAssociations() { + assocConf, err := assoc.AssociationConf() + if err != nil { + return corev1.PodTemplateSpec{}, err + } + if !assocConf.CAIsConfigured() { continue } - caSecretName := association.AssociationConf().GetCASecretName() + caSecretName := assocConf.GetCASecretName() caVolume := volume.NewSecretVolumeWithMountPath( caSecretName, - fmt.Sprintf("%s-certs", association.AssociationType()), - certificatesDir(association), + fmt.Sprintf("%s-certs", assoc.AssociationType()), + certificatesDir(assoc), ) vols = append(vols, caVolume) } diff --git a/pkg/controller/beat/common/reconcile.go b/pkg/controller/beat/common/reconcile.go index 6a2da641ed..691e61462e 100644 --- a/pkg/controller/beat/common/reconcile.go +++ b/pkg/controller/beat/common/reconcile.go @@ -142,7 +142,11 @@ func updateStatus(params DriverParams, ready, desired int32) error { } beat.Status.AvailableNodes = ready beat.Status.ExpectedNodes = desired - beat.Status.Health = CalculateHealth(beat.GetAssociations(), ready, desired) + health, err := CalculateHealth(beat.GetAssociations(), ready, desired) + if err != nil { + return err + } + beat.Status.Health = health beat.Status.Version = common.LowestVersionFromPods(beat.Status.Version, pods, VersionLabelName) return params.Client.Status().Update(context.Background(), &beat) diff --git a/pkg/controller/beat/controller.go b/pkg/controller/beat/controller.go index 1d6c26321f..238e0c2db0 100644 --- a/pkg/controller/beat/controller.go +++ b/pkg/controller/beat/controller.go @@ -132,9 +132,13 @@ func (r *ReconcileBeat) Reconcile(ctx context.Context, request reconcile.Request defer tracing.EndTransaction(tx) var beat beatv1beta1.Beat - if err := association.FetchWithAssociations(ctx, r.Client, request, &beat); err != nil { + err := r.Client.Get(ctx, request.NamespacedName, &beat) + if err != nil { if apierrors.IsNotFound(err) { - return reconcile.Result{}, r.onDelete(request.NamespacedName) + return reconcile.Result{}, r.onDelete(types.NamespacedName{ + Namespace: request.Namespace, + Name: request.Name, + }) } return reconcile.Result{}, tracing.CaptureError(ctx, err) } @@ -156,7 +160,11 @@ func (r *ReconcileBeat) Reconcile(ctx context.Context, request reconcile.Request func (r *ReconcileBeat) doReconcile(ctx context.Context, beat beatv1beta1.Beat) *reconciler.Results { results := reconciler.NewResult(ctx) - if !association.AreConfiguredIfSet(beat.GetAssociations(), r.recorder) { + areAssocsConfigured, err := association.AreConfiguredIfSet(beat.GetAssociations(), r.recorder) + if err != nil { + return results.WithError(err) + } + if !areAssocsConfigured { return results } diff --git a/pkg/controller/common/association/association.go b/pkg/controller/common/association/association.go index 90d3406697..180ac11387 100644 --- a/pkg/controller/common/association/association.go +++ b/pkg/controller/common/association/association.go @@ -31,7 +31,10 @@ func WriteAssocsToConfigHash(client k8s.Client, associations []commonv1.Associat } func writeAuthSecretToConfigHash(client k8s.Client, assoc commonv1.Association, configHash hash.Hash) error { - assocConf := assoc.AssociationConf() + assocConf, err := assoc.AssociationConf() + if err != nil { + return err + } if !assocConf.AuthIsConfigured() { return nil } @@ -57,7 +60,10 @@ func writeAuthSecretToConfigHash(client k8s.Client, assoc commonv1.Association, } func writeCASecretToConfigHash(client k8s.Client, assoc commonv1.Association, configHash hash.Hash) error { - assocConf := assoc.AssociationConf() + assocConf, err := assoc.AssociationConf() + if err != nil { + return err + } if !assocConf.GetCACertProvided() { return nil } diff --git a/pkg/controller/common/stackmon/config.go b/pkg/controller/common/stackmon/config.go index efd69bf3aa..6080d163da 100644 --- a/pkg/controller/common/stackmon/config.go +++ b/pkg/controller/common/stackmon/config.go @@ -105,10 +105,14 @@ func buildOutputConfig(client k8s.Client, assoc commonv1.Association) (map[strin return nil, volume.SecretVolume{}, err } + assocConf, err := assoc.AssociationConf() + if err != nil { + return nil, nil, err + } outputConfig := map[string]interface{}{ "username": credentials.Username, "password": credentials.Password, - "hosts": []string{assoc.AssociationConf().GetURL()}, + "hosts": []string{assocConf.GetURL()}, } caDirPath := fmt.Sprintf( @@ -117,12 +121,12 @@ func buildOutputConfig(client k8s.Client, assoc commonv1.Association) (map[strin ) var caVolume volume.VolumeLike - if assoc.AssociationConf().GetCACertProvided() { + if assocConf.GetCACertProvided() { sslCAPath := filepath.Join(caDirPath, certificates.CAFileName) outputConfig["ssl.certificate_authorities"] = []string{sslCAPath} volumeName := caVolumeName(assoc) caVolume = volume.NewSecretVolumeWithMountPath( - assoc.AssociationConf().GetCASecretName(), volumeName, caDirPath, + assocConf.GetCASecretName(), volumeName, caDirPath, ) } diff --git a/pkg/controller/common/stackmon/monitoring/monitoring.go b/pkg/controller/common/stackmon/monitoring/monitoring.go index 8370ae7bec..36bb4277d7 100644 --- a/pkg/controller/common/stackmon/monitoring/monitoring.go +++ b/pkg/controller/common/stackmon/monitoring/monitoring.go @@ -20,17 +20,21 @@ type HasMonitoring interface { // IsReconcilable return true if a resource has at least one association defined in its specification // and all defined associations are configured. -func IsReconcilable(resource HasMonitoring) bool { +func IsReconcilable(resource HasMonitoring) (bool, error) { if !IsDefined(resource) { - return false + return false, nil } allRefs := append(resource.GetMonitoringMetricsRefs(), resource.GetMonitoringLogsRefs()...) for _, ref := range allRefs { - if !resource.MonitoringAssociation(ref).AssociationConf().IsConfigured() { - return false + assocConf, err := resource.MonitoringAssociation(ref).AssociationConf() + if err != nil { + return false, err + } + if !assocConf.IsConfigured() { + return false, nil } } - return true + return true, nil } // IsDefined return true if a resource has at least one association for Stack Monitoring defined in its specification diff --git a/pkg/controller/common/stackmon/monitoring/monitoring_test.go b/pkg/controller/common/stackmon/monitoring/monitoring_test.go index 6f05901392..37f4d250c0 100644 --- a/pkg/controller/common/stackmon/monitoring/monitoring_test.go +++ b/pkg/controller/common/stackmon/monitoring/monitoring_test.go @@ -169,7 +169,8 @@ func TestIsReconcilable(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - got := IsReconcilable(&tc.es) + got, err := IsReconcilable(&tc.es) + assert.NoError(t, err) if got != tc.want { t.Errorf("IsReconcilable() got = %v, want %v", got, tc.want) return diff --git a/pkg/controller/elasticsearch/driver/driver.go b/pkg/controller/elasticsearch/driver/driver.go index 02b94167c8..1ebc278431 100644 --- a/pkg/controller/elasticsearch/driver/driver.go +++ b/pkg/controller/elasticsearch/driver/driver.go @@ -314,7 +314,11 @@ func (d *defaultDriver) Reconcile(ctx context.Context) *reconciler.Results { // requeue if associations are defined but not yet configured, otherwise we may be in a situation where we deploy // Elasticsearch Pods once, then change their spec a few seconds later once the association is configured - if !association.AreConfiguredIfSet(d.ES.GetAssociations(), d.Recorder()) { + areAssocsConfigured, err := association.AreConfiguredIfSet(d.ES.GetAssociations(), d.Recorder()) + if err != nil { + return results.WithError(err) + } + if !areAssocsConfigured { results.WithReconciliationState(defaultRequeue.WithReason("Some associations are not reconciled")) } diff --git a/pkg/controller/elasticsearch/elasticsearch_controller.go b/pkg/controller/elasticsearch/elasticsearch_controller.go index 481880840a..7c007df3e3 100644 --- a/pkg/controller/elasticsearch/elasticsearch_controller.go +++ b/pkg/controller/elasticsearch/elasticsearch_controller.go @@ -23,7 +23,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" esv1 "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1" - "github.com/elastic/cloud-on-k8s/pkg/controller/association" "github.com/elastic/cloud-on-k8s/pkg/controller/common" "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" "github.com/elastic/cloud-on-k8s/pkg/controller/common/events" @@ -226,8 +225,7 @@ func (r *ReconcileElasticsearch) fetchElasticsearchWithAssociations(ctx context. span, _ := apm.StartSpan(ctx, "fetch_elasticsearch", tracing.SpanTypeApp) defer span.End() - err := association.FetchWithAssociations(ctx, r.Client, request, es) - if err != nil { + if err := r.Client.Get(ctx, request.NamespacedName, es); err != nil { if apierrors.IsNotFound(err) { // Object not found, cleanup in-memory state. Children resources are garbage-collected either by // the operator (see `onDelete`), either by k8s through the ownerReference mechanism. diff --git a/pkg/controller/elasticsearch/stackmon/beat_config.go b/pkg/controller/elasticsearch/stackmon/beat_config.go index 3dcb35eedb..36d3a70c84 100644 --- a/pkg/controller/elasticsearch/stackmon/beat_config.go +++ b/pkg/controller/elasticsearch/stackmon/beat_config.go @@ -25,7 +25,11 @@ var ( // ReconcileConfigSecrets reconciles the secrets holding beats configuration func ReconcileConfigSecrets(client k8s.Client, es esv1.Elasticsearch) error { - if !monitoring.IsReconcilable(&es) { + isMonitoringReconcilable, err := monitoring.IsReconcilable(&es) + if err != nil { + return err + } + if !isMonitoringReconcilable { return nil } diff --git a/pkg/controller/elasticsearch/stackmon/sidecar.go b/pkg/controller/elasticsearch/stackmon/sidecar.go index bfad9cbdcd..4abc3d9eeb 100644 --- a/pkg/controller/elasticsearch/stackmon/sidecar.go +++ b/pkg/controller/elasticsearch/stackmon/sidecar.go @@ -55,7 +55,11 @@ func Filebeat(client k8s.Client, es esv1.Elasticsearch) (stackmon.BeatSidecar, e // WithMonitoring updates the Elasticsearch Pod template builder to deploy Metricbeat and Filebeat in sidecar containers // in the Elasticsearch pod and injects the volumes for the beat configurations and the ES CA certificates. func WithMonitoring(client k8s.Client, builder *defaults.PodTemplateBuilder, es esv1.Elasticsearch) (*defaults.PodTemplateBuilder, error) { - if !monitoring.IsReconcilable(&es) { + isMonitoringReconcilable, err := monitoring.IsReconcilable(&es) + if err != nil { + return nil, err + } + if !isMonitoringReconcilable { return builder, nil } diff --git a/pkg/controller/enterprisesearch/config.go b/pkg/controller/enterprisesearch/config.go index 02590db3fd..7af3f034bf 100644 --- a/pkg/controller/enterprisesearch/config.go +++ b/pkg/controller/enterprisesearch/config.go @@ -51,7 +51,7 @@ func ReadinessProbeSecretVolume(ent entv1.EnterpriseSearch) volume.SecretVolume return volume.NewSecretVolume(ConfigName(ent.Name), "readiness-probe", ReadinessProbeMountPath, ReadinessProbeFilename, 0444) } -// Reconcile reconciles the configuration of Enterprise Search: it generates the right configuration and +// ReconcileConfig reconciles the configuration of Enterprise Search: it generates the right configuration and // stores it in a secret that is kept up to date. // The secret contains 2 entries: // - the Enterprise Search configuration file @@ -290,7 +290,11 @@ func defaultConfig(ent entv1.EnterpriseSearch, ipFamily corev1.IPFamily) (*setti } func associationConfig(c k8s.Client, ent entv1.EnterpriseSearch, userCfgHasAuth bool) (*settings.CanonicalConfig, error) { - if !ent.AssociationConf().IsConfigured() { + entAssocConf, err := ent.AssociationConf() + if err != nil { + return nil, err + } + if !entAssocConf.IsConfigured() { return settings.NewCanonicalConfig(), nil } @@ -311,14 +315,14 @@ func associationConfig(c k8s.Client, ent entv1.EnterpriseSearch, userCfgHasAuth return nil, err } if err := cfg.MergeWith(settings.MustCanonicalConfig(map[string]string{ - "elasticsearch.host": ent.AssociationConf().URL, + "elasticsearch.host": entAssocConf.URL, "elasticsearch.username": credentials.Username, "elasticsearch.password": credentials.Password, })); err != nil { return nil, err } - if ent.AssociationConf().GetCACertProvided() { + if entAssocConf.GetCACertProvided() { if err := cfg.MergeWith(settings.MustCanonicalConfig(map[string]interface{}{ "elasticsearch.ssl.enabled": true, "elasticsearch.ssl.certificate_authority": filepath.Join(ESCertsPath, certificates.CAFileName), diff --git a/pkg/controller/enterprisesearch/deployment.go b/pkg/controller/enterprisesearch/deployment.go index 5a9e50ef18..43d29e744d 100644 --- a/pkg/controller/enterprisesearch/deployment.go +++ b/pkg/controller/enterprisesearch/deployment.go @@ -24,12 +24,19 @@ func (r *ReconcileEnterpriseSearch) reconcileDeployment( span, _ := apm.StartSpan(ctx, "reconcile_deployment", tracing.SpanTypeApp) defer span.End() - deploy := deployment.New(r.deploymentParams(ent, configHash)) + deployParams, err := r.deploymentParams(ent, configHash) + if err != nil { + return appsv1.Deployment{}, err + } + deploy := deployment.New(deployParams) return deployment.Reconcile(r.K8sClient(), deploy, &ent) } -func (r *ReconcileEnterpriseSearch) deploymentParams(ent entv1.EnterpriseSearch, configHash string) deployment.Params { - podSpec := newPodSpec(ent, configHash) +func (r *ReconcileEnterpriseSearch) deploymentParams(ent entv1.EnterpriseSearch, configHash string) (deployment.Params, error) { + podSpec, err := newPodSpec(ent, configHash) + if err != nil { + return deployment.Params{}, err + } deploymentLabels := Labels(ent.Name) @@ -45,5 +52,5 @@ func (r *ReconcileEnterpriseSearch) deploymentParams(ent entv1.EnterpriseSearch, Labels: deploymentLabels, PodTemplateSpec: podSpec, Strategy: appsv1.DeploymentStrategy{Type: appsv1.RollingUpdateDeploymentStrategyType}, - } + }, nil } diff --git a/pkg/controller/enterprisesearch/enterprisesearch_controller.go b/pkg/controller/enterprisesearch/enterprisesearch_controller.go index 983be529bb..ca4ff6756e 100644 --- a/pkg/controller/enterprisesearch/enterprisesearch_controller.go +++ b/pkg/controller/enterprisesearch/enterprisesearch_controller.go @@ -148,7 +148,7 @@ func (r *ReconcileEnterpriseSearch) Reconcile(ctx context.Context, request recon defer tracing.EndTransaction(tx) var ent entv1.EnterpriseSearch - if err := association.FetchWithAssociations(ctx, r.Client, request, &ent); err != nil { + if err := r.Client.Get(ctx, request.NamespacedName, &ent); err != nil { if apierrors.IsNotFound(err) { return reconcile.Result{}, r.onDelete(types.NamespacedName{ Namespace: request.Namespace, @@ -163,7 +163,11 @@ func (r *ReconcileEnterpriseSearch) Reconcile(ctx context.Context, request recon return reconcile.Result{}, nil } - if !association.IsConfiguredIfSet(&ent, r.recorder) { + isEsAssocConfigured, err := association.IsConfiguredIfSet(&ent, r.recorder) + if err != nil { + return reconcile.Result{}, err + } + if !isEsAssocConfigured { return reconcile.Result{}, nil } @@ -212,7 +216,11 @@ func (r *ReconcileEnterpriseSearch) doReconcile(ctx context.Context, ent entv1.E return reconcile.Result{}, err } logger := log.WithValues("namespace", ent.Namespace, "ent_name", ent.Name) - if !association.AllowVersion(entVersion, ent.Associated(), logger, r.recorder) { + assocAllowed, err := association.AllowVersion(entVersion, ent.Associated(), logger, r.recorder) + if err != nil { + return reconcile.Result{}, err + } + if !assocAllowed { return reconcile.Result{}, nil // will eventually retry once updated } diff --git a/pkg/controller/enterprisesearch/pod.go b/pkg/controller/enterprisesearch/pod.go index 896ca2dcb9..359a23d752 100644 --- a/pkg/controller/enterprisesearch/pod.go +++ b/pkg/controller/enterprisesearch/pod.go @@ -52,7 +52,7 @@ var ( } ) -func newPodSpec(ent entv1.EnterpriseSearch, configHash string) corev1.PodTemplateSpec { +func newPodSpec(ent entv1.EnterpriseSearch, configHash string) (corev1.PodTemplateSpec, error) { // ensure the Pod gets rotated on config change annotations := map[string]string{ConfigHashAnnotationName: configHash} @@ -75,24 +75,31 @@ func newPodSpec(ent entv1.EnterpriseSearch, configHash string) corev1.PodTemplat WithVolumeMounts(cfgVolume.VolumeMount(), readinessProbeVolume.VolumeMount(), logsVolume.VolumeMount()). WithInitContainerDefaults() - builder = withESCertsVolume(builder, ent) + builder, err := withESCertsVolume(builder, ent) + if err != nil { + return corev1.PodTemplateSpec{}, err + } builder = withHTTPCertsVolume(builder, ent) - return builder.PodTemplate + return builder.PodTemplate, nil } -func withESCertsVolume(builder *defaults.PodTemplateBuilder, ent entv1.EnterpriseSearch) *defaults.PodTemplateBuilder { - if !ent.AssociationConf().CAIsConfigured() { - return builder +func withESCertsVolume(builder *defaults.PodTemplateBuilder, ent entv1.EnterpriseSearch) (*defaults.PodTemplateBuilder, error) { + esAssocConf, err := ent.AssociationConf() + if err != nil { + return nil, err + } + if !esAssocConf.CAIsConfigured() { + return builder, nil } vol := volume.NewSecretVolumeWithMountPath( - ent.AssociationConf().GetCASecretName(), + esAssocConf.GetCASecretName(), "es-certs", ESCertsPath, ) return builder. WithVolumes(vol.Volume()). - WithVolumeMounts(vol.VolumeMount()) + WithVolumeMounts(vol.VolumeMount()), nil } func withHTTPCertsVolume(builder *defaults.PodTemplateBuilder, ent entv1.EnterpriseSearch) *defaults.PodTemplateBuilder { diff --git a/pkg/controller/enterprisesearch/pod_test.go b/pkg/controller/enterprisesearch/pod_test.go index ce7bef535b..049e6fbe33 100644 --- a/pkg/controller/enterprisesearch/pod_test.go +++ b/pkg/controller/enterprisesearch/pod_test.go @@ -43,7 +43,8 @@ func Test_newPodSpec(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := newPodSpec(tt.ent, "amFpbWVsZXNjaGF0c2V0dm91cz8=") + got, err := newPodSpec(tt.ent, "amFpbWVsZXNjaGF0c2V0dm91cz8=") + assert.NoError(t, err) tt.assertions(got) }) } diff --git a/pkg/controller/enterprisesearch/version_upgrade.go b/pkg/controller/enterprisesearch/version_upgrade.go index 229b9e3e84..80b298e9b9 100644 --- a/pkg/controller/enterprisesearch/version_upgrade.go +++ b/pkg/controller/enterprisesearch/version_upgrade.go @@ -62,7 +62,12 @@ func (r *VersionUpgrade) Handle(ctx context.Context) error { return err } - if upgradeRequested && !r.ent.AssociationConf().AuthIsConfigured() { + esAssocConf, err := r.ent.AssociationConf() + if err != nil { + return err + } + + if upgradeRequested && !esAssocConf.AuthIsConfigured() { // A version upgrade is scheduled, but we don't know how to reach the Enterprise Search API // since we don't have any Elasticsearch user available. // Move on with the upgrade: this will cause the Pod in the new version to crash at startup with explicit logs. diff --git a/pkg/controller/kibana/config_settings.go b/pkg/controller/kibana/config_settings.go index ef223d0d4b..0631fc5f46 100644 --- a/pkg/controller/kibana/config_settings.go +++ b/pkg/controller/kibana/config_settings.go @@ -118,7 +118,11 @@ func NewConfigSettings(ctx context.Context, client k8s.Client, kb kbv1.Kibana, v return CanonicalConfig{}, err } - if !kb.EsAssociation().AssociationConf().IsConfigured() { + esAssocConf, err := kb.EsAssociation().AssociationConf() + if err != nil { + return CanonicalConfig{}, err + } + if !esAssocConf.IsConfigured() { // merge the configuration with userSettings last so they take precedence if err := cfg.MergeWith( reusableSettings, @@ -161,7 +165,7 @@ func NewConfigSettings(ctx context.Context, client k8s.Client, kb kbv1.Kibana, v kibanaTLSCfg, entSearchCfg, monitoringCfg, - settings.MustCanonicalConfig(elasticsearchTLSSettings(kb)), + settings.MustCanonicalConfig(elasticsearchTLSSettings(*esAssocConf)), credentialsCfg, userSettings, ) @@ -276,7 +280,7 @@ func baseSettings(kb *kbv1.Kibana, ipFamily corev1.IPFamily) (map[string]interfa conf[XpackMonitoringUIContainerElasticsearchEnabled] = true } - assocConf := kb.EsAssociation().AssociationConf() + assocConf, _ := kb.EsAssociation().AssociationConf() if assocConf.URLIsConfigured() { conf[ElasticsearchHosts] = []string{assocConf.GetURL()} } @@ -295,13 +299,13 @@ func kibanaTLSSettings(kb kbv1.Kibana) map[string]interface{} { } } -func elasticsearchTLSSettings(kb kbv1.Kibana) map[string]interface{} { +func elasticsearchTLSSettings(esAssocConf commonv1.AssociationConf) map[string]interface{} { cfg := map[string]interface{}{ ElasticsearchSslVerificationMode: "certificate", } - if kb.EsAssociation().AssociationConf().GetCACertProvided() { - esCertsVolumeMountPath := esCaCertSecretVolume(kb).VolumeMount().MountPath + if esAssocConf.GetCACertProvided() { + esCertsVolumeMountPath := esCaCertSecretVolume(esAssocConf).VolumeMount().MountPath cfg[ElasticsearchSslCertificateAuthorities] = path.Join(esCertsVolumeMountPath, certificates.CAFileName) } @@ -309,18 +313,18 @@ func elasticsearchTLSSettings(kb kbv1.Kibana) map[string]interface{} { } // esCaCertSecretVolume returns a SecretVolume to hold the Elasticsearch CA certs for the given Kibana resource. -func esCaCertSecretVolume(kb kbv1.Kibana) volume.SecretVolume { +func esCaCertSecretVolume(esAssocConf commonv1.AssociationConf) volume.SecretVolume { return volume.NewSecretVolumeWithMountPath( - kb.EsAssociation().AssociationConf().GetCASecretName(), + esAssocConf.GetCASecretName(), "elasticsearch-certs", esCertsVolumeMountPath, ) } // entCaCertSecretVolume returns a SecretVolume to hold the EnterpriseSearch CA certs for the given Kibana resource. -func entCaCertSecretVolume(kb kbv1.Kibana) volume.SecretVolume { +func entCaCertSecretVolume(entAssocConf commonv1.AssociationConf) volume.SecretVolume { return volume.NewSecretVolumeWithMountPath( - kb.EntAssociation().AssociationConf().GetCASecretName(), + entAssocConf.GetCASecretName(), "ent-certs", entCertsVolumeMountPath, ) @@ -328,7 +332,7 @@ func entCaCertSecretVolume(kb kbv1.Kibana) volume.SecretVolume { func enterpriseSearchSettings(kb kbv1.Kibana) map[string]interface{} { cfg := map[string]interface{}{} - assocConf := kb.EntAssociation().AssociationConf() + assocConf, _ := kb.EntAssociation().AssociationConf() if assocConf.URLIsConfigured() { cfg[EnterpriseSearchHost] = assocConf.GetURL() } diff --git a/pkg/controller/kibana/controller.go b/pkg/controller/kibana/controller.go index 45ff798d23..9c6f9faaf9 100644 --- a/pkg/controller/kibana/controller.go +++ b/pkg/controller/kibana/controller.go @@ -23,7 +23,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" kbv1 "github.com/elastic/cloud-on-k8s/pkg/apis/kibana/v1" - "github.com/elastic/cloud-on-k8s/pkg/controller/association" "github.com/elastic/cloud-on-k8s/pkg/controller/common" "github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates" "github.com/elastic/cloud-on-k8s/pkg/controller/common/events" @@ -132,7 +131,8 @@ func (r *ReconcileKibana) Reconcile(ctx context.Context, request reconcile.Reque // retrieve the kibana object var kb kbv1.Kibana - if err := association.FetchWithAssociations(ctx, r.Client, request, &kb); err != nil { + err := r.Client.Get(ctx, request.NamespacedName, &kb) + if err != nil { if apierrors.IsNotFound(err) { return reconcile.Result{}, r.onDelete(types.NamespacedName{ Namespace: request.Namespace, diff --git a/pkg/controller/kibana/driver.go b/pkg/controller/kibana/driver.go index c6bf1fc0e7..beccd10c70 100644 --- a/pkg/controller/kibana/driver.go +++ b/pkg/controller/kibana/driver.go @@ -98,10 +98,18 @@ func (d *driver) Reconcile( params operator.Parameters, ) *reconciler.Results { results := reconciler.NewResult(ctx) - if !association.IsConfiguredIfSet(kb.EsAssociation(), d.recorder) { + isEsAssocConfigured, err := association.IsConfiguredIfSet(kb.EsAssociation(), d.recorder) + if err != nil { + return results.WithError(err) + } + if !isEsAssocConfigured { return results } - if !association.IsConfiguredIfSet(kb.EntAssociation(), d.recorder) { + isEntAssocConfigured, err := association.IsConfiguredIfSet(kb.EntAssociation(), d.recorder) + if err != nil { + return results.WithError(err) + } + if !isEntAssocConfigured { return results } @@ -130,7 +138,11 @@ func (d *driver) Reconcile( } logger := log.WithValues("namespace", kb.Namespace, "kb_name", kb.Name) - if !association.AllowVersion(d.version, kb, logger, d.Recorder()) { + assocAllowed, err := association.AllowVersion(d.version, kb, logger, d.Recorder()) + if err != nil { + return results.WithError(err) + } + if !assocAllowed { return results // will eventually retry } @@ -216,7 +228,11 @@ func (d *driver) deploymentParams(kb *kbv1.Kibana) (deployment.Params, error) { return deployment.Params{}, err } - kibanaPodSpec, err := NewPodTemplateSpec(d.client, *kb, keystoreResources, d.buildVolumes(kb)) + volumes, err := d.buildVolumes(kb) + if err != nil { + return deployment.Params{}, err + } + kibanaPodSpec, err := NewPodTemplateSpec(d.client, *kb, keystoreResources, volumes) if err != nil { return deployment.Params{}, err } @@ -278,16 +294,24 @@ func (d *driver) deploymentParams(kb *kbv1.Kibana) (deployment.Params, error) { }, nil } -func (d *driver) buildVolumes(kb *kbv1.Kibana) []commonvolume.VolumeLike { +func (d *driver) buildVolumes(kb *kbv1.Kibana) ([]commonvolume.VolumeLike, error) { volumes := []commonvolume.VolumeLike{DataVolume, ConfigSharedVolume, ConfigVolume(*kb)} - if kb.EsAssociation().AssociationConf().CAIsConfigured() { - esCertsVolume := esCaCertSecretVolume(*kb) + esAssocConf, err := kb.EsAssociation().AssociationConf() + if err != nil { + return nil, err + } + if esAssocConf.CAIsConfigured() { + esCertsVolume := esCaCertSecretVolume(*esAssocConf) volumes = append(volumes, esCertsVolume) } - if kb.EntAssociation().AssociationConf().CAIsConfigured() { - entCertsVolume := entCaCertSecretVolume(*kb) + entAssocConf, err := kb.EntAssociation().AssociationConf() + if err != nil { + return nil, err + } + if entAssocConf.CAIsConfigured() { + entCertsVolume := entCaCertSecretVolume(*entAssocConf) volumes = append(volumes, entCertsVolume) } @@ -295,7 +319,7 @@ func (d *driver) buildVolumes(kb *kbv1.Kibana) []commonvolume.VolumeLike { httpCertsVolume := certificates.HTTPCertSecretVolume(kbv1.KBNamer, kb.Name) volumes = append(volumes, httpCertsVolume) } - return volumes + return volumes, nil } func NewService(kb kbv1.Kibana) *corev1.Service { diff --git a/pkg/controller/kibana/stackmon/beat_config.go b/pkg/controller/kibana/stackmon/beat_config.go index 7970df515e..6013983bff 100644 --- a/pkg/controller/kibana/stackmon/beat_config.go +++ b/pkg/controller/kibana/stackmon/beat_config.go @@ -25,7 +25,11 @@ var ( // ReconcileConfigSecrets reconciles the secrets holding beats configuration func ReconcileConfigSecrets(client k8s.Client, kb kbv1.Kibana) error { - if !monitoring.IsReconcilable(&kb) { + isMonitoringReconcilable, err := monitoring.IsReconcilable(&kb) + if err != nil { + return err + } + if !isMonitoringReconcilable { return nil } diff --git a/pkg/controller/kibana/stackmon/sidecar.go b/pkg/controller/kibana/stackmon/sidecar.go index 61a6e33b08..3a9dff6bc9 100644 --- a/pkg/controller/kibana/stackmon/sidecar.go +++ b/pkg/controller/kibana/stackmon/sidecar.go @@ -69,7 +69,11 @@ func Filebeat(client k8s.Client, kb kbv1.Kibana) (stackmon.BeatSidecar, error) { // WithMonitoring updates the Kibana Pod template builder to deploy Metricbeat and Filebeat in sidecar containers // in the Kibana pod and injects the volumes for the beat configurations and the ES CA certificates. func WithMonitoring(client k8s.Client, builder *defaults.PodTemplateBuilder, kb kbv1.Kibana) (*defaults.PodTemplateBuilder, error) { - if !monitoring.IsReconcilable(&kb) { + isMonitoringReconcilable, err := monitoring.IsReconcilable(&kb) + if err != nil { + return nil, err + } + if !isMonitoringReconcilable { return builder, nil } diff --git a/pkg/controller/maps/config.go b/pkg/controller/maps/config.go index 92eb9fcf8b..b2fff545f4 100644 --- a/pkg/controller/maps/config.go +++ b/pkg/controller/maps/config.go @@ -109,7 +109,11 @@ func tlsConfig(ems emsv1alpha1.ElasticMapsServer) *settings.CanonicalConfig { func associationConfig(c k8s.Client, ems emsv1alpha1.ElasticMapsServer) (*settings.CanonicalConfig, error) { cfg := settings.NewCanonicalConfig() - if !ems.AssociationConf().IsConfigured() { + assocConf, err := ems.AssociationConf() + if err != nil { + return nil, err + } + if !assocConf.IsConfigured() { return cfg, nil } credentials, err := association.ElasticsearchAuthSettings(c, &ems) @@ -117,14 +121,14 @@ func associationConfig(c k8s.Client, ems emsv1alpha1.ElasticMapsServer) (*settin return nil, err } if err := cfg.MergeWith(settings.MustCanonicalConfig(map[string]string{ - "elasticsearch.host": ems.AssociationConf().URL, + "elasticsearch.host": assocConf.URL, "elasticsearch.username": credentials.Username, "elasticsearch.password": credentials.Password, })); err != nil { return nil, err } - if ems.AssociationConf().GetCACertProvided() { + if assocConf.GetCACertProvided() { if err := cfg.MergeWith(settings.MustCanonicalConfig(map[string]interface{}{ "elasticsearch.ssl.verificationMode": "certificate", "elasticsearch.ssl.certificateAuthorities": filepath.Join(ESCertsPath, certificates.CAFileName), diff --git a/pkg/controller/maps/controller.go b/pkg/controller/maps/controller.go index ffb92550f7..91eafa85a4 100644 --- a/pkg/controller/maps/controller.go +++ b/pkg/controller/maps/controller.go @@ -152,7 +152,7 @@ func (r *ReconcileMapsServer) Reconcile(ctx context.Context, request reconcile.R // retrieve the EMS object var ems emsv1alpha1.ElasticMapsServer - if err := association.FetchWithAssociations(ctx, r.Client, request, &ems); err != nil { + if err := r.Client.Get(ctx, request.NamespacedName, &ems); err != nil { if apierrors.IsNotFound(err) { return reconcile.Result{}, r.onDelete(types.NamespacedName{ Namespace: request.Namespace, @@ -185,7 +185,11 @@ func (r *ReconcileMapsServer) Reconcile(ctx context.Context, request reconcile.R return reconcile.Result{}, r.onDelete(k8s.ExtractNamespacedName(&ems)) } - if !association.IsConfiguredIfSet(&ems, r.recorder) { + isEsAssocConfigured, err := association.IsConfiguredIfSet(&ems, r.recorder) + if err != nil { + return reconcile.Result{}, err + } + if !isEsAssocConfigured { return reconcile.Result{}, nil } @@ -227,7 +231,11 @@ func (r *ReconcileMapsServer) doReconcile(ctx context.Context, ems emsv1alpha1.E return reconcile.Result{}, err } logger := log.WithValues("namespace", ems.Namespace, "maps_name", ems.Name) // TODO mapping explosion - if !association.AllowVersion(emsVersion, ems.Associated(), logger, r.recorder) { + assocAllowed, err := association.AllowVersion(emsVersion, ems.Associated(), logger, r.recorder) + if err != nil { + return reconcile.Result{}, err + } + if !assocAllowed { return reconcile.Result{}, nil // will eventually retry once updated } @@ -324,12 +332,19 @@ func (r *ReconcileMapsServer) reconcileDeployment( span, _ := apm.StartSpan(ctx, "reconcile_deployment", tracing.SpanTypeApp) defer span.End() - deploy := deployment.New(r.deploymentParams(ems, configHash)) + deployParams, err := r.deploymentParams(ems, configHash) + if err != nil { + return appsv1.Deployment{}, err + } + deploy := deployment.New(deployParams) return deployment.Reconcile(r.K8sClient(), deploy, &ems) } -func (r *ReconcileMapsServer) deploymentParams(ems emsv1alpha1.ElasticMapsServer, configHash string) deployment.Params { - podSpec := newPodSpec(ems, configHash) +func (r *ReconcileMapsServer) deploymentParams(ems emsv1alpha1.ElasticMapsServer, configHash string) (deployment.Params, error) { + podSpec, err := newPodSpec(ems, configHash) + if err != nil { + return deployment.Params{}, err + } deploymentLabels := labels(ems.Name) @@ -345,7 +360,7 @@ func (r *ReconcileMapsServer) deploymentParams(ems emsv1alpha1.ElasticMapsServer Labels: deploymentLabels, PodTemplateSpec: podSpec, Strategy: appsv1.DeploymentStrategy{Type: appsv1.RollingUpdateDeploymentStrategyType}, - } + }, nil } func (r *ReconcileMapsServer) updateStatus(ems emsv1alpha1.ElasticMapsServer, deploy appsv1.Deployment) error { diff --git a/pkg/controller/maps/pod.go b/pkg/controller/maps/pod.go index 3025dfc17b..2731b7a073 100644 --- a/pkg/controller/maps/pod.go +++ b/pkg/controller/maps/pod.go @@ -56,7 +56,7 @@ func readinessProbe(useTLS bool) corev1.Probe { } } -func newPodSpec(ems emsv1alpha1.ElasticMapsServer, configHash string) corev1.PodTemplateSpec { +func newPodSpec(ems emsv1alpha1.ElasticMapsServer, configHash string) (corev1.PodTemplateSpec, error) { // ensure the Pod gets rotated on config change annotations := map[string]string{configHashAnnotationName: configHash} @@ -77,29 +77,40 @@ func newPodSpec(ems emsv1alpha1.ElasticMapsServer, configHash string) corev1.Pod WithVolumeMounts(cfgVolume.VolumeMount(), logsVolume.VolumeMount()). WithInitContainerDefaults() - builder = withESCertsVolume(builder, ems) + builder, err := withESCertsVolume(builder, ems) + if err != nil { + return corev1.PodTemplateSpec{}, err + } builder = withHTTPCertsVolume(builder, ems) - if !ems.AssociationConf().IsConfigured() { + esAssocConf, err := ems.AssociationConf() + if err != nil { + return corev1.PodTemplateSpec{}, err + } + if !esAssocConf.IsConfigured() { // supported as of 7.14, harmless on prior versions, but both Elasticsearch connection and this must not be specified builder = builder.WithEnv(corev1.EnvVar{Name: "ELASTICSEARCH_PREVALIDATED", Value: "true"}) } - return builder.PodTemplate + return builder.PodTemplate, nil } -func withESCertsVolume(builder *defaults.PodTemplateBuilder, ems emsv1alpha1.ElasticMapsServer) *defaults.PodTemplateBuilder { - if !ems.AssociationConf().CAIsConfigured() { - return builder +func withESCertsVolume(builder *defaults.PodTemplateBuilder, ems emsv1alpha1.ElasticMapsServer) (*defaults.PodTemplateBuilder, error) { + esAssocConf, err := ems.AssociationConf() + if err != nil { + return nil, err + } + if !esAssocConf.CAIsConfigured() { + return builder, nil } vol := volume.NewSecretVolumeWithMountPath( - ems.AssociationConf().GetCASecretName(), + esAssocConf.GetCASecretName(), "es-certs", ESCertsPath, ) return builder. WithVolumes(vol.Volume()). - WithVolumeMounts(vol.VolumeMount()) + WithVolumeMounts(vol.VolumeMount()), nil } func withHTTPCertsVolume(builder *defaults.PodTemplateBuilder, ems emsv1alpha1.ElasticMapsServer) *defaults.PodTemplateBuilder {