From 6160a3bdc26dafa4906109c0a1041812f251cc22 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Tue, 9 Jun 2020 15:31:09 +0200 Subject: [PATCH 01/11] Add KibanaRef to Beats --- cmd/manager/main.go | 5 +- config/crds/all-crds.yaml | 24 ++- .../crds/bases/beat.k8s.elastic.co_beats.yaml | 24 ++- pkg/apis/apm/v1/apmserver_types.go | 4 +- pkg/apis/beat/v1beta1/beat_types.go | 123 ++++++++++---- .../beat/v1beta1/zz_generated.deepcopy.go | 50 +++++- pkg/apis/common/v1/association.go | 3 + .../association/controller/apm_es.go | 8 +- .../association/controller/apm_kibana.go | 6 +- .../association/controller/beat_es.go | 18 ++- .../association/controller/beat_kibana.go | 58 +++++++ pkg/controller/beat/common/config.go | 68 +++++--- pkg/controller/beat/common/config_test.go | 10 +- pkg/controller/beat/common/driver.go | 2 +- pkg/controller/beat/common/health.go | 8 +- pkg/controller/beat/common/health_test.go | 151 +++++++++++------- pkg/controller/beat/common/pod.go | 27 +++- pkg/controller/beat/common/reconcile.go | 3 +- pkg/controller/beat/controller.go | 2 +- .../common/association/association.go | 16 +- .../common/association/association_test.go | 85 ++++------ pkg/controller/elasticsearch/user/roles.go | 3 + pkg/controller/kibana/driver.go | 2 +- 23 files changed, 490 insertions(+), 210 deletions(-) create mode 100644 pkg/controller/association/controller/beat_kibana.go diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 06df2f17b4..fd44a97b61 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -349,7 +349,6 @@ func execute() { if manageBeatAutodiscoverRBAC { beatcommon.EnableAutodiscoverRBACManagement() } - if err = apmserver.Add(mgr, params); err != nil { log.Error(err, "unable to create controller", "controller", "ApmServer") os.Exit(1) @@ -390,6 +389,10 @@ func execute() { log.Error(err, "unable to create controller", "controller", "beat-es-association") os.Exit(1) } + if err = associationctl.AddBeatKibana(mgr, accessReviewer, params); err != nil { + log.Error(err, "unable to create controller", "controller", "beat-kibana-association") + os.Exit(1) + } if err = remoteca.Add(mgr, accessReviewer, params); err != nil { log.Error(err, "unable to create controller", "controller", "RemoteClusterCertificateAuthorites") diff --git a/config/crds/all-crds.yaml b/config/crds/all-crds.yaml index 7da53c175f..24b6282a2d 100644 --- a/config/crds/all-crds.yaml +++ b/config/crds/all-crds.yaml @@ -555,6 +555,21 @@ spec: description: Image is the Beat Docker image to deploy. Version and Type have to match the Beat in the image. type: string + kibanaRef: + description: KibanaRef is a reference to a Kibana instance running in + the same Kubernetes cluster. It allows APM agent central configuration + management in Kibana. + properties: + name: + description: Name of the Kubernetes object. + type: string + namespace: + description: Namespace of the Kubernetes object. If empty, defaults + to the current namespace. + type: string + required: + - name + type: object serviceAccountName: description: ServiceAccountName is used to check access from the current resource to Elasticsearch resource in a different namespace. Can only @@ -576,17 +591,20 @@ spec: status: description: BeatStatus defines the observed state of a Beat. properties: - associationStatus: - description: AssociationStatus is the status of an association resource. - type: string availableNodes: format: int32 type: integer + elasticsearchAssociationStatus: + description: AssociationStatus is the status of an association resource. + type: string expectedNodes: format: int32 type: integer health: type: string + kibanaAssocationStatus: + description: AssociationStatus is the status of an association resource. + type: string type: object version: v1beta1 versions: diff --git a/config/crds/bases/beat.k8s.elastic.co_beats.yaml b/config/crds/bases/beat.k8s.elastic.co_beats.yaml index 7d3c513864..1343b33cd3 100644 --- a/config/crds/bases/beat.k8s.elastic.co_beats.yaml +++ b/config/crds/bases/beat.k8s.elastic.co_beats.yaml @@ -12160,6 +12160,21 @@ spec: description: Image is the Beat Docker image to deploy. Version and Type have to match the Beat in the image. type: string + kibanaRef: + description: KibanaRef is a reference to a Kibana instance running in + the same Kubernetes cluster. It allows APM agent central configuration + management in Kibana. + properties: + name: + description: Name of the Kubernetes object. + type: string + namespace: + description: Namespace of the Kubernetes object. If empty, defaults + to the current namespace. + type: string + required: + - name + type: object serviceAccountName: description: ServiceAccountName is used to check access from the current resource to Elasticsearch resource in a different namespace. Can only @@ -12181,17 +12196,20 @@ spec: status: description: BeatStatus defines the observed state of a Beat. properties: - associationStatus: - description: AssociationStatus is the status of an association resource. - type: string availableNodes: format: int32 type: integer + elasticsearchAssociationStatus: + description: AssociationStatus is the status of an association resource. + type: string expectedNodes: format: int32 type: integer health: type: string + kibanaAssocationStatus: + description: AssociationStatus is the status of an association resource. + type: string type: object type: object version: v1beta1 diff --git a/pkg/apis/apm/v1/apmserver_types.go b/pkg/apis/apm/v1/apmserver_types.go index f78ecbe529..e17f558076 100644 --- a/pkg/apis/apm/v1/apmserver_types.go +++ b/pkg/apis/apm/v1/apmserver_types.go @@ -210,11 +210,11 @@ func (akb *ApmKibanaAssociation) Associated() commonv1.Associated { } func (akb *ApmKibanaAssociation) AssociationConfAnnotationName() string { - return "association.k8s.elastic.co/kb-conf" + return commonv1.KibanaConfigAnnotationName } func (akb *ApmKibanaAssociation) AssociatedType() string { - return "kibana" + return commonv1.KibanaAssociationType } func (akb *ApmKibanaAssociation) AssociationRef() commonv1.ObjectSelector { diff --git a/pkg/apis/beat/v1beta1/beat_types.go b/pkg/apis/beat/v1beta1/beat_types.go index 3813299354..a6413e27fe 100644 --- a/pkg/apis/beat/v1beta1/beat_types.go +++ b/pkg/apis/beat/v1beta1/beat_types.go @@ -25,6 +25,10 @@ type BeatSpec struct { // +kubebuilder:validation:Optional ElasticsearchRef commonv1.ObjectSelector `json:"elasticsearchRef,omitempty"` + // KibanaRef is a reference to a Kibana instance running in the same Kubernetes cluster. + // It allows automatic setup of dashboards and visualizations. + KibanaRef commonv1.ObjectSelector `json:"kibanaRef,omitempty"` + // Image is the Beat Docker image to deploy. Version and Type have to match the Beat in the image. // +kubebuilder:validation:Optional Image string `json:"image,omitempty"` @@ -70,7 +74,10 @@ type BeatStatus struct { Health BeatHealth `json:"health,omitempty"` // +kubebuilder:validation:Optional - Association commonv1.AssociationStatus `json:"associationStatus,omitempty"` + ElasticsearchAssociation commonv1.AssociationStatus `json:"elasticsearchAssociationStatus,omitempty"` + + // +kubebuilder:validation:Optional + KibanaAssocation commonv1.AssociationStatus `json:"kibanaAssocationStatus,omitempty"` } type BeatHealth string @@ -106,65 +113,121 @@ type Beat struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec BeatSpec `json:"spec,omitempty"` - Status BeatStatus `json:"status,omitempty"` - assocConf *commonv1.AssociationConf `json:"-"` //nolint:govet + Spec BeatSpec `json:"spec,omitempty"` + Status BeatStatus `json:"status,omitempty"` + esAssocConf *commonv1.AssociationConf `json:"-"` // nolint:govet + kbAssocConf *commonv1.AssociationConf `json:"-"` // nolint:govet +} + +var _ commonv1.Associated = &Beat{} + +func (b *Beat) GetAssociations() []commonv1.Association { + return []commonv1.Association{ + &BeatESAssociation{Beat: b}, + &BeatKibanaAssociation{Beat: b}, + } +} + +func (b *Beat) ServiceAccountName() string { + return b.Spec.ServiceAccountName +} + +type BeatESAssociation struct { + *Beat } -func (b *Beat) Associated() commonv1.Associated { - if b != nil { - return b +var _ commonv1.Association = &BeatESAssociation{} + +func (b *BeatESAssociation) Associated() commonv1.Associated { + if b == nil { + return nil + } + if b.Beat == nil { + b.Beat = &Beat{} } - return &Beat{} + return b.Beat } -func (b *Beat) AssociatedType() string { +func (b *BeatESAssociation) AssociatedType() string { return commonv1.ElasticsearchAssociationType } -func (b *Beat) AssociationRef() commonv1.ObjectSelector { +func (b *BeatESAssociation) AssociationRef() commonv1.ObjectSelector { return b.Spec.ElasticsearchRef.WithDefaultNamespace(b.Namespace) } -func (b *Beat) AssociationConfAnnotationName() string { +func (b *BeatESAssociation) AssociationConfAnnotationName() string { return commonv1.ElasticsearchConfigAnnotationName } -func (b *Beat) GetAssociations() []commonv1.Association { - return []commonv1.Association{b} +func (b *BeatESAssociation) AssociationConf() *commonv1.AssociationConf { + return b.esAssocConf } -// IsMarkedForDeletion returns true if the Beat is going to be deleted -func (b *Beat) IsMarkedForDeletion() bool { - return !b.DeletionTimestamp.IsZero() +func (b *BeatESAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { + b.esAssocConf = conf } -func (b *Beat) ServiceAccountName() string { - return b.Spec.ServiceAccountName +func (b *BeatESAssociation) AssociationStatus() commonv1.AssociationStatus { + return b.Status.ElasticsearchAssociation } -func (b *Beat) ElasticsearchRef() commonv1.ObjectSelector { - return b.Spec.ElasticsearchRef +func (b *BeatESAssociation) SetAssociationStatus(status commonv1.AssociationStatus) { + b.Status.ElasticsearchAssociation = status } -func (b *Beat) AssociationConf() *commonv1.AssociationConf { - return b.assocConf +type BeatKibanaAssociation struct { + *Beat } -func (b *Beat) SetAssociationConf(assocConf *commonv1.AssociationConf) { - b.assocConf = assocConf +var _ commonv1.Association = &BeatKibanaAssociation{} + +func (b *BeatKibanaAssociation) AssociationConf() *commonv1.AssociationConf { + return b.kbAssocConf } -func (b *Beat) AssociationStatus() commonv1.AssociationStatus { - return b.Status.Association +func (b *BeatKibanaAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { + b.kbAssocConf = conf } -func (b *Beat) SetAssociationStatus(status commonv1.AssociationStatus) { - b.Status.Association = status +func (b *BeatKibanaAssociation) AssociationStatus() commonv1.AssociationStatus { + return b.Status.KibanaAssocation } -var _ commonv1.Associated = &Beat{} -var _ commonv1.Association = &Beat{} +func (b *BeatKibanaAssociation) SetAssociationStatus(status commonv1.AssociationStatus) { + b.Status.KibanaAssocation = status +} + +func (b *BeatKibanaAssociation) Associated() commonv1.Associated { + if b == nil { + return nil + } + if b.Beat == nil { + b.Beat = &Beat{} + } + return b.Beat +} + +func (b *BeatKibanaAssociation) AssociatedType() string { + return commonv1.KibanaAssociationType +} + +func (b *BeatKibanaAssociation) AssociationRef() commonv1.ObjectSelector { + return b.Spec.KibanaRef.WithDefaultNamespace(b.Namespace) +} + +func (b *BeatKibanaAssociation) AssociationConfAnnotationName() string { + return commonv1.KibanaConfigAnnotationName +} + +// IsMarkedForDeletion returns true if the Beat is going to be deleted +func (b *Beat) IsMarkedForDeletion() bool { + return !b.DeletionTimestamp.IsZero() +} + +func (b *Beat) ElasticsearchRef() commonv1.ObjectSelector { + return b.Spec.ElasticsearchRef +} // +kubebuilder:object:root=true diff --git a/pkg/apis/beat/v1beta1/zz_generated.deepcopy.go b/pkg/apis/beat/v1beta1/zz_generated.deepcopy.go index 93e1f0eab8..7f87e88cfd 100644 --- a/pkg/apis/beat/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/beat/v1beta1/zz_generated.deepcopy.go @@ -20,8 +20,13 @@ func (in *Beat) DeepCopyInto(out *Beat) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status - if in.assocConf != nil { - in, out := &in.assocConf, &out.assocConf + if in.esAssocConf != nil { + in, out := &in.esAssocConf, &out.esAssocConf + *out = new(v1.AssociationConf) + **out = **in + } + if in.kbAssocConf != nil { + in, out := &in.kbAssocConf, &out.kbAssocConf *out = new(v1.AssociationConf) **out = **in } @@ -45,6 +50,46 @@ func (in *Beat) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BeatESAssociation) DeepCopyInto(out *BeatESAssociation) { + *out = *in + if in.Beat != nil { + in, out := &in.Beat, &out.Beat + *out = new(Beat) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeatESAssociation. +func (in *BeatESAssociation) DeepCopy() *BeatESAssociation { + if in == nil { + return nil + } + out := new(BeatESAssociation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BeatKibanaAssociation) DeepCopyInto(out *BeatKibanaAssociation) { + *out = *in + if in.Beat != nil { + in, out := &in.Beat, &out.Beat + *out = new(Beat) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeatKibanaAssociation. +func (in *BeatKibanaAssociation) DeepCopy() *BeatKibanaAssociation { + if in == nil { + return nil + } + out := new(BeatKibanaAssociation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BeatList) DeepCopyInto(out *BeatList) { *out = *in @@ -81,6 +126,7 @@ func (in *BeatList) DeepCopyObject() runtime.Object { func (in *BeatSpec) DeepCopyInto(out *BeatSpec) { *out = *in out.ElasticsearchRef = in.ElasticsearchRef + out.KibanaRef = in.KibanaRef if in.Config != nil { in, out := &in.Config, &out.Config *out = (*in).DeepCopy() diff --git a/pkg/apis/common/v1/association.go b/pkg/apis/common/v1/association.go index 52c4e0fdcd..3e6bc59eeb 100644 --- a/pkg/apis/common/v1/association.go +++ b/pkg/apis/common/v1/association.go @@ -16,6 +16,9 @@ const ( ElasticsearchConfigAnnotationName = "association.k8s.elastic.co/es-conf" ElasticsearchAssociationType = "elasticsearch" + KibanaAssociationType = "kibana" + KibanaConfigAnnotationName = "association.k8s.elastic.co/kb-conf" + AssociationUnknown AssociationStatus = "" AssociationPending AssociationStatus = "Pending" AssociationEstablished AssociationStatus = "Established" diff --git a/pkg/controller/association/controller/apm_es.go b/pkg/controller/association/controller/apm_es.go index 5f316c0b8f..e2e2d6d5ac 100644 --- a/pkg/controller/association/controller/apm_es.go +++ b/pkg/controller/association/controller/apm_es.go @@ -29,7 +29,7 @@ const ( // ApmAssociationLabelNamespace marks resources created for an association originating from APM. ApmAssociationLabelNamespace = "apmassociation.k8s.elastic.co/namespace" // ApmAssociationLabelNamespace marks resources created for an association originating from APM. - ApmAssociationTypeLabelNamespace = "apmassociation.k8s.elastic.co/type" + ApmAssociationLabelType = "apmassociation.k8s.elastic.co/type" ) func AddApmES(mgr manager.Manager, accessReviewer rbac.AccessReviewer, params operator.Parameters) error { @@ -44,9 +44,9 @@ func AddApmES(mgr manager.Manager, accessReviewer rbac.AccessReviewer, params op AssociationName: "apm-es", AssociationLabels: func(associated types.NamespacedName) map[string]string { return map[string]string{ - ApmAssociationLabelName: associated.Name, - ApmAssociationLabelNamespace: associated.Namespace, - ApmAssociationTypeLabelNamespace: commonv1.ElasticsearchAssociationType, + ApmAssociationLabelName: associated.Name, + ApmAssociationLabelNamespace: associated.Namespace, + ApmAssociationLabelType: commonv1.ElasticsearchAssociationType, } }, UserSecretSuffix: "apm-user", diff --git a/pkg/controller/association/controller/apm_kibana.go b/pkg/controller/association/controller/apm_kibana.go index 248c91b720..0b3357dde7 100644 --- a/pkg/controller/association/controller/apm_kibana.go +++ b/pkg/controller/association/controller/apm_kibana.go @@ -33,9 +33,9 @@ func AddApmKibana(mgr manager.Manager, accessReviewer rbac.AccessReviewer, param AssociationName: "apm-kibana", AssociationLabels: func(associated types.NamespacedName) map[string]string { return map[string]string{ - ApmAssociationLabelName: associated.Name, - ApmAssociationLabelNamespace: associated.Namespace, - ApmAssociationTypeLabelNamespace: "kibana", + ApmAssociationLabelName: associated.Name, + ApmAssociationLabelNamespace: associated.Namespace, + ApmAssociationLabelType: "kibana", } }, UserSecretSuffix: "apm-kb-user", diff --git a/pkg/controller/association/controller/beat_es.go b/pkg/controller/association/controller/beat_es.go index 97aea1451a..7126e40c23 100644 --- a/pkg/controller/association/controller/beat_es.go +++ b/pkg/controller/association/controller/beat_es.go @@ -20,16 +20,17 @@ import ( ) const ( - // BeatESAssociationLabelName marks resources created by this controller for easier retrieval. - BeatESAssociationLabelName = "beatassociation.k8s.elastic.co/name" - - // BeatESAssociationLabelNamespace marks resources created by this controller for easier retrieval. - BeatESAssociationLabelNamespace = "beatassociation.k8s.elastic.co/namespace" + // BeatAssociationLabelName marks resources created by this controller for easier retrieval. + BeatAssociationLabelName = "beatassociation.k8s.elastic.co/name" + // BeatAssociationLabelNamespace marks resources created by this controller for easier retrieval. + BeatAssociationLabelNamespace = "beatassociation.k8s.elastic.co/namespace" + // BeatAssociationLabelType marks the type of association + BeatAssociationLabelType = "beatassociation.k8s.elastic.co/type" ) func AddBeatES(mgr manager.Manager, accessReviewer rbac.AccessReviewer, params operator.Parameters) error { return association.AddAssociationController(mgr, accessReviewer, params, association.AssociationInfo{ - AssociationObjTemplate: func() commonv1.Association { return &beatv1beta1.Beat{} }, + AssociationObjTemplate: func() commonv1.Association { return &beatv1beta1.BeatESAssociation{} }, ElasticsearchRef: func(c k8s.Client, association commonv1.Association) (bool, commonv1.ObjectSelector, error) { return true, association.AssociationRef(), nil }, @@ -39,8 +40,9 @@ func AddBeatES(mgr manager.Manager, accessReviewer rbac.AccessReviewer, params o AssociatedShortName: "beat", AssociationLabels: func(associated types.NamespacedName) map[string]string { return map[string]string{ - BeatESAssociationLabelName: associated.Name, - BeatESAssociationLabelNamespace: associated.Namespace, + BeatAssociationLabelName: associated.Name, + BeatAssociationLabelNamespace: associated.Namespace, + BeatAssociationLabelType: commonv1.ElasticsearchAssociationType, } }, UserSecretSuffix: "beat-user", diff --git a/pkg/controller/association/controller/beat_kibana.go b/pkg/controller/association/controller/beat_kibana.go new file mode 100644 index 0000000000..477f5a7a6e --- /dev/null +++ b/pkg/controller/association/controller/beat_kibana.go @@ -0,0 +1,58 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package controller + +import ( + beatv1beta1 "github.com/elastic/cloud-on-k8s/pkg/apis/beat/v1beta1" + commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1" + "github.com/elastic/cloud-on-k8s/pkg/controller/association" + "github.com/elastic/cloud-on-k8s/pkg/controller/common/operator" + "github.com/elastic/cloud-on-k8s/pkg/controller/common/watches" + "github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/user" + "github.com/elastic/cloud-on-k8s/pkg/controller/kibana" + "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" + "github.com/elastic/cloud-on-k8s/pkg/utils/rbac" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +func AddBeatKibana(mgr manager.Manager, accessReviewer rbac.AccessReviewer, params operator.Parameters) error { + return association.AddAssociationController(mgr, accessReviewer, params, association.AssociationInfo{ + AssociationObjTemplate: func() commonv1.Association { return &beatv1beta1.BeatKibanaAssociation{} }, + ElasticsearchRef: getElasticsearchFromKibana, + ExternalServiceURL: getKibanaExternalURL, + AssociatedNamer: kibana.Namer, + AssociationName: "beat-kibana", + AssociatedShortName: "beat", + AssociationLabels: func(associated types.NamespacedName) map[string]string { + return map[string]string{ + BeatAssociationLabelName: associated.Name, + BeatAssociationLabelNamespace: associated.Namespace, + BeatAssociationLabelType: commonv1.KibanaAssociationType, + } + }, + UserSecretSuffix: "beat-kb-user", + CASecretLabelName: kibana.KibanaNameLabelName, + ESUserRole: func(commonv1.Associated) (string, error) { + return user.SuperUserBuiltinRole, nil + }, + SetDynamicWatches: func(association commonv1.Association, w watches.DynamicWatches) error { + kibanaKey := association.AssociationRef().NamespacedName() + watchName := association.GetNamespace() + "-" + association.GetName() + "-kibana-watch" + if err := w.Kibanas.AddHandler(watches.NamedWatch{ + Name: watchName, + Watched: []types.NamespacedName{kibanaKey}, + Watcher: k8s.ExtractNamespacedName(association), + }); err != nil { + return err + } + return nil + }, + ClearDynamicWatches: func(associated types.NamespacedName, w watches.DynamicWatches) { + watchName := associated.Namespace + "-" + associated.Name + "-kibana-watch" + w.Kibanas.RemoveHandlerForKey(watchName) + }, + }) +} diff --git a/pkg/controller/beat/common/config.go b/pkg/controller/beat/common/config.go index 633dcc50e3..bfcd2d10e9 100644 --- a/pkg/controller/beat/common/config.go +++ b/pkg/controller/beat/common/config.go @@ -13,7 +13,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" beatv1beta1 "github.com/elastic/cloud-on-k8s/pkg/apis/beat/v1beta1" - commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/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/reconciler" @@ -22,39 +21,54 @@ import ( ) // setOutput will set the output section in Beat config according to the association configuration. -func setOutput(cfg *settings.CanonicalConfig, client k8s.Client, associated commonv1.Association) error { +func buildOutputConfig(client k8s.Client, associated beatv1beta1.BeatESAssociation) (*settings.CanonicalConfig, error) { if !associated.AssociationConf().IsConfigured() { - return nil + return settings.NewCanonicalConfig(), nil } - username, password, err := association.ElasticsearchAuthSettings(client, associated) + username, password, err := association.ElasticsearchAuthSettings(client, &associated) if err != nil { - return err + return settings.NewCanonicalConfig(), err } - esOutput := settings.MustCanonicalConfig( - map[string]interface{}{ - "output.elasticsearch": map[string]interface{}{ - "hosts": []string{associated.AssociationConf().GetURL()}, - "username": username, - "password": password, - }, - }) - - if err := cfg.MergeWith(esOutput); err != nil { - return err + esOutput := map[string]interface{}{ + "output.elasticsearch": map[string]interface{}{ + "hosts": []string{associated.AssociationConf().GetURL()}, + "username": username, + "password": password, + }, } if associated.AssociationConf().GetCACertProvided() { - if err := cfg.MergeWith(settings.MustCanonicalConfig( - map[string]interface{}{ - "output.elasticsearch.ssl.certificate_authorities": path.Join(CAMountPath, CAFileName), - })); err != nil { - return err - } + esOutput["output.elasticsearch.ssl.certificate_authorities"] = []string{path.Join(certificatesDir(&associated), CAFileName)} } - return nil + return settings.MustCanonicalConfig(esOutput), nil +} + +func buildKibanaConfig(client k8s.Client, associated beatv1beta1.BeatKibanaAssociation) (*settings.CanonicalConfig, error) { + if !associated.AssociationConf().IsConfigured() { + return settings.NewCanonicalConfig(), nil + } + + username, password, err := association.ElasticsearchAuthSettings(client, &associated) + if err != nil { + return settings.NewCanonicalConfig(), err + } + + kibanaCfg := map[string]interface{}{ + "setup.dashboards.enabled": true, + "setup.kibana": map[string]interface{}{ + "host": associated.AssociationConf().GetURL(), + "username": username, + "password": password, + }, + } + + if associated.AssociationConf().GetCACertProvided() { + kibanaCfg["setup.kibana.ssl.certificate_authorities"] = []string{path.Join(certificatesDir(&associated), CAFileName)} + } + return settings.MustCanonicalConfig(kibanaCfg), nil } func buildBeatConfig( @@ -65,10 +79,16 @@ func buildBeatConfig( ) ([]byte, error) { cfg := settings.NewCanonicalConfig() - if err := setOutput(cfg, client, &beat); err != nil { + outputCfg, err := buildOutputConfig(client, beatv1beta1.BeatESAssociation{Beat: &beat}) + if err != nil { return nil, err } + kibanaCfg, err := buildKibanaConfig(client, beatv1beta1.BeatKibanaAssociation{Beat: &beat}) + err = cfg.MergeWith(outputCfg, kibanaCfg) + if err != nil { + return nil, err + } // use only the default config or only the provided config - no overriding, no merging userConfig := beat.Spec.Config if userConfig == nil { diff --git a/pkg/controller/beat/common/config_test.go b/pkg/controller/beat/common/config_test.go index 36d1b740ac..47a1117268 100644 --- a/pkg/controller/beat/common/config_test.go +++ b/pkg/controller/beat/common/config_test.go @@ -39,7 +39,8 @@ func Test_buildBeatConfig(t *testing.T) { defaultConfig := settings.MustParseConfig([]byte("default: true")) userConfig := &commonv1.Config{Data: map[string]interface{}{"user": "true"}} userCanonicalConfig := settings.MustCanonicalConfig(userConfig.Data) - outputCAYaml := settings.MustParseConfig([]byte("output.elasticsearch.ssl.certificate_authorities: /mnt/elastic-internal/es-certs/ca.crt")) + outputCAYaml := settings.MustParseConfig([]byte(`output.elasticsearch.ssl.certificate_authorities: + - /mnt/elastic-internal/elasticsearch-certs/ca.crt`)) outputYaml := settings.MustParseConfig([]byte(`output: elasticsearch: hosts: @@ -53,7 +54,8 @@ func Test_buildBeatConfig(t *testing.T) { Namespace: "ns", }, } - withAssociation.SetAssociationConf(&commonv1.AssociationConf{ + esAssoc := beatv1beta1.BeatESAssociation{Beat: &withAssociation} + esAssoc.SetAssociationConf(&commonv1.AssociationConf{ AuthSecretName: "secret", AuthSecretKey: "elastic", CACertProvided: false, @@ -61,7 +63,9 @@ func Test_buildBeatConfig(t *testing.T) { URL: "url", }) withAssociationWithCA := *withAssociation.DeepCopy() - withAssociationWithCA.AssociationConf().CACertProvided = true + + esAssocWithCA := beatv1beta1.BeatESAssociation{Beat: &withAssociationWithCA} + esAssocWithCA.AssociationConf().CACertProvided = true withAssociationWithCAAndConfig := *withAssociationWithCA.DeepCopy() withAssociationWithCAAndConfig.Spec.Config = userConfig diff --git a/pkg/controller/beat/common/driver.go b/pkg/controller/beat/common/driver.go index 1257581e35..31dc9c1d7d 100644 --- a/pkg/controller/beat/common/driver.go +++ b/pkg/controller/beat/common/driver.go @@ -76,7 +76,7 @@ func Reconcile( } // we need to deref the secret here (if any) to include it in the configHash otherwise Beat will not be rolled on content changes - if err := commonassociation.WriteAssocToConfigHash(params.Client, ¶ms.Beat, configHash); err != nil { + if err := commonassociation.WriteAssocsToConfigHash(params.Client, params.Beat.GetAssociations(), configHash); err != nil { return results.WithError(err) } diff --git a/pkg/controller/beat/common/health.go b/pkg/controller/beat/common/health.go index 675d371e10..aac7343c2d 100644 --- a/pkg/controller/beat/common/health.go +++ b/pkg/controller/beat/common/health.go @@ -10,9 +10,11 @@ import ( ) // CalculateHealth returns health of the Beat based on association status, desired count and ready count. -func CalculateHealth(associated v1.Association, ready, desired int32) beatv1beta1.BeatHealth { - if associated.AssociationConf().IsConfigured() && associated.AssociationStatus() != v1.AssociationEstablished { - return beatv1beta1.BeatRedHealth +func CalculateHealth(associated []v1.Association, ready, desired int32) beatv1beta1.BeatHealth { + for _, assoc := range associated { + if assoc.AssociationConf().IsConfigured() && assoc.AssociationStatus() != v1.AssociationEstablished { + return beatv1beta1.BeatRedHealth + } } switch { diff --git a/pkg/controller/beat/common/health_test.go b/pkg/controller/beat/common/health_test.go index ea3907a61e..b35de185f3 100644 --- a/pkg/controller/beat/common/health_test.go +++ b/pkg/controller/beat/common/health_test.go @@ -15,94 +15,137 @@ import ( ) func Test_CalculateHealth(t *testing.T) { - noAssociation := &beatv1beta1.Beat{} - createAssociation := func(associationEstablished bool) commonv1.Association { - result := &beatv1beta1.Beat{} - result.SetAssociationConf(&commonv1.AssociationConf{ + type params struct { + esAssoc bool + esAssocEstablished bool + kbAssoc bool + kbAssocEstablished bool + } + + var noAssociation []commonv1.Association + createAssociation := func(assocDef params) []commonv1.Association { + beat := &beatv1beta1.Beat{} + var result []commonv1.Association + dummyConf := commonv1.AssociationConf{ AuthSecretName: "name", AuthSecretKey: "key", CACertProvided: true, CASecretName: "name", URL: "url", - }) - - if associationEstablished { - result.SetAssociationStatus(commonv1.AssociationEstablished) } - + if assocDef.esAssoc { + esAssoc := beatv1beta1.BeatESAssociation{Beat: beat} + esAssoc.SetAssociationConf(&dummyConf) + if assocDef.esAssocEstablished { + esAssoc.SetAssociationStatus(commonv1.AssociationEstablished) + } + result = append(result, &esAssoc) + } + if assocDef.kbAssoc { + kbAssoc := beatv1beta1.BeatKibanaAssociation{Beat: beat} + kbAssoc.SetAssociationConf(&dummyConf) + if assocDef.kbAssocEstablished { + kbAssoc.SetAssociationStatus(commonv1.AssociationEstablished) + } + result = append(result, &kbAssoc) + } return result } for _, tt := range []struct { name string - association commonv1.Association + associations []commonv1.Association ready, desired int32 want beatv1beta1.BeatHealth }{ { - name: "no association, 0 desired", - association: noAssociation, - ready: 0, - desired: 0, - want: beatv1beta1.BeatGreenHealth, + name: "no association, 0 desired", + associations: noAssociation, + ready: 0, + desired: 0, + want: beatv1beta1.BeatGreenHealth, + }, + { + name: "no association, all ready", + associations: noAssociation, + ready: 3, + desired: 3, + want: beatv1beta1.BeatGreenHealth, + }, + { + name: "no association, some ready", + associations: noAssociation, + ready: 1, + desired: 5, + want: beatv1beta1.BeatYellowHealth, }, { - name: "no association, all ready", - association: noAssociation, - ready: 3, - desired: 3, - want: beatv1beta1.BeatGreenHealth, + name: "no association, none ready", + associations: noAssociation, + ready: 0, + desired: 4, + want: beatv1beta1.BeatRedHealth, }, { - name: "no association, some ready", - association: noAssociation, - ready: 1, - desired: 5, - want: beatv1beta1.BeatYellowHealth, + name: "association not established, all ready", + associations: createAssociation(params{esAssoc: true}), + ready: 2, + desired: 2, + want: beatv1beta1.BeatRedHealth, }, { - name: "no association, none ready", - association: noAssociation, - ready: 0, - desired: 4, - want: beatv1beta1.BeatRedHealth, + name: "association established, 0 desired", + associations: createAssociation(params{esAssoc: true, esAssocEstablished: true}), + want: beatv1beta1.BeatGreenHealth, }, { - name: "association not established, all ready", - association: createAssociation(false), - ready: 2, - desired: 2, - want: beatv1beta1.BeatRedHealth, + name: "association established, all ready", + associations: createAssociation(params{esAssoc: true, esAssocEstablished: true}), + ready: 2, + desired: 2, + want: beatv1beta1.BeatGreenHealth, }, { - name: "association established, 0 desired", - association: createAssociation(true), - want: beatv1beta1.BeatGreenHealth, + name: "association established, some ready", + associations: createAssociation(params{esAssoc: true, esAssocEstablished: true}), + ready: 1, + desired: 5, + want: beatv1beta1.BeatYellowHealth, }, { - name: "association established, all ready", - association: createAssociation(true), - ready: 2, - desired: 2, - want: beatv1beta1.BeatGreenHealth, + name: "association established, none ready", + associations: createAssociation(params{esAssoc: true, esAssocEstablished: true}), + ready: 0, + desired: 4, + want: beatv1beta1.BeatRedHealth, }, { - name: "association established, some ready", - association: createAssociation(true), - ready: 1, - desired: 5, - want: beatv1beta1.BeatYellowHealth, + name: "multiple associations one established, all ready", + associations: createAssociation(params{ + esAssoc: true, + esAssocEstablished: true, + kbAssoc: true, + kbAssocEstablished: false, + }), + ready: 1, + desired: 1, + want: beatv1beta1.BeatRedHealth, }, { - name: "association established, none ready", - association: createAssociation(true), - ready: 0, - desired: 4, - want: beatv1beta1.BeatRedHealth, + name: "multiple associations all established, all ready", + associations: createAssociation(params{ + esAssoc: true, + esAssocEstablished: true, + kbAssoc: true, + kbAssocEstablished: true, + }), + ready: 1, + desired: 1, + want: beatv1beta1.BeatGreenHealth, }, } { t.Run(tt.name, func(t *testing.T) { - got := beatcommon.CalculateHealth(tt.association, tt.ready, tt.desired) + got := beatcommon.CalculateHealth(tt.associations, tt.ready, tt.desired) require.Equal(t, tt.want, got) }) } diff --git a/pkg/controller/beat/common/pod.go b/pkg/controller/beat/common/pod.go index ced1e0a729..5d8b789826 100644 --- a/pkg/controller/beat/common/pod.go +++ b/pkg/controller/beat/common/pod.go @@ -8,6 +8,7 @@ import ( "fmt" "hash" + commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -19,9 +20,8 @@ import ( ) const ( - CAVolumeName = "es-certs" - CAMountPath = "/mnt/elastic-internal/es-certs/" - CAFileName = "ca.crt" + CABaseMountPath = "/mnt/elastic-internal/" + CAFileName = "ca.crt" ConfigVolumeName = "config" ConfigMountPath = "/etc/beat.yml" @@ -51,6 +51,10 @@ var ( } ) +func certificatesDir(association commonv1.Association) string { + return fmt.Sprintf("%s/%s-certs", CABaseMountPath, association.AssociatedType()) +} + func buildPodTemplate( params DriverParams, defaultImage container.Image, @@ -114,11 +118,18 @@ func buildPodTemplate( dataVolume, } - if params.Beat.AssociationConf().CAIsConfigured() { - volumes = append(volumes, volume.NewSecretVolumeWithMountPath( - params.Beat.AssociationConf().GetCASecretName(), - CAVolumeName, - CAMountPath)) + for _, association := range params.Beat.GetAssociations() { + if !association.AssociationConf().CAIsConfigured() { + continue + } + caSecretName := association.AssociationConf().GetCASecretName() + caVolume := volume.NewSecretVolumeWithMountPath( + caSecretName, + association.AssociatedType()+"-certs", + certificatesDir(association), + ) + volumes = append(volumes, caVolume) + } for _, v := range volumes { diff --git a/pkg/controller/beat/common/reconcile.go b/pkg/controller/beat/common/reconcile.go index e078d5aa51..2c519fe86c 100644 --- a/pkg/controller/beat/common/reconcile.go +++ b/pkg/controller/beat/common/reconcile.go @@ -137,8 +137,7 @@ func updateStatus(params DriverParams, ready, desired int32) error { beat.Status.AvailableNodes = ready beat.Status.ExpectedNodes = desired - beat.Status.Health = CalculateHealth(&beat, ready, desired) - beat.Status.Association = beat.AssociationStatus() + beat.Status.Health = CalculateHealth(beat.GetAssociations(), ready, desired) return params.Client.Status().Update(&beat) } diff --git a/pkg/controller/beat/controller.go b/pkg/controller/beat/controller.go index 61be0d1f31..ad65646d35 100644 --- a/pkg/controller/beat/controller.go +++ b/pkg/controller/beat/controller.go @@ -180,7 +180,7 @@ func (r *ReconcileBeat) Reconcile(request reconcile.Request) (reconcile.Result, func (r *ReconcileBeat) doReconcile(ctx context.Context, beat beatv1beta1.Beat) *reconciler.Results { results := reconciler.NewResult(ctx) - if !association.IsConfiguredIfSet(&beat, r.recorder) { + if !association.AreConfiguredIfSet(beat.GetAssociations(), r.recorder) { return results } diff --git a/pkg/controller/common/association/association.go b/pkg/controller/common/association/association.go index 0d9836b161..6bf1bb591a 100644 --- a/pkg/controller/common/association/association.go +++ b/pkg/controller/common/association/association.go @@ -16,13 +16,17 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) -// WriteAssocToConfigHash dereferences auth secret (if any) to include it in the configHash. -func WriteAssocToConfigHash(client k8s.Client, assoc commonv1.Association, configHash hash.Hash) error { - if err := writeAuthSecretToConfigHash(client, assoc, configHash); err != nil { - return err +// WriteAssocsToConfigHash dereferences auth and CA secrets (if any) to include it in the configHash. +func WriteAssocsToConfigHash(client k8s.Client, associations []commonv1.Association, configHash hash.Hash) error { + for _, assoc := range associations { + if err := writeAuthSecretToConfigHash(client, assoc, configHash); err != nil { + return err + } + if err := writeCASecretToConfigHash(client, assoc, configHash); err != nil { + return err + } } - - return writeCASecretToConfigHash(client, assoc, configHash) + return nil } func writeAuthSecretToConfigHash(client k8s.Client, assoc commonv1.Association, configHash hash.Hash) error { diff --git a/pkg/controller/common/association/association_test.go b/pkg/controller/common/association/association_test.go index 7a3b4c336a..11e42b8803 100644 --- a/pkg/controller/common/association/association_test.go +++ b/pkg/controller/common/association/association_test.go @@ -18,29 +18,32 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) +func associationFixture(conf *commonv1.AssociationConf) commonv1.Association { + withAssoc := &beatv1beta1.Beat{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}} + esAssoc := beatv1beta1.BeatESAssociation{Beat: withAssoc} + esAssoc.SetAssociationConf(conf) + return &esAssoc +} + func Test_writeAuthSecretToConfigHash(t *testing.T) { for _, tt := range []struct { name string client k8s.Client - assoc func() commonv1.Association + assoc commonv1.Association wantHashed string wantErr bool }{ { name: "no association", - assoc: func() commonv1.Association { return &beatv1beta1.Beat{} }, + assoc: associationFixture(nil), }, { name: "association secret missing", client: k8s.WrappedFakeClient(), - assoc: func() commonv1.Association { - withAssoc := &beatv1beta1.Beat{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}} - withAssoc.SetAssociationConf(&commonv1.AssociationConf{ - AuthSecretName: "secret-name", - AuthSecretKey: "secret-key", - }) - return withAssoc - }, + assoc: associationFixture(&commonv1.AssociationConf{ + AuthSecretName: "secret-name", + AuthSecretKey: "secret-key", + }), wantHashed: "", wantErr: true, }, @@ -52,14 +55,10 @@ func Test_writeAuthSecretToConfigHash(t *testing.T) { Namespace: "test-ns", }, }), - assoc: func() commonv1.Association { - withAssoc := &beatv1beta1.Beat{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}} - withAssoc.SetAssociationConf(&commonv1.AssociationConf{ - AuthSecretName: "secret-name", - AuthSecretKey: "non-existing-key", - }) - return withAssoc - }, + assoc: associationFixture(&commonv1.AssociationConf{ + AuthSecretName: "secret-name", + AuthSecretKey: "non-existing-key", + }), wantHashed: "", wantErr: true, }, @@ -74,20 +73,16 @@ func Test_writeAuthSecretToConfigHash(t *testing.T) { "secret-key": []byte("123"), }, }), - assoc: func() commonv1.Association { - withAssoc := &beatv1beta1.Beat{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}} - withAssoc.SetAssociationConf(&commonv1.AssociationConf{ - AuthSecretName: "secret-name", - AuthSecretKey: "secret-key", - }) - return withAssoc - }, + assoc: associationFixture(&commonv1.AssociationConf{ + AuthSecretName: "secret-name", + AuthSecretKey: "secret-key", + }), wantHashed: "123", }, } { t.Run(tt.name, func(t *testing.T) { configHashPassed := sha256.New224() - gotErr := writeAuthSecretToConfigHash(tt.client, tt.assoc(), configHashPassed) + gotErr := writeAuthSecretToConfigHash(tt.client, tt.assoc, configHashPassed) require.Equal(t, tt.wantErr, gotErr != nil) configHash := sha256.New224() @@ -101,24 +96,20 @@ func Test_writeCASecretToConfigHash(t *testing.T) { for _, tt := range []struct { name string client k8s.Client - assoc func() commonv1.Association + assoc commonv1.Association wantHashed string wantErr bool }{ { name: "no association", - assoc: func() commonv1.Association { return &beatv1beta1.Beat{} }, + assoc: associationFixture(nil), }, { name: "association ca secret missing", client: k8s.WrappedFakeClient(), - assoc: func() commonv1.Association { - withAssoc := &beatv1beta1.Beat{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}} - withAssoc.SetAssociationConf(&commonv1.AssociationConf{ - CASecretName: "ca-secret-name", - }) - return withAssoc - }, + assoc: associationFixture(&commonv1.AssociationConf{ + CASecretName: "ca-secret-name", + }), wantHashed: "", wantErr: true, }, @@ -130,13 +121,9 @@ func Test_writeCASecretToConfigHash(t *testing.T) { Namespace: "test-ns", }, }), - assoc: func() commonv1.Association { - withAssoc := &beatv1beta1.Beat{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}} - withAssoc.SetAssociationConf(&commonv1.AssociationConf{ - CASecretName: "ca-secret-name", - }) - return withAssoc - }, + assoc: associationFixture(&commonv1.AssociationConf{ + CASecretName: "ca-secret-name", + }), wantHashed: "", wantErr: true, }, @@ -151,19 +138,15 @@ func Test_writeCASecretToConfigHash(t *testing.T) { certificates.CertFileName: []byte("456"), }, }), - assoc: func() commonv1.Association { - withAssoc := &beatv1beta1.Beat{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}} - withAssoc.SetAssociationConf(&commonv1.AssociationConf{ - CASecretName: "ca-secret-name", - }) - return withAssoc - }, + assoc: associationFixture(&commonv1.AssociationConf{ + CASecretName: "ca-secret-name", + }), wantHashed: "456", }, } { t.Run(tt.name, func(t *testing.T) { configHashPassed := sha256.New224() - gotErr := writeCASecretToConfigHash(tt.client, tt.assoc(), configHashPassed) + gotErr := writeCASecretToConfigHash(tt.client, tt.assoc, configHashPassed) require.Equal(t, tt.wantErr, gotErr != nil) configHash := sha256.New224() diff --git a/pkg/controller/elasticsearch/user/roles.go b/pkg/controller/elasticsearch/user/roles.go index f47ffa011e..f2734ed682 100644 --- a/pkg/controller/elasticsearch/user/roles.go +++ b/pkg/controller/elasticsearch/user/roles.go @@ -28,6 +28,9 @@ const ( // ApmAgentUserRole is the name of the role used by APMServer instances to connect to Kibana ApmAgentUserRole = "eck_apm_agent_user_role" + + // KibanaUserBuiltinRole is the name of the built-in Kibana user role + KibanaUserBuiltinRole = "kibana_user" ) var ( diff --git a/pkg/controller/kibana/driver.go b/pkg/controller/kibana/driver.go index 6f3faae13f..f897bd9998 100644 --- a/pkg/controller/kibana/driver.go +++ b/pkg/controller/kibana/driver.go @@ -212,7 +212,7 @@ func (d *driver) deploymentParams(kb *kbv1.Kibana) (deployment.Params, error) { } // we need to deref the secret here to include it in the checksum otherwise Kibana will not be rolled on contents changes - if err := commonassociation.WriteAssocToConfigHash(d.client, kb, configChecksum); err != nil { + if err := commonassociation.WriteAssocsToConfigHash(d.client, kb.GetAssociations(), configChecksum); err != nil { return deployment.Params{}, err } From b5268538e8d54bf3ca9a6a3ca21cf535b21d4a0a Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 10:13:49 +0200 Subject: [PATCH 02/11] Only add Kibana config for Beats that support it --- config/crds/all-crds.yaml | 4 +- .../crds/bases/beat.k8s.elastic.co_beats.yaml | 4 +- pkg/controller/beat/common/config.go | 22 +++++---- pkg/controller/beat/common/config_test.go | 45 ++++++++++++++----- pkg/controller/beat/common/driver.go | 5 +-- pkg/controller/beat/filebeat/config.go | 17 ++++++- pkg/controller/beat/filebeat/filebeat.go | 7 ++- pkg/controller/beat/metricbeat/config.go | 13 ++++++ pkg/controller/beat/metricbeat/metricbeat.go | 7 ++- pkg/controller/beat/otherbeat/otherbeat.go | 2 +- 10 files changed, 96 insertions(+), 30 deletions(-) diff --git a/config/crds/all-crds.yaml b/config/crds/all-crds.yaml index 24b6282a2d..3deed40165 100644 --- a/config/crds/all-crds.yaml +++ b/config/crds/all-crds.yaml @@ -557,8 +557,8 @@ spec: type: string kibanaRef: description: KibanaRef is a reference to a Kibana instance running in - the same Kubernetes cluster. It allows APM agent central configuration - management in Kibana. + the same Kubernetes cluster. It allows automatic setup of dashboards + and visualizations. properties: name: description: Name of the Kubernetes object. diff --git a/config/crds/bases/beat.k8s.elastic.co_beats.yaml b/config/crds/bases/beat.k8s.elastic.co_beats.yaml index 1343b33cd3..5aace01443 100644 --- a/config/crds/bases/beat.k8s.elastic.co_beats.yaml +++ b/config/crds/bases/beat.k8s.elastic.co_beats.yaml @@ -12162,8 +12162,8 @@ spec: type: string kibanaRef: description: KibanaRef is a reference to a Kibana instance running in - the same Kubernetes cluster. It allows APM agent central configuration - management in Kibana. + the same Kubernetes cluster. It allows automatic setup of dashboards + and visualizations. properties: name: description: Name of the Kubernetes object. diff --git a/pkg/controller/beat/common/config.go b/pkg/controller/beat/common/config.go index bfcd2d10e9..85dc7cbc80 100644 --- a/pkg/controller/beat/common/config.go +++ b/pkg/controller/beat/common/config.go @@ -20,6 +20,13 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) +type DefaultConfigs struct { + // Managed config is handled by ECK and will not be nullified by user provided config. + Managed *settings.CanonicalConfig + // Unmanaged config is default config that will be overridden by any user provided config. + Unmanaged *settings.CanonicalConfig +} + // setOutput will set 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() { @@ -46,7 +53,8 @@ func buildOutputConfig(client k8s.Client, associated beatv1beta1.BeatESAssociati return settings.MustCanonicalConfig(esOutput), nil } -func buildKibanaConfig(client k8s.Client, associated beatv1beta1.BeatKibanaAssociation) (*settings.CanonicalConfig, error) { +// 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() { return settings.NewCanonicalConfig(), nil } @@ -75,7 +83,7 @@ func buildBeatConfig( log logr.Logger, client k8s.Client, beat beatv1beta1.Beat, - defaultConfig *settings.CanonicalConfig, + defaultConfigs DefaultConfigs, ) ([]byte, error) { cfg := settings.NewCanonicalConfig() @@ -83,16 +91,14 @@ func buildBeatConfig( if err != nil { return nil, err } - kibanaCfg, err := buildKibanaConfig(client, beatv1beta1.BeatKibanaAssociation{Beat: &beat}) - - err = cfg.MergeWith(outputCfg, kibanaCfg) + err = cfg.MergeWith(outputCfg, defaultConfigs.Managed) if err != nil { return nil, err } // use only the default config or only the provided config - no overriding, no merging userConfig := beat.Spec.Config if userConfig == nil { - if err := cfg.MergeWith(defaultConfig); err != nil { + if err := cfg.MergeWith(defaultConfigs.Unmanaged); err != nil { return nil, err } } else { @@ -112,10 +118,10 @@ func buildBeatConfig( func reconcileConfig( params DriverParams, - defaultConfig *settings.CanonicalConfig, + defaultConfigs DefaultConfigs, configHash hash.Hash, ) error { - cfgBytes, err := buildBeatConfig(params.Logger, params.Client, params.Beat, defaultConfig) + cfgBytes, err := buildBeatConfig(params.Logger, params.Client, params.Beat, defaultConfigs) if err != nil { return err } diff --git a/pkg/controller/beat/common/config_test.go b/pkg/controller/beat/common/config_test.go index 47a1117268..4629653b90 100644 --- a/pkg/controller/beat/common/config_test.go +++ b/pkg/controller/beat/common/config_test.go @@ -36,7 +36,8 @@ func Test_buildBeatConfig(t *testing.T) { }, ) - defaultConfig := settings.MustParseConfig([]byte("default: true")) + unmanagedDefaultCfg := settings.MustParseConfig([]byte("default: true")) + managedDefaultCfg := settings.MustParseConfig([]byte("setup.kibana: true")) userConfig := &commonv1.Config{Data: map[string]interface{}{"user": "true"}} userCanonicalConfig := settings.MustCanonicalConfig(userConfig.Data) outputCAYaml := settings.MustParseConfig([]byte(`output.elasticsearch.ssl.certificate_authorities: @@ -83,7 +84,7 @@ func Test_buildBeatConfig(t *testing.T) { name string client k8s.Client beat beatv1beta1.Beat - defaultConfig *settings.CanonicalConfig + defaultConfig DefaultConfigs want *settings.CanonicalConfig wantErr bool }{ @@ -94,8 +95,8 @@ func Test_buildBeatConfig(t *testing.T) { { name: "no association, only default config", beat: beatv1beta1.Beat{}, - defaultConfig: defaultConfig, - want: defaultConfig, + defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + want: unmanagedDefaultCfg, }, { name: "no association, only user config", @@ -109,15 +110,27 @@ func Test_buildBeatConfig(t *testing.T) { beat: beatv1beta1.Beat{Spec: beatv1beta1.BeatSpec{ Config: userConfig, }}, - defaultConfig: defaultConfig, + defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, want: userCanonicalConfig, }, + { + name: "no association, both default configs and user config", + beat: beatv1beta1.Beat{Spec: beatv1beta1.BeatSpec{ + Config: userConfig, + }}, + defaultConfig: DefaultConfigs{ + Unmanaged: unmanagedDefaultCfg, + Managed: managedDefaultCfg, + }, + want: merge(userCanonicalConfig, managedDefaultCfg), + }, + { name: "association without ca, only default config", client: clientWithSecret, beat: withAssociation, - defaultConfig: defaultConfig, - want: merge(defaultConfig, outputYaml), + defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + want: merge(unmanagedDefaultCfg, outputYaml), }, { name: "association without ca, only user config", @@ -129,15 +142,15 @@ func Test_buildBeatConfig(t *testing.T) { name: "association without ca, default and user config", client: clientWithSecret, beat: withAssociationWithConfig, - defaultConfig: defaultConfig, + defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, want: merge(userCanonicalConfig, outputYaml), }, { name: "association with ca, only default config", client: clientWithSecret, beat: withAssociationWithCA, - defaultConfig: defaultConfig, - want: merge(defaultConfig, outputYaml, outputCAYaml), + defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + want: merge(unmanagedDefaultCfg, outputYaml, outputCAYaml), }, { name: "association with ca, only user config", @@ -149,9 +162,19 @@ func Test_buildBeatConfig(t *testing.T) { name: "association with ca, default and user config", client: clientWithSecret, beat: withAssociationWithCAAndConfig, - defaultConfig: defaultConfig, + defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, want: merge(userCanonicalConfig, outputYaml, outputCAYaml), }, + { + name: "association with ca, both default configs and user config", + client: clientWithSecret, + beat: withAssociationWithCAAndConfig, + defaultConfig: DefaultConfigs{ + Unmanaged: unmanagedDefaultCfg, + Managed: managedDefaultCfg, + }, + want: merge(userCanonicalConfig, managedDefaultCfg, outputYaml, outputCAYaml), + }, } { t.Run(tt.name, func(t *testing.T) { gotYaml, gotErr := buildBeatConfig(logrtesting.NullLogger{}, tt.client, tt.beat, tt.defaultConfig) diff --git a/pkg/controller/beat/common/driver.go b/pkg/controller/beat/common/driver.go index 31dc9c1d7d..752586e07f 100644 --- a/pkg/controller/beat/common/driver.go +++ b/pkg/controller/beat/common/driver.go @@ -17,7 +17,6 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/controller/common/container" "github.com/elastic/cloud-on-k8s/pkg/controller/common/defaults" "github.com/elastic/cloud-on-k8s/pkg/controller/common/reconciler" - "github.com/elastic/cloud-on-k8s/pkg/controller/common/settings" "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) @@ -56,7 +55,7 @@ func ValidateBeatSpec(spec beatv1beta1.BeatSpec) error { func Reconcile( params DriverParams, - defaultConfig *settings.CanonicalConfig, + defaultConfigs DefaultConfigs, defaultImage container.Image, modifyPodFunc func(builder *defaults.PodTemplateBuilder), ) *reconciler.Results { @@ -71,7 +70,7 @@ func Reconcile( } configHash := sha256.New224() - if err := reconcileConfig(params, defaultConfig, configHash); err != nil { + if err := reconcileConfig(params, defaultConfigs, configHash); err != nil { return results.WithError(err) } diff --git a/pkg/controller/beat/filebeat/config.go b/pkg/controller/beat/filebeat/config.go index a62429032a..7b88dd23c5 100644 --- a/pkg/controller/beat/filebeat/config.go +++ b/pkg/controller/beat/filebeat/config.go @@ -4,7 +4,11 @@ package filebeat -import "github.com/elastic/cloud-on-k8s/pkg/controller/common/settings" +import ( + "github.com/elastic/cloud-on-k8s/pkg/apis/beat/v1beta1" + "github.com/elastic/cloud-on-k8s/pkg/controller/beat/common" + "github.com/elastic/cloud-on-k8s/pkg/controller/common/settings" +) var ( defaultConfig = settings.MustParseConfig([]byte( @@ -24,3 +28,14 @@ processors: - add_host_metadata: {} `)) ) + +func (d *Driver) configParams() (common.DefaultConfigs, error) { + kibanaConfig, err := common.BuildKibanaConfig(d.Client, v1beta1.BeatKibanaAssociation{Beat: &d.Beat}) + if err != nil { + return common.DefaultConfigs{}, err + } + return common.DefaultConfigs{ + Managed: kibanaConfig, + Unmanaged: defaultConfig, + }, nil +} diff --git a/pkg/controller/beat/filebeat/filebeat.go b/pkg/controller/beat/filebeat/filebeat.go index 7f36d78241..4922dcef7b 100644 --- a/pkg/controller/beat/filebeat/filebeat.go +++ b/pkg/controller/beat/filebeat/filebeat.go @@ -59,9 +59,14 @@ func (d *Driver) Reconcile() *reconciler.Results { } } + defaultConfigs, err := d.configParams() + if err != nil { + return reconciler.NewResult(d.DriverParams.Context).WithError(err) + } + return beatcommon.Reconcile( d.DriverParams, - defaultConfig, + defaultConfigs, container.FilebeatImage, f) } diff --git a/pkg/controller/beat/metricbeat/config.go b/pkg/controller/beat/metricbeat/config.go index ec12450303..633f5549ec 100644 --- a/pkg/controller/beat/metricbeat/config.go +++ b/pkg/controller/beat/metricbeat/config.go @@ -5,6 +5,8 @@ package metricbeat import ( + "github.com/elastic/cloud-on-k8s/pkg/apis/beat/v1beta1" + "github.com/elastic/cloud-on-k8s/pkg/controller/beat/common" "github.com/elastic/cloud-on-k8s/pkg/controller/common/settings" ) @@ -42,3 +44,14 @@ processors: - add_cloud_metadata: {} `)) ) + +func (d *Driver) configParams() (common.DefaultConfigs, error) { + kibanaConfig, err := common.BuildKibanaConfig(d.Client, v1beta1.BeatKibanaAssociation{Beat: &d.Beat}) + if err != nil { + return common.DefaultConfigs{}, err + } + return common.DefaultConfigs{ + Managed: kibanaConfig, + Unmanaged: defaultConfig, + }, nil +} diff --git a/pkg/controller/beat/metricbeat/metricbeat.go b/pkg/controller/beat/metricbeat/metricbeat.go index c98b65b91e..3bd2cd8b71 100644 --- a/pkg/controller/beat/metricbeat/metricbeat.go +++ b/pkg/controller/beat/metricbeat/metricbeat.go @@ -62,9 +62,14 @@ func (d *Driver) Reconcile() *reconciler.Results { builder.WithArgs("-e", "-c", beatcommon.ConfigMountPath, "-system.hostfs=/hostfs") } + defaultConfigs, err := d.configParams() + if err != nil { + return reconciler.NewResult(d.DriverParams.Context).WithError(err) + } + return beatcommon.Reconcile( d.DriverParams, - defaultConfig, + defaultConfigs, container.MetricbeatImage, f) } diff --git a/pkg/controller/beat/otherbeat/otherbeat.go b/pkg/controller/beat/otherbeat/otherbeat.go index 74a460d80e..e8878a4fcb 100644 --- a/pkg/controller/beat/otherbeat/otherbeat.go +++ b/pkg/controller/beat/otherbeat/otherbeat.go @@ -28,5 +28,5 @@ func NewDriver(params beatcommon.DriverParams) beatcommon.Driver { } func (d *Driver) Reconcile() *reconciler.Results { - return beatcommon.Reconcile(d.DriverParams, nil, "", nil) + return beatcommon.Reconcile(d.DriverParams, beatcommon.DefaultConfigs{}, "", nil) } From b26615660aa76198058452177dd0e58e817871c5 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 10:22:21 +0200 Subject: [PATCH 03/11] regenerate api docs --- docs/reference/api-docs.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/api-docs.asciidoc b/docs/reference/api-docs.asciidoc index b7f1ad4b26..8f3844282e 100644 --- a/docs/reference/api-docs.asciidoc +++ b/docs/reference/api-docs.asciidoc @@ -176,6 +176,7 @@ BeatSpec defines the desired state of a Beat. | *`type`* __string__ | Type is the type of the Beat to deploy (filebeat, metricbeat, etc.). Any string can be used, but well-known types will be recognized and will allow to provide sane default configurations. | *`version`* __string__ | Version of the Beat. | *`elasticsearchRef`* __xref:{anchor_prefix}-github.aaakk.us.kg-elastic-cloud-on-k8s-pkg-apis-common-v1-objectselector[$$ObjectSelector$$]__ | ElasticsearchRef is a reference to an Elasticsearch cluster running in the same Kubernetes cluster. +| *`kibanaRef`* __xref:{anchor_prefix}-github.aaakk.us.kg-elastic-cloud-on-k8s-pkg-apis-common-v1-objectselector[$$ObjectSelector$$]__ | KibanaRef is a reference to a Kibana instance running in the same Kubernetes cluster. It allows automatic setup of dashboards and visualizations. | *`image`* __string__ | Image is the Beat Docker image to deploy. Version and Type have to match the Beat in the image. | *`config`* __xref:{anchor_prefix}-github.aaakk.us.kg-elastic-cloud-on-k8s-pkg-apis-common-v1-config[$$Config$$]__ | Config holds the Beat configuration. If provided, it will override the default configuration. | *`serviceAccountName`* __string__ | ServiceAccountName is used to check access from the current resource to Elasticsearch resource in a different namespace. Can only be used if ECK is enforcing RBAC on references. From dd13b70d5388183fd38c3cc553f0641af551866f Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 10:25:28 +0200 Subject: [PATCH 04/11] fix typo, reorder --- pkg/apis/beat/v1beta1/beat_types.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/apis/beat/v1beta1/beat_types.go b/pkg/apis/beat/v1beta1/beat_types.go index a6413e27fe..c74fcdbe9f 100644 --- a/pkg/apis/beat/v1beta1/beat_types.go +++ b/pkg/apis/beat/v1beta1/beat_types.go @@ -77,7 +77,7 @@ type BeatStatus struct { ElasticsearchAssociation commonv1.AssociationStatus `json:"elasticsearchAssociationStatus,omitempty"` // +kubebuilder:validation:Optional - KibanaAssocation commonv1.AssociationStatus `json:"kibanaAssocationStatus,omitempty"` + KibanaAssocation commonv1.AssociationStatus `json:"kibanaAssociationStatus,omitempty"` } type BeatHealth string @@ -132,6 +132,15 @@ func (b *Beat) ServiceAccountName() string { return b.Spec.ServiceAccountName } +// IsMarkedForDeletion returns true if the Beat is going to be deleted +func (b *Beat) IsMarkedForDeletion() bool { + return !b.DeletionTimestamp.IsZero() +} + +func (b *Beat) ElasticsearchRef() commonv1.ObjectSelector { + return b.Spec.ElasticsearchRef +} + type BeatESAssociation struct { *Beat } @@ -220,15 +229,6 @@ func (b *BeatKibanaAssociation) AssociationConfAnnotationName() string { return commonv1.KibanaConfigAnnotationName } -// IsMarkedForDeletion returns true if the Beat is going to be deleted -func (b *Beat) IsMarkedForDeletion() bool { - return !b.DeletionTimestamp.IsZero() -} - -func (b *Beat) ElasticsearchRef() commonv1.ObjectSelector { - return b.Spec.ElasticsearchRef -} - // +kubebuilder:object:root=true // BeatList contains a list of Beats. From 81a19e6e91b9dc7242cca4e456a5552baf06f054 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 10:26:32 +0200 Subject: [PATCH 05/11] fix godoc --- pkg/controller/association/controller/apm_es.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/association/controller/apm_es.go b/pkg/controller/association/controller/apm_es.go index e2e2d6d5ac..6b47249c2b 100644 --- a/pkg/controller/association/controller/apm_es.go +++ b/pkg/controller/association/controller/apm_es.go @@ -28,7 +28,7 @@ const ( ApmAssociationLabelName = "apmassociation.k8s.elastic.co/name" // ApmAssociationLabelNamespace marks resources created for an association originating from APM. ApmAssociationLabelNamespace = "apmassociation.k8s.elastic.co/namespace" - // ApmAssociationLabelNamespace marks resources created for an association originating from APM. + // ApmAssociationLabelType marks resources created for an association originating from APM. ApmAssociationLabelType = "apmassociation.k8s.elastic.co/type" ) From 8768deecbf291a7e0cb67f681f09f169142a560c Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 14:43:52 +0200 Subject: [PATCH 06/11] review input --- config/crds/all-crds.yaml | 2 +- .../crds/bases/beat.k8s.elastic.co_beats.yaml | 2 +- pkg/apis/beat/v1beta1/beat_types.go | 12 ++++++------ .../association/controller/apm_kibana.go | 2 +- .../association/controller/beat_kibana.go | 2 ++ pkg/controller/beat/common/config.go | 18 +++++++++--------- pkg/controller/beat/common/config_test.go | 18 +++++++++--------- pkg/controller/beat/common/driver.go | 4 ++-- pkg/controller/beat/common/health.go | 4 ++-- pkg/controller/beat/filebeat/config.go | 6 +++--- pkg/controller/beat/filebeat/filebeat.go | 4 ++-- pkg/controller/beat/metricbeat/config.go | 6 +++--- pkg/controller/beat/metricbeat/metricbeat.go | 4 ++-- pkg/controller/beat/otherbeat/otherbeat.go | 2 +- 14 files changed, 44 insertions(+), 42 deletions(-) diff --git a/config/crds/all-crds.yaml b/config/crds/all-crds.yaml index 3deed40165..72e2b2901d 100644 --- a/config/crds/all-crds.yaml +++ b/config/crds/all-crds.yaml @@ -602,7 +602,7 @@ spec: type: integer health: type: string - kibanaAssocationStatus: + kibanaAssociationStatus: description: AssociationStatus is the status of an association resource. type: string type: object diff --git a/config/crds/bases/beat.k8s.elastic.co_beats.yaml b/config/crds/bases/beat.k8s.elastic.co_beats.yaml index 5aace01443..602c4e39a8 100644 --- a/config/crds/bases/beat.k8s.elastic.co_beats.yaml +++ b/config/crds/bases/beat.k8s.elastic.co_beats.yaml @@ -12207,7 +12207,7 @@ spec: type: integer health: type: string - kibanaAssocationStatus: + kibanaAssociationStatus: description: AssociationStatus is the status of an association resource. type: string type: object diff --git a/pkg/apis/beat/v1beta1/beat_types.go b/pkg/apis/beat/v1beta1/beat_types.go index c74fcdbe9f..5f7a0739c8 100644 --- a/pkg/apis/beat/v1beta1/beat_types.go +++ b/pkg/apis/beat/v1beta1/beat_types.go @@ -74,10 +74,10 @@ type BeatStatus struct { Health BeatHealth `json:"health,omitempty"` // +kubebuilder:validation:Optional - ElasticsearchAssociation commonv1.AssociationStatus `json:"elasticsearchAssociationStatus,omitempty"` + ElasticsearchAssociationStatus commonv1.AssociationStatus `json:"elasticsearchAssociationStatus,omitempty"` // +kubebuilder:validation:Optional - KibanaAssocation commonv1.AssociationStatus `json:"kibanaAssociationStatus,omitempty"` + KibanaAssocationStatus commonv1.AssociationStatus `json:"kibanaAssociationStatus,omitempty"` } type BeatHealth string @@ -178,11 +178,11 @@ func (b *BeatESAssociation) SetAssociationConf(conf *commonv1.AssociationConf) { } func (b *BeatESAssociation) AssociationStatus() commonv1.AssociationStatus { - return b.Status.ElasticsearchAssociation + return b.Status.ElasticsearchAssociationStatus } func (b *BeatESAssociation) SetAssociationStatus(status commonv1.AssociationStatus) { - b.Status.ElasticsearchAssociation = status + b.Status.ElasticsearchAssociationStatus = status } type BeatKibanaAssociation struct { @@ -200,11 +200,11 @@ func (b *BeatKibanaAssociation) SetAssociationConf(conf *commonv1.AssociationCon } func (b *BeatKibanaAssociation) AssociationStatus() commonv1.AssociationStatus { - return b.Status.KibanaAssocation + return b.Status.KibanaAssocationStatus } func (b *BeatKibanaAssociation) SetAssociationStatus(status commonv1.AssociationStatus) { - b.Status.KibanaAssocation = status + b.Status.KibanaAssocationStatus = status } func (b *BeatKibanaAssociation) Associated() commonv1.Associated { diff --git a/pkg/controller/association/controller/apm_kibana.go b/pkg/controller/association/controller/apm_kibana.go index 0b3357dde7..6c441d79b2 100644 --- a/pkg/controller/association/controller/apm_kibana.go +++ b/pkg/controller/association/controller/apm_kibana.go @@ -35,7 +35,7 @@ func AddApmKibana(mgr manager.Manager, accessReviewer rbac.AccessReviewer, param return map[string]string{ ApmAssociationLabelName: associated.Name, ApmAssociationLabelNamespace: associated.Namespace, - ApmAssociationLabelType: "kibana", + ApmAssociationLabelType: commonv1.KibanaAssociationType, } }, UserSecretSuffix: "apm-kb-user", diff --git a/pkg/controller/association/controller/beat_kibana.go b/pkg/controller/association/controller/beat_kibana.go index 477f5a7a6e..2675e9d25a 100644 --- a/pkg/controller/association/controller/beat_kibana.go +++ b/pkg/controller/association/controller/beat_kibana.go @@ -38,6 +38,8 @@ func AddBeatKibana(mgr manager.Manager, accessReviewer rbac.AccessReviewer, para ESUserRole: func(commonv1.Associated) (string, error) { return user.SuperUserBuiltinRole, nil }, + // The generic association controller watches Elasticsearch by default but we are interested in changes to + // Kibana as well for the purposes of establishing the association. SetDynamicWatches: func(association commonv1.Association, w watches.DynamicWatches) error { kibanaKey := association.AssociationRef().NamespacedName() watchName := association.GetNamespace() + "-" + association.GetName() + "-kibana-watch" diff --git a/pkg/controller/beat/common/config.go b/pkg/controller/beat/common/config.go index 85dc7cbc80..5ecccaf9de 100644 --- a/pkg/controller/beat/common/config.go +++ b/pkg/controller/beat/common/config.go @@ -20,14 +20,14 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) -type DefaultConfigs struct { +type DefaultConfig struct { // Managed config is handled by ECK and will not be nullified by user provided config. Managed *settings.CanonicalConfig // Unmanaged config is default config that will be overridden by any user provided config. Unmanaged *settings.CanonicalConfig } -// setOutput will set the output section in Beat config according to the association configuration. +// 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() { return settings.NewCanonicalConfig(), nil @@ -50,7 +50,7 @@ func buildOutputConfig(client k8s.Client, associated beatv1beta1.BeatESAssociati esOutput["output.elasticsearch.ssl.certificate_authorities"] = []string{path.Join(certificatesDir(&associated), CAFileName)} } - return settings.MustCanonicalConfig(esOutput), nil + return settings.NewCanonicalConfigFrom(esOutput) } // BuildKibanaConfig builds on optional Kibana configuration for dashboard setup and visualizations. @@ -76,14 +76,14 @@ func BuildKibanaConfig(client k8s.Client, associated beatv1beta1.BeatKibanaAssoc if associated.AssociationConf().GetCACertProvided() { kibanaCfg["setup.kibana.ssl.certificate_authorities"] = []string{path.Join(certificatesDir(&associated), CAFileName)} } - return settings.MustCanonicalConfig(kibanaCfg), nil + return settings.NewCanonicalConfigFrom(kibanaCfg) } func buildBeatConfig( log logr.Logger, client k8s.Client, beat beatv1beta1.Beat, - defaultConfigs DefaultConfigs, + defaultConfig DefaultConfig, ) ([]byte, error) { cfg := settings.NewCanonicalConfig() @@ -91,14 +91,14 @@ func buildBeatConfig( if err != nil { return nil, err } - err = cfg.MergeWith(outputCfg, defaultConfigs.Managed) + err = cfg.MergeWith(outputCfg, defaultConfig.Managed) if err != nil { return nil, err } // use only the default config or only the provided config - no overriding, no merging userConfig := beat.Spec.Config if userConfig == nil { - if err := cfg.MergeWith(defaultConfigs.Unmanaged); err != nil { + if err := cfg.MergeWith(defaultConfig.Unmanaged); err != nil { return nil, err } } else { @@ -118,10 +118,10 @@ func buildBeatConfig( func reconcileConfig( params DriverParams, - defaultConfigs DefaultConfigs, + defaultConfig DefaultConfig, configHash hash.Hash, ) error { - cfgBytes, err := buildBeatConfig(params.Logger, params.Client, params.Beat, defaultConfigs) + cfgBytes, err := buildBeatConfig(params.Logger, params.Client, params.Beat, defaultConfig) if err != nil { return err } diff --git a/pkg/controller/beat/common/config_test.go b/pkg/controller/beat/common/config_test.go index 4629653b90..f0dc9d48d6 100644 --- a/pkg/controller/beat/common/config_test.go +++ b/pkg/controller/beat/common/config_test.go @@ -84,7 +84,7 @@ func Test_buildBeatConfig(t *testing.T) { name string client k8s.Client beat beatv1beta1.Beat - defaultConfig DefaultConfigs + defaultConfig DefaultConfig want *settings.CanonicalConfig wantErr bool }{ @@ -95,7 +95,7 @@ func Test_buildBeatConfig(t *testing.T) { { name: "no association, only default config", beat: beatv1beta1.Beat{}, - defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + defaultConfig: DefaultConfig{Unmanaged: unmanagedDefaultCfg}, want: unmanagedDefaultCfg, }, { @@ -110,7 +110,7 @@ func Test_buildBeatConfig(t *testing.T) { beat: beatv1beta1.Beat{Spec: beatv1beta1.BeatSpec{ Config: userConfig, }}, - defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + defaultConfig: DefaultConfig{Unmanaged: unmanagedDefaultCfg}, want: userCanonicalConfig, }, { @@ -118,7 +118,7 @@ func Test_buildBeatConfig(t *testing.T) { beat: beatv1beta1.Beat{Spec: beatv1beta1.BeatSpec{ Config: userConfig, }}, - defaultConfig: DefaultConfigs{ + defaultConfig: DefaultConfig{ Unmanaged: unmanagedDefaultCfg, Managed: managedDefaultCfg, }, @@ -129,7 +129,7 @@ func Test_buildBeatConfig(t *testing.T) { name: "association without ca, only default config", client: clientWithSecret, beat: withAssociation, - defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + defaultConfig: DefaultConfig{Unmanaged: unmanagedDefaultCfg}, want: merge(unmanagedDefaultCfg, outputYaml), }, { @@ -142,14 +142,14 @@ func Test_buildBeatConfig(t *testing.T) { name: "association without ca, default and user config", client: clientWithSecret, beat: withAssociationWithConfig, - defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + defaultConfig: DefaultConfig{Unmanaged: unmanagedDefaultCfg}, want: merge(userCanonicalConfig, outputYaml), }, { name: "association with ca, only default config", client: clientWithSecret, beat: withAssociationWithCA, - defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + defaultConfig: DefaultConfig{Unmanaged: unmanagedDefaultCfg}, want: merge(unmanagedDefaultCfg, outputYaml, outputCAYaml), }, { @@ -162,14 +162,14 @@ func Test_buildBeatConfig(t *testing.T) { name: "association with ca, default and user config", client: clientWithSecret, beat: withAssociationWithCAAndConfig, - defaultConfig: DefaultConfigs{Unmanaged: unmanagedDefaultCfg}, + defaultConfig: DefaultConfig{Unmanaged: unmanagedDefaultCfg}, want: merge(userCanonicalConfig, outputYaml, outputCAYaml), }, { name: "association with ca, both default configs and user config", client: clientWithSecret, beat: withAssociationWithCAAndConfig, - defaultConfig: DefaultConfigs{ + defaultConfig: DefaultConfig{ Unmanaged: unmanagedDefaultCfg, Managed: managedDefaultCfg, }, diff --git a/pkg/controller/beat/common/driver.go b/pkg/controller/beat/common/driver.go index 752586e07f..ad724abe0e 100644 --- a/pkg/controller/beat/common/driver.go +++ b/pkg/controller/beat/common/driver.go @@ -55,7 +55,7 @@ func ValidateBeatSpec(spec beatv1beta1.BeatSpec) error { func Reconcile( params DriverParams, - defaultConfigs DefaultConfigs, + defaultConfig DefaultConfig, defaultImage container.Image, modifyPodFunc func(builder *defaults.PodTemplateBuilder), ) *reconciler.Results { @@ -70,7 +70,7 @@ func Reconcile( } configHash := sha256.New224() - if err := reconcileConfig(params, defaultConfigs, configHash); err != nil { + if err := reconcileConfig(params, defaultConfig, configHash); err != nil { return results.WithError(err) } diff --git a/pkg/controller/beat/common/health.go b/pkg/controller/beat/common/health.go index aac7343c2d..865a38c539 100644 --- a/pkg/controller/beat/common/health.go +++ b/pkg/controller/beat/common/health.go @@ -10,8 +10,8 @@ import ( ) // CalculateHealth returns health of the Beat based on association status, desired count and ready count. -func CalculateHealth(associated []v1.Association, ready, desired int32) beatv1beta1.BeatHealth { - for _, assoc := range associated { +func CalculateHealth(associations []v1.Association, ready, desired int32) beatv1beta1.BeatHealth { + for _, assoc := range associations { if assoc.AssociationConf().IsConfigured() && assoc.AssociationStatus() != v1.AssociationEstablished { return beatv1beta1.BeatRedHealth } diff --git a/pkg/controller/beat/filebeat/config.go b/pkg/controller/beat/filebeat/config.go index 7b88dd23c5..ef45472664 100644 --- a/pkg/controller/beat/filebeat/config.go +++ b/pkg/controller/beat/filebeat/config.go @@ -29,12 +29,12 @@ processors: `)) ) -func (d *Driver) configParams() (common.DefaultConfigs, error) { +func (d *Driver) defaultConfig() (common.DefaultConfig, error) { kibanaConfig, err := common.BuildKibanaConfig(d.Client, v1beta1.BeatKibanaAssociation{Beat: &d.Beat}) if err != nil { - return common.DefaultConfigs{}, err + return common.DefaultConfig{}, err } - return common.DefaultConfigs{ + return common.DefaultConfig{ Managed: kibanaConfig, Unmanaged: defaultConfig, }, nil diff --git a/pkg/controller/beat/filebeat/filebeat.go b/pkg/controller/beat/filebeat/filebeat.go index 4922dcef7b..8a9ace793f 100644 --- a/pkg/controller/beat/filebeat/filebeat.go +++ b/pkg/controller/beat/filebeat/filebeat.go @@ -59,14 +59,14 @@ func (d *Driver) Reconcile() *reconciler.Results { } } - defaultConfigs, err := d.configParams() + defaultConfig, err := d.defaultConfig() if err != nil { return reconciler.NewResult(d.DriverParams.Context).WithError(err) } return beatcommon.Reconcile( d.DriverParams, - defaultConfigs, + defaultConfig, container.FilebeatImage, f) } diff --git a/pkg/controller/beat/metricbeat/config.go b/pkg/controller/beat/metricbeat/config.go index 633f5549ec..b5d22059c3 100644 --- a/pkg/controller/beat/metricbeat/config.go +++ b/pkg/controller/beat/metricbeat/config.go @@ -45,12 +45,12 @@ processors: `)) ) -func (d *Driver) configParams() (common.DefaultConfigs, error) { +func (d *Driver) defaultConfig() (common.DefaultConfig, error) { kibanaConfig, err := common.BuildKibanaConfig(d.Client, v1beta1.BeatKibanaAssociation{Beat: &d.Beat}) if err != nil { - return common.DefaultConfigs{}, err + return common.DefaultConfig{}, err } - return common.DefaultConfigs{ + return common.DefaultConfig{ Managed: kibanaConfig, Unmanaged: defaultConfig, }, nil diff --git a/pkg/controller/beat/metricbeat/metricbeat.go b/pkg/controller/beat/metricbeat/metricbeat.go index 3bd2cd8b71..8811085daa 100644 --- a/pkg/controller/beat/metricbeat/metricbeat.go +++ b/pkg/controller/beat/metricbeat/metricbeat.go @@ -62,14 +62,14 @@ func (d *Driver) Reconcile() *reconciler.Results { builder.WithArgs("-e", "-c", beatcommon.ConfigMountPath, "-system.hostfs=/hostfs") } - defaultConfigs, err := d.configParams() + defaultConfig, err := d.defaultConfig() if err != nil { return reconciler.NewResult(d.DriverParams.Context).WithError(err) } return beatcommon.Reconcile( d.DriverParams, - defaultConfigs, + defaultConfig, container.MetricbeatImage, f) } diff --git a/pkg/controller/beat/otherbeat/otherbeat.go b/pkg/controller/beat/otherbeat/otherbeat.go index e8878a4fcb..1e996f251f 100644 --- a/pkg/controller/beat/otherbeat/otherbeat.go +++ b/pkg/controller/beat/otherbeat/otherbeat.go @@ -28,5 +28,5 @@ func NewDriver(params beatcommon.DriverParams) beatcommon.Driver { } func (d *Driver) Reconcile() *reconciler.Results { - return beatcommon.Reconcile(d.DriverParams, beatcommon.DefaultConfigs{}, "", nil) + return beatcommon.Reconcile(d.DriverParams, beatcommon.DefaultConfig{}, "", nil) } From ccf960a1fcb3a72ce0620ad779204584b2b48a09 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 14:53:44 +0200 Subject: [PATCH 07/11] inline constant --- pkg/controller/beat/common/pod.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/controller/beat/common/pod.go b/pkg/controller/beat/common/pod.go index 5d8b789826..f49c0dcadb 100644 --- a/pkg/controller/beat/common/pod.go +++ b/pkg/controller/beat/common/pod.go @@ -20,8 +20,7 @@ import ( ) const ( - CABaseMountPath = "/mnt/elastic-internal/" - CAFileName = "ca.crt" + CAFileName = "ca.crt" ConfigVolumeName = "config" ConfigMountPath = "/etc/beat.yml" @@ -52,7 +51,7 @@ var ( ) func certificatesDir(association commonv1.Association) string { - return fmt.Sprintf("%s/%s-certs", CABaseMountPath, association.AssociatedType()) + return fmt.Sprintf("/mnt/elastic-internal/%s-certs", association.AssociatedType()) } func buildPodTemplate( From aeff078afa30861235ab98eb119c3e8903090077 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 15:56:33 +0200 Subject: [PATCH 08/11] use Kibana admin role --- pkg/controller/association/controller/beat_kibana.go | 2 +- pkg/controller/elasticsearch/user/roles.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/controller/association/controller/beat_kibana.go b/pkg/controller/association/controller/beat_kibana.go index 2675e9d25a..0eeb47bcb1 100644 --- a/pkg/controller/association/controller/beat_kibana.go +++ b/pkg/controller/association/controller/beat_kibana.go @@ -36,7 +36,7 @@ func AddBeatKibana(mgr manager.Manager, accessReviewer rbac.AccessReviewer, para UserSecretSuffix: "beat-kb-user", CASecretLabelName: kibana.KibanaNameLabelName, ESUserRole: func(commonv1.Associated) (string, error) { - return user.SuperUserBuiltinRole, nil + return user.KibanaAdminBuiltinRole, nil }, // The generic association controller watches Elasticsearch by default but we are interested in changes to // Kibana as well for the purposes of establishing the association. diff --git a/pkg/controller/elasticsearch/user/roles.go b/pkg/controller/elasticsearch/user/roles.go index f2734ed682..cfc5fb65b0 100644 --- a/pkg/controller/elasticsearch/user/roles.go +++ b/pkg/controller/elasticsearch/user/roles.go @@ -29,8 +29,8 @@ const ( // ApmAgentUserRole is the name of the role used by APMServer instances to connect to Kibana ApmAgentUserRole = "eck_apm_agent_user_role" - // KibanaUserBuiltinRole is the name of the built-in Kibana user role - KibanaUserBuiltinRole = "kibana_user" + // KibanaAdminBuiltinRole is the name of the built-in Kibana admin role + KibanaAdminBuiltinRole = "kibana_admin" ) var ( From 3565513ed26a1477469e97cd5dd4c891d8d0b474 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 10 Jun 2020 18:17:54 +0200 Subject: [PATCH 09/11] add unit test for BuildKibanaConfig --- pkg/controller/beat/common/config_test.go | 120 ++++++++++++++++++++-- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/pkg/controller/beat/common/config_test.go b/pkg/controller/beat/common/config_test.go index f0dc9d48d6..7c02ff5854 100644 --- a/pkg/controller/beat/common/config_test.go +++ b/pkg/controller/beat/common/config_test.go @@ -18,6 +18,15 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) +func merge(cs ...*settings.CanonicalConfig) *settings.CanonicalConfig { + result := settings.NewCanonicalConfig() + err := result.MergeWith(cs...) + if err != nil { + panic(err) + } + return result +} + func Test_buildBeatConfig(t *testing.T) { clientWithSecret := k8s.WrappedFakeClient( &corev1.Secret{ @@ -74,12 +83,6 @@ func Test_buildBeatConfig(t *testing.T) { withAssociationWithConfig := *withAssociation.DeepCopy() withAssociationWithConfig.Spec.Config = userConfig - merge := func(cs ...*settings.CanonicalConfig) *settings.CanonicalConfig { - result := settings.NewCanonicalConfig() - _ = result.MergeWith(cs...) - return result - } - for _, tt := range []struct { name string client k8s.Client @@ -186,3 +189,108 @@ func Test_buildBeatConfig(t *testing.T) { }) } } + +func TestBuildKibanaConfig(t *testing.T) { + + secretFixture := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secret", + Namespace: "test-ns", + }, + Data: map[string][]byte{"elastic": []byte("123")}, + } + kibanaAssocConf := commonv1.AssociationConf{ + AuthSecretName: "auth-secret", + AuthSecretKey: "elastic", + CACertProvided: false, + CASecretName: "ca-secret", + URL: "url", + } + + kibanaAssocConfWithCA := kibanaAssocConf + kibanaAssocConfWithCA.CACertProvided = true + + associationFixture := func(conf commonv1.AssociationConf) beatv1beta1.BeatKibanaAssociation { + assoc := beatv1beta1.BeatKibanaAssociation{Beat: &beatv1beta1.Beat{ + ObjectMeta: metav1.ObjectMeta{ + Name: "beat", + Namespace: "test-ns", + }, + Spec: beatv1beta1.BeatSpec{ + KibanaRef: commonv1.ObjectSelector{ + Name: "auth-secret", + Namespace: "test-ns", + }}}} + + assoc.SetAssociationConf(&conf) + return assoc + } + + expectedConfig := settings.MustParseConfig([]byte(`setup.dashboards.enabled: true +setup.kibana: + host: url + username: elastic + password: "123" +`)) + + expectedCAConfig := settings.MustParseConfig([]byte(`setup.kibana.ssl.certificate_authorities: + - "/mnt/elastic-internal/kibana-certs/ca.crt" +`)) + + type args struct { + client k8s.Client + associated beatv1beta1.BeatKibanaAssociation + } + tests := []struct { + name string + args args + want *settings.CanonicalConfig + wantErr bool + }{ + { + name: "no association", + args: args{ + associated: beatv1beta1.BeatKibanaAssociation{Beat: &beatv1beta1.Beat{}}, + }, + want: nil, + wantErr: false, + }, + { + name: "association: no auth-secret", + args: args{ + client: k8s.WrappedFakeClient(), + associated: associationFixture(kibanaAssocConf), + }, + wantErr: true, + }, + { + name: "association: no ca", + args: args{ + client: k8s.WrappedFakeClient(secretFixture), + associated: associationFixture(kibanaAssocConf), + }, + want: expectedConfig, + wantErr: false, + }, + { + name: "association: with ca", + args: args{ + client: k8s.WrappedFakeClient(secretFixture), + associated: associationFixture(kibanaAssocConfWithCA), + }, + want: merge(expectedConfig, expectedCAConfig), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := BuildKibanaConfig(tt.args.client, tt.args.associated) + if (err != nil) != tt.wantErr { + t.Errorf("BuildKibanaConfig() error = %v, wantErr %v", err, tt.wantErr) + return + } + diff := tt.want.Diff(got, nil) + require.Empty(t, diff) + }) + } +} From 23ee22d65330dff9f0a864ee18b48a2f82774211 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 12 Jun 2020 09:09:40 +0200 Subject: [PATCH 10/11] make kibana user version specific --- .../association/controller/apm_es.go | 6 ++--- .../association/controller/apm_es_test.go | 8 +++--- .../association/controller/beat_kibana.go | 27 ++++++++++++++++--- pkg/controller/elasticsearch/user/roles.go | 3 --- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/pkg/controller/association/controller/apm_es.go b/pkg/controller/association/controller/apm_es.go index 6b47249c2b..7269897b5d 100644 --- a/pkg/controller/association/controller/apm_es.go +++ b/pkg/controller/association/controller/apm_es.go @@ -51,7 +51,7 @@ func AddApmES(mgr manager.Manager, accessReviewer rbac.AccessReviewer, params op }, UserSecretSuffix: "apm-user", CASecretLabelName: eslabel.ClusterNameLabelName, - ESUserRole: getRoles, + ESUserRole: getAPMElasticsearchRoles, }) } @@ -67,8 +67,8 @@ func getElasticsearchExternalURL(c k8s.Client, association commonv1.Association) return services.ExternalServiceURL(es), nil } -// getRoles returns for a given version of the APM Server the set of required roles. -func getRoles(associated commonv1.Associated) (string, error) { +// getAPMElasticsearchRoles returns for a given version of the APM Server the set of required roles. +func getAPMElasticsearchRoles(associated commonv1.Associated) (string, error) { apmServer, ok := associated.(*apmv1.ApmServer) if !ok { return "", pkgerrors.Errorf( diff --git a/pkg/controller/association/controller/apm_es_test.go b/pkg/controller/association/controller/apm_es_test.go index 794b3c857f..7267d3d7b2 100644 --- a/pkg/controller/association/controller/apm_es_test.go +++ b/pkg/controller/association/controller/apm_es_test.go @@ -11,7 +11,7 @@ import ( commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1" ) -func Test_getRoles(t *testing.T) { +func Test_getAPMElasticsearchRoles(t *testing.T) { type args struct { associated commonv1.Associated } @@ -78,13 +78,13 @@ func Test_getRoles(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := getRoles(tt.args.associated) + got, err := getAPMElasticsearchRoles(tt.args.associated) if (err != nil) != tt.wantErr { - t.Errorf("getRoles() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("getAPMElasticsearchRoles() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { - t.Errorf("getRoles() = %v, want %v", got, tt.want) + t.Errorf("getAPMElasticsearchRoles() = %v, want %v", got, tt.want) } }) } diff --git a/pkg/controller/association/controller/beat_kibana.go b/pkg/controller/association/controller/beat_kibana.go index 0eeb47bcb1..41e0484235 100644 --- a/pkg/controller/association/controller/beat_kibana.go +++ b/pkg/controller/association/controller/beat_kibana.go @@ -9,11 +9,12 @@ import ( commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1" "github.com/elastic/cloud-on-k8s/pkg/controller/association" "github.com/elastic/cloud-on-k8s/pkg/controller/common/operator" + "github.com/elastic/cloud-on-k8s/pkg/controller/common/version" "github.com/elastic/cloud-on-k8s/pkg/controller/common/watches" - "github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/user" "github.com/elastic/cloud-on-k8s/pkg/controller/kibana" "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" "github.com/elastic/cloud-on-k8s/pkg/utils/rbac" + pkgerrors "github.com/pkg/errors" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/manager" ) @@ -35,9 +36,7 @@ func AddBeatKibana(mgr manager.Manager, accessReviewer rbac.AccessReviewer, para }, UserSecretSuffix: "beat-kb-user", CASecretLabelName: kibana.KibanaNameLabelName, - ESUserRole: func(commonv1.Associated) (string, error) { - return user.KibanaAdminBuiltinRole, nil - }, + ESUserRole: getBeatKibanaRoles, // The generic association controller watches Elasticsearch by default but we are interested in changes to // Kibana as well for the purposes of establishing the association. SetDynamicWatches: func(association commonv1.Association, w watches.DynamicWatches) error { @@ -58,3 +57,23 @@ func AddBeatKibana(mgr manager.Manager, accessReviewer rbac.AccessReviewer, para }, }) } + +func getBeatKibanaRoles(associated commonv1.Associated) (string, error) { + beat, ok := associated.(*beatv1beta1.Beat) + if !ok { + return "", pkgerrors.Errorf( + "Beat expected, got %s/%s", + associated.GetObjectKind().GroupVersionKind().Group, + associated.GetObjectKind().GroupVersionKind().Kind, + ) + } + + v, err := version.Parse(beat.Spec.Version) + if err != nil { + return "", err + } + if v.IsSameOrAfter(version.From(7, 5, 0)) { + return "kibana_admin", nil + } + return "kibana_user", nil +} diff --git a/pkg/controller/elasticsearch/user/roles.go b/pkg/controller/elasticsearch/user/roles.go index cfc5fb65b0..f47ffa011e 100644 --- a/pkg/controller/elasticsearch/user/roles.go +++ b/pkg/controller/elasticsearch/user/roles.go @@ -28,9 +28,6 @@ const ( // ApmAgentUserRole is the name of the role used by APMServer instances to connect to Kibana ApmAgentUserRole = "eck_apm_agent_user_role" - - // KibanaAdminBuiltinRole is the name of the built-in Kibana admin role - KibanaAdminBuiltinRole = "kibana_admin" ) var ( From 0e07422c821268e188f25403ef20b4b8b5d00a5d Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 12 Jun 2020 09:49:13 +0200 Subject: [PATCH 11/11] contextualize log statement --- pkg/controller/beat/common/config.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/controller/beat/common/config.go b/pkg/controller/beat/common/config.go index 5ecccaf9de..e4b83d7680 100644 --- a/pkg/controller/beat/common/config.go +++ b/pkg/controller/beat/common/config.go @@ -110,7 +110,11 @@ func buildBeatConfig( if err = cfg.MergeWith(userCfg); err != nil { return nil, err } - log.V(1).Info("Replacing ECK-managed configuration by user-provided configuration") + log.V(1).Info( + "Replacing ECK-managed configuration by user-provided configuration", + "beat_name", beat.Name, + "namespace", beat.Namespace, + ) } return cfg.Render()