Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add internal service for operator api requests #826

Merged
merged 2 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/v1alpha1/humiocluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
25 changes: 25 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions charts/humio-operator/crds/core.humio.com_humioclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions config/crd/bases/core.humio.com_humioclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions controllers/humiocluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -1599,6 +1600,30 @@ func (r *HumioClusterReconciler) ensureHeadlessServiceExists(ctx context.Context
return nil
}

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)
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 {
Expand Down
15 changes: 15 additions & 0 deletions controllers/humiocluster_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const (
viewGroupPermissionsConfigMapNameSuffix = "view-group-permissions"
rolePermissionsConfigMapNameSuffix = "role-permissions"
idpCertificateSecretNameSuffix = "idp-certificate"

// nodepool internal
NodePoolFeatureAllowedAPIRequestType = "OperatorInternal"
)

type HumioNodePool struct {
Expand Down Expand Up @@ -432,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
}

Expand Down Expand Up @@ -834,6 +842,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
}
Expand Down
30 changes: 30 additions & 0 deletions controllers/humiocluster_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,40 @@ func constructHeadlessService(hc *humiov1alpha1.HumioCluster) *corev1.Service {
}
}

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),
},
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeClusterIP,
Selector: mergeHumioServiceLabels(hc.Name, map[string]string{
kubernetes.FeatureLabelName: NodePoolFeatureAllowedAPIRequestType,
}),
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())
Expand Down
7 changes: 5 additions & 2 deletions controllers/humiocluster_tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(),
Expand Down
29 changes: 25 additions & 4 deletions controllers/suite/clusters/humiocluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -2204,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"))
})
})

Expand Down Expand Up @@ -3225,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
Expand All @@ -3247,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 {
Expand All @@ -3266,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())
Expand Down
2 changes: 1 addition & 1 deletion pkg/helpers/clusterinterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/helpers/clusterinterface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/humio/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

}
Expand Down
3 changes: 2 additions & 1 deletion pkg/humio/client_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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{}
}
1 change: 1 addition & 0 deletions pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading