diff --git a/Makefile b/Makefile index 81b4c6bfa..206aadc47 100644 --- a/Makefile +++ b/Makefile @@ -71,17 +71,22 @@ endif # By default we target amd64 as this is by far the most common local build environment # We actually build images for amd64 and arm64 # ---------------------------------------------------------------------------------------------------------------------- -IMAGE_ARCH ?= amd64 -ARCH ?= amd64 +UNAME_S = $(shell uname -s) +UNAME_M = $(shell uname -m) +ifeq (x86_64, $(UNAME_M)) + IMAGE_ARCH = amd64 + ARCH = amd64 +else + IMAGE_ARCH = $(UNAME_M) + ARCH = $(UNAME_M) +endif + OS ?= linux -UNAME_S := $(shell uname -s) GOPROXY ?= https://proxy.golang.org # ---------------------------------------------------------------------------------------------------------------------- # Set the location of the Operator SDK executable # ---------------------------------------------------------------------------------------------------------------------- -UNAME_S = $(shell uname -s) -UNAME_M = $(shell uname -m) OPERATOR_SDK_VERSION := v1.9.0 # ---------------------------------------------------------------------------------------------------------------------- diff --git a/api/v1/coherence_types.go b/api/v1/coherence_types.go index 001f23cf0..22fb4b75d 100644 --- a/api/v1/coherence_types.go +++ b/api/v1/coherence_types.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -864,13 +864,14 @@ func (in *PersistentStorageSpec) CreatePersistentVolumeClaim(deployment *Coheren in.PersistentVolumeClaim.DeepCopyInto(&spec) } - labels := deployment.CreateCommonLabels() + labels := deployment.CreateGlobalLabels() labels[LabelComponent] = LabelComponentPVC return &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: labels, + Name: name, + Labels: labels, + Annotations: deployment.CreateGlobalAnnotations(), }, Spec: spec, } @@ -1099,7 +1100,7 @@ func (in *NamedPortSpec) CreateService(deployment CoherenceResource) *corev1.Ser name, _ := in.GetServiceName(deployment) // The labels for the service - svcLabels := deployment.CreateCommonLabels() + svcLabels := deployment.CreateGlobalLabels() svcLabels[LabelComponent] = LabelComponentPortService svcLabels[LabelPort] = in.Name if in.Service != nil { @@ -1109,9 +1110,15 @@ func (in *NamedPortSpec) CreateService(deployment CoherenceResource) *corev1.Ser } // The service annotations - var ann map[string]string + ann := deployment.CreateGlobalAnnotations() if in.Service != nil && in.Service.Annotations != nil { - ann = in.Service.Annotations + if ann == nil { + ann = in.Service.Annotations + } else { + for k, v := range in.Service.Annotations { + ann[k] = v + } + } } // Create the Service serviceSpec @@ -1181,7 +1188,7 @@ func (in *NamedPortSpec) CreateServiceMonitor(deployment CoherenceResource) *mon } // The labels for the ServiceMonitor - labels := deployment.CreateCommonLabels() + labels := deployment.CreateGlobalLabels() labels[LabelComponent] = LabelComponentPortServiceMonitor for k, v := range in.ServiceMonitor.Labels { labels[k] = v @@ -1206,9 +1213,10 @@ func (in *NamedPortSpec) CreateServiceMonitor(deployment CoherenceResource) *mon return &monitoringv1.ServiceMonitor{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: deployment.GetNamespace(), - Labels: labels, + Name: name, + Namespace: deployment.GetNamespace(), + Labels: labels, + Annotations: deployment.CreateGlobalAnnotations(), }, Spec: spec, } @@ -2979,6 +2987,25 @@ func (in *PersistentVolumeClaimObjectMeta) toObjectMeta() metav1.ObjectMeta { } } +// ----- GlobalSpec --------------------------------------------------------- + +// GlobalSpec is attributes that will be applied to all resources managed by the Operator. +type GlobalSpec struct { + // Map of string keys and values that can be used to organize and categorize + // (scope and select) objects. May match selectors of replication controllers + // and services. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // +optional + Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"` + + // Annotations is an unstructured key value map stored with a resource that may be + // set by external tools to store and retrieve arbitrary metadata. They are not + // queryable and should be preserved when modifying objects. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + // +optional + Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"` +} + // ----- helper methods ----------------------------------------------------- // Int32PtrToStringWithDefault converts an int32 pointer to a string using the default if the pointer is nil. diff --git a/api/v1/coherencejobresource_types.go b/api/v1/coherencejobresource_types.go index 0e04bbbc3..3d04c08f4 100644 --- a/api/v1/coherencejobresource_types.go +++ b/api/v1/coherencejobresource_types.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -7,6 +7,7 @@ package v1 import ( + "github.com/oracle/coherence-operator/pkg/operator" "golang.org/x/mod/semver" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -68,6 +69,13 @@ func (in *CoherenceJob) GetEnvVarFrom() []corev1.EnvFromSource { return in.Spec.EnvFrom } +func (in *CoherenceJob) GetGlobalSpec() *GlobalSpec { + if in == nil { + return nil + } + return in.Spec.Global +} + // GetSpec returns this resource's CoherenceResourceSpec func (in *CoherenceJob) GetSpec() *CoherenceResourceSpec { return &in.Spec.CoherenceResourceSpec @@ -193,6 +201,25 @@ func (in *CoherenceJob) FindPortServiceName(name string) (string, bool) { return in.Spec.FindPortServiceName(name, in) } +// CreateGlobalLabels creates the common label set for all resources. +func (in *CoherenceJob) CreateGlobalLabels() map[string]string { + labels := operator.GetGlobalLabelsNoError() + if labels == nil { + labels = make(map[string]string) + } + + globalSpec := in.GetGlobalSpec() + if globalSpec != nil { + for k, v := range globalSpec.Labels { + labels[k] = v + } + } + for k, v := range in.CreateCommonLabels() { + labels[k] = v + } + return labels +} + // CreateCommonLabels creates the deployment's common label set. func (in *CoherenceJob) CreateCommonLabels() map[string]string { labels := make(map[string]string) @@ -211,6 +238,21 @@ func (in *CoherenceJob) CreateCommonLabels() map[string]string { return labels } +// CreateGlobalAnnotations creates the common annotation set for all resources. +func (in *CoherenceJob) CreateGlobalAnnotations() map[string]string { + annotations := operator.GetGlobalAnnotationsNoError() + globalSpec := in.GetGlobalSpec() + if globalSpec != nil && globalSpec.Annotations != nil { + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range globalSpec.Annotations { + annotations[k] = v + } + } + return annotations +} + // CreateAnnotations returns the annotations to apply to this cluster's // deployment (StatefulSet). func (in *CoherenceJob) CreateAnnotations() map[string]string { @@ -409,6 +451,8 @@ type CoherenceJobResourceSpec struct { // Cannot be updated. // +optional EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"` + // Global contains attributes that will be applied to all resources managed by the Coherence Operator. + Global *GlobalSpec `json:"global,omitempty"` } // GetRestartPolicy returns the name of the application image to use @@ -458,7 +502,7 @@ func (in *CoherenceJobResourceSpec) IsSyncCompletions() bool { } // CreateJobResource creates the deployment's Job resource. -func (in *CoherenceJobResourceSpec) CreateJobResource(deployment CoherenceResource) Resource { +func (in *CoherenceJobResourceSpec) CreateJobResource(deployment *CoherenceJob) Resource { job := in.CreateJob(deployment) return Resource{ @@ -469,13 +513,14 @@ func (in *CoherenceJobResourceSpec) CreateJobResource(deployment CoherenceResour } // CreateJob creates the deployment's Job. -func (in *CoherenceJobResourceSpec) CreateJob(deployment CoherenceResource) batchv1.Job { +func (in *CoherenceJobResourceSpec) CreateJob(deployment *CoherenceJob) batchv1.Job { + ann := deployment.CreateGlobalAnnotations() job := batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Namespace: deployment.GetNamespace(), Name: deployment.GetName(), - Labels: deployment.CreateCommonLabels(), - Annotations: deployment.CreateAnnotations(), + Labels: deployment.CreateGlobalLabels(), + Annotations: ann, }, } diff --git a/api/v1/coherenceresource.go b/api/v1/coherenceresource.go index 988aab094..9626da465 100644 --- a/api/v1/coherenceresource.go +++ b/api/v1/coherenceresource.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -43,6 +43,10 @@ type CoherenceResource interface { FindPortServiceName(name string) (string, bool) // CreateCommonLabels creates the deployment's common label set. CreateCommonLabels() map[string]string + // CreateGlobalLabels creates the common label set for all resources. + CreateGlobalLabels() map[string]string + // CreateGlobalAnnotations creates the common annotation set for all resources. + CreateGlobalAnnotations() map[string]string // CreateAnnotations returns the annotations to apply to this cluster's // deployment (StatefulSet). CreateAnnotations() map[string]string @@ -92,4 +96,6 @@ type CoherenceResource interface { IsForceExit() bool // GetEnvVarFrom returns the array of EnvVarSource configurations GetEnvVarFrom() []corev1.EnvFromSource + // GetGlobalSpec returns the attributes to be applied to all resources + GetGlobalSpec() *GlobalSpec } diff --git a/api/v1/coherenceresource_types.go b/api/v1/coherenceresource_types.go index 09b137c35..ddce1345f 100644 --- a/api/v1/coherenceresource_types.go +++ b/api/v1/coherenceresource_types.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -8,6 +8,7 @@ package v1 import ( "fmt" + "github.com/oracle/coherence-operator/pkg/operator" "golang.org/x/mod/semver" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" @@ -186,6 +187,13 @@ func (in *Coherence) GetEnvVarFrom() []corev1.EnvFromSource { return in.Spec.EnvFrom } +func (in *Coherence) GetGlobalSpec() *GlobalSpec { + if in == nil { + return nil + } + return in.Spec.Global +} + // FindFullyQualifiedPortServiceNames returns a map of the exposed ports of this resource mapped to their Service's // fully qualified domain name. func (in *Coherence) FindFullyQualifiedPortServiceNames() map[string]string { @@ -226,6 +234,24 @@ func (in *Coherence) FindPortServiceName(name string) (string, bool) { return in.Spec.FindPortServiceName(name, in) } +// CreateGlobalLabels creates the common label set for all resources. +func (in *Coherence) CreateGlobalLabels() map[string]string { + labels := operator.GetGlobalLabelsNoError() + if labels == nil { + labels = make(map[string]string) + } + globalSpec := in.GetGlobalSpec() + if globalSpec != nil { + for k, v := range globalSpec.Labels { + labels[k] = v + } + } + for k, v := range in.CreateCommonLabels() { + labels[k] = v + } + return labels +} + // CreateCommonLabels creates the deployment's common label set. func (in *Coherence) CreateCommonLabels() map[string]string { labels := make(map[string]string) @@ -244,17 +270,37 @@ func (in *Coherence) CreateCommonLabels() map[string]string { return labels } +// CreateGlobalAnnotations creates the common annotation set for all resources. +func (in *Coherence) CreateGlobalAnnotations() map[string]string { + annotations := operator.GetGlobalAnnotationsNoError() + globalSpec := in.GetGlobalSpec() + if globalSpec != nil && globalSpec.Annotations != nil { + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range globalSpec.Annotations { + annotations[k] = v + } + } + return annotations +} + // CreateAnnotations returns the annotations to apply to this cluster's // deployment (StatefulSet). func (in *Coherence) CreateAnnotations() map[string]string { - var annotations map[string]string + annotations := in.CreateGlobalAnnotations() + if in.Spec.StatefulSetAnnotations != nil { - annotations = make(map[string]string) + if annotations == nil { + annotations = make(map[string]string) + } for k, v := range in.Spec.StatefulSetAnnotations { annotations[k] = v } } else if in.Annotations != nil { - annotations = make(map[string]string) + if annotations == nil { + annotations = make(map[string]string) + } for k, v := range in.Annotations { annotations[k] = v } @@ -449,6 +495,8 @@ type CoherenceStatefulSetResourceSpec struct { // Cannot be updated. // +optional EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"` + // Global contains attributes that will be applied to all resources managed by the Coherence Operator. + Global *GlobalSpec `json:"global,omitempty"` } // CreateStatefulSetResource creates the deployment's StatefulSet resource. @@ -468,7 +516,7 @@ func (in *CoherenceStatefulSetResourceSpec) CreateStatefulSet(deployment *Cohere ObjectMeta: metav1.ObjectMeta{ Namespace: deployment.GetNamespace(), Name: deployment.GetName(), - Labels: deployment.CreateCommonLabels(), + Labels: deployment.CreateGlobalLabels(), Annotations: deployment.CreateAnnotations(), }, } diff --git a/api/v1/coherenceresourcespec_types.go b/api/v1/coherenceresourcespec_types.go index efd420ff5..249cd3539 100644 --- a/api/v1/coherenceresourcespec_types.go +++ b/api/v1/coherenceresourcespec_types.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -508,7 +508,7 @@ func (in *CoherenceResourceSpec) CreatePodSelectorLabels(deployment CoherenceRes // CreateWKAService creates the headless WKA Service func (in *CoherenceResourceSpec) CreateWKAService(deployment CoherenceResource) Resource { - labels := deployment.CreateCommonLabels() + labels := deployment.CreateGlobalLabels() labels[LabelComponent] = LabelComponentWKA // The selector for the service (match all Pods with the same cluster label) @@ -517,14 +517,18 @@ func (in *CoherenceResourceSpec) CreateWKAService(deployment CoherenceResource) selector[LabelComponent] = LabelComponentCoherencePod selector[LabelCoherenceWKAMember] = "true" + ann := deployment.CreateGlobalAnnotations() + if ann == nil { + ann = make(map[string]string) + } + ann["service.alpha.kubernetes.io/tolerate-unready-endpoints"] = "true" + svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Namespace: deployment.GetNamespace(), - Name: deployment.GetWkaServiceName(), - Labels: labels, - Annotations: map[string]string{ - "service.alpha.kubernetes.io/tolerate-unready-endpoints": "true", - }, + Namespace: deployment.GetNamespace(), + Name: deployment.GetWkaServiceName(), + Labels: labels, + Annotations: ann, }, Spec: corev1.ServiceSpec{ ClusterIP: corev1.ClusterIPNone, @@ -545,7 +549,7 @@ func (in *CoherenceResourceSpec) CreateWKAService(deployment CoherenceResource) // CreateHeadlessService creates the headless Service for the deployment's StatefulSet. func (in *CoherenceResourceSpec) CreateHeadlessService(deployment CoherenceResource) Resource { // The labels for the service - svcLabels := deployment.CreateCommonLabels() + svcLabels := deployment.CreateGlobalLabels() svcLabels[LabelComponent] = LabelComponentCoherenceHeadless // The selector for the service @@ -561,9 +565,10 @@ func (in *CoherenceResourceSpec) CreateHeadlessService(deployment CoherenceResou // Create the Service svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Namespace: deployment.GetNamespace(), - Name: deployment.GetHeadlessServiceName(), - Labels: svcLabels, + Namespace: deployment.GetNamespace(), + Name: deployment.GetHeadlessServiceName(), + Labels: svcLabels, + Annotations: deployment.CreateGlobalAnnotations(), }, Spec: corev1.ServiceSpec{ ClusterIP: "None", @@ -618,7 +623,36 @@ func (in *CoherenceResourceSpec) createDefaultServicePorts() []corev1.ServicePor func (in *CoherenceResourceSpec) CreatePodTemplateSpec(deployment CoherenceResource) corev1.PodTemplateSpec { // Create the PodSpec labels - podLabels := in.CreatePodSelectorLabels(deployment) + selectorLabels := in.CreatePodSelectorLabels(deployment) + globalLabels := deployment.CreateGlobalLabels() + + podLabels := make(map[string]string) + for k, v := range globalLabels { + podLabels[k] = v + } + for k, v := range selectorLabels { + podLabels[k] = v + } + + var annotations map[string]string + globalAnnotations := deployment.CreateGlobalAnnotations() + if globalAnnotations != nil { + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range globalAnnotations { + annotations[k] = v + } + } + if in.Annotations != nil { + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range in.Annotations { + annotations[k] = v + } + } + // Add the WKA member label podLabels[LabelCoherenceWKAMember] = strconv.FormatBool(in.Coherence.IsWKAMember()) // Add any labels specified for the deployment @@ -639,7 +673,7 @@ func (in *CoherenceResourceSpec) CreatePodTemplateSpec(deployment CoherenceResou podTemplate := corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: podLabels, - Annotations: in.Annotations, + Annotations: annotations, }, Spec: corev1.PodSpec{ Affinity: in.EnsurePodAffinity(deployment), diff --git a/api/v1/create_job_coherencespec_test.go b/api/v1/create_job_coherencespec_test.go index 49e0e8dc1..fb8ec0094 100644 --- a/api/v1/create_job_coherencespec_test.go +++ b/api/v1/create_job_coherencespec_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -430,3 +430,65 @@ func TestCreateJobWithCoherenceSpecWithMultipleWkaAddresses(t *testing.T) { // assert that the Job is as expected assertJobCreation(t, deployment, jobExpected) } + +func TestCreateJobWithGlobalLabels(t *testing.T) { + m := make(map[string]string) + m["one"] = "value-one" + m["two"] = "value-two" + + spec := coh.CoherenceJobResourceSpec{ + Global: &coh.GlobalSpec{ + Labels: m, + }, + } + + // Create the test deployment + deployment := createTestCoherenceJobDeployment(spec) + // Create expected Job + jobExpected := createMinimalExpectedJob(deployment) + labelsExpected := jobExpected.Labels + labelsExpected["one"] = "value-one" + labelsExpected["two"] = "value-two" + + podLabelsExpected := jobExpected.Spec.Template.Labels + podLabelsExpected["one"] = "value-one" + podLabelsExpected["two"] = "value-two" + + // assert that the Job is as expected + assertJobCreation(t, deployment, jobExpected) +} + +func TestCreateJobWithGlobalAnnotations(t *testing.T) { + m := make(map[string]string) + m["one"] = "value-one" + m["two"] = "value-two" + + spec := coh.CoherenceJobResourceSpec{ + Global: &coh.GlobalSpec{ + Annotations: m, + }, + } + + // Create the test deployment + deployment := createTestCoherenceJobDeployment(spec) + // Create expected Job + jobExpected := createMinimalExpectedJob(deployment) + annExpected := jobExpected.Annotations + if annExpected == nil { + annExpected = make(map[string]string) + } + annExpected["one"] = "value-one" + annExpected["two"] = "value-two" + jobExpected.Annotations = annExpected + + podAnnExpected := jobExpected.Spec.Template.Annotations + if podAnnExpected == nil { + podAnnExpected = make(map[string]string) + } + podAnnExpected["one"] = "value-one" + podAnnExpected["two"] = "value-two" + jobExpected.Spec.Template.Annotations = podAnnExpected + + // assert that the Job is as expected + assertJobCreation(t, deployment, jobExpected) +} diff --git a/api/v1/create_services_for_ports_test.go b/api/v1/create_services_for_ports_test.go index 4ef377ae9..1f3b220e9 100644 --- a/api/v1/create_services_for_ports_test.go +++ b/api/v1/create_services_for_ports_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -33,9 +33,7 @@ func TestCreateServicesWithAdditionalPortsEmpty(t *testing.T) { } func TestCreateServicesWithPortsWithOneAdditionalPortWithServiceEnabledFalse(t *testing.T) { - protocol := corev1.ProtocolUDP - spec := coh.CoherenceResourceSpec{ Ports: []coh.NamedPortSpec{ { @@ -60,9 +58,7 @@ func TestCreateServicesWithPortsWithOneAdditionalPortWithServiceEnabledFalse(t * } func TestCreateServicesWithPortsWithOneAdditionalPort(t *testing.T) { - protocol := corev1.ProtocolUDP - spec := coh.CoherenceResourceSpec{ Ports: []coh.NamedPortSpec{ { @@ -503,6 +499,141 @@ func TestCreateServicesWithPortsWithTwoAdditionalPorts(t *testing.T) { assertService(t, deployment, &svcExpectedOne, &svcExpectedTwo) } +func TestCreateServiceWithGlobalLabels(t *testing.T) { + m := make(map[string]string) + m["one"] = "value-one" + m["two"] = "value-two" + + protocol := corev1.ProtocolUDP + + spec := coh.CoherenceStatefulSetResourceSpec{ + Global: &coh.GlobalSpec{ + Labels: m, + }, + CoherenceResourceSpec: coh.CoherenceResourceSpec{ + Ports: []coh.NamedPortSpec{ + { + Name: "test-port-one", + Port: 9876, + Protocol: &protocol, + NodePort: int32Ptr(2020), + HostPort: int32Ptr(1234), + HostIP: stringPtr("10.10.1.0"), + Service: &coh.ServiceSpec{ + Enabled: boolPtr(true), + }, + }, + }, + }, + } + + // Create the test deployment + deployment := createTestCoherenceDeployment(spec) + + // Create the expected labels + labels := deployment.CreateCommonLabels() + labels[coh.LabelComponent] = coh.LabelComponentPortService + labels[coh.LabelPort] = "test-port-one" + labels["one"] = "value-one" + labels["two"] = "value-two" + + // Create the expected service selector labels + selectorLabels := deployment.CreateCommonLabels() + selectorLabels[coh.LabelComponent] = coh.LabelComponentCoherencePod + + // Create expected Service + svcExpected := corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-test-port-one", deployment.Name), + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "test-port-one", + Protocol: protocol, + Port: 9876, + TargetPort: intstr.FromInt32(9876), + NodePort: 2020, + }, + }, + Selector: selectorLabels, + }, + } + + // assert that the Services are as expected + assertService(t, deployment, &svcExpected) +} + +func TestCreateServiceWithGlobalAnnotations(t *testing.T) { + m := make(map[string]string) + m["one"] = "value-one" + m["two"] = "value-two" + + protocol := corev1.ProtocolUDP + + spec := coh.CoherenceStatefulSetResourceSpec{ + Global: &coh.GlobalSpec{ + Annotations: m, + }, + CoherenceResourceSpec: coh.CoherenceResourceSpec{ + Ports: []coh.NamedPortSpec{ + { + Name: "test-port-one", + Port: 9876, + Protocol: &protocol, + NodePort: int32Ptr(2020), + HostPort: int32Ptr(1234), + HostIP: stringPtr("10.10.1.0"), + Service: &coh.ServiceSpec{ + Enabled: boolPtr(true), + }, + }, + }, + }, + } + + // Create the test deployment + deployment := createTestCoherenceDeployment(spec) + + // Create the expected labels + labels := deployment.CreateCommonLabels() + labels[coh.LabelComponent] = coh.LabelComponentPortService + labels[coh.LabelPort] = "test-port-one" + + ann := make(map[string]string) + ann["one"] = "value-one" + ann["two"] = "value-two" + + // Create the expected service selector labels + selectorLabels := deployment.CreateCommonLabels() + selectorLabels[coh.LabelComponent] = coh.LabelComponentCoherencePod + + // Create expected Service + svcExpected := corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-test-port-one", deployment.Name), + Labels: labels, + Annotations: ann, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "test-port-one", + Protocol: protocol, + Port: 9876, + TargetPort: intstr.FromInt32(9876), + NodePort: 2020, + }, + }, + Selector: selectorLabels, + }, + } + + // assert that the Services are as expected + assertService(t, deployment, &svcExpected) +} + func assertService(t *testing.T, deployment *coh.Coherence, servicesExpected ...metav1.Object) { g := NewGomegaWithT(t) diff --git a/api/v1/create_statefulset_test.go b/api/v1/create_statefulset_test.go index 9c1f2a659..d1a022685 100644 --- a/api/v1/create_statefulset_test.go +++ b/api/v1/create_statefulset_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -662,3 +662,65 @@ func TestCreateStatefulSetWithTopologySpreadConstraints(t *testing.T) { // assert that the StatefulSet is as expected assertStatefulSetCreation(t, deployment, stsExpected) } + +func TestCreateStatefulSetWithGlobalLabels(t *testing.T) { + m := make(map[string]string) + m["one"] = "value-one" + m["two"] = "value-two" + + spec := coh.CoherenceStatefulSetResourceSpec{ + Global: &coh.GlobalSpec{ + Labels: m, + }, + } + + // Create the test deployment + deployment := createTestCoherenceDeployment(spec) + // Create expected StatefulSet + stsExpected := createMinimalExpectedStatefulSet(deployment) + labelsExpected := stsExpected.Labels + labelsExpected["one"] = "value-one" + labelsExpected["two"] = "value-two" + + podLabelsExpected := stsExpected.Spec.Template.Labels + podLabelsExpected["one"] = "value-one" + podLabelsExpected["two"] = "value-two" + + // assert that the Job is as expected + assertStatefulSetCreation(t, deployment, stsExpected) +} + +func TestCreateStatefulSetWithGlobalAnnotations(t *testing.T) { + m := make(map[string]string) + m["one"] = "value-one" + m["two"] = "value-two" + + spec := coh.CoherenceStatefulSetResourceSpec{ + Global: &coh.GlobalSpec{ + Annotations: m, + }, + } + + // Create the test deployment + deployment := createTestCoherenceDeployment(spec) + // Create expected Job + stsExpected := createMinimalExpectedStatefulSet(deployment) + annExpected := stsExpected.Annotations + if annExpected == nil { + annExpected = make(map[string]string) + } + annExpected["one"] = "value-one" + annExpected["two"] = "value-two" + stsExpected.Annotations = annExpected + + podAnnExpected := stsExpected.Spec.Template.Annotations + if podAnnExpected == nil { + podAnnExpected = make(map[string]string) + } + podAnnExpected["one"] = "value-one" + podAnnExpected["two"] = "value-two" + stsExpected.Spec.Template.Annotations = podAnnExpected + + // assert that the Job is as expected + assertStatefulSetCreation(t, deployment, stsExpected) +} diff --git a/api/v1/hasher_test.go b/api/v1/hasher_test.go index e84381685..0088f5279 100644 --- a/api/v1/hasher_test.go +++ b/api/v1/hasher_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -28,5 +28,8 @@ func TestHash(t *testing.T) { coh.EnsureHashLabel(deployment) + // If this test fails you have probably added a new field to CoherenceResourceSpec + // This will break backwards compatibility. This field needs to be added to + // both CoherenceStatefulSetResourceSpec and CoherenceJobResourceSpec instead g.Expect(deployment.GetLabels()["coherence-hash"]).To(Equal("5cb9fd9f96")) } diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index db03cd762..9b47746ef 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -347,6 +347,11 @@ func (in *CoherenceJobResourceSpec) DeepCopyInto(out *CoherenceJobResourceSpec) (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Global != nil { + in, out := &in.Global, &out.Global + *out = new(GlobalSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CoherenceJobResourceSpec. @@ -888,6 +893,11 @@ func (in *CoherenceStatefulSetResourceSpec) DeepCopyInto(out *CoherenceStatefulS (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Global != nil { + in, out := &in.Global, &out.Global + *out = new(GlobalSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CoherenceStatefulSetResourceSpec. @@ -1023,6 +1033,35 @@ func (in *ConfigMapVolumeSpec) DeepCopy() *ConfigMapVolumeSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalSpec) DeepCopyInto(out *GlobalSpec) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalSpec. +func (in *GlobalSpec) DeepCopy() *GlobalSpec { + if in == nil { + return nil + } + out := new(GlobalSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImageSpec) DeepCopyInto(out *ImageSpec) { *out = *in diff --git a/controllers/coherence_controller.go b/controllers/coherence_controller.go index 99be2fc2c..cb28a4341 100644 --- a/controllers/coherence_controller.go +++ b/controllers/coherence_controller.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -25,6 +25,7 @@ import ( "github.com/spf13/viper" coreV1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" @@ -192,7 +193,7 @@ func (in *CoherenceReconciler) Reconcile(ctx context.Context, request ctrl.Reque } // ensure that the Operator configuration Secret exists - if err = in.ensureOperatorSecret(ctx, request.Namespace, in.GetClient(), in.Log); err != nil { + if err = in.ensureOperatorSecret(ctx, deployment, in.GetClient(), in.Log); err != nil { err = errors.Wrap(err, "ensuring Operator configuration secret") return in.HandleErrAndRequeue(ctx, err, nil, fmt.Sprintf(reconcileFailedMessage, request.Name, request.Namespace, err), in.Log) } @@ -475,8 +476,16 @@ func (in *CoherenceReconciler) finalizeDeployment(ctx context.Context, c *coh.Co } // ensureOperatorSecret ensures that the Operator configuration secret exists in the namespace. -func (in *CoherenceReconciler) ensureOperatorSecret(ctx context.Context, namespace string, c client.Client, log logr.Logger) error { - s := &coreV1.Secret{} +func (in *CoherenceReconciler) ensureOperatorSecret(ctx context.Context, deployment *coh.Coherence, c client.Client, log logr.Logger) error { + namespace := deployment.Namespace + s := &coreV1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: coh.OperatorConfigName, + Namespace: namespace, + Labels: deployment.CreateGlobalLabels(), + Annotations: deployment.CreateGlobalAnnotations(), + }, + } err := c.Get(ctx, types.NamespacedName{Name: coh.OperatorConfigName, Namespace: namespace}, s) if err != nil && !apierrors.IsNotFound(err) { @@ -485,9 +494,6 @@ func (in *CoherenceReconciler) ensureOperatorSecret(ctx context.Context, namespa restHostAndPort := rest.GetServerHostAndPort() - s.SetNamespace(namespace) - s.SetName(coh.OperatorConfigName) - oldValue := s.Data[coh.OperatorConfigKeyHost] if oldValue == nil || string(oldValue) != restHostAndPort { // data is different so create/update diff --git a/controllers/coherencejob_controller.go b/controllers/coherencejob_controller.go index d7d09a486..af4f1978b 100644 --- a/controllers/coherencejob_controller.go +++ b/controllers/coherencejob_controller.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -20,6 +20,7 @@ import ( "github.com/pkg/errors" coreV1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -53,7 +54,7 @@ func (in *CoherenceJobReconciler) Reconcile(ctx context.Context, request ctrl.Re return in.ReconcileDeployment(ctx, request, deployment) } -func (in *CoherenceJobReconciler) ReconcileDeployment(ctx context.Context, request ctrl.Request, deployment coh.CoherenceResource) (ctrl.Result, error) { +func (in *CoherenceJobReconciler) ReconcileDeployment(ctx context.Context, request ctrl.Request, deployment *coh.CoherenceJob) (ctrl.Result, error) { var err error log := in.Log.WithValues("namespace", request.Namespace, "name", request.Name) @@ -144,7 +145,7 @@ func (in *CoherenceJobReconciler) ReconcileDeployment(ctx context.Context, reque } // ensure that the Operator configuration Secret exists - if err = in.ensureOperatorSecret(ctx, request.Namespace, in.GetClient(), in.Log); err != nil { + if err = in.ensureOperatorSecret(ctx, deployment, in.GetClient(), in.Log); err != nil { err = errors.Wrap(err, "ensuring Operator configuration secret") return in.HandleErrAndRequeue(ctx, err, nil, fmt.Sprintf(reconcileFailedMessage, request.Name, request.Namespace, err), in.Log) } @@ -329,8 +330,16 @@ func (in *CoherenceJobReconciler) ensureVersionAnnotationApplied(ctx context.Con } // ensureOperatorSecret ensures that the Operator configuration secret exists in the namespace. -func (in *CoherenceJobReconciler) ensureOperatorSecret(ctx context.Context, namespace string, c client.Client, log logr.Logger) error { - s := &coreV1.Secret{} +func (in *CoherenceJobReconciler) ensureOperatorSecret(ctx context.Context, deployment *coh.CoherenceJob, c client.Client, log logr.Logger) error { + namespace := deployment.Namespace + s := &coreV1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: coh.OperatorConfigName, + Namespace: namespace, + Labels: deployment.CreateGlobalLabels(), + Annotations: deployment.CreateGlobalAnnotations(), + }, + } err := c.Get(ctx, types.NamespacedName{Name: coh.OperatorConfigName, Namespace: namespace}, s) if err != nil && !apierrors.IsNotFound(err) { @@ -339,9 +348,6 @@ func (in *CoherenceJobReconciler) ensureOperatorSecret(ctx context.Context, name restHostAndPort := rest.GetServerHostAndPort() - s.SetNamespace(namespace) - s.SetName(coh.OperatorConfigName) - oldValue := s.Data[coh.OperatorConfigKeyHost] if oldValue == nil || string(oldValue) != restHostAndPort { // data is different so create/update diff --git a/controllers/webhook/webhook.go b/controllers/webhook/webhook.go index bfe8dcb56..b0e8167b4 100644 --- a/controllers/webhook/webhook.go +++ b/controllers/webhook/webhook.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -109,8 +109,10 @@ func baseWebhookSecret(ns string) *corev1.Secret { Kind: "Secret", }, ObjectMeta: metav1.ObjectMeta{ - Name: viper.GetString(operator.FlagWebhookSecret), - Namespace: ns, + Name: viper.GetString(operator.FlagWebhookSecret), + Namespace: ns, + Labels: operator.GetGlobalLabelsNoError(), + Annotations: operator.GetGlobalAnnotationsNoError(), }, Type: "kubernetes.io/tls", } @@ -303,13 +305,18 @@ func createMutatingWebhookConfiguration(ns string) admissionv1.MutatingWebhookCo noSideEffects := admissionv1.SideEffectClassNone path := coh.MutatingWebHookPath clientConfig := createWebhookClientConfig(ns, path) + labels := operator.GetGlobalLabelsNoError() + ann := operator.GetGlobalAnnotationsNoError() + if ann == nil { + ann = make(map[string]string) + } + ann[certTypeAnnotation] = viper.GetString(operator.FlagCertType) return admissionv1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ - Name: viper.GetString(operator.FlagMutatingWebhookName), - Annotations: map[string]string{ - certTypeAnnotation: viper.GetString(operator.FlagCertType), - }, + Name: viper.GetString(operator.FlagMutatingWebhookName), + Labels: labels, + Annotations: ann, }, TypeMeta: metav1.TypeMeta{ Kind: "MutatingWebhookConfiguration", @@ -346,13 +353,18 @@ func createValidatingWebhookConfiguration(ns string) admissionv1.ValidatingWebho noSideEffects := admissionv1.SideEffectClassNone path := coh.ValidatingWebHookPath clientConfig := createWebhookClientConfig(ns, path) + labels := operator.GetGlobalLabelsNoError() + ann := operator.GetGlobalAnnotationsNoError() + if ann == nil { + ann = make(map[string]string) + } + ann[certTypeAnnotation] = viper.GetString(operator.FlagCertType) return admissionv1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ - Name: viper.GetString(operator.FlagValidatingWebhookName), - Annotations: map[string]string{ - certTypeAnnotation: viper.GetString(operator.FlagCertType), - }, + Name: viper.GetString(operator.FlagValidatingWebhookName), + Labels: labels, + Annotations: ann, }, TypeMeta: metav1.TypeMeta{ Kind: "ValidatingWebhookConfiguration", @@ -406,13 +418,17 @@ func createWebhookClientConfig(ns, path string) admissionv1.WebhookClientConfig func issuer(ns string, group string, apiVersion string) *unstructured.Unstructured { apiString := fmt.Sprintf("%s/%s", group, apiVersion) certIssuer := viper.GetString(operator.FlagCertIssuer) + labels := operator.GetGlobalLabelsNoError() + ann := operator.GetGlobalAnnotationsNoError() return &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": apiString, "kind": "Issuer", "metadata": map[string]interface{}{ - "name": certIssuer, - "namespace": ns, + "name": certIssuer, + "namespace": ns, + "labels": labels, + "annotations": ann, }, "spec": map[string]interface{}{ "selfSigned": map[string]interface{}{}, @@ -426,13 +442,17 @@ func certificate(ns string, group string, apiVersion string) *unstructured.Unstr name := viper.GetString(operator.FlagWebhookService) certIssuer := viper.GetString(operator.FlagCertIssuer) dns := operator.GetWebhookServiceDNSNames() + labels := operator.GetGlobalLabelsNoError() + ann := operator.GetGlobalAnnotationsNoError() return &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": apiString, "kind": "Certificate", "metadata": map[string]interface{}{ - "name": certManagerCertName, - "namespace": ns, + "name": certManagerCertName, + "namespace": ns, + "labels": labels, + "annotations": ann, }, "spec": map[string]interface{}{ "commonName": fmt.Sprintf("%s.%s.svc", name, ns), diff --git a/controllers/webhook/webhook_controller.go b/controllers/webhook/webhook_controller.go index 460cef928..318cf97ca 100644 --- a/controllers/webhook/webhook_controller.go +++ b/controllers/webhook/webhook_controller.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -119,7 +119,7 @@ func (r *CertReconciler) Reconcile(ctx context.Context, request reconcile.Reques // It also returns the duration after which a certificate rotation should be scheduled. func (r *CertReconciler) ReconcileResources(ctx context.Context) error { var err error - secretName := viper.GetString(operator.FlagWebhookSecret) + secretName := operator.GetViper().GetString(operator.FlagWebhookSecret) namespace := operator.GetNamespace() updateSecret := true diff --git a/docs/about/04_coherence_spec.adoc b/docs/about/04_coherence_spec.adoc index 9730f8c38..27c015809 100644 --- a/docs/about/04_coherence_spec.adoc +++ b/docs/about/04_coherence_spec.adoc @@ -34,6 +34,7 @@ TIP: This document was generated from comments in the Go structs in the pkg/api/ * <> * <> * <> +* <> * <> * <> * <> @@ -300,6 +301,19 @@ m| optional | Specify whether the ConfigMap or its keys must be defined m| * <> +=== GlobalSpec + +GlobalSpec is attributes that will be applied to all resources managed by the Operator. + +[cols="1,10,1,1"options="header"] +|=== +| Field | Description | Type | Required +m| labels | Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ m| map[string]string | false +m| annotations | Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ m| map[string]string | false +|=== + +<
> + === ImageSpec ImageSpec defines the settings for a Docker image @@ -906,6 +920,7 @@ m| haBeforeUpdate | Whether to perform a StatusHA test on the cluster before per m| allowUnsafeDelete | AllowUnsafeDelete controls whether the Operator will add a finalizer to the Coherence resource so that it can intercept deletion of the resource and initiate a controlled shutdown of the Coherence cluster. The default value is `false`. The primary use for setting this flag to `true` is in CI/CD environments so that cleanup jobs can delete a whole namespace without requiring the Operator to have removed finalizers from any Coherence resources deployed into that namespace. It is not recommended to set this flag to `true` in a production environment, especially when using Coherence persistence features. m| *bool | false m| actions | Actions to execute once all the Pods are ready after an initial deployment m| []<> | false m| envFrom | List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. m| []https://{k8s-doc-link}/#envfromsource-v1-core[corev1.EnvFromSource] | false +m| global | Global contains attributes that will be applied to all resources managed by the Coherence Operator. m| *<> | false |=== <
> diff --git a/docs/coherence/030_cache_config.adoc b/docs/coherence/030_cache_config.adoc index 19b756bbc..f5fbd1df5 100644 --- a/docs/coherence/030_cache_config.adoc +++ b/docs/coherence/030_cache_config.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -17,7 +17,7 @@ property will be set in the Coherence JVM. When the `spec.coherence.cacheConfig` is blank or not specified, Coherence use its default behaviour to find the cache configuration file to use. Typically, this is to use the first occurrence of `coherence-cache-config.xml` that is found on the classpath -(consult the https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/develop-applications/understanding-configuration.html#GUID-360B798E-2120-44A9-8B09-1FDD9AB40EB5[Coherence documentation] +(consult the https://{commercial-docs-base-url}/develop-applications/understanding-configuration.html#GUID-360B798E-2120-44A9-8B09-1FDD9AB40EB5[Coherence documentation] for an explanation of the default behaviour). To set a specific cache configuration file to use set the `spec.coherence.cacheConfig` field, for example: diff --git a/docs/coherence/040_override_file.adoc b/docs/coherence/040_override_file.adoc index 1f5d3d5e7..7b5182d6b 100644 --- a/docs/coherence/040_override_file.adoc +++ b/docs/coherence/040_override_file.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -17,7 +17,7 @@ By setting this field the `coherence.override` system property will be set in th When the `spec.coherence.overrideConfig` is blank or not specified, Coherence use its default behaviour to find the operational configuration file to use. Typically, this is to use the first occurrence of `tangosol-coherence-override.xml` that is found on the classpath -(consult the https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/develop-applications/understanding-configuration.html#GUID-360B798E-2120-44A9-8B09-1FDD9AB40EB5[Coherence documentation] +(consult the https://{commercial-docs-base-url}/develop-applications/understanding-configuration.html#GUID-360B798E-2120-44A9-8B09-1FDD9AB40EB5[Coherence documentation] for an explanation of the default behaviour). To set a specific operational configuration file to use set the `spec.coherence.overrideConfig` field, for example: diff --git a/docs/coherence/070_wka.adoc b/docs/coherence/070_wka.adoc index 5f8f7c287..b9efdd8e7 100644 --- a/docs/coherence/070_wka.adoc +++ b/docs/coherence/070_wka.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -14,7 +14,7 @@ A Coherence cluster is made up of one or more JVMs. In order for these JVMs to f discover other cluster members. The default mechanism for discovery is multicast broadcast but this does not work in most container environments. Coherence provides an alternative mechanism where the addresses of the hosts where the members of the cluster will run is provided in the form of a -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/develop-applications/setting-cluster.html#GUID-E8CC7C9A-5739-4D12-B88E-A3575F20D63B["well known address" (or WKA) list]. +https://{commercial-docs-base-url}/develop-applications/setting-cluster.html#GUID-E8CC7C9A-5739-4D12-B88E-A3575F20D63B["well known address" (or WKA) list]. This address list is then used by Coherence when it starts in a JVM to discover other cluster members running on the hosts in the WKA list. diff --git a/docs/coherence/080_persistence.adoc b/docs/coherence/080_persistence.adoc index 74266c751..e56c1870a 100644 --- a/docs/coherence/080_persistence.adoc +++ b/docs/coherence/080_persistence.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, 2023, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -17,7 +17,7 @@ as required. The `Coherence` CRD allows the default persistence mode, and the storage location of persistence data to be configured. Persistence can be configured in the `spec.coherence.persistence` section of the CRD. -See the https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/administer/persisting-caches.html#GUID-3DC46E44-21E4-4DC4-9D12-231DE57FE7A1[Coherence Persistence] +See the https://{commercial-docs-base-url}/administer/persisting-caches.html#GUID-3DC46E44-21E4-4DC4-9D12-231DE57FE7A1[Coherence Persistence] documentation for more details of how persistence works and its configuration. == Persistence Mode diff --git a/docs/logging/020_logging.adoc b/docs/logging/020_logging.adoc index bc380dfb1..f8d886be6 100644 --- a/docs/logging/020_logging.adoc +++ b/docs/logging/020_logging.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -32,7 +32,7 @@ to write to log files. One way to do this is to add a Java Util Logging configur Coherence to use the JDK logger. In the `jvm.args` section of the `Coherence` CRD the system properties should be added to set the configuration file used by Java util logging and to configure Coherence logging. -See the Coherence https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/develop-applications/operational-configuration-elements.html[Logging Config] +See the Coherence https://{commercial-docs-base-url}/develop-applications/operational-configuration-elements.html[Logging Config] documentation for more details. There are alternative ways to configure the Java util logger besides using a configuration file, just as there are diff --git a/docs/management/020_management_over_rest.adoc b/docs/management/020_management_over_rest.adoc index 5157f85a6..29a17f900 100644 --- a/docs/management/020_management_over_rest.adoc +++ b/docs/management/020_management_over_rest.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -21,7 +21,7 @@ Once the Management port has been exposed, for example via a load balancer or po endpoint is available at `http://host:port/management/coherence/cluster`. The Swagger JSON document for the API is available at `http://host:port/management/coherence/cluster/metadata-catalog`. -See the https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/rest-reference/[REST API for Managing Oracle Coherence] +See the https://{commercial-docs-base-url}/rest-reference/[REST API for Managing Oracle Coherence] documentation for full details on each of the endpoints. NOTE: Note: Use of Management over REST is available only when using the operator with clusters running @@ -223,9 +223,9 @@ Management over REST can be used for all Coherence management functions, the sam standard MBean access over JMX. Please see the -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/rest-reference/[Coherence REST API] for more information on these features. +https://{commercial-docs-base-url}/rest-reference/[Coherence REST API] for more information on these features. -* https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/manage/using-jmx-manage-oracle-coherence.html#GUID-D160B16B-7C1B-4641-AE94-3310DF8082EC[Connecting JVisualVM to Management over REST] +* https://{commercial-docs-base-url}/manage/using-jmx-manage-oracle-coherence.html#GUID-D160B16B-7C1B-4641-AE94-3310DF8082EC[Connecting JVisualVM to Management over REST] * <> -* https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/rest-reference/op-management-coherence-cluster-members-memberidentifier-diagnostic-cmd-jfrcmd-post.html[Produce and extract a Java Flight Recorder (JFR) file] -* https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/rest-reference/api-reporter.html[Access the Reporter] +* https://{commercial-docs-base-url}/rest-reference/op-management-coherence-cluster-members-memberidentifier-diagnostic-cmd-jfrcmd-post.html[Produce and extract a Java Flight Recorder (JFR) file] +* https://{commercial-docs-base-url}/rest-reference/api-reporter.html[Access the Reporter] diff --git a/docs/management/100_tmb_test.adoc b/docs/management/100_tmb_test.adoc index c0c7dfdbf..97503b547 100644 --- a/docs/management/100_tmb_test.adoc +++ b/docs/management/100_tmb_test.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, 2022, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -12,7 +12,7 @@ Coherence provides utilities that can be used to test network performance, which obviously has a big impact on a distributed system such as Coherence. The documentation for these utilities can be found in the official -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/administer/performing-network-performance-test.html#GUID-7267AB06-6353-416E-B9FD-A75F7FBFE523[Coherence Documentation]. +https://{commercial-docs-base-url}/administer/performing-network-performance-test.html#GUID-7267AB06-6353-416E-B9FD-A75F7FBFE523[Coherence Documentation]. Whilst generally these tests would be run on server hardware, with more and more Coherence deployments moving into the cloud and into Kubernetes these tests can also be performed in `Pods` to measure inter-Pod network performance. @@ -69,7 +69,7 @@ spec: ---- <1> This example uses a Coherence CE image, but any image with `coherence.jar` in it could be used. <2> The command line that the container will execute is exactly the same as that for the listener process in the -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/administer/performing-network-performance-test.html#GUID-7267AB06-6353-416E-B9FD-A75F7FBFE523[Coherence Documentation]. +https://{commercial-docs-base-url}/administer/performing-network-performance-test.html#GUID-7267AB06-6353-416E-B9FD-A75F7FBFE523[Coherence Documentation]. Start the listener `Pod`: [source,bash] @@ -113,7 +113,7 @@ spec: - tmb://message-bus-listener:8000 # <2> ---- <1> Again, the command line is the same as that for the sender process in the -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/administer/performing-network-performance-test.html#GUID-7267AB06-6353-416E-B9FD-A75F7FBFE523[Coherence Documentation]. +https://{commercial-docs-base-url}/administer/performing-network-performance-test.html#GUID-7267AB06-6353-416E-B9FD-A75F7FBFE523[Coherence Documentation]. <2> The `peer` address uses the `Service` name `message-bus-listener` from the sender `yaml`. Start the sender `Pod`: diff --git a/docs/metrics/020_metrics.adoc b/docs/metrics/020_metrics.adoc index 076f7bdb5..35afb46ca 100644 --- a/docs/metrics/020_metrics.adoc +++ b/docs/metrics/020_metrics.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2020, 2022, Oracle and/or its affiliates. + Copyright (c) 2020, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -25,7 +25,7 @@ The example below shows how to enable and access Coherence metrics. Once the metrics port has been exposed, for example via a load balancer or port-forward command, the metrics endpoint is available at `http://host:port/metrics`. -See the https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/manage/using-coherence-metrics.html[Using Coherence Metrics] +See the https://{commercial-docs-base-url}/manage/using-coherence-metrics.html[Using Coherence Metrics] documentation for full details on the available metrics. === Deploy Coherence with Metrics Enabled diff --git a/docs/other/041_global_labels.adoc b/docs/other/041_global_labels.adoc new file mode 100644 index 000000000..5813f08e0 --- /dev/null +++ b/docs/other/041_global_labels.adoc @@ -0,0 +1,187 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2020, 2024, Oracle and/or its affiliates. + Licensed under the Universal Permissive License v 1.0 as shown at + http://oss.oracle.com/licenses/upl. + +/////////////////////////////////////////////////////////////////////////////// + += Global Labels and Annotations + +== Global Labels and Annotations + +It is possible to specify a global set of labels and annotations that will be applied to all resources. +Global labels and annotations can be specified in two ways: + +* For an individual `Coherence` deployment, in which case they will be applied to all the Kubernetes resources +created for that deployment +* As part of the Operator install, in which case they will be applied to all Kubernetes resources managed by the +Operator, including all Coherence clusters and related resources + +== Specify Global Labels for a Coherence Resource + +The `Coherence` CRD contains a `global` field that allows global labels and annotations to be specified. + +[source,yaml] +---- +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +spec: + replicas: 3 + global: + labels: + one: "label-one" + two: "label-two" +---- + +If the yaml above is applied to Kubernetes, then every resource the Operator creates for the `storage` Coherence +deployment, it will add the two labels, `one=label-one` and `two=label-two`. This includes the `StatefulSet`, +the `Pods`, any `Service` such as the stateful set service, the WKA service, etc. + +If any of the labels in the `global` section are also in the Pod labels section or for the Services for exposed ports, +those labels will take precedence. + +For example + +[source,yaml] +---- +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +spec: + replicas: 3 + labels: + one: "pod-label-one" + global: + labels: + one: "label-one" + two: "label-one" +---- + +In the yaml above, the global label `one=label-one` and `two=labl-two` will be applied to every resource created for +the `Coherence` deployment except for the Pods. The Operator uses the `spec.labels` field to define Pods specific labels, +so in this case the Pod labels will be `one=pod-label-one` from the `spec.labels` field and `two=labl-two` from the global +labels. + +== Specify Global Labels when Installing the Operator + +The Operator `runner` binary has various command line flags that can be specified on its command line. +Two of these flags when starting the Operator are: + +* `--global-label` to specify a global label key and value +* `--global-annotation` to specify a global annotation key and value + +Both of these command line flags can be specified multiple times if required. + +For example: + +[source,bash] +---- +runner operator --global-label one=label-one --global-annoataion foo=bar --global-label two=label-two +---- + +The command above will start the Operator with two global labels,`one=label-one` and `two=labl-two` and with +one global annotation `foo=bar`. + +The Operator will then apply these labels and annotations to every Kubernetes resource that it creates. + +=== Installing Using the Manifest Files + +When installing the Operator using the manifest yaml files, additional command line flags can be configured +by manually editing the yaml file before installing. + +Download the yaml manifest file from the GitHub repo +https://github.com/oracle/coherence-operator/releases/download/v3.3.3/coherence-operator.yaml + +Find the section of the yaml file the defines the Operator container args, the default looks like this + +[source,yaml] +.coherence-operator.yaml +---- + - args: + - operator + - --enable-leader-election +---- + +Then edit the argument list to add the required `--global-label` and `--global-annotation` flags. + +For example, to add the same `--global-label one=label-one --global-annotation foo=bar --global-label two=label-two` +flags, the file would look like this: + +[source,yaml] +.coherence-operator.yaml +---- + - args: + - operator + - --enable-leader-election + - --global-label + - one=label-one + - --global-annotation + - foo=bar + - --global-label + - two=label-two` +---- + +[IMPORTANT] +==== +Container arguments must each be a separate entry in the arg list. +This is valid + +[source,yaml] +.coherence-operator.yaml +---- + - args: + - operator + - --enable-leader-election + - --global-label + - one=label-one +---- + +This is not valid + + +[source,yaml] +.coherence-operator.yaml +---- + - args: + - operator + - --enable-leader-election + - --global-label one=label-one +---- +==== + + +=== Installing Using the Helm Chart + +If installing the Operator using the Helm chart, the global labels and annotations can be specified as values +as part of the Helm command or in a values file. + +For example, to add the same `--global-label one=label-one --global-annotation foo=bar --global-label two=label-two` +flags, create a simple values file: + +[source] +.global-values.yaml +---- +globalLabels: + one: "label-one" + two: "label-two" + +globalAnnotations: + foo: "bar" +---- + +Use the values file when installing the Helm chart +[source,bash] +---- +helm install \ + --namespace \ + --values global-values.yaml + coherence \ + coherence/coherence-operator +---- + + + diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 4f1b66b75..9d3ad3afe 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at # http://oss.oracle.com/licenses/upl. # @@ -134,7 +134,7 @@ backend: items: - includes: - "docs/logging/*.adoc" - - title: "Other Pod Settings" + - title: "Other Settings" pathprefix: "/other" glyph: type: "icon" diff --git a/examples/090_tls/README.adoc b/examples/090_tls/README.adoc index be4052561..f89f207b0 100644 --- a/examples/090_tls/README.adoc +++ b/examples/090_tls/README.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2021, 2022, Oracle and/or its affiliates. + Copyright (c) 2021, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -407,7 +407,7 @@ These images can run secure or insecure depending on various system properties p When configuring Coherence to use TLS, we need to configure a socket provider that Coherence can use to create secure socket. We then tell Coherence to use this provider in various places, such as Extend connections, cluster member TCMP connections etc. This configuration is typically done by adding the provider configuration to the Coherence operational configuration override file. -The Coherence documentation has a lot of details on configuring socket providers in the section on https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/secure/using-ssl-secure-communication.html#GUID-21CBAF48-BA78-4373-AC90-BF668CF31776[Using SSL Secure Communication] +The Coherence documentation has a lot of details on configuring socket providers in the section on https://{commercial-docs-base-url}/secure/using-ssl-secure-communication.html#GUID-21CBAF48-BA78-4373-AC90-BF668CF31776[Using SSL Secure Communication] Below is an example that we will use on the server cluster members [source,xml] @@ -503,7 +503,7 @@ The configuration above is included in both of the example images that we built [#tcmp] == Secure Cluster Membership -Now we have a "tls" socket provider we can use it to secure Coherence. The Coherence documentation has a section on https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/secure/using-ssl-secure-communication.html#GUID-21CBAF48-BA78-4373-AC90-BF668CF31776[Securing Coherence TCMP with TLS]. +Now we have a "tls" socket provider we can use it to secure Coherence. The Coherence documentation has a section on https://{commercial-docs-base-url}/secure/using-ssl-secure-communication.html#GUID-21CBAF48-BA78-4373-AC90-BF668CF31776[Securing Coherence TCMP with TLS]. Securing communication between cluster members is very simple, we just set the `coherence.socketprovider` system property to the name of the socket provider we want to use. In our case this will be the "tls" provider we configured above, so we would use `-Dcoherence.socketprovider=tls` The yaml below is a `Coherence` resource that will cause the Operator to create a three member Coherence cluster. @@ -724,7 +724,7 @@ kubectl -n coherence-test delete -f manifests/coherence-cluster.yaml [#extend] === Secure Extend Connections -A common connection type to secure are client connections into the cluster from Coherence Extend clients. The Coherence documentation contains details on https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/secure/using-ssl-secure-communication.html#GUID-0F636928-8731-4228-909C-8B8AB09613DB[Using SSL to Secure Extend Client Communication] for more in-depth details. +A common connection type to secure are client connections into the cluster from Coherence Extend clients. The Coherence documentation contains details on https://{commercial-docs-base-url}/secure/using-ssl-secure-communication.html#GUID-0F636928-8731-4228-909C-8B8AB09613DB[Using SSL to Secure Extend Client Communication] for more in-depth details. As with securing TCMP, we can specify a socket provider in the Extend proxy configuration in the server's cache configuration file and also in the remote scheme in the client's cache configuration. In this example we will use exactly the same TLS socket provider configuration that we created above. The only difference being the name of the `PasswordProvider` class used by the client. At the time of writing this, Coherence does not include an implementation of `PasswordProvider` that reads from a file. The Coherence Operator injects one into the classpath of the server, but our simple client is not managed by the Operator. We have added a simple `FileBasedPasswordProvider` class to the client code in this example. diff --git a/examples/200_autoscaler/README.adoc b/examples/200_autoscaler/README.adoc index 93d5f1ab0..eec1b54e4 100644 --- a/examples/200_autoscaler/README.adoc +++ b/examples/200_autoscaler/README.adoc @@ -1,3 +1,10 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2021, 2024, Oracle and/or its affiliates. + Licensed under the Universal Permissive License v 1.0 as shown at + http://oss.oracle.com/licenses/upl. + +/////////////////////////////////////////////////////////////////////////////// = Autoscaling Coherence Clusters == Kubernetes Horizontal Pod autoscaler Example @@ -62,9 +69,9 @@ The Java code in this example contains a simple MBean class `HeapUsage` and corr that obtain heap use metrics in the way detailed above. There is also a configuration file `custom-mbeans.xml` that Coherence will use to automatically add the custom MBean to Coherence management and metrics. There is Coherence documentation on -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/manage/using-coherence-metrics.html#GUID-CFC31D23-06B8-49AF-8996-ADBA806E0DD9[how to add custom metrics] +https://{commercial-docs-base-url}/manage/using-coherence-metrics.html#GUID-CFC31D23-06B8-49AF-8996-ADBA806E0DD9[how to add custom metrics] and -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/manage/registering-custom-mbeans.html#GUID-1EE749C5-BC0D-4353-B5FE-1C5DCDEAE48C[how to register custom MBeans]. +https://{commercial-docs-base-url}/manage/registering-custom-mbeans.html#GUID-1EE749C5-BC0D-4353-B5FE-1C5DCDEAE48C[how to register custom MBeans]. The custom heap use MBean will be added with an ObjectName of `Coherence:type=HeapUsage,nodeId=1` where `nodeId` will change to match the Coherence member id for the specific JVM. There will be one heap usage MBean for each cluster member. diff --git a/examples/no-operator/03_extend_tls/README.adoc b/examples/no-operator/03_extend_tls/README.adoc index 0f6e2de04..b4a96ed29 100644 --- a/examples/no-operator/03_extend_tls/README.adoc +++ b/examples/no-operator/03_extend_tls/README.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2021, 2022, Oracle and/or its affiliates. + Copyright (c) 2021, 2024, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -42,7 +42,7 @@ We will use these files to securely provide the passwords to the client and serv == Configure Coherence Extend TLS The Coherence documentation explains how to -https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/secure/using-ssl-secure-communication.html#GUID-90E20139-3945-4993-9048-7FBC93B243A3[Use TLS Secure Communication]. +https://{commercial-docs-base-url}/secure/using-ssl-secure-communication.html#GUID-90E20139-3945-4993-9048-7FBC93B243A3[Use TLS Secure Communication]. This example is going to use a standard approach to securing Extend with TLS. To provide the keystores and credentials the example will make use of Kubernetes `Secrets` to mount those files as `Volumes` in the `StatefulSet`. This is much more flexible and secure than baking them into an application's code or image. === Configure the Extend Proxy diff --git a/helm-charts/coherence-operator/templates/deployment.yaml b/helm-charts/coherence-operator/templates/deployment.yaml index 7bb025e74..b7c78677c 100644 --- a/helm-charts/coherence-operator/templates/deployment.yaml +++ b/helm-charts/coherence-operator/templates/deployment.yaml @@ -117,6 +117,16 @@ spec: {{- if (eq .Values.webhooks false) }} - --enable-webhook=false {{- end }} +{{- if (.Values.globalLabels) }} +{{- range $k, $v := .Values.globalLabels }} + - --global-label={{ $k }}={{ $v }} +{{- end }} +{{- end }} +{{- if (.Values.globalAnnotations) }} +{{- range $k, $v := .Values.globalAnnotations }} + - --global-annotation={{ $k }}={{ $v }} +{{- end }} +{{- end }} {{- end }} command: - "/files/runner" diff --git a/helm-charts/coherence-operator/values.yaml b/helm-charts/coherence-operator/values.yaml index 1126bf86c..783fada00 100644 --- a/helm-charts/coherence-operator/values.yaml +++ b/helm-charts/coherence-operator/values.yaml @@ -60,6 +60,14 @@ deploymentLabels: # Additional annotations that are added to the Operator Deployment. deploymentAnnotations: +# --------------------------------------------------------------------------- +# Additional labels that are added to all te resources managed by the Operator Deployment. +globalLabels: + +# --------------------------------------------------------------------------- +# Additional annotations that are added to all te resources managed by the Operator Deployment. +globalAnnotations: + # --------------------------------------------------------------------------- # Operator Pod securityContext # This sets the securityContext configuration for the Pod, for example diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index e25356f40..3f90e91a3 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -52,9 +52,12 @@ const ( FlagCertType = "cert-type" FlagCertIssuer = "cert-issuer" FlagCoherenceImage = "coherence-image" - FlagDevMode = "coherence-dev-mode" FlagCRD = "install-crd" + FlagDevMode = "coherence-dev-mode" + FlagDryRun = "dry-run" FlagEnableWebhook = "enable-webhook" + FlagGlobalAnnotation = "global-annotation" + FlagGlobalLabel = "global-label" FlagHealthAddress = "health-addr" FlagLeaderElection = "enable-leader-election" FlagMetricsAddress = "metrics-addr" @@ -89,13 +92,15 @@ const ( var setupLog = ctrl.Log.WithName("setup") +var currentViper *viper.Viper + var ( operatorVersion = "999.0.0" DefaultSiteLabels = []string{corev1.LabelTopologyZone, corev1.LabelFailureDomainBetaZone} DefaultRackLabels = []string{LabelOciNodeFaultDomain, corev1.LabelTopologyZone, corev1.LabelFailureDomainBetaZone} ) -func SetupOperatorManagerFlags(cmd *cobra.Command) { +func SetupOperatorManagerFlags(cmd *cobra.Command, v *viper.Viper) { flags := cmd.Flags() flags.String(FlagMetricsAddress, ":8080", "The address the metric endpoint binds to.") flags.String(FlagHealthAddress, ":8088", "The address the health endpoint binds to.") @@ -103,34 +108,22 @@ func SetupOperatorManagerFlags(cmd *cobra.Command) { "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - SetupFlags(cmd) + SetupFlags(cmd, v) // Add flags registered by imported packages (e.g. glog and controller-runtime) flagSet := pflag.NewFlagSet("operator", pflag.ContinueOnError) flagSet.AddGoFlagSet(flag.CommandLine) - if err := viper.BindPFlags(flagSet); err != nil { - setupLog.Error(err, "binding flags") - os.Exit(1) - } - - // Validate the command line flags and environment variables - if err := ValidateFlags(); err != nil { - fmt.Println(err.Error()) - _ = cmd.Help() - os.Exit(1) - } - } -func SetupFlags(cmd *cobra.Command) { +func SetupFlags(cmd *cobra.Command, v *viper.Viper) { f, err := data.Assets.Open("assets/config.json") if err != nil { setupLog.Error(err, "finding config.json asset") os.Exit(1) } - viper.SetConfigType("json") - if err := viper.ReadConfig(f); err != nil { + v.SetConfigType("json") + if err := v.ReadConfig(f); err != nil { setupLog.Error(err, "reading configuration file") os.Exit(1) } @@ -250,55 +243,85 @@ func SetupFlags(cmd *cobra.Command) { "webhook-service", "The K8s service used for the webhook", ) + cmd.Flags().StringArray( + FlagGlobalAnnotation, + nil, + "An annotation to apply to all resources managed by the Operator (can be used multiple times)") + cmd.Flags().StringArray( + FlagGlobalLabel, + nil, + "A label to apply to all resources managed by the Operator (can be used multiple times)") // enable using dashed notation in flags and underscores in env - viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - if err := viper.BindPFlags(cmd.Flags()); err != nil { + if err := v.BindPFlags(cmd.Flags()); err != nil { setupLog.Error(err, "binding flags") os.Exit(1) } - viper.AutomaticEnv() + v.AutomaticEnv() } -func ValidateFlags() error { - certValidity := viper.GetDuration(FlagCACertValidity) - certRotateBefore := viper.GetDuration(FlagCACertRotateBefore) +func ValidateFlags(v *viper.Viper) error { + var err error + certValidity := v.GetDuration(FlagCACertValidity) + certRotateBefore := v.GetDuration(FlagCACertRotateBefore) if certRotateBefore > certValidity { return fmt.Errorf("%s must be larger than %s", FlagCACertValidity, FlagCACertRotateBefore) } - certType := viper.GetString(FlagCertType) + certType := v.GetString(FlagCertType) if certType != CertTypeSelfSigned && certType != CertTypeCertManager && certType != CertTypeManual { return fmt.Errorf("%s parameter is invalid", FlagCertType) } - return nil + _, err = GetGlobalAnnotations(v) + if err != nil { + return err + } + + _, err = GetGlobalLabels(v) + if err != nil { + return err + } + + return err +} + +func SetViper(v *viper.Viper) { + currentViper = v +} + +func GetViper() *viper.Viper { + if currentViper == nil { + return viper.GetViper() + } + return currentViper } func IsDevMode() bool { - return viper.GetBool(FlagDevMode) + return GetViper().GetBool(FlagDevMode) } func GetDefaultCoherenceImage() string { - return viper.GetString(FlagCoherenceImage) + return GetViper().GetString(FlagCoherenceImage) } func GetDefaultOperatorImage() string { - return viper.GetString(FlagOperatorImage) + return GetViper().GetString(FlagOperatorImage) } func GetRestHost() string { - return viper.GetString(FlagRestHost) + return GetViper().GetString(FlagRestHost) } func GetRestPort() int32 { - return viper.GetInt32(FlagRestPort) + return GetViper().GetInt32(FlagRestPort) } func GetRestServiceName() string { - s := viper.GetString(FlagServiceName) + s := GetViper().GetString(FlagServiceName) if s != "" { ns := GetNamespace() return s + "." + ns + ".svc" @@ -307,47 +330,51 @@ func GetRestServiceName() string { } func GetRestServicePort() int32 { - return viper.GetInt32(FlagServicePort) + return GetViper().GetInt32(FlagServicePort) } func GetSiteLabel() []string { - return viper.GetStringSlice(FlagSiteLabel) + return GetViper().GetStringSlice(FlagSiteLabel) } func GetRackLabel() []string { - return viper.GetStringSlice(FlagRackLabel) + return GetViper().GetStringSlice(FlagRackLabel) } func ShouldInstallCRDs() bool { - return viper.GetBool(FlagCRD) + return GetViper().GetBool(FlagCRD) && !IsDryRun() } func ShouldEnableWebhooks() bool { - return viper.GetBool(FlagEnableWebhook) + return GetViper().GetBool(FlagEnableWebhook) && !IsDryRun() +} + +func IsDryRun() bool { + return GetViper().GetBool(FlagDryRun) } func ShouldUseSelfSignedCerts() bool { - return viper.GetString(FlagCertType) == CertTypeSelfSigned + return GetViper().GetString(FlagCertType) == CertTypeSelfSigned } func ShouldUseCertManager() bool { - return viper.GetString(FlagCertType) == CertTypeCertManager + return GetViper().GetString(FlagCertType) == CertTypeCertManager } func GetNamespace() string { - return viper.GetString(FlagOperatorNamespace) + return GetViper().GetString(FlagOperatorNamespace) } func GetWebhookCertDir() string { - return viper.GetString(FlagWebhookCertDir) + return GetViper().GetString(FlagWebhookCertDir) } func GetCACertRotateBefore() time.Duration { - return viper.GetDuration(FlagCACertRotateBefore) + return GetViper().GetDuration(FlagCACertRotateBefore) } func GetWebhookServiceDNSNames() []string { var dns []string - s := viper.GetString(FlagWebhookService) + s := GetViper().GetString(FlagWebhookService) if IsDevMode() { dns = []string{s} } else { @@ -401,3 +428,45 @@ func GetWatchNamespace() []string { } return watches } + +func GetGlobalAnnotationsNoError() map[string]string { + m, _ := GetGlobalAnnotations(GetViper()) + return m +} + +func GetGlobalAnnotations(v *viper.Viper) (map[string]string, error) { + args := v.GetStringSlice(FlagGlobalAnnotation) + return stringSliceToMap(args, FlagGlobalAnnotation) +} + +func GetGlobalLabelsNoError() map[string]string { + m, _ := GetGlobalLabels(GetViper()) + return m +} + +func GetGlobalLabels(v *viper.Viper) (map[string]string, error) { + args := v.GetStringSlice(FlagGlobalLabel) + return stringSliceToMap(args, FlagGlobalLabel) +} + +func stringSliceToMap(args []string, flag string) (map[string]string, error) { + var m map[string]string + if args != nil { + m = make(map[string]string) + for _, arg := range args { + kv := strings.SplitN(arg, "=", 2) + if len(kv) <= 1 { + return nil, fmt.Errorf("invalid argument --%s=%s - must be in the format --%s=key=value", + flag, arg, flag) + } + key := strings.TrimSpace(kv[0]) + value := strings.TrimSpace(kv[1]) + if key == "" || value == "" { + return nil, fmt.Errorf("invalid argument --%s=%s - must be in the format --%s=\"key=value\" where the key and value cannot be blank", + flag, arg, flag) + } + m[key] = value + } + } + return m, nil +} diff --git a/pkg/runner/cmd_initialise.go b/pkg/runner/cmd_initialise.go index 23f02277f..639bc6fb6 100644 --- a/pkg/runner/cmd_initialise.go +++ b/pkg/runner/cmd_initialise.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -11,6 +11,7 @@ import ( v1 "github.com/oracle/coherence-operator/api/v1" "github.com/oracle/coherence-operator/pkg/utils" "github.com/spf13/cobra" + "github.com/spf13/viper" "os" ) @@ -186,7 +187,7 @@ func initialiseWithEnv(cmd *cobra.Command, getEnv EnvFunction) (bool, error) { } if len(c) != 0 { fmt.Printf("Running post initialisation command: %s\n", c) - _, err = ExecuteWithArgs(nil, c) + _, err = ExecuteWithArgsAndViper(nil, c, viper.GetViper()) return true, err } diff --git a/pkg/runner/cmd_operator.go b/pkg/runner/cmd_operator.go index 476d7e3a8..6d695a4e2 100644 --- a/pkg/runner/cmd_operator.go +++ b/pkg/runner/cmd_operator.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -57,7 +57,7 @@ func init() { } // operatorCommand runs the Coherence Operator manager -func operatorCommand() *cobra.Command { +func operatorCommand(v *viper.Viper) *cobra.Command { cmd := &cobra.Command{ Use: CommandOperator, Short: "Run the Coherence Operator", @@ -67,7 +67,7 @@ func operatorCommand() *cobra.Command { }, } - operator.SetupOperatorManagerFlags(cmd) + operator.SetupOperatorManagerFlags(cmd, v) return cmd } @@ -75,8 +75,8 @@ func operatorCommand() *cobra.Command { func execute() error { ctrl.SetLogger(zap.New(zap.UseDevMode(true))) - setupLog.Info(fmt.Sprintf("Operator Coherence Image: %s", viper.GetString(operator.FlagCoherenceImage))) - setupLog.Info(fmt.Sprintf("Operator Image: %s", viper.GetString(operator.FlagOperatorImage))) + setupLog.Info(fmt.Sprintf("Operator Coherence Image: %s", operator.GetDefaultCoherenceImage())) + setupLog.Info(fmt.Sprintf("Operator Image: %s", operator.GetDefaultOperatorImage())) cfg := ctrl.GetConfigOrDie() cs, err := clients.NewForConfig(cfg) @@ -160,59 +160,62 @@ func execute() error { return errors.Wrap(err, "unable to create CoherenceJob controller") } - // We intercept the signal handler here so that we can do clean-up before the Manager stops - handler := ctrl.SetupSignalHandler() + dryRun := operator.IsDryRun() + if !dryRun { + // We intercept the signal handler here so that we can do clean-up before the Manager stops + handler := ctrl.SetupSignalHandler() - // Set-up webhooks if required - var cr *webhook.CertReconciler - if operator.ShouldEnableWebhooks() { - // Set up the webhook certificate reconciler - cr = &webhook.CertReconciler{ - Clientset: cs, - } - if err := cr.SetupWithManager(handler, mgr); err != nil { - return errors.Wrap(err, " unable to create webhook certificate controller") + // Set-up webhooks if required + var cr *webhook.CertReconciler + if operator.ShouldEnableWebhooks() { + // Set up the webhook certificate reconciler + cr = &webhook.CertReconciler{ + Clientset: cs, + } + if err := cr.SetupWithManager(handler, mgr); err != nil { + return errors.Wrap(err, " unable to create webhook certificate controller") + } + + // Set up the webhooks + if err = (&coh.Coherence{}).SetupWebhookWithManager(mgr); err != nil { + return errors.Wrap(err, " unable to create webhook") + } + } else { + setupLog.Info("Operator is running with web-hooks disabled") } - // Set up the webhooks - if err = (&coh.Coherence{}).SetupWebhookWithManager(mgr); err != nil { - return errors.Wrap(err, " unable to create webhook") + // Create the REST server + restServer := rest.NewServer(cs) + if err := restServer.SetupWithManager(mgr); err != nil { + return errors.Wrap(err, " unable to start REST server") } - } else { - setupLog.Info("Operator is running with web-hooks disabled") - } - // Create the REST server - restServer := rest.NewServer(cs) - if err := restServer.SetupWithManager(mgr); err != nil { - return errors.Wrap(err, " unable to start REST server") - } + var health healthz.Checker = func(_ *http.Request) error { + <-restServer.Running() + return nil + } - var health healthz.Checker = func(_ *http.Request) error { - <-restServer.Running() - return nil - } + if err := mgr.AddHealthzCheck("health", health); err != nil { + return errors.Wrap(err, "unable to set up health check") + } + if err := mgr.AddReadyzCheck("ready", health); err != nil { + return errors.Wrap(err, "unable to set up ready check") + } - if err := mgr.AddHealthzCheck("health", health); err != nil { - return errors.Wrap(err, "unable to set up health check") - } - if err := mgr.AddReadyzCheck("ready", health); err != nil { - return errors.Wrap(err, "unable to set up ready check") - } + // +kubebuilder:scaffold:builder - // +kubebuilder:scaffold:builder + go func() { + <-handler.Done() + if cr != nil { + cr.Cleanup() + } + }() - go func() { - <-handler.Done() - if cr != nil { - cr.Cleanup() + setupLog.Info("starting manager") + if err := mgr.Start(handler); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) } - }() - - setupLog.Info("starting manager") - if err := mgr.Start(handler); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) } return nil diff --git a/pkg/runner/cmd_operator_test.go b/pkg/runner/cmd_operator_test.go new file mode 100644 index 000000000..4ead725e8 --- /dev/null +++ b/pkg/runner/cmd_operator_test.go @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. + * Licensed under the Universal Permissive License v 1.0 as shown at + * http://oss.oracle.com/licenses/upl. + */ + +package runner + +import ( + . "github.com/onsi/gomega" + coh "github.com/oracle/coherence-operator/api/v1" + "github.com/oracle/coherence-operator/pkg/operator" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +func TestBasicOperator(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + } + + args := []string{"operator", "--dry-run"} + env := EnvVarsFromDeployment(d) + + e, err := ExecuteWithArgsAndNewViper(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + + l := operator.GetGlobalLabelsNoError() + g.Expect(l).NotTo(BeNil()) + g.Expect(len(l)).To(Equal(0)) + + a := operator.GetGlobalAnnotationsNoError() + g.Expect(a).NotTo(BeNil()) + g.Expect(len(a)).To(Equal(0)) +} + +func TestOperatorWithSingleGlobalLabel(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + } + + args := []string{"operator", "--dry-run", "--global-label", "one=value-one"} + env := EnvVarsFromDeployment(d) + + e, err := ExecuteWithArgsAndNewViper(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + + l := operator.GetGlobalLabelsNoError() + g.Expect(l).NotTo(BeNil()) + g.Expect(len(l)).To(Equal(1)) + g.Expect(l["one"]).To(Equal("value-one")) +} + +func TestOperatorWithMultipleGlobalLabels(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + } + + args := []string{"operator", "--dry-run", + "--global-label", "one=value-one", + "--global-label", "two=value-two", + "--global-label", "three=value-three", + } + env := EnvVarsFromDeployment(d) + + e, err := ExecuteWithArgsAndNewViper(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + + l := operator.GetGlobalLabelsNoError() + g.Expect(l).NotTo(BeNil()) + g.Expect(len(l)).To(Equal(3)) + g.Expect(l["one"]).To(Equal("value-one")) + g.Expect(l["two"]).To(Equal("value-two")) + g.Expect(l["three"]).To(Equal("value-three")) +} + +func TestOperatorWithSingleGlobalAnnotation(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + } + + args := []string{"operator", "--dry-run", "--global-annotation", "one=value-one"} + env := EnvVarsFromDeployment(d) + + e, err := ExecuteWithArgsAndNewViper(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + + l := operator.GetGlobalAnnotationsNoError() + g.Expect(l).NotTo(BeNil()) + g.Expect(len(l)).To(Equal(1)) + g.Expect(l["one"]).To(Equal("value-one")) +} + +func TestOperatorWithMultipleGlobalAnnotations(t *testing.T) { + g := NewGomegaWithT(t) + + d := &coh.Coherence{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + } + + args := []string{"operator", "--dry-run", + "--global-annotation", "one=value-one", + "--global-annotation", "two=value-two", + "--global-annotation", "three=value-three", + } + env := EnvVarsFromDeployment(d) + + e, err := ExecuteWithArgsAndNewViper(env, args) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(e).NotTo(BeNil()) + + l := operator.GetGlobalAnnotationsNoError() + g.Expect(l).NotTo(BeNil()) + g.Expect(len(l)).To(Equal(3)) + g.Expect(l["one"]).To(Equal("value-one")) + g.Expect(l["two"]).To(Equal("value-two")) + g.Expect(l["three"]).To(Equal("value-three")) +} diff --git a/pkg/runner/cmd_server.go b/pkg/runner/cmd_server.go index c0dac4617..083d3f6a5 100644 --- a/pkg/runner/cmd_server.go +++ b/pkg/runner/cmd_server.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -23,7 +23,7 @@ const ( // serverCommand creates the corba "server" sub-command func serverCommand() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: CommandServer, Short: "Start a Coherence server", Long: "Starts a Coherence server", @@ -31,6 +31,8 @@ func serverCommand() *cobra.Command { return run(cmd, server) }, } + + return cmd } // Configure the runner to run a Coherence Server diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 7eefa8ade..b308e9db5 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -13,6 +13,7 @@ import ( "fmt" "github.com/go-logr/logr" v1 "github.com/oracle/coherence-operator/api/v1" + "github.com/oracle/coherence-operator/pkg/operator" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -75,9 +76,6 @@ var ( // log is the logger used by the runner log = ctrl.Log.WithName("runner") - - // dryRun is true if the execution is a dry-run - dryRun = false ) // contextKey allows type safe Context Values. @@ -95,8 +93,8 @@ type Execution struct { } // NewRootCommand builds the root cobra command that handles our command line tool. -func NewRootCommand(env map[string]string) (*cobra.Command, *viper.Viper) { - v := viper.New() +func NewRootCommand(env map[string]string, v *viper.Viper) *cobra.Command { + operator.SetViper(v) // rootCommand is the Cobra root Command to execute rootCmd := &cobra.Command{ @@ -112,7 +110,7 @@ func NewRootCommand(env map[string]string) (*cobra.Command, *viper.Viper) { }, } - rootCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "Just print information about the commands that would execute") + rootCmd.PersistentFlags().Bool(operator.FlagDryRun, false, "Just print information about the commands that would execute") rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", fmt.Sprintf("config file (default is $HOME/%s.yaml)", defaultConfig)) rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration") @@ -123,12 +121,12 @@ func NewRootCommand(env map[string]string) (*cobra.Command, *viper.Viper) { rootCmd.AddCommand(statusCommand()) rootCmd.AddCommand(readyCommand()) rootCmd.AddCommand(nodeCommand()) - rootCmd.AddCommand(operatorCommand()) + rootCmd.AddCommand(operatorCommand(v)) rootCmd.AddCommand(networkTestCommand()) rootCmd.AddCommand(jShellCommand()) rootCmd.AddCommand(sleepCommand()) - return rootCmd, v + return rootCmd } func initializeConfig(cmd *cobra.Command, v *viper.Viper, env map[string]string) error { @@ -151,7 +149,8 @@ func initializeConfig(cmd *cobra.Command, v *viper.Viper, env map[string]string) // if we cannot parse the config file. if err := v.ReadInConfig(); err != nil { // It's okay if there isn't a config file - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + var configFileNotFoundError viper.ConfigFileNotFoundError + if !errors.As(err, &configFileNotFoundError) { return err } } @@ -174,7 +173,11 @@ func initializeConfig(cmd *cobra.Command, v *viper.Viper, env map[string]string) // Bind the current command's flags to viper bindFlags(cmd, v) - + parent := cmd.Parent() + if parent != nil { + _ = v.BindPFlags(cmd.Parent().Flags()) + } + _ = v.BindPFlags(cmd.PersistentFlags()) return nil } @@ -198,12 +201,18 @@ func bindFlags(cmd *cobra.Command, v *viper.Viper) { // Execute runs the runner with a given environment. func Execute() (Execution, error) { - return ExecuteWithArgs(nil, nil) + return ExecuteWithArgsAndViper(nil, nil, viper.GetViper()) } -// ExecuteWithArgs runs the runner with a given environment and argument overrides. -func ExecuteWithArgs(env map[string]string, args []string) (Execution, error) { - cmd, v := NewRootCommand(env) +// ExecuteWithArgsAndNewViper runs the runner with a given environment and argument overrides. +func ExecuteWithArgsAndNewViper(env map[string]string, args []string) (Execution, error) { + return ExecuteWithArgsAndViper(env, args, viper.New()) +} + +// ExecuteWithArgsAndViper runs the runner with a given environment and argument overrides. +func ExecuteWithArgsAndViper(env map[string]string, args []string, v *viper.Viper) (Execution, error) { + cmd := NewRootCommand(env, v) + if len(args) > 0 { cmd.SetArgs(args) } @@ -267,6 +276,7 @@ func maybeRun(cmd *cobra.Command, fn MaybeRunFunction) error { sep = ", " } + dryRun := operator.IsDryRun() log.Info("Executing command", "dryRun", dryRun, "application", e.App, "path", e.OsCmd.Path, "args", strings.Join(e.OsCmd.Args, " "), "env", b.String()) diff --git a/pkg/runner/runner_application_test.go b/pkg/runner/runner_application_test.go index 14721bc35..93ac3153b 100644 --- a/pkg/runner/runner_application_test.go +++ b/pkg/runner/runner_application_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -36,7 +36,7 @@ func TestApplicationArgsEmpty(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -66,7 +66,7 @@ func TestApplicationArgs(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "Foo", "Bar") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -100,7 +100,7 @@ func TestApplicationArgsWithEnvVarExpansion(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "foo-value", "bar-value") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -130,7 +130,7 @@ func TestApplicationMain(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := ReplaceArg(GetMinimalExpectedArgs(), "$DEFAULT$", "com.oracle.test.Main") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -163,7 +163,7 @@ func TestApplicationWorkingDirectory(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgsWithoutAppClasspath() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_application_type_test.go b/pkg/runner/runner_application_type_test.go index fa576ee5b..3c66e6c77 100644 --- a/pkg/runner/runner_application_type_test.go +++ b/pkg/runner/runner_application_type_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -34,7 +34,7 @@ func TestApplicationTypeNone(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -65,7 +65,7 @@ func TestApplicationTypeNoneWithMain(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := ReplaceArg(GetMinimalExpectedArgs(), DefaultMain, "com.foo.Bar") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -95,7 +95,7 @@ func TestApplicationTypeCoherence(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -126,7 +126,7 @@ func TestApplicationTypeCoherenceWithMain(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := ReplaceArg(GetMinimalExpectedArgs(), DefaultMain, "com.foo.Bar") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -156,7 +156,7 @@ func TestApplicationTypeJava(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -187,7 +187,7 @@ func TestApplicationTypeJavaWithMain(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := ReplaceArg(GetMinimalExpectedArgs(), DefaultMain, "com.foo.Bar") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -217,7 +217,7 @@ func TestApplicationTypeHelidon(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := ReplaceArg(GetMinimalExpectedArgs(), DefaultMain, HelidonMain) - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -248,7 +248,7 @@ func TestApplicationTypeHelidonWithMain(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := ReplaceArg(GetMinimalExpectedArgs(), DefaultMain, "com.foo.Bar") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(e).NotTo(BeNil()) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_coherence_test.go b/pkg/runner/runner_coherence_test.go index 16da291e3..00e2b0545 100644 --- a/pkg/runner/runner_coherence_test.go +++ b/pkg/runner/runner_coherence_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -32,7 +32,7 @@ func TestCoherenceClusterName(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.cluster="), "-Dcoherence.cluster=test-cluster") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -63,7 +63,7 @@ func TestCoherenceCacheConfig(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.cacheconfig="), "-Dcoherence.cacheconfig=test-config.xml") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -94,7 +94,7 @@ func TestCoherenceOperationalConfig(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.k8s.override="), "-Dcoherence.k8s.override=test-override.xml") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -125,7 +125,7 @@ func TestCoherenceStorageEnabledTrue(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.distributed.localstorage="), "-Dcoherence.distributed.localstorage=true") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -156,7 +156,7 @@ func TestCoherenceStorageEnabledFalse(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.distributed.localstorage="), "-Dcoherence.distributed.localstorage=false") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -186,7 +186,7 @@ func TestCoherenceExcludeFromWKATrue(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -217,7 +217,7 @@ func TestCoherenceLogLevel(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.log.level="), "-Dcoherence.log.level=9") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -248,7 +248,7 @@ func TestCoherenceTracingRatio(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-Dcoherence.tracing.ratio=0.012340") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -278,7 +278,7 @@ func TestCoherenceAllowEndangeredEmptyList(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -308,7 +308,7 @@ func TestCoherenceAllowEndangered(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-Dcoherence.k8s.operator.statusha.allowendangered=foo,bar") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -342,7 +342,7 @@ func TestCoherenceExistingWKADeploymentSameNamespace(t *testing.T) { expectedArgs := GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.wka") expectedArgs = append(expectedArgs, "-Dcoherence.wka=data"+coh.WKAServiceNameSuffix+".foo.svc") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -376,7 +376,7 @@ func TestCoherenceExistingWKADeploymentDifferentNamespace(t *testing.T) { expectedArgs := GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.wka") expectedArgs = append(expectedArgs, "-Dcoherence.wka=data"+coh.WKAServiceNameSuffix+".back-end.svc") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -406,7 +406,7 @@ func TestCoherenceEnableIpMonitor(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.ipmonitor.pingtimeout") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -436,7 +436,7 @@ func TestCoherenceDisableIpMonitor(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -466,7 +466,7 @@ func TestCoherenceDefaultIpMonitor(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_initialise_test.go b/pkg/runner/runner_initialise_test.go index bd4f99fc1..0d44baaee 100644 --- a/pkg/runner/runner_initialise_test.go +++ b/pkg/runner/runner_initialise_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -36,7 +36,7 @@ func TestInitialise(t *testing.T) { args, err := createArgs() g.Expect(err).NotTo(HaveOccurred()) - ex, err := ExecuteWithArgs(env, args) + ex, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(ex).NotTo(BeNil()) g.Expect(ex.OsCmd).To(BeNil()) @@ -56,7 +56,7 @@ func TestInitialiseWithCommand(t *testing.T) { args = append(args, "--cmd", "server,--dry-run") - ex, err := ExecuteWithArgs(env, args) + ex, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(ex).NotTo(BeNil()) g.Expect(ex.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_jvm_jibclasspath_test.go b/pkg/runner/runner_jvm_jibclasspath_test.go index db4259bbc..7afe9ad77 100644 --- a/pkg/runner/runner_jvm_jibclasspath_test.go +++ b/pkg/runner/runner_jvm_jibclasspath_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -37,7 +37,7 @@ func TestJibClasspath(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -69,7 +69,7 @@ func TestJibClasspathFile(t *testing.T) { defer os.Remove(f.Name()) expectedArgs := GetMinimalExpectedArgsWithAppClasspathFile() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -101,7 +101,7 @@ func TestJibMainClassFile(t *testing.T) { defer os.Remove(f.Name()) expectedArgs := GetMinimalExpectedArgsWithAppMainClassFile() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -135,7 +135,7 @@ func TestJibClasspathFileAndMainClassFile(t *testing.T) { defer os.Remove(f2.Name()) expectedArgs := GetMinimalExpectedArgsWithAppClasspathFileAndMainClassFile() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_jvm_memory_test.go b/pkg/runner/runner_jvm_memory_test.go index 9e0f1fc60..bee3f8401 100644 --- a/pkg/runner/runner_jvm_memory_test.go +++ b/pkg/runner/runner_jvm_memory_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -37,7 +37,7 @@ func TestJvmHeapSize(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:InitialHeapSize=10g", "-XX:MaxHeapSize=10g") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -69,7 +69,7 @@ func TestJvmInitialHeapSize(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:InitialHeapSize=10g") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -101,7 +101,7 @@ func TestJvmMaxHeapSize(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:MaxHeapSize=10g") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -135,7 +135,7 @@ func TestJvmHeapSizeOverridesInitialAndMaxHeapSize(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:InitialHeapSize=5g", "-XX:MaxHeapSize=5g") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -167,7 +167,7 @@ func TestJvmMaxRam(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:MaxRAM=10g") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -201,7 +201,7 @@ func TestJvmRamPercent(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgs(), "-XX:InitialRAMPercentage=5.500", "-XX:MaxRAMPercentage=5.500", "-XX:MinRAMPercentage=5.500") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -234,7 +234,7 @@ func TestJvmInitialRamPercent(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:InitialRAMPercentage=5.500") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -267,7 +267,7 @@ func TestJvmMaxRamPercent(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:MaxRAMPercentage=5.500") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -300,7 +300,7 @@ func TestJvmMinRamPercent(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:MinRAMPercentage=5.500") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -340,7 +340,7 @@ func TestJvmRamPercentOverridesInitialMaxAndMin(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgs(), "-XX:InitialRAMPercentage=5.500", "-XX:MaxRAMPercentage=5.500", "-XX:MinRAMPercentage=5.500") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -372,7 +372,7 @@ func TestJvmStackSize(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-Xss500k") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -404,7 +404,7 @@ func TestJvmMetaspaceSize(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:MetaspaceSize=5g", "-XX:MaxMetaspaceSize=5g") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -436,7 +436,7 @@ func TestJvmDirectMemorySize(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:MaxDirectMemorySize=5g") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -468,7 +468,7 @@ func TestJvmNativeMemoryTracking(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-XX:NativeMemoryTracking="), "-XX:NativeMemoryTracking=detail") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -502,7 +502,7 @@ func TestJvmOOMHeapDumpOff(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgsWithoutPrefix("-XX:+HeapDumpOnOutOfMemoryError") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -536,7 +536,7 @@ func TestJvmOOMExitOff(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgsWithoutPrefix("-XX:+ExitOnOutOfMemoryError") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_jvm_test.go b/pkg/runner/runner_jvm_test.go index fbdb06499..d4c596c07 100644 --- a/pkg/runner/runner_jvm_test.go +++ b/pkg/runner/runner_jvm_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -35,7 +35,7 @@ func TestJvmArgsEmpty(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -65,7 +65,7 @@ func TestJvmArgs(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-Dfoo=foo-value", "-Dbar=bar-value") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -99,7 +99,7 @@ func TestJvmArgsWithEnvExpansion(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-Dfoo=foo-value", "-Dbar=bar-value") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -129,7 +129,7 @@ func TestJvmUseContainerLimitsFalse(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgsWithoutPrefix("-XX:+UseContainerSupport") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -159,7 +159,7 @@ func TestJvmUseContainerLimitsTrue(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -191,7 +191,7 @@ func TestJvmGarbageCollectorG1(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-XX:+UseG1GC"), "-XX:+UseG1GC") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -223,7 +223,7 @@ func TestJvmGarbageCollectorCMS(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-XX:+UseG1GC"), "-XX:+UseConcMarkSweepGC") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -255,7 +255,7 @@ func TestJvmGarbageCollectorParallel(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-XX:+UseG1GC"), "-XX:+UseParallelGC") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -294,7 +294,7 @@ func TestJvmGarbageCollectorLoggingTrue(t *testing.T) { "-XX:+PrintGCApplicationStoppedTime", "-XX:+PrintGCApplicationConcurrentTime") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -326,7 +326,7 @@ func TestJvmGarbageCollectorArgsEmpty(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -358,7 +358,7 @@ func TestJvmGarbageCollectorArgs(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedArgs(), "-XX:Arg1", "-XX:Arg2") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_persistence_test.go b/pkg/runner/runner_persistence_test.go index 72d667106..66162c4bc 100644 --- a/pkg/runner/runner_persistence_test.go +++ b/pkg/runner/runner_persistence_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -37,7 +37,7 @@ func TestServerWithPersistenceMode(t *testing.T) { expectedArgs := append(GetMinimalExpectedArgsWithoutPrefix("-Dcoherence.distributed.persistence-mode="), "-Dcoherence.distributed.persistence-mode=active") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -57,7 +57,7 @@ func TestServerWithPersistenceDirectory(t *testing.T) { expectedCommand := GetJavaCommand() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -77,7 +77,7 @@ func TestServerWithSnapshotDirectory(t *testing.T) { expectedCommand := GetJavaCommand() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_spring_test.go b/pkg/runner/runner_spring_test.go index 5781f0cbf..2abc30b0f 100644 --- a/pkg/runner/runner_spring_test.go +++ b/pkg/runner/runner_spring_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -36,7 +36,7 @@ func TestSpringBootApplication(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedSpringBootArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -68,7 +68,7 @@ func TestSpringBootFatJarApplication(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedSpringBootFatJarArgs(jar) - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -101,7 +101,7 @@ func TestSpringBootFatJarApplicationWithCustomMain(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := append(GetMinimalExpectedSpringBootFatJarArgs(jar), "-Dloader.main=foo.Bar") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -133,7 +133,7 @@ func TestSpringBootBuildpacks(t *testing.T) { expectedCommand := getBuildpackLauncher() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 43dfe81ca..f528e0c92 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -32,7 +32,7 @@ func TestMinimalDeployment(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedArgs() - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) @@ -65,7 +65,7 @@ func TestMinimalServerSkipCoherenceVersionCheck(t *testing.T) { "-Dcoherence.k8s.operator.health.enabled=false", "-Dcoherence.health.http.port=6676") - e, err := ExecuteWithArgs(env, args) + e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) g.Expect(e).NotTo(BeNil()) g.Expect(e.OsCmd).NotTo(BeNil()) diff --git a/pkg/utils/storage.go b/pkg/utils/storage.go index 26435ac03..83c1e9764 100644 --- a/pkg/utils/storage.go +++ b/pkg/utils/storage.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -37,7 +37,7 @@ type Storage interface { // GetPrevious obtains the deployment resources for the version prior to the specified version GetPrevious() coh.Resources // Store will store the deployment resources, this will create a new version in the store - Store(coh.Resources, metav1.Object) error + Store(coh.Resources, coh.CoherenceResource) error // Destroy will destroy the store Destroy() // GetHash will return the hash label of the owning resource @@ -121,7 +121,7 @@ func (in *secretStore) GetPrevious() coh.Resources { return in.previous } -func (in *secretStore) Store(r coh.Resources, owner metav1.Object) error { +func (in *secretStore) Store(r coh.Resources, owner coh.CoherenceResource) error { secret, exists, err := in.getSecret() if err != nil { // an error occurred other than NotFound @@ -147,8 +147,25 @@ func (in *secretStore) Store(r coh.Resources, owner metav1.Object) error { labels = make(map[string]string) } labels[coh.LabelCoherenceHash] = owner.GetLabels()[coh.LabelCoherenceHash] + + globalLabels := owner.CreateGlobalLabels() + for k, v := range globalLabels { + labels[k] = v + } secret.SetLabels(labels) + ann := secret.GetAnnotations() + globalAnn := owner.CreateGlobalAnnotations() + if globalAnn != nil { + if ann == nil { + ann = make(map[string]string) + } + for k, v := range globalAnn { + ann[k] = v + } + } + secret.SetAnnotations(ann) + secret.Data[storeKeyLatest] = newLatest secret.Data[storeKeyPrevious] = oldLatest diff --git a/test/e2e/helm/helm_test.go b/test/e2e/helm/helm_test.go index e04e49022..4e8289e44 100644 --- a/test/e2e/helm/helm_test.go +++ b/test/e2e/helm/helm_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -552,6 +552,61 @@ func TestSetAdditionalDeploymentAnnotations(t *testing.T) { g.Expect(len(annotations)).To(BeZero()) } +func TestGlobalLabelsAndAnnotations(t *testing.T) { + g := NewGomegaWithT(t) + + cmd, err := createHelmCommand("--set", "globalLabels.one=label-one", + "--set", "globalLabels.two=label-two", + "--set", "globalAnnotations.three=annotation-three", + "--set", "globalAnnotations.four=annotation-four") + g.Expect(err).NotTo(HaveOccurred()) + AssertHelmInstallWithStatefulSetSubTest(t, "basic", cmd, g, AssertLabelsAndAnnotations) + + //result, err := helmInstall("--set", "globalLabels.one=label-one", + // "--set", "globalLabels.two=label-two", + // "--set", "globalAnnotations.three=annotation-three", + // "--set", "globalAnnotations.four=annotation-four") + // + //g.Expect(err).NotTo(HaveOccurred()) + //g.Expect(result).NotTo(BeNil()) + // + //dep := &appsv1.Deployment{} + //err = result.Get("coherence-operator", dep) + //g.Expect(err).NotTo(HaveOccurred()) + // + //t.Logf("Asserting Helm install. Deploying Coherence resource") + //ns := helper.GetTestNamespace() + //deployment, err := helper.NewSingleCoherenceFromYaml(ns, "coherence.yaml") + //g.Expect(err).NotTo(HaveOccurred()) + // + //defer deleteCoherence(t, &deployment) + // + //err = testContext.Client.Create(goctx.TODO(), &deployment) + //g.Expect(err).NotTo(HaveOccurred()) + // + //var sts *appsv1.StatefulSet + //sts, err = helper.WaitForStatefulSetForDeployment(testContext, ns, &deployment, helper.RetryInterval, helper.Timeout) + //g.Expect(err).NotTo(HaveOccurred()) + // + //g.Expect(sts.Labels).NotTo(BeNil()) + //g.Expect(sts.Labels["one"]).To(Equal("label-one")) + //g.Expect(sts.Labels["two"]).To(Equal("label-two")) + // + //g.Expect(sts.Annotations).NotTo(BeNil()) + //g.Expect(sts.Annotations["three"]).To(Equal("annotation-three")) + //g.Expect(sts.Annotations["four"]).To(Equal("annotation-four")) +} + +func AssertLabelsAndAnnotations(t *testing.T, g *GomegaWithT, _ *coh.Coherence, sts *appsv1.StatefulSet) { + g.Expect(sts.Labels).NotTo(BeNil()) + g.Expect(sts.Labels["one"]).To(Equal("label-one")) + g.Expect(sts.Labels["two"]).To(Equal("label-two")) + + g.Expect(sts.Annotations).NotTo(BeNil()) + g.Expect(sts.Annotations["three"]).To(Equal("annotation-three")) + g.Expect(sts.Annotations["four"]).To(Equal("annotation-four")) +} + func AssertResources() error { ns := helper.GetTestNamespace() pods, err := helper.ListOperatorPods(testContext, ns) @@ -647,6 +702,17 @@ func createHelmCommand(args ...string) (*exec.Cmd, error) { type SubTest func() error +type StatefulSetSubTest func(*testing.T, *GomegaWithT, *coh.Coherence, *appsv1.StatefulSet) + +type SubTestRunner struct { + Test SubTest +} + +func (in SubTestRunner) run(_ *testing.T, g *GomegaWithT, _ *coh.Coherence, _ *appsv1.StatefulSet) { + err := in.Test() + g.Expect(err).NotTo(HaveOccurred()) +} + var emptySubTest = func() error { return nil } @@ -729,6 +795,13 @@ func AssertRBAC(allowNode bool) error { } func AssertHelmInstallWithSubTest(t *testing.T, id string, cmd *exec.Cmd, g *GomegaWithT, test SubTest) { + runner := SubTestRunner{ + Test: test, + } + AssertHelmInstallWithStatefulSetSubTest(t, id, cmd, g, runner.run) +} + +func AssertHelmInstallWithStatefulSetSubTest(t *testing.T, id string, cmd *exec.Cmd, g *GomegaWithT, test StatefulSetSubTest) { ns := helper.GetTestNamespace() t.Cleanup(func() { @@ -770,11 +843,11 @@ func AssertHelmInstallWithSubTest(t *testing.T, id string, cmd *exec.Cmd, g *Gom err = testContext.Client.Create(goctx.TODO(), &deployment) g.Expect(err).NotTo(HaveOccurred()) - _, err = helper.WaitForStatefulSetForDeployment(testContext, ns, &deployment, helper.RetryInterval, helper.Timeout) + var sts *appsv1.StatefulSet + sts, err = helper.WaitForStatefulSetForDeployment(testContext, ns, &deployment, helper.RetryInterval, helper.Timeout) g.Expect(err).NotTo(HaveOccurred()) - err = test() - g.Expect(err).NotTo(HaveOccurred()) + test(t, g, &deployment, sts) } func deleteCoherence(t *testing.T, d *coh.Coherence) { diff --git a/test/e2e/helper/e2e-helpers.go b/test/e2e/helper/e2e-helpers.go index f85070f0c..4ca984add 100644 --- a/test/e2e/helper/e2e-helpers.go +++ b/test/e2e/helper/e2e-helpers.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -168,7 +168,7 @@ func NewContext(startController bool, watchNamespaces ...string) (TestContext, e } // configure viper for the flags and env-vars - operator.SetupFlags(Cmd) + operator.SetupFlags(Cmd, viper.GetViper()) flagSet := pflag.NewFlagSet("operator", pflag.ContinueOnError) flagSet.AddGoFlagSet(flag.CommandLine) if err := viper.BindPFlags(flagSet); err != nil { diff --git a/test/e2e/local/clustering_test.go b/test/e2e/local/clustering_test.go index 4c59cc18a..1c8217c72 100644 --- a/test/e2e/local/clustering_test.go +++ b/test/e2e/local/clustering_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -11,6 +11,7 @@ import ( "fmt" . "github.com/onsi/gomega" coh "github.com/oracle/coherence-operator/api/v1" + "github.com/oracle/coherence-operator/pkg/utils" "github.com/oracle/coherence-operator/test/e2e/helper" appsv1 "k8s.io/api/apps/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -41,7 +42,7 @@ func TestDeploymentWithOneReplica(t *testing.T) { helper.AssertDeployments(testContext, t, "deployment-one-replica.yaml") } -// Test that a deployment works using the a yaml file containing two Coherence +// Test that a deployment works using a yaml file containing two Coherence // specs that have the same cluster name. func TestTwoDeploymentsOneCluster(t *testing.T) { // Make sure we defer clean-up when we're done!! @@ -163,3 +164,53 @@ func TestAllowUnsafeDelete(t *testing.T) { hasFinalizer := controllerutil.ContainsFinalizer(&data, coh.CoherenceFinalizer) g.Expect(hasFinalizer).To(BeFalse()) } + +// Test that a deployment works using global labels +func TestGlobalLabels(t *testing.T) { + // Make sure we defer clean-up when we're done!! + testContext.CleanupAfterTest(t) + g := NewWithT(t) + + deployments, pods := helper.AssertDeployments(testContext, t, "deployment-global-label.yaml") + + podLabels := pods[0].GetLabels() + g.Expect(podLabels["one"]).To(Equal("value-one"), "expected label \"one\" in Pod") + g.Expect(podLabels["two"]).To(Equal("value-two"), "expected label \"two\" in Pod") + + data, ok := deployments["global-label"] + g.Expect(ok).To(BeTrue(), "did not find expected 'global-label' deployment") + + storage, err := utils.NewStorage(data.GetNamespacedName(), testContext.Manager) + g.Expect(err).NotTo(HaveOccurred()) + latest := storage.GetLatest() + for _, res := range latest.Items { + l := res.Spec.GetLabels() + g.Expect(l["one"]).To(Equal("value-one"), fmt.Sprintf("expected label \"one\" in %s %s", res.Kind.Name(), res.Name)) + g.Expect(l["two"]).To(Equal("value-two"), fmt.Sprintf("expected label \"two\" in %s %s", res.Kind.Name(), res.Name)) + } +} + +// Test that a deployment works using global labels +func TestGlobalAnnotations(t *testing.T) { + // Make sure we defer clean-up when we're done!! + testContext.CleanupAfterTest(t) + g := NewWithT(t) + + deployments, pods := helper.AssertDeployments(testContext, t, "deployment-global-annotation.yaml") + + podAnnotations := pods[0].GetAnnotations() + g.Expect(podAnnotations["one"]).To(Equal("value-one"), "expected label \"one\" in Pod") + g.Expect(podAnnotations["two"]).To(Equal("value-two"), "expected label \"two\" in Pod") + + data, ok := deployments["global-annotation"] + g.Expect(ok).To(BeTrue(), "did not find expected 'global-annotation' deployment") + + storage, err := utils.NewStorage(data.GetNamespacedName(), testContext.Manager) + g.Expect(err).NotTo(HaveOccurred()) + latest := storage.GetLatest() + for _, res := range latest.Items { + l := res.Spec.GetAnnotations() + g.Expect(l["one"]).To(Equal("value-one"), fmt.Sprintf("expected label \"one\" in %s %s", res.Kind.Name(), res.Name)) + g.Expect(l["two"]).To(Equal("value-two"), fmt.Sprintf("expected label \"two\" in %s %s", res.Kind.Name(), res.Name)) + } +} diff --git a/test/e2e/local/deployment-global-annotation.yaml b/test/e2e/local/deployment-global-annotation.yaml new file mode 100644 index 000000000..d4fe44f91 --- /dev/null +++ b/test/e2e/local/deployment-global-annotation.yaml @@ -0,0 +1,10 @@ +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: global-annotation +spec: + global: + annotations: + one: "value-one" + two: "value-two" + diff --git a/test/e2e/local/deployment-global-label.yaml b/test/e2e/local/deployment-global-label.yaml new file mode 100644 index 000000000..cec6cd5db --- /dev/null +++ b/test/e2e/local/deployment-global-label.yaml @@ -0,0 +1,10 @@ +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: global-label +spec: + global: + labels: + one: "value-one" + two: "value-two" + diff --git a/test/e2e/local/status_query_test.go b/test/e2e/local/status_query_test.go index 377e14f14..af43bf7a1 100644 --- a/test/e2e/local/status_query_test.go +++ b/test/e2e/local/status_query_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -26,12 +26,12 @@ func TestStatusForExistingCuster(t *testing.T) { // Should be Ready args := []string{"status", "--operator-url", "http://localhost:8000", "--namespace", ns, "--name", "minimal-cluster", "--timeout", "1m"} - _, err := runner.ExecuteWithArgs(env, args) + _, err := runner.ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) // Should not be Scaling args = []string{"status", "--operator-url", "http://localhost:8000", "--namespace", ns, "--name", "minimal-cluster", "--condition", string(coh.ConditionTypeScaling), "--timeout", "1m"} - _, err = runner.ExecuteWithArgs(env, args) + _, err = runner.ExecuteWithArgsAndNewViper(env, args) g.Expect(err).To(HaveOccurred()) } @@ -45,6 +45,6 @@ func TestStatusForNonExistentCluster(t *testing.T) { args := []string{"status", "--operator-url", "http://localhost:8000", "--namespace", ns, "--name", "foo", "--timeout", "30s"} // should not be found - _, err := runner.ExecuteWithArgs(env, args) + _, err := runner.ExecuteWithArgsAndNewViper(env, args) g.Expect(err).To(HaveOccurred()) }