From 4c342795b0cdf1b0c96d03d3926c82bb74c69235 Mon Sep 17 00:00:00 2001 From: Chandan Kumar Date: Mon, 16 Dec 2024 13:26:38 +0530 Subject: [PATCH] Added webhook support for Default Images Signed-off-by: Chandan Kumar --- .../watcher.openstack.org_watcherapis.yaml | 4 ++ ...watcher.openstack.org_watcherappliers.yaml | 6 +- ....openstack.org_watcherdecisionengines.yaml | 6 +- api/bases/watcher.openstack.org_watchers.yaml | 12 ++++ api/v1beta1/common_types.go | 60 +++++++++++++++++++ api/v1beta1/watcher_types.go | 7 +-- api/v1beta1/watcher_webhook.go | 20 +++++++ api/v1beta1/watcherapi_types.go | 2 + api/v1beta1/watcherapplier_types.go | 3 +- api/v1beta1/watcherdecisionengine_types.go | 3 +- api/v1beta1/zz_generated.deepcopy.go | 49 +++++++++++++++ .../watcher.openstack.org_watcherapis.yaml | 4 ++ ...watcher.openstack.org_watcherappliers.yaml | 6 +- ....openstack.org_watcherdecisionengines.yaml | 6 +- .../bases/watcher.openstack.org_watchers.yaml | 12 ++++ config/default/kustomization.yaml | 3 + config/default/manager_default_images.yaml | 19 ++++++ main.go | 4 ++ tests/functional/base_test.go | 17 ++++++ tests/functional/watcher_controller_test.go | 28 +++++++++ .../functional/watcherapi_controller_test.go | 20 +++++++ .../default/watcher/01-assert.yaml | 28 +++++++++ 22 files changed, 299 insertions(+), 20 deletions(-) create mode 100644 config/default/manager_default_images.yaml diff --git a/api/bases/watcher.openstack.org_watcherapis.yaml b/api/bases/watcher.openstack.org_watcherapis.yaml index 992fdba..9aaa6da 100644 --- a/api/bases/watcher.openstack.org_watcherapis.yaml +++ b/api/bases/watcher.openstack.org_watcherapis.yaml @@ -39,6 +39,10 @@ spec: spec: description: WatcherAPISpec defines the desired state of WatcherAPI properties: + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string databaseAccount: default: watcher description: DatabaseAccount - MariaDBAccount CR name used for watcher diff --git a/api/bases/watcher.openstack.org_watcherappliers.yaml b/api/bases/watcher.openstack.org_watcherappliers.yaml index 186d38b..6abda7c 100644 --- a/api/bases/watcher.openstack.org_watcherappliers.yaml +++ b/api/bases/watcher.openstack.org_watcherappliers.yaml @@ -39,9 +39,9 @@ spec: spec: description: WatcherApplierSpec defines the desired state of WatcherApplier properties: - foo: - description: Foo is an example field of WatcherApplier. Edit watcherapplier_types.go - to remove/update + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) type: string type: object status: diff --git a/api/bases/watcher.openstack.org_watcherdecisionengines.yaml b/api/bases/watcher.openstack.org_watcherdecisionengines.yaml index b3cf6c9..a417fcb 100644 --- a/api/bases/watcher.openstack.org_watcherdecisionengines.yaml +++ b/api/bases/watcher.openstack.org_watcherdecisionengines.yaml @@ -40,9 +40,9 @@ spec: spec: description: WatcherDecisionEngineSpec defines the desired state of WatcherDecisionEngine properties: - foo: - description: Foo is an example field of WatcherDecisionEngine. Edit - watcherdecisionengine_types.go to remove/update + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) type: string type: object status: diff --git a/api/bases/watcher.openstack.org_watchers.yaml b/api/bases/watcher.openstack.org_watchers.yaml index 28bae46..a62b19b 100644 --- a/api/bases/watcher.openstack.org_watchers.yaml +++ b/api/bases/watcher.openstack.org_watchers.yaml @@ -39,6 +39,12 @@ spec: spec: description: WatcherSpec defines the desired state of Watcher properties: + apiContainerImageURL: + description: APIContainerImageURL + type: string + applierContainerImageURL: + description: ApplierContainerImageURL + type: string databaseAccount: default: watcher description: DatabaseAccount - MariaDBAccount CR name used for watcher @@ -49,6 +55,9 @@ spec: MariaDB instance name Required to use the mariadb-operator instance to create the DB and user type: string + decisionengineContainerImageURL: + description: DecisionEngineContainerImageURL + type: string passwordSelectors: default: service: WatcherPassword @@ -77,7 +86,10 @@ spec: to register in keystone type: string required: + - apiContainerImageURL + - applierContainerImageURL - databaseInstance + - decisionengineContainerImageURL - rabbitMqClusterName type: object status: diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index ce06af8..80d5c21 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -16,6 +16,15 @@ limitations under the License. package v1beta1 +import "os" + +// Container image fall-back defaults +const ( + WatcherAPIContainerImage = "quay.io/podified-antelope-centos9/openstack-watcher-api:current-podified" + WatcherDecisionEngineContainerImage = "quay.io/podified-antelope-centos9/openstack-watcher-decision-engine:current-podified" + WatcherApplierContainerImage = "quay.io/podified-antelope-centos9/openstack-watcher-applier:current-podified" +) + // WatcherCommon defines a spec based reusable for all the CRDs type WatcherCommon struct { // +kubebuilder:validation:Optional @@ -64,3 +73,54 @@ type PasswordSelector struct { // Service - Selector to get the watcher service user password from the Secret Service string `json:"service"` } + +// WatcherContainerImages +type WatcherContainerImages struct { + // +kubebuilder:validation:Optional + // The service specific Container Image URL (will be set to environmental default if empty) + ContainerImage string `json:"containerImage"` +} + +type WatcherImages struct { + // +kubebuilder:validation:Required + // APIContainerImageURL + APIContainerImageURL string `json:"apiContainerImageURL"` + + // +kubebuilder:validation:Required + // DecisionEngineContainerImageURL + DecisionEngineContainerImageURL string `json:"decisionengineContainerImageURL"` + + // +kubebuilder:validation:Required + // ApplierContainerImageURL + ApplierContainerImageURL string `json:"applierContainerImageURL"` +} + +func (r *WatcherImages) Default(defaults WatcherDefaults) { + if r.APIContainerImageURL == "" { + r.APIContainerImageURL = defaults.APIContainerImageURL + } + if r.DecisionEngineContainerImageURL == "" { + r.DecisionEngineContainerImageURL = defaults.DecisionEngineContainerImageURL + } + if r.ApplierContainerImageURL == "" { + r.ApplierContainerImageURL = defaults.ApplierContainerImageURL + } +} + +// GetEnvDefault - Get the value associated with key from environment variables, but use baseDefault as a value in the case of an empty string +func GetEnvDefault(key string, baseDefault string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return baseDefault +} + +func SetupDefaults() { + // Acquire environmental defaults and initialize Nova defaults with them + watcherDefaults := WatcherDefaults{ + APIContainerImageURL: GetEnvDefault("WATCHER_API_IMAGE_URL_DEFAULT", WatcherAPIContainerImage), + ApplierContainerImageURL: GetEnvDefault("WATCHER_APPLIER_IMAGE_URL_DEFAULT", WatcherApplierContainerImage), + DecisionEngineContainerImageURL: GetEnvDefault("WATCHER_DECISION_ENGINE_IMAGE_URL_DEFAULT", WatcherDecisionEngineContainerImage), + } + SetupWatcherDefaults(watcherDefaults) +} diff --git a/api/v1beta1/watcher_types.go b/api/v1beta1/watcher_types.go index 1dafb90..cc8dad8 100644 --- a/api/v1beta1/watcher_types.go +++ b/api/v1beta1/watcher_types.go @@ -27,6 +27,9 @@ type WatcherSpec struct { // Important: Run "make" to regenerate code after modifying this file WatcherTemplate `json:",inline"` + + // +kubebuilder:validation:Required + WatcherImages `json:",inline"` } // WatcherStatus defines the observed state of Watcher @@ -68,7 +71,3 @@ type WatcherList struct { func init() { SchemeBuilder.Register(&Watcher{}, &WatcherList{}) } - -// SetupDefaults - initializes any CRD field defaults based on environment variables (the defaulting mechanism itself is implemented via webhooks) -func SetupDefaults() { -} diff --git a/api/v1beta1/watcher_webhook.go b/api/v1beta1/watcher_webhook.go index 76ed051..af9a8f0 100644 --- a/api/v1beta1/watcher_webhook.go +++ b/api/v1beta1/watcher_webhook.go @@ -23,9 +23,23 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) +// WatcherDefaults - +type WatcherDefaults struct { + APIContainerImageURL string + DecisionEngineContainerImageURL string + ApplierContainerImageURL string +} + +var watcherDefaults WatcherDefaults + // log is for logging in this package. var watcherlog = logf.Log.WithName("watcher-resource") +func SetupWatcherDefaults(defaults WatcherDefaults) { + watcherDefaults = defaults + watcherlog.Info("Watcher defaults initialized", "defaults", defaults) +} + //+kubebuilder:webhook:path=/mutate-watcher-openstack-org-v1beta1-watcher,mutating=true,failurePolicy=fail,sideEffects=None,groups=watcher.openstack.org,resources=watchers,verbs=create;update,versions=v1beta1,name=mwatcher.kb.io,admissionReviewVersions=v1 var _ webhook.Defaulter = &Watcher{} @@ -34,6 +48,12 @@ var _ webhook.Defaulter = &Watcher{} func (r *Watcher) Default() { watcherlog.Info("default", "name", r.Name) + r.Spec.Default() +} + +// Default - set defaults for this WatcherCore spec. +func (spec *WatcherSpec) Default() { + spec.WatcherImages.Default(watcherDefaults) } //+kubebuilder:webhook:path=/validate-watcher-openstack-org-v1beta1-watcher,mutating=false,failurePolicy=fail,sideEffects=None,groups=watcher.openstack.org,resources=watchers,verbs=create;update,versions=v1beta1,name=vwatcher.kb.io,admissionReviewVersions=v1 diff --git a/api/v1beta1/watcherapi_types.go b/api/v1beta1/watcherapi_types.go index 03a49d3..c3c8631 100644 --- a/api/v1beta1/watcherapi_types.go +++ b/api/v1beta1/watcherapi_types.go @@ -27,6 +27,8 @@ type WatcherAPISpec struct { // Important: Run "make" to regenerate code after modifying this file WatcherCommon `json:",inline"` + + WatcherContainerImages `json:",inline"` } // WatcherAPIStatus defines the observed state of WatcherAPI diff --git a/api/v1beta1/watcherapplier_types.go b/api/v1beta1/watcherapplier_types.go index b177d88..30c17ee 100644 --- a/api/v1beta1/watcherapplier_types.go +++ b/api/v1beta1/watcherapplier_types.go @@ -28,8 +28,7 @@ type WatcherApplierSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - // Foo is an example field of WatcherApplier. Edit watcherapplier_types.go to remove/update - Foo string `json:"foo,omitempty"` + WatcherContainerImages `json:",inline"` } // WatcherApplierStatus defines the observed state of WatcherApplier diff --git a/api/v1beta1/watcherdecisionengine_types.go b/api/v1beta1/watcherdecisionengine_types.go index f42f9c7..df5a065 100644 --- a/api/v1beta1/watcherdecisionengine_types.go +++ b/api/v1beta1/watcherdecisionengine_types.go @@ -28,8 +28,7 @@ type WatcherDecisionEngineSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - // Foo is an example field of WatcherDecisionEngine. Edit watcherdecisionengine_types.go to remove/update - Foo string `json:"foo,omitempty"` + WatcherContainerImages `json:",inline"` } // WatcherDecisionEngineStatus defines the observed state of WatcherDecisionEngine diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index cbdefed..ffd69cf 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -130,6 +130,7 @@ func (in *WatcherAPIList) DeepCopyObject() runtime.Object { func (in *WatcherAPISpec) DeepCopyInto(out *WatcherAPISpec) { *out = *in out.WatcherCommon = in.WatcherCommon + out.WatcherContainerImages = in.WatcherContainerImages } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherAPISpec. @@ -226,6 +227,7 @@ func (in *WatcherApplierList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherApplierSpec) DeepCopyInto(out *WatcherApplierSpec) { *out = *in + out.WatcherContainerImages = in.WatcherContainerImages } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherApplierSpec. @@ -269,6 +271,21 @@ func (in *WatcherCommon) DeepCopy() *WatcherCommon { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WatcherContainerImages) DeepCopyInto(out *WatcherContainerImages) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherContainerImages. +func (in *WatcherContainerImages) DeepCopy() *WatcherContainerImages { + if in == nil { + return nil + } + out := new(WatcherContainerImages) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherDecisionEngine) DeepCopyInto(out *WatcherDecisionEngine) { *out = *in @@ -331,6 +348,7 @@ func (in *WatcherDecisionEngineList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherDecisionEngineSpec) DeepCopyInto(out *WatcherDecisionEngineSpec) { *out = *in + out.WatcherContainerImages = in.WatcherContainerImages } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherDecisionEngineSpec. @@ -358,6 +376,36 @@ func (in *WatcherDecisionEngineStatus) DeepCopy() *WatcherDecisionEngineStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WatcherDefaults) DeepCopyInto(out *WatcherDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherDefaults. +func (in *WatcherDefaults) DeepCopy() *WatcherDefaults { + if in == nil { + return nil + } + out := new(WatcherDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WatcherImages) DeepCopyInto(out *WatcherImages) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherImages. +func (in *WatcherImages) DeepCopy() *WatcherImages { + if in == nil { + return nil + } + out := new(WatcherImages) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherList) DeepCopyInto(out *WatcherList) { *out = *in @@ -394,6 +442,7 @@ func (in *WatcherList) DeepCopyObject() runtime.Object { func (in *WatcherSpec) DeepCopyInto(out *WatcherSpec) { *out = *in out.WatcherTemplate = in.WatcherTemplate + out.WatcherImages = in.WatcherImages } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherSpec. diff --git a/config/crd/bases/watcher.openstack.org_watcherapis.yaml b/config/crd/bases/watcher.openstack.org_watcherapis.yaml index 992fdba..9aaa6da 100644 --- a/config/crd/bases/watcher.openstack.org_watcherapis.yaml +++ b/config/crd/bases/watcher.openstack.org_watcherapis.yaml @@ -39,6 +39,10 @@ spec: spec: description: WatcherAPISpec defines the desired state of WatcherAPI properties: + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string databaseAccount: default: watcher description: DatabaseAccount - MariaDBAccount CR name used for watcher diff --git a/config/crd/bases/watcher.openstack.org_watcherappliers.yaml b/config/crd/bases/watcher.openstack.org_watcherappliers.yaml index 186d38b..6abda7c 100644 --- a/config/crd/bases/watcher.openstack.org_watcherappliers.yaml +++ b/config/crd/bases/watcher.openstack.org_watcherappliers.yaml @@ -39,9 +39,9 @@ spec: spec: description: WatcherApplierSpec defines the desired state of WatcherApplier properties: - foo: - description: Foo is an example field of WatcherApplier. Edit watcherapplier_types.go - to remove/update + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) type: string type: object status: diff --git a/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml b/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml index b3cf6c9..a417fcb 100644 --- a/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml +++ b/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml @@ -40,9 +40,9 @@ spec: spec: description: WatcherDecisionEngineSpec defines the desired state of WatcherDecisionEngine properties: - foo: - description: Foo is an example field of WatcherDecisionEngine. Edit - watcherdecisionengine_types.go to remove/update + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) type: string type: object status: diff --git a/config/crd/bases/watcher.openstack.org_watchers.yaml b/config/crd/bases/watcher.openstack.org_watchers.yaml index 28bae46..a62b19b 100644 --- a/config/crd/bases/watcher.openstack.org_watchers.yaml +++ b/config/crd/bases/watcher.openstack.org_watchers.yaml @@ -39,6 +39,12 @@ spec: spec: description: WatcherSpec defines the desired state of Watcher properties: + apiContainerImageURL: + description: APIContainerImageURL + type: string + applierContainerImageURL: + description: ApplierContainerImageURL + type: string databaseAccount: default: watcher description: DatabaseAccount - MariaDBAccount CR name used for watcher @@ -49,6 +55,9 @@ spec: MariaDB instance name Required to use the mariadb-operator instance to create the DB and user type: string + decisionengineContainerImageURL: + description: DecisionEngineContainerImageURL + type: string passwordSelectors: default: service: WatcherPassword @@ -77,7 +86,10 @@ spec: to register in keystone type: string required: + - apiContainerImageURL + - applierContainerImageURL - databaseInstance + - decisionengineContainerImageURL - rabbitMqClusterName type: object status: diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index d8f571d..c3f02b5 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -41,6 +41,9 @@ patches: # 'CERTMANAGER' needs to be enabled to use ca injection #- path: webhookcainjection_patch.yaml +# Injects our custom images (ENV variable settings) +- path: manager_default_images.yaml + # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. # Uncomment the following replacements to add the cert-manager CA injection annotations #replacements: diff --git a/config/default/manager_default_images.yaml b/config/default/manager_default_images.yaml new file mode 100644 index 0000000..f18a439 --- /dev/null +++ b/config/default/manager_default_images.yaml @@ -0,0 +1,19 @@ +# This patch inject custom ENV settings to the manager container +# Used to set our default image locations +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + env: + - name: WATCHER_API_IMAGE_URL_DEFAULT + value: quay.io/tripleoantelopecentos9/openstack-watcher-api:current-tripleo + - name: WATCHER_DECISION_ENGINE_IMAGE_URL_DEFAULT + value: quay.io/tripleoantelopecentos9/openstack-watcher-decision-engine:current-tripleo + - name: WATCHER_APPLIER_IMAGE_URL_DEFAULT + value: quay.io/tripleoantelopecentos9/openstack-watcher-applier:current-tripleo diff --git a/main.go b/main.go index 084ea8c..8f39ef0 100644 --- a/main.go +++ b/main.go @@ -148,6 +148,10 @@ func main() { if err != nil { os.Exit(1) } + + // Acquire environmental defaults and initialize operator defaults with them + watcherv1beta1.SetupDefaults() + checker := healthz.Ping // Setup webhooks if requested diff --git a/tests/functional/base_test.go b/tests/functional/base_test.go index 69f4a92..08db079 100644 --- a/tests/functional/base_test.go +++ b/tests/functional/base_test.go @@ -29,6 +29,10 @@ import ( corev1 "k8s.io/api/core/v1" ) +const ( + ContainerImage = "test://nova" +) + func GetDefaultWatcherSpec() map[string]interface{} { return map[string]interface{}{ "databaseInstance": "openstack", @@ -40,6 +44,19 @@ func GetDefaultWatcherAPISpec() map[string]interface{} { return map[string]interface{}{ "databaseInstance": "openstack", "secret": SecretName, + "containerImage": ContainerImage, + } +} + +func GetDefaultWatcherApplierSpec() map[string]interface{} { + return map[string]interface{}{ + "containerImage": ContainerImage, + } +} + +func GetDefaultWatcherDecisionEngineSpec() map[string]interface{} { + return map[string]interface{}{ + "containerImage": ContainerImage, } } diff --git a/tests/functional/watcher_controller_test.go b/tests/functional/watcher_controller_test.go index 6553dea..0e4e26c 100644 --- a/tests/functional/watcher_controller_test.go +++ b/tests/functional/watcher_controller_test.go @@ -9,6 +9,7 @@ import ( rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" keystonev1beta1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" watcherv1beta1 "github.com/openstack-k8s-operators/watcher-operator/api/v1beta1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/fields" @@ -371,4 +372,31 @@ var _ = Describe("Watcher controller", func() { }) }) + When("Watcher CR is created without container images defined", func() { + BeforeEach(func() { + DeferCleanup(th.DeleteInstance, CreateWatcher(watcherTest.Instance, GetDefaultWatcherSpec())) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + watcherTest.Instance.Namespace, + GetWatcher(watcherTest.Instance).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + spec := GetDefaultWatcherSpec() + // This watcher is created without any container image is specified in + // the request + DeferCleanup(th.DeleteInstance, CreateWatcher(watcherTest.Instance, spec)) + + }) + It("It has the expected container image defaults", func() { + Watcher := GetWatcher(watcherTest.Instance) + Expect(Watcher.Spec.APIContainerImageURL).To(Equal(util.GetEnvVar("RELATED_IMAGE_WATCHER_API_IMAGE_URL_DEFAULT", watcherv1beta1.WatcherAPIContainerImage))) + Expect(Watcher.Spec.DecisionEngineContainerImageURL).To(Equal(util.GetEnvVar("RELATED_IMAGE_WATCHER_DECISION_ENGINE_IMAGE_URL_DEFAULT", watcherv1beta1.WatcherDecisionEngineContainerImage))) + Expect(Watcher.Spec.ApplierContainerImageURL).To(Equal(util.GetEnvVar("RELATED_IMAGE_WATCHER_APPLIER_IMAGE_URL_DEFAULT", watcherv1beta1.WatcherApplierContainerImage))) + }) + }) + }) diff --git a/tests/functional/watcherapi_controller_test.go b/tests/functional/watcherapi_controller_test.go index daaade8..61cf6da 100644 --- a/tests/functional/watcherapi_controller_test.go +++ b/tests/functional/watcherapi_controller_test.go @@ -9,6 +9,7 @@ import ( //revive:disable-next-line:dot-imports condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" watcherv1beta1 "github.com/openstack-k8s-operators/watcher-operator/api/v1beta1" corev1 "k8s.io/api/core/v1" @@ -174,3 +175,22 @@ var _ = Describe("WatcherAPI controller", func() { }) }) }) + +var _ = Describe("WatcherAPI controller", func() { + BeforeEach(func() { + mariadb.CreateMariaDBDatabase(watcherTest.WatcherDatabaseName.Namespace, watcherTest.WatcherDatabaseName.Name, mariadbv1.MariaDBDatabaseSpec{}) + DeferCleanup(k8sClient.Delete, ctx, mariadb.GetMariaDBDatabase(watcherTest.WatcherDatabaseName)) + }) + When("WatcherAPI CR is created without container image defined", func() { + BeforeEach(func() { + spec := GetDefaultWatcherAPISpec() + spec["containerImage"] = "" + api := CreateWatcherAPI(watcherTest.Instance, spec) + DeferCleanup(th.DeleteInstance, api) + }) + It("has the expected container image default", func() { + WatcherAPI := GetWatcherAPI(watcherTest.Instance) + Expect(WatcherAPI.Spec.ContainerImage).To(Equal(util.GetEnvVar("RELATED_IMAGE_WATCHER_API_IMAGE_URL_DEFAULT", watcherv1beta1.WatcherAPIContainerImage))) + }) + }) +}) diff --git a/tests/kuttl/test-suites/default/watcher/01-assert.yaml b/tests/kuttl/test-suites/default/watcher/01-assert.yaml index 8c67fc8..f25c768 100644 --- a/tests/kuttl/test-suites/default/watcher/01-assert.yaml +++ b/tests/kuttl/test-suites/default/watcher/01-assert.yaml @@ -89,6 +89,7 @@ metadata: name: rabbitmq-transport-url-watcher-kuttl-watcher-transport namespace: watcher-kuttl-default --- +<<<<<<< HEAD apiVersion: keystone.openstack.org/v1beta1 kind: KeystoneService metadata: @@ -114,3 +115,30 @@ commands: oc exec -n watcher-kuttl-default openstackclient -- openstack service list -f value -c Name -c Type |[ $(grep -c ^watcher) == 1 ] SERVICEID=$(oc exec -n watcher-kuttl-default openstackclient -- openstack service list -f value -c Name -c Type -c ID | grep watcher| awk '{print $1}') [ $(oc get -n watcher-kuttl-default keystoneservice watcher -o jsonpath={.status.serviceID}) == $SERVICEID ] +--- +# when using image digests the containerImage URLs are SHA's so we verify them with a script +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: + - script: | + tupleTemplate='{{ range (index .spec.template.spec.containers 1).env }}{{ .name }}{{ "#" }}{{ .value}}{{"\n"}}{{ end }}' + imageTuples=$(oc get -n watcher-kuttl-default deployment watcher-operator-controller-manager -o go-template="$tupleTemplate") + # format of imageTuple is: RELATED_IMAGE_WATCHER_# separated by newlines + for ITEM in $(echo $imageTuples); do + # it is an image + if echo $ITEM | grep 'RELATED_IMAGE' &> /dev/null; then + NAME=$(echo $ITEM | sed -e 's|^RELATED_IMAGE_WATCHER_\([^_]*\)_.*|\1|') + IMG_FROM_ENV=$(echo $ITEM | sed -e 's|^.*#\(.*\)|\1|') + template='{{.spec.containerImage}}' + case $NAME in + API) + SERVICE_IMAGE=$(oc get -n $NAMESPACE watcherapi watcher-api -o go-template="$template") + ;; + esac + if [ "$SERVICE_IMAGE" != "$IMG_FROM_ENV" ]; then + echo "$NAME image does not equal $VALUE" + exit 1 + fi + fi + done + exit 0