From 45fde794d86c338bd243b835c55031e3114f9fe2 Mon Sep 17 00:00:00 2001 From: Jestin Woods Date: Fri, 12 Jul 2024 14:05:01 -0700 Subject: [PATCH 1/2] Add internal service for operator api requests --- api/v1alpha1/humiocluster_types.go | 9 +++++ api/v1alpha1/zz_generated.deepcopy.go | 25 ++++++++++++ .../crds/core.humio.com_humioclusters.yaml | 24 ++++++++++++ .../bases/core.humio.com_humioclusters.yaml | 24 ++++++++++++ controllers/humiocluster_controller.go | 30 +++++++++++++++ controllers/humiocluster_defaults.go | 10 +++++ controllers/humiocluster_services.go | 38 +++++++++++++++++++ controllers/humiocluster_tls.go | 7 +++- .../clusters/humiocluster_controller_test.go | 15 ++++++++ pkg/helpers/clusterinterface.go | 2 +- pkg/helpers/clusterinterface_test.go | 2 +- pkg/humio/client.go | 2 +- pkg/humio/client_mock.go | 2 +- 13 files changed, 184 insertions(+), 6 deletions(-) diff --git a/api/v1alpha1/humiocluster_types.go b/api/v1alpha1/humiocluster_types.go index 0bd8dba2..95a4d621 100644 --- a/api/v1alpha1/humiocluster_types.go +++ b/api/v1alpha1/humiocluster_types.go @@ -252,6 +252,15 @@ type HumioNodeSpec struct { // PriorityClassName is the name of the priority class that will be used by the Humio pods PriorityClassName string `json:"priorityClassName,omitempty"` + + // HumioNodePoolFeatures defines the features that are allowed by the node pool + NodePoolFeatures HumioNodePoolFeatures `json:"nodePoolFeatures,omitempty"` +} + +type HumioNodePoolFeatures struct { + // AllowedAPIRequestTypes is a list of API request types that are allowed by the node pool. Current options are: + // OperatorInternal. Defaults to [OperatorInternal]. To disallow all API request types, set this to []. + AllowedAPIRequestTypes *[]string `json:"allowedAPIRequestTypes,omitempty"` } type HumioUpdateStrategy struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index d4573670..cfae6cd5 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -999,6 +999,30 @@ func (in *HumioLicenseStatus) DeepCopy() *HumioLicenseStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HumioNodePoolFeatures) DeepCopyInto(out *HumioNodePoolFeatures) { + *out = *in + if in.AllowedAPIRequestTypes != nil { + in, out := &in.AllowedAPIRequestTypes, &out.AllowedAPIRequestTypes + *out = new([]string) + if **in != nil { + in, out := *in, *out + *out = make([]string, len(*in)) + copy(*out, *in) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HumioNodePoolFeatures. +func (in *HumioNodePoolFeatures) DeepCopy() *HumioNodePoolFeatures { + if in == nil { + return nil + } + out := new(HumioNodePoolFeatures) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HumioNodePoolSpec) DeepCopyInto(out *HumioNodePoolSpec) { *out = *in @@ -1191,6 +1215,7 @@ func (in *HumioNodeSpec) DeepCopyInto(out *HumioNodeSpec) { *out = new(HumioUpdateStrategy) **out = **in } + in.NodePoolFeatures.DeepCopyInto(&out.NodePoolFeatures) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HumioNodeSpec. diff --git a/charts/humio-operator/crds/core.humio.com_humioclusters.yaml b/charts/humio-operator/crds/core.humio.com_humioclusters.yaml index 760d4f1e..7b3d2058 100644 --- a/charts/humio-operator/crds/core.humio.com_humioclusters.yaml +++ b/charts/humio-operator/crds/core.humio.com_humioclusters.yaml @@ -5717,6 +5717,18 @@ spec: nodeCount: description: NodeCount is the desired number of humio cluster nodes type: integer + nodePoolFeatures: + description: HumioNodePoolFeatures defines the features that are allowed + by the node pool + properties: + allowedAPIRequestTypes: + description: |- + AllowedAPIRequestTypes is a list of API request types that are allowed by the node pool. Current options are: + OperatorInternal. Defaults to [OperatorInternal]. To disallow all API request types, set this to []. + items: + type: string + type: array + type: object nodePools: description: NodePools can be used to define additional groups of Humio cluster pods that share a set of configuration. @@ -11205,6 +11217,18 @@ spec: description: NodeCount is the desired number of humio cluster nodes type: integer + nodePoolFeatures: + description: HumioNodePoolFeatures defines the features + that are allowed by the node pool + properties: + allowedAPIRequestTypes: + description: |- + AllowedAPIRequestTypes is a list of API request types that are allowed by the node pool. Current options are: + OperatorInternal. Defaults to [OperatorInternal]. To disallow all API request types, set this to []. + items: + type: string + type: array + type: object nodeUUIDPrefix: description: |- NodeUUIDPrefix is the prefix for the Humio Node's UUID. By default this does not include the zone. If it's diff --git a/config/crd/bases/core.humio.com_humioclusters.yaml b/config/crd/bases/core.humio.com_humioclusters.yaml index 760d4f1e..7b3d2058 100644 --- a/config/crd/bases/core.humio.com_humioclusters.yaml +++ b/config/crd/bases/core.humio.com_humioclusters.yaml @@ -5717,6 +5717,18 @@ spec: nodeCount: description: NodeCount is the desired number of humio cluster nodes type: integer + nodePoolFeatures: + description: HumioNodePoolFeatures defines the features that are allowed + by the node pool + properties: + allowedAPIRequestTypes: + description: |- + AllowedAPIRequestTypes is a list of API request types that are allowed by the node pool. Current options are: + OperatorInternal. Defaults to [OperatorInternal]. To disallow all API request types, set this to []. + items: + type: string + type: array + type: object nodePools: description: NodePools can be used to define additional groups of Humio cluster pods that share a set of configuration. @@ -11205,6 +11217,18 @@ spec: description: NodeCount is the desired number of humio cluster nodes type: integer + nodePoolFeatures: + description: HumioNodePoolFeatures defines the features + that are allowed by the node pool + properties: + allowedAPIRequestTypes: + description: |- + AllowedAPIRequestTypes is a list of API request types that are allowed by the node pool. Current options are: + OperatorInternal. Defaults to [OperatorInternal]. To disallow all API request types, set this to []. + items: + type: string + type: array + type: object nodeUUIDPrefix: description: |- NodeUUIDPrefix is the prefix for the Humio Node's UUID. By default this does not include the zone. If it's diff --git a/controllers/humiocluster_controller.go b/controllers/humiocluster_controller.go index 845ae1db..a51646e6 100644 --- a/controllers/humiocluster_controller.go +++ b/controllers/humiocluster_controller.go @@ -171,6 +171,12 @@ func (r *HumioClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request withNodeCount(len(podStatusList))) }(ctx, r.HumioClient, hc) + if err := r.ensureInternalServiceExists(ctx, hc, humioNodePools.Filter(NodePoolFilterHasNode)); err != nil { + return r.updateStatus(ctx, r.Client.Status(), hc, statusOptions(). + withMessage(err.Error()). + withState(humiov1alpha1.HumioClusterStateConfigError)) + } + for _, pool := range humioNodePools.Items { if err := r.ensureOrphanedPvcsAreDeleted(ctx, hc, pool); err != nil { return r.updateStatus(ctx, r.Client.Status(), hc, statusOptions(). @@ -1599,6 +1605,30 @@ func (r *HumioClusterReconciler) ensureHeadlessServiceExists(ctx context.Context return nil } +func (r *HumioClusterReconciler) ensureInternalServiceExists(ctx context.Context, hc *humiov1alpha1.HumioCluster, hnpl []*HumioNodePool) error { + r.Log.Info("ensuring internal service") + existingService, err := kubernetes.GetService(ctx, r, internalServiceName(hc.Name), hc.Namespace) + service := constructInternalService(hc, hnpl) + if k8serrors.IsNotFound(err) { + if err := controllerutil.SetControllerReference(hc, service, r.Scheme()); err != nil { + return r.logErrorAndReturn(err, "could not set controller reference") + } + err = r.Create(ctx, service) + if err != nil { + return r.logErrorAndReturn(err, "unable to create internal service for HumioCluster") + } + return nil + } + if servicesMatchTest, err := servicesMatch(existingService, service); !servicesMatchTest || err != nil { + r.Log.Info(fmt.Sprintf("service %s requires update: %s", existingService.Name, err)) + updateService(existingService, service) + if err = r.Update(ctx, existingService); err != nil { + return r.logErrorAndReturn(err, fmt.Sprintf("could not update service %s", service.Name)) + } + } + return nil +} + // ensureNodePoolSpecificResourcesHaveLabelWithNodePoolName updates resources that were created prior to the introduction of node pools. // We need this because multiple resources now includes an additional label containing the name of the node pool a given resource belongs to. func (r *HumioClusterReconciler) ensureNodePoolSpecificResourcesHaveLabelWithNodePoolName(ctx context.Context, hnp *HumioNodePool) error { diff --git a/controllers/humiocluster_defaults.go b/controllers/humiocluster_defaults.go index 92858f36..8d416a02 100644 --- a/controllers/humiocluster_defaults.go +++ b/controllers/humiocluster_defaults.go @@ -63,6 +63,9 @@ const ( viewGroupPermissionsConfigMapNameSuffix = "view-group-permissions" rolePermissionsConfigMapNameSuffix = "role-permissions" idpCertificateSecretNameSuffix = "idp-certificate" + + // nodepool internal + NodePoolFeatureAllowedAPIRequestType = "OperatorInternal" ) type HumioNodePool struct { @@ -834,6 +837,13 @@ func (hnp *HumioNodePool) OkToDeletePvc() bool { return hnp.GetDataVolumePersistentVolumeClaimPolicy().ReclaimType == humiov1alpha1.HumioPersistentVolumeReclaimTypeOnNodeDelete } +func (hnp *HumioNodePool) GetNodePoolFeatureAllowedAPIRequestTypes() []string { + if hnp.humioNodeSpec.NodePoolFeatures.AllowedAPIRequestTypes != nil { + return *hnp.humioNodeSpec.NodePoolFeatures.AllowedAPIRequestTypes + } + return []string{NodePoolFeatureAllowedAPIRequestType} +} + func viewGroupPermissionsOrDefault(hc *humiov1alpha1.HumioCluster) string { return hc.Spec.ViewGroupPermissions } diff --git a/controllers/humiocluster_services.go b/controllers/humiocluster_services.go index 9234c533..871d5280 100644 --- a/controllers/humiocluster_services.go +++ b/controllers/humiocluster_services.go @@ -93,10 +93,48 @@ func constructHeadlessService(hc *humiov1alpha1.HumioCluster) *corev1.Service { } } +func constructInternalService(hc *humiov1alpha1.HumioCluster, hnpl []*HumioNodePool) *corev1.Service { + selectorLabels := kubernetes.LabelsForHumio(hc.Name) + for _, nodePool := range hnpl { + for _, allowedAPIRequestType := range nodePool.GetNodePoolFeatureAllowedAPIRequestTypes() { + if allowedAPIRequestType == NodePoolFeatureAllowedAPIRequestType { + selectorLabels[kubernetes.NodePoolLabelName] = nodePool.GetNodePoolName() + } + } + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: internalServiceName(hc.Name), + Namespace: hc.Namespace, + Labels: kubernetes.LabelsForHumio(hc.Name), + Annotations: humioHeadlessServiceAnnotationsOrDefault(hc), + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Selector: selectorLabels, + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: HumioPort, + }, + { + Name: "es", + Port: elasticPort, + }, + }, + }, + } +} + func headlessServiceName(clusterName string) string { return fmt.Sprintf("%s-headless", clusterName) } +func internalServiceName(clusterName string) string { + return fmt.Sprintf("%s-internal", clusterName) +} + func servicesMatch(existingService *corev1.Service, service *corev1.Service) (bool, error) { existingLabels := helpers.MapToSortedString(existingService.GetLabels()) labels := helpers.MapToSortedString(service.GetLabels()) diff --git a/controllers/humiocluster_tls.go b/controllers/humiocluster_tls.go index bf07ebec..a1fe1c19 100644 --- a/controllers/humiocluster_tls.go +++ b/controllers/humiocluster_tls.go @@ -175,6 +175,7 @@ func constructClusterCACertificateBundle(hc *humiov1alpha1.HumioCluster) cmapi.C DNSNames: []string{ fmt.Sprintf("%s.%s", hc.Name, hc.Namespace), fmt.Sprintf("%s-headless.%s", hc.Name, hc.Namespace), + fmt.Sprintf("%s-internal.%s", hc.Name, hc.Namespace), }, IssuerRef: cmmeta.ObjectReference{ Name: constructCAIssuer(hc).Name, @@ -196,8 +197,10 @@ func ConstructNodeCertificate(hnp *HumioNodePool, nodeSuffix string) cmapi.Certi DNSNames: []string{ fmt.Sprintf("%s-core-%s.%s.%s", hnp.GetNodePoolName(), nodeSuffix, headlessServiceName(hnp.GetClusterName()), hnp.GetNamespace()), // Used for intra-cluster communication fmt.Sprintf("%s-core-%s", hnp.GetNodePoolName(), nodeSuffix), // Used for auth sidecar - fmt.Sprintf("%s.%s", hnp.GetNodePoolName(), hnp.GetNamespace()), // Used by humio-operator and ingress controllers to reach the Humio API - fmt.Sprintf("%s-headless.%s", hnp.GetClusterName(), hnp.GetNamespace()), // Used by humio-operator and ingress controllers to reach the Humio API + fmt.Sprintf("%s.%s", hnp.GetNodePoolName(), hnp.GetNamespace()), // Used by ingress controllers to reach the Humio API + fmt.Sprintf("%s-headless.%s", hnp.GetClusterName(), hnp.GetNamespace()), // Used for intra-cluster communication + fmt.Sprintf("%s-internal.%s", hnp.GetClusterName(), hnp.GetNamespace()), // Used by humio-operator to reach the Humio API + }, IssuerRef: cmmeta.ObjectReference{ Name: hnp.GetClusterName(), diff --git a/controllers/suite/clusters/humiocluster_controller_test.go b/controllers/suite/clusters/humiocluster_controller_test.go index ade52922..97ca7ee5 100644 --- a/controllers/suite/clusters/humiocluster_controller_test.go +++ b/controllers/suite/clusters/humiocluster_controller_test.go @@ -2158,6 +2158,21 @@ var _ = Describe("HumioCluster Controller", func() { return service.Spec.Selector }, testTimeout, suite.TestInterval).Should(HaveKeyWithValue("humio.com/node-pool", key.Name)) + suite.UsingClusterBy(key.Name, "Confirming internal service has the correct HTTP and ES ports") + internalSvc, _ := kubernetes.GetService(ctx, k8sClient, fmt.Sprintf("%s-internal", key.Name), key.Namespace) + Expect(internalSvc.Spec.Type).To(BeIdenticalTo(corev1.ServiceTypeClusterIP)) + for _, port := range internalSvc.Spec.Ports { + if port.Name == "http" { + Expect(port.Port).Should(Equal(int32(8080))) + } + if port.Name == "es" { + Expect(port.Port).Should(Equal(int32(9200))) + } + } + + internalSvc, _ = kubernetes.GetService(ctx, k8sClient, key.Name, key.Namespace) + Expect(svc.Annotations).To(BeNil()) + suite.UsingClusterBy(key.Name, "Confirming headless service has the correct HTTP and ES ports") headlessSvc, _ := kubernetes.GetService(ctx, k8sClient, fmt.Sprintf("%s-headless", key.Name), key.Namespace) Expect(headlessSvc.Spec.Type).To(BeIdenticalTo(corev1.ServiceTypeClusterIP)) diff --git a/pkg/helpers/clusterinterface.go b/pkg/helpers/clusterinterface.go index 29fb3c41..83b4a0db 100644 --- a/pkg/helpers/clusterinterface.go +++ b/pkg/helpers/clusterinterface.go @@ -94,7 +94,7 @@ func (c Cluster) Url(ctx context.Context, k8sClient client.Client) (*url.URL, er if !TLSEnabled(&humioManagedCluster) { protocol = "http" } - baseURL, _ := url.Parse(fmt.Sprintf("%s://%s-headless.%s:%d/", protocol, c.managedClusterName, c.namespace, 8080)) + baseURL, _ := url.Parse(fmt.Sprintf("%s://%s-internal.%s:%d/", protocol, c.managedClusterName, c.namespace, 8080)) return baseURL, nil } diff --git a/pkg/helpers/clusterinterface_test.go b/pkg/helpers/clusterinterface_test.go index e2548e96..45dd5fbe 100644 --- a/pkg/helpers/clusterinterface_test.go +++ b/pkg/helpers/clusterinterface_test.go @@ -189,7 +189,7 @@ func TestCluster_HumioConfig_managedHumioCluster(t *testing.T) { if !TLSEnabled(&tt.managedHumioCluster) { protocol = "http" } - expectedURL := fmt.Sprintf("%s://%s-headless.%s:8080/", protocol, tt.managedHumioCluster.Name, tt.managedHumioCluster.Namespace) + expectedURL := fmt.Sprintf("%s://%s-internal.%s:8080/", protocol, tt.managedHumioCluster.Name, tt.managedHumioCluster.Namespace) if cluster.Config().Address.String() != expectedURL { t.Errorf("url not correct, expected: %s, got: %s", expectedURL, cluster.Config().Address) } diff --git a/pkg/humio/client.go b/pkg/humio/client.go index 9625a9a0..72467fc7 100644 --- a/pkg/humio/client.go +++ b/pkg/humio/client.go @@ -226,7 +226,7 @@ func (h *ClientConfig) GetBaseURL(config *humioapi.Config, req reconcile.Request if !helpers.TLSEnabled(hc) { protocol = "http" } - baseURL, _ := url.Parse(fmt.Sprintf("%s://%s-headless.%s:%d/", protocol, hc.Name, hc.Namespace, 8080)) + baseURL, _ := url.Parse(fmt.Sprintf("%s://%s-internal.%s:%d/", protocol, hc.Name, hc.Namespace, 8080)) return baseURL } diff --git a/pkg/humio/client_mock.go b/pkg/humio/client_mock.go index 2ef66fe2..7b771da7 100644 --- a/pkg/humio/client_mock.go +++ b/pkg/humio/client_mock.go @@ -82,7 +82,7 @@ func (h *MockClientConfig) GetClusters(config *humioapi.Config, req reconcile.Re } func (h *MockClientConfig) GetBaseURL(config *humioapi.Config, req reconcile.Request, hc *humiov1alpha1.HumioCluster) *url.URL { - baseURL, _ := url.Parse(fmt.Sprintf("http://%s-headless.%s:%d/", hc.Name, hc.Namespace, 8080)) + baseURL, _ := url.Parse(fmt.Sprintf("http://%s-internal.%s:%d/", hc.Name, hc.Namespace, 8080)) return baseURL } From f0469ca0f1aa467aaaab0737d574e8a4b387c34f Mon Sep 17 00:00:00 2001 From: Jestin Woods Date: Mon, 15 Jul 2024 16:56:35 -0700 Subject: [PATCH 2/2] updates --- controllers/humiocluster_controller.go | 11 ++--- controllers/humiocluster_defaults.go | 5 +++ controllers/humiocluster_services.go | 24 ++++------ .../clusters/humiocluster_controller_test.go | 44 +++++++++++-------- pkg/humio/client_mock.go | 1 + pkg/kubernetes/kubernetes.go | 1 + 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/controllers/humiocluster_controller.go b/controllers/humiocluster_controller.go index a51646e6..2f2afaf3 100644 --- a/controllers/humiocluster_controller.go +++ b/controllers/humiocluster_controller.go @@ -143,6 +143,7 @@ func (r *HumioClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request r.ensureLicenseIsValid, r.ensureValidCASecret, r.ensureHeadlessServiceExists, + r.ensureInternalServiceExists, r.validateUserDefinedServiceAccountsExists, } { if err := fun(ctx, hc); err != nil { @@ -171,12 +172,6 @@ func (r *HumioClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request withNodeCount(len(podStatusList))) }(ctx, r.HumioClient, hc) - if err := r.ensureInternalServiceExists(ctx, hc, humioNodePools.Filter(NodePoolFilterHasNode)); err != nil { - return r.updateStatus(ctx, r.Client.Status(), hc, statusOptions(). - withMessage(err.Error()). - withState(humiov1alpha1.HumioClusterStateConfigError)) - } - for _, pool := range humioNodePools.Items { if err := r.ensureOrphanedPvcsAreDeleted(ctx, hc, pool); err != nil { return r.updateStatus(ctx, r.Client.Status(), hc, statusOptions(). @@ -1605,10 +1600,10 @@ func (r *HumioClusterReconciler) ensureHeadlessServiceExists(ctx context.Context return nil } -func (r *HumioClusterReconciler) ensureInternalServiceExists(ctx context.Context, hc *humiov1alpha1.HumioCluster, hnpl []*HumioNodePool) error { +func (r *HumioClusterReconciler) ensureInternalServiceExists(ctx context.Context, hc *humiov1alpha1.HumioCluster) error { r.Log.Info("ensuring internal service") existingService, err := kubernetes.GetService(ctx, r, internalServiceName(hc.Name), hc.Namespace) - service := constructInternalService(hc, hnpl) + service := constructInternalService(hc) if k8serrors.IsNotFound(err) { if err := controllerutil.SetControllerReference(hc, service, r.Scheme()); err != nil { return r.logErrorAndReturn(err, "could not set controller reference") diff --git a/controllers/humiocluster_defaults.go b/controllers/humiocluster_defaults.go index 8d416a02..a9c768bc 100644 --- a/controllers/humiocluster_defaults.go +++ b/controllers/humiocluster_defaults.go @@ -435,6 +435,11 @@ func (hnp *HumioNodePool) GetPodLabels() map[string]string { labels[k] = v } } + for _, feature := range hnp.GetNodePoolFeatureAllowedAPIRequestTypes() { + if feature == NodePoolFeatureAllowedAPIRequestType { + labels[kubernetes.FeatureLabelName] = NodePoolFeatureAllowedAPIRequestType + } + } return labels } diff --git a/controllers/humiocluster_services.go b/controllers/humiocluster_services.go index 871d5280..ccb22d0f 100644 --- a/controllers/humiocluster_services.go +++ b/controllers/humiocluster_services.go @@ -93,26 +93,18 @@ func constructHeadlessService(hc *humiov1alpha1.HumioCluster) *corev1.Service { } } -func constructInternalService(hc *humiov1alpha1.HumioCluster, hnpl []*HumioNodePool) *corev1.Service { - selectorLabels := kubernetes.LabelsForHumio(hc.Name) - for _, nodePool := range hnpl { - for _, allowedAPIRequestType := range nodePool.GetNodePoolFeatureAllowedAPIRequestTypes() { - if allowedAPIRequestType == NodePoolFeatureAllowedAPIRequestType { - selectorLabels[kubernetes.NodePoolLabelName] = nodePool.GetNodePoolName() - } - } - } - +func constructInternalService(hc *humiov1alpha1.HumioCluster) *corev1.Service { return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: internalServiceName(hc.Name), - Namespace: hc.Namespace, - Labels: kubernetes.LabelsForHumio(hc.Name), - Annotations: humioHeadlessServiceAnnotationsOrDefault(hc), + Name: internalServiceName(hc.Name), + Namespace: hc.Namespace, + Labels: kubernetes.LabelsForHumio(hc.Name), }, Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - Selector: selectorLabels, + Type: corev1.ServiceTypeClusterIP, + Selector: mergeHumioServiceLabels(hc.Name, map[string]string{ + kubernetes.FeatureLabelName: NodePoolFeatureAllowedAPIRequestType, + }), Ports: []corev1.ServicePort{ { Name: "http", diff --git a/controllers/suite/clusters/humiocluster_controller_test.go b/controllers/suite/clusters/humiocluster_controller_test.go index 97ca7ee5..d4101ba3 100644 --- a/controllers/suite/clusters/humiocluster_controller_test.go +++ b/controllers/suite/clusters/humiocluster_controller_test.go @@ -1969,6 +1969,7 @@ var _ = Describe("HumioCluster Controller", func() { for _, pod := range clusterPods { Expect(pod.Labels["humio.com/new-important-label"]).Should(Equal("true")) Expect(pod.Labels["app.kubernetes.io/managed-by"]).Should(Equal("humio-operator")) + Expect(pod.Labels["humio.com/feature"]).Should(Equal("OperatorInternal")) } return true }, testTimeout, suite.TestInterval).Should(BeTrue()) @@ -2158,21 +2159,6 @@ var _ = Describe("HumioCluster Controller", func() { return service.Spec.Selector }, testTimeout, suite.TestInterval).Should(HaveKeyWithValue("humio.com/node-pool", key.Name)) - suite.UsingClusterBy(key.Name, "Confirming internal service has the correct HTTP and ES ports") - internalSvc, _ := kubernetes.GetService(ctx, k8sClient, fmt.Sprintf("%s-internal", key.Name), key.Namespace) - Expect(internalSvc.Spec.Type).To(BeIdenticalTo(corev1.ServiceTypeClusterIP)) - for _, port := range internalSvc.Spec.Ports { - if port.Name == "http" { - Expect(port.Port).Should(Equal(int32(8080))) - } - if port.Name == "es" { - Expect(port.Port).Should(Equal(int32(9200))) - } - } - - internalSvc, _ = kubernetes.GetService(ctx, k8sClient, key.Name, key.Namespace) - Expect(svc.Annotations).To(BeNil()) - suite.UsingClusterBy(key.Name, "Confirming headless service has the correct HTTP and ES ports") headlessSvc, _ := kubernetes.GetService(ctx, k8sClient, fmt.Sprintf("%s-headless", key.Name), key.Namespace) Expect(headlessSvc.Spec.Type).To(BeIdenticalTo(corev1.ServiceTypeClusterIP)) @@ -2219,6 +2205,26 @@ var _ = Describe("HumioCluster Controller", func() { Expect(k8sClient.Get(ctx, key, headlessSvc)).Should(Succeed()) return headlessSvc.Labels }, testTimeout, suite.TestInterval).Should(HaveKeyWithValue(updatedLabelsKey, updatedLabelsValue)) + + suite.UsingClusterBy(key.Name, "Confirming internal service has the correct HTTP and ES ports") + internalSvc, _ := kubernetes.GetService(ctx, k8sClient, fmt.Sprintf("%s-internal", key.Name), key.Namespace) + Expect(internalSvc.Spec.Type).To(BeIdenticalTo(corev1.ServiceTypeClusterIP)) + for _, port := range internalSvc.Spec.Ports { + if port.Name == "http" { + Expect(port.Port).Should(Equal(int32(8080))) + } + if port.Name == "es" { + Expect(port.Port).Should(Equal(int32(9200))) + } + } + internalSvc, _ = kubernetes.GetService(ctx, k8sClient, fmt.Sprintf("%s-internal", key.Name), key.Namespace) + Expect(internalSvc.Annotations).To(BeNil()) + + suite.UsingClusterBy(key.Name, "Confirming internal service has the correct selector") + Eventually(func() map[string]string { + internalSvc, _ := kubernetes.GetService(ctx, k8sClient, fmt.Sprintf("%s-internal", key.Name), key.Namespace) + return internalSvc.Spec.Selector + }, testTimeout, suite.TestInterval).Should(HaveKeyWithValue("humio.com/feature", "OperatorInternal")) }) }) @@ -3240,7 +3246,7 @@ var _ = Describe("HumioCluster Controller", func() { suite.CreateAndBootstrapCluster(ctx, k8sClient, humioClientForTestSuite, toCreate, true, humiov1alpha1.HumioClusterStateRunning, testTimeout) defer suite.CleanupCluster(ctx, k8sClient, toCreate) - Expect(kubernetes.ListPersistentVolumeClaims(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetPodLabels())).To(HaveLen(0)) + Expect(kubernetes.ListPersistentVolumeClaims(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetNodePoolLabels())).To(HaveLen(0)) suite.UsingClusterBy(key.Name, "Updating cluster to use persistent volumes") var updatedHumioCluster humiov1alpha1.HumioCluster @@ -3262,7 +3268,7 @@ var _ = Describe("HumioCluster Controller", func() { }).Should(Succeed()) Eventually(func() ([]corev1.PersistentVolumeClaim, error) { - return kubernetes.ListPersistentVolumeClaims(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetPodLabels()) + return kubernetes.ListPersistentVolumeClaims(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetNodePoolLabels()) }, testTimeout, suite.TestInterval).Should(HaveLen(toCreate.Spec.NodeCount)) Eventually(func() string { @@ -3281,8 +3287,8 @@ var _ = Describe("HumioCluster Controller", func() { }, testTimeout, suite.TestInterval).Should(BeIdenticalTo(humiov1alpha1.HumioClusterStateRunning)) suite.UsingClusterBy(key.Name, "Confirming pods are using PVC's and no PVC is left unused") - pvcList, _ := kubernetes.ListPersistentVolumeClaims(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetPodLabels()) - foundPodList, _ := kubernetes.ListPods(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetPodLabels()) + pvcList, _ := kubernetes.ListPersistentVolumeClaims(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetNodePoolLabels()) + foundPodList, _ := kubernetes.ListPods(ctx, k8sClient, key.Namespace, controllers.NewHumioNodeManagerFromHumioCluster(toCreate).GetNodePoolLabels()) for _, pod := range foundPodList { _, err := controllers.FindPvcForPod(pvcList, pod) Expect(err).ShouldNot(HaveOccurred()) diff --git a/pkg/humio/client_mock.go b/pkg/humio/client_mock.go index 7b771da7..6c06b126 100644 --- a/pkg/humio/client_mock.go +++ b/pkg/humio/client_mock.go @@ -339,4 +339,5 @@ func (h *MockClientConfig) ClearHumioClientConnections() { h.apiClient.OnPremLicense = humioapi.OnPremLicense{} h.apiClient.Action = humioapi.Action{} h.apiClient.Alert = humioapi.Alert{} + h.apiClient.FilterAlert = humioapi.FilterAlert{} } diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 75ba9e51..29f2da65 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -26,6 +26,7 @@ import ( const ( NodeIdLabelName = "humio.com/node-id" NodePoolLabelName = "humio.com/node-pool" + FeatureLabelName = "humio.com/feature" ) // LabelsForHumio returns the set of common labels for Humio resources.