diff --git a/api/bases/watcher.openstack.org_watcherappliers.yaml b/api/bases/watcher.openstack.org_watcherappliers.yaml index 4b6e01a1..a44661ec 100644 --- a/api/bases/watcher.openstack.org_watcherappliers.yaml +++ b/api/bases/watcher.openstack.org_watcherappliers.yaml @@ -43,13 +43,6 @@ spec: description: The service specific Container Image URL (will be set to environmental default if empty) type: string - nodeSelector: - additionalProperties: - type: string - description: |- - NodeSelector to target subset of worker nodes running this component. Setting here overrides - any global NodeSelector settings within the Watcher CR. - type: object replicas: default: 1 description: Replicas of Watcher service to run diff --git a/api/bases/watcher.openstack.org_watcherdecisionengines.yaml b/api/bases/watcher.openstack.org_watcherdecisionengines.yaml index 9e03459f..acfcb9f6 100644 --- a/api/bases/watcher.openstack.org_watcherdecisionengines.yaml +++ b/api/bases/watcher.openstack.org_watcherdecisionengines.yaml @@ -44,13 +44,6 @@ spec: description: The service specific Container Image URL (will be set to environmental default if empty) type: string - nodeSelector: - additionalProperties: - type: string - description: |- - NodeSelector to target subset of worker nodes running this component. Setting here overrides - any global NodeSelector settings within the Watcher CR. - type: object replicas: default: 1 description: Replicas of Watcher service to run diff --git a/api/bases/watcher.openstack.org_watchers.yaml b/api/bases/watcher.openstack.org_watchers.yaml index ee557748..d22ad36a 100644 --- a/api/bases/watcher.openstack.org_watchers.yaml +++ b/api/bases/watcher.openstack.org_watchers.yaml @@ -42,6 +42,83 @@ spec: apiContainerImageURL: description: APIContainerImageURL type: string + apiServiceTemplate: + default: + replicas: 1 + description: APIServiceTemplate - define the watcher-api service + properties: + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this component. Setting here overrides + any global NodeSelector settings within the Watcher CR. + type: object + replicas: + default: 1 + description: Replicas of Watcher service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + type: object applierContainerImageURL: description: ApplierContainerImageURL type: string @@ -63,6 +140,13 @@ spec: description: MemcachedInstance is the name of the Memcached CR that all watcher service will use. type: string + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this component. Setting here overrides + any global NodeSelector settings within the Watcher CR. + type: object passwordSelectors: default: service: WatcherPassword @@ -97,6 +181,7 @@ spec: type: string required: - apiContainerImageURL + - apiServiceTemplate - applierContainerImageURL - databaseInstance - decisionengineContainerImageURL @@ -105,6 +190,11 @@ spec: status: description: WatcherStatus defines the observed state of Watcher properties: + apiServiceReadyCount: + description: APIServiceReadyCount defines the number or replicas ready + from watcher-api + format: int32 + type: integer conditions: description: Conditions items: diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index f7c9cca9..26cf138e 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -50,6 +50,11 @@ type WatcherCommon struct { // +kubebuilder:default=false // PreserveJobs - do not delete jobs after they finished e.g. to check logs PreserveJobs bool `json:"preserveJobs"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this component. Setting here overrides + // any global NodeSelector settings within the Watcher CR. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` } // WatcherTemplate defines the fields used in the top level CR @@ -78,6 +83,11 @@ type WatcherTemplate struct { // +kubebuilder:default=watcher // DatabaseAccount - MariaDBAccount CR name used for watcher DB, defaults to watcher DatabaseAccount string `json:"databaseAccount"` + + // +kubebuilder:validation:Required + // +kubebuilder:default={replicas:1} + // APIServiceTemplate - define the watcher-api service + APIServiceTemplate WatcherAPITemplate `json:"apiServiceTemplate"` } // PasswordSelector to identify the DB and AdminUser password from the Secret @@ -94,11 +104,6 @@ type WatcherSubCrsCommon struct { // The service specific Container Image URL (will be set to environmental default if empty) ContainerImage string `json:"containerImage"` - // +kubebuilder:validation:Optional - // NodeSelector to target subset of worker nodes running this component. Setting here overrides - // any global NodeSelector settings within the Watcher CR. - NodeSelector *map[string]string `json:"nodeSelector,omitempty"` - // +kubebuilder:validation:Optional // +kubebuilder:default=1 // +kubebuilder:validation:Maximum=32 @@ -117,6 +122,27 @@ type WatcherSubCrsCommon struct { ServiceAccount string `json:"serviceAccount"` } +// WatcherSubCrsTemplate define de common part of the input parameters specified by the user to +// create a 2nd CR via higher level CRDs. +type WatcherSubCrsTemplate struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of Watcher service to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this component. Setting here overrides + // any global NodeSelector settings within the Watcher CR. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` +} + type WatcherImages struct { // +kubebuilder:validation:Required // APIContainerImageURL diff --git a/api/v1beta1/conditions.go b/api/v1beta1/conditions.go index 20b1e982..3beed3f0 100644 --- a/api/v1beta1/conditions.go +++ b/api/v1beta1/conditions.go @@ -5,6 +5,8 @@ import "github.com/openstack-k8s-operators/lib-common/modules/common/condition" const ( // WatcherRabbitMQTransportURLReadyCondition - WatcherRabbitMQTransportURLReadyCondition condition.Type = "WatcherRabbitMQTransportURLReady" + // WatcherAPIReadyCondition - + WatcherAPIReadyCondition condition.Type = "WatcherAPIReady" ) const ( @@ -14,4 +16,12 @@ const ( WatcherRabbitMQTransportURLReadyMessage = "WatcherRabbitMQTransportURL successfully created" // WatcherRabbitMQTransportURLReadyErrorMessage - WatcherRabbitMQTransportURLReadyErrorMessage = "WatcherRabbitMQTransportURL error occured %s" + // WatcherAPIReadyInitMessage - + WatcherAPIReadyInitMessage = "WatcherAPI creation not started" + // WatcherAPIReadyRunningMessage - + WatcherAPIReadyRunningMessage = "WatcherAPI creation in progress" + // WatcherAPIReadyMessage - + WatcherAPIReadyMessage = "WatcherAPI successfully created" + // WatcherAPIReadyErrorMessage - + WatcherAPIReadyErrorMessage = "WatcherAPI error occured %s" ) diff --git a/api/v1beta1/watcher_types.go b/api/v1beta1/watcher_types.go index ae36b54e..a50cc23f 100644 --- a/api/v1beta1/watcher_types.go +++ b/api/v1beta1/watcher_types.go @@ -52,6 +52,9 @@ type WatcherStatus struct { // then the controller has not processed the latest changes injected by // the opentack-operator in the top-level CR (e.g. the ContainerImage) ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // APIServiceReadyCount defines the number or replicas ready from watcher-api + APIServiceReadyCount int32 `json:"apiServiceReadyCount,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1beta1/watcherapi_types.go b/api/v1beta1/watcherapi_types.go index 2b6b9da3..c7e08173 100644 --- a/api/v1beta1/watcherapi_types.go +++ b/api/v1beta1/watcherapi_types.go @@ -53,6 +53,12 @@ type WatcherAPIStatus struct { Hash map[string]string `json:"hash,omitempty"` } +// WatcherAPITemplate defines the input parameters specified by the user to +// create a WatcherAPI via higher level CRDs. +type WatcherAPITemplate struct { + WatcherSubCrsTemplate `json:",inline"` +} + //+kubebuilder:object:root=true //+kubebuilder:subresource:status diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 768464b0..51e3612b 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -45,7 +45,7 @@ func (in *Watcher) DeepCopyInto(out *Watcher) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -129,7 +129,7 @@ func (in *WatcherAPIList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherAPISpec) DeepCopyInto(out *WatcherAPISpec) { *out = *in - out.WatcherCommon = in.WatcherCommon + in.WatcherCommon.DeepCopyInto(&out.WatcherCommon) in.WatcherSubCrsCommon.DeepCopyInto(&out.WatcherSubCrsCommon) } @@ -172,6 +172,22 @@ func (in *WatcherAPIStatus) DeepCopy() *WatcherAPIStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WatcherAPITemplate) DeepCopyInto(out *WatcherAPITemplate) { + *out = *in + in.WatcherSubCrsTemplate.DeepCopyInto(&out.WatcherSubCrsTemplate) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherAPITemplate. +func (in *WatcherAPITemplate) DeepCopy() *WatcherAPITemplate { + if in == nil { + return nil + } + out := new(WatcherAPITemplate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherApplier) DeepCopyInto(out *WatcherApplier) { *out = *in @@ -266,6 +282,17 @@ func (in *WatcherApplierStatus) DeepCopy() *WatcherApplierStatus { func (in *WatcherCommon) DeepCopyInto(out *WatcherCommon) { *out = *in out.PasswordSelectors = in.PasswordSelectors + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *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 WatcherCommon. @@ -433,7 +460,7 @@ func (in *WatcherList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherSpec) DeepCopyInto(out *WatcherSpec) { *out = *in - out.WatcherTemplate = in.WatcherTemplate + in.WatcherTemplate.DeepCopyInto(&out.WatcherTemplate) out.WatcherImages = in.WatcherImages } @@ -479,6 +506,33 @@ func (in *WatcherStatus) DeepCopy() *WatcherStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherSubCrsCommon) DeepCopyInto(out *WatcherSubCrsCommon) { *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherSubCrsCommon. +func (in *WatcherSubCrsCommon) DeepCopy() *WatcherSubCrsCommon { + if in == nil { + return nil + } + out := new(WatcherSubCrsCommon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WatcherSubCrsTemplate) DeepCopyInto(out *WatcherSubCrsTemplate) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + in.Resources.DeepCopyInto(&out.Resources) if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = new(map[string]string) @@ -490,20 +544,14 @@ func (in *WatcherSubCrsCommon) DeepCopyInto(out *WatcherSubCrsCommon) { } } } - if in.Replicas != nil { - in, out := &in.Replicas, &out.Replicas - *out = new(int32) - **out = **in - } - in.Resources.DeepCopyInto(&out.Resources) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherSubCrsCommon. -func (in *WatcherSubCrsCommon) DeepCopy() *WatcherSubCrsCommon { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherSubCrsTemplate. +func (in *WatcherSubCrsTemplate) DeepCopy() *WatcherSubCrsTemplate { if in == nil { return nil } - out := new(WatcherSubCrsCommon) + out := new(WatcherSubCrsTemplate) in.DeepCopyInto(out) return out } @@ -511,7 +559,8 @@ func (in *WatcherSubCrsCommon) DeepCopy() *WatcherSubCrsCommon { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WatcherTemplate) DeepCopyInto(out *WatcherTemplate) { *out = *in - out.WatcherCommon = in.WatcherCommon + in.WatcherCommon.DeepCopyInto(&out.WatcherCommon) + in.APIServiceTemplate.DeepCopyInto(&out.APIServiceTemplate) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherTemplate. diff --git a/config/crd/bases/watcher.openstack.org_watcherappliers.yaml b/config/crd/bases/watcher.openstack.org_watcherappliers.yaml index 4b6e01a1..a44661ec 100644 --- a/config/crd/bases/watcher.openstack.org_watcherappliers.yaml +++ b/config/crd/bases/watcher.openstack.org_watcherappliers.yaml @@ -43,13 +43,6 @@ spec: description: The service specific Container Image URL (will be set to environmental default if empty) type: string - nodeSelector: - additionalProperties: - type: string - description: |- - NodeSelector to target subset of worker nodes running this component. Setting here overrides - any global NodeSelector settings within the Watcher CR. - type: object replicas: default: 1 description: Replicas of Watcher service to run diff --git a/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml b/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml index 9e03459f..acfcb9f6 100644 --- a/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml +++ b/config/crd/bases/watcher.openstack.org_watcherdecisionengines.yaml @@ -44,13 +44,6 @@ spec: description: The service specific Container Image URL (will be set to environmental default if empty) type: string - nodeSelector: - additionalProperties: - type: string - description: |- - NodeSelector to target subset of worker nodes running this component. Setting here overrides - any global NodeSelector settings within the Watcher CR. - type: object replicas: default: 1 description: Replicas of Watcher service to run diff --git a/config/crd/bases/watcher.openstack.org_watchers.yaml b/config/crd/bases/watcher.openstack.org_watchers.yaml index ee557748..d22ad36a 100644 --- a/config/crd/bases/watcher.openstack.org_watchers.yaml +++ b/config/crd/bases/watcher.openstack.org_watchers.yaml @@ -42,6 +42,83 @@ spec: apiContainerImageURL: description: APIContainerImageURL type: string + apiServiceTemplate: + default: + replicas: 1 + description: APIServiceTemplate - define the watcher-api service + properties: + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this component. Setting here overrides + any global NodeSelector settings within the Watcher CR. + type: object + replicas: + default: 1 + description: Replicas of Watcher service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + type: object applierContainerImageURL: description: ApplierContainerImageURL type: string @@ -63,6 +140,13 @@ spec: description: MemcachedInstance is the name of the Memcached CR that all watcher service will use. type: string + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this component. Setting here overrides + any global NodeSelector settings within the Watcher CR. + type: object passwordSelectors: default: service: WatcherPassword @@ -97,6 +181,7 @@ spec: type: string required: - apiContainerImageURL + - apiServiceTemplate - applierContainerImageURL - databaseInstance - decisionengineContainerImageURL @@ -105,6 +190,11 @@ spec: status: description: WatcherStatus defines the observed state of Watcher properties: + apiServiceReadyCount: + description: APIServiceReadyCount defines the number or replicas ready + from watcher-api + format: int32 + type: integer conditions: description: Conditions items: diff --git a/controllers/watcher_controller.go b/controllers/watcher_controller.go index 4a40d498..50f1e448 100644 --- a/controllers/watcher_controller.go +++ b/controllers/watcher_controller.go @@ -289,6 +289,15 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re return ctrlResult, nil } + // Create Watcher API + _, _, err = r.ensureAPI(ctx, instance) + + if err != nil { + return ctrl.Result{}, err + } + + // End of Watcher API creation + // // remove finalizers from unused MariaDBAccount records // this assumes all database-depedendent deployments are up and @@ -374,6 +383,10 @@ func (r *WatcherReconciler) initConditions(instance *watcherv1beta1.Watcher) err condition.DBSyncReadyCondition, condition.InitReason, condition.DBSyncReadyInitMessage), + condition.UnknownCondition( + watcherv1beta1.WatcherAPIReadyCondition, + condition.InitReason, + watcherv1beta1.WatcherAPIReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -730,6 +743,79 @@ func (r *WatcherReconciler) createSubLevelSecret( return secretName, err } +func (r *WatcherReconciler) ensureAPI( + ctx context.Context, + instance *watcherv1beta1.Watcher, +) (*watcherv1beta1.WatcherAPI, controllerutil.OperationResult, error) { + Log := r.GetLogger(ctx) + Log.Info(fmt.Sprintf("Creating WatcherAPI '%s'", instance.Name)) + + watcherAPISpec := watcherv1beta1.WatcherAPISpec{ + Secret: instance.Name, + WatcherCommon: watcherv1beta1.WatcherCommon{ + ServiceUser: instance.Spec.ServiceUser, + PasswordSelectors: instance.Spec.PasswordSelectors, + MemcachedInstance: instance.Spec.MemcachedInstance, + NodeSelector: instance.Spec.APIServiceTemplate.NodeSelector, + PreserveJobs: instance.Spec.PreserveJobs, + }, + WatcherSubCrsCommon: watcherv1beta1.WatcherSubCrsCommon{ + ContainerImage: instance.Spec.APIContainerImageURL, + Replicas: instance.Spec.APIServiceTemplate.Replicas, + Resources: instance.Spec.APIServiceTemplate.Resources, + ServiceAccount: "watcher-" + instance.Name, + }, + } + + // If NodeSelector is not specified in Watcher APIServiceTemplate, the current + // API instance inherits the value from the top-level Watcher CR. + if watcherAPISpec.NodeSelector == nil { + watcherAPISpec.NodeSelector = instance.Spec.NodeSelector + } + + apiDeployment := &watcherv1beta1.WatcherAPI{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-api", instance.Name), + Namespace: instance.Namespace, + }, + } + + op, err := controllerutil.CreateOrPatch(ctx, r.Client, apiDeployment, func() error { + apiDeployment.Spec = watcherAPISpec + err := controllerutil.SetControllerReference(instance, apiDeployment, r.Scheme) + if err != nil { + return err + } + return nil + }) + + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + watcherv1beta1.WatcherAPIReadyCondition, + condition.ErrorReason, + condition.SeverityError, + watcherv1beta1.WatcherAPIReadyErrorMessage, + err.Error())) + return nil, op, err + } + if op != controllerutil.OperationResultNone { + Log.Info(fmt.Sprintf("WatcherAPI %s , WatcherPI.Name %s.", string(op), apiDeployment.Name)) + } + + if apiDeployment.Generation == apiDeployment.Status.ObservedGeneration { + c := apiDeployment.Status.Conditions.Mirror(watcherv1beta1.WatcherAPIReadyCondition) + // NOTE(gibi): it can be nil if the WatcherAPI CR is created but no + // reconciliation is run on it to initialize the ReadyCondition yet. + if c != nil { + instance.Status.Conditions.Set(c) + } + instance.Status.APIServiceReadyCount = apiDeployment.Status.ReadyCount + } + + return apiDeployment, op, nil + +} + func (r *WatcherReconciler) reconcileDelete(ctx context.Context, instance *watcherv1beta1.Watcher, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) Log.Info(fmt.Sprintf("Reconcile Service '%s' delete started", instance.Name)) diff --git a/tests/functional/base_test.go b/tests/functional/base_test.go index ef6a4317..459199f0 100644 --- a/tests/functional/base_test.go +++ b/tests/functional/base_test.go @@ -39,10 +39,15 @@ func GetDefaultWatcherSpec() map[string]interface{} { // Second Watcher Spec to test proper parameters substitution func GetNonDefaultWatcherSpec() map[string]interface{} { return map[string]interface{}{ - "secret": SecretName, - "preserveJobs": true, - "databaseInstance": "fakeopenstack", - "serviceUser": "fakeuser", + "apiContainerImageURL": "fake-API-Container-URL", + "secret": SecretName, + "preserveJobs": true, + "databaseInstance": "fakeopenstack", + "serviceUser": "fakeuser", + "apiServiceTemplate": map[string]interface{}{ + "replicas": 2, + "nodeSelector": map[string]string{"foo": "bar"}, + }, } } diff --git a/tests/functional/watcher_controller_test.go b/tests/functional/watcher_controller_test.go index 2b28ac85..4af5edae 100644 --- a/tests/functional/watcher_controller_test.go +++ b/tests/functional/watcher_controller_test.go @@ -8,6 +8,7 @@ import ( //revive:disable-next-line:dot-imports "os" + memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" 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" @@ -16,6 +17,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -180,10 +182,17 @@ var _ = Describe("Watcher controller", func() { }) - When("Watcher DB and RabbitMQ are created", func() { + When("Watcher is created with default Spec", func() { BeforeEach(func() { DeferCleanup(th.DeleteInstance, CreateWatcher(watcherTest.Instance, GetDefaultWatcherSpec())) DeferCleanup(k8sClient.Delete, ctx, CreateWatcherMessageBusSecret(watcherTest.Instance.Namespace, "rabbitmq-secret")) + memcachedSpec := memcachedv1.MemcachedSpec{ + MemcachedSpecCore: memcachedv1.MemcachedSpecCore{ + Replicas: ptr.To(int32(1)), + }, + } + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(watcherTest.Watcher.Namespace, MemcachedInstance, memcachedSpec)) + infra.SimulateMemcachedReady(watcherTest.MemcachedNamespace) DeferCleanup( mariadb.DeleteDBService, mariadb.CreateDBService( @@ -194,6 +203,7 @@ var _ = Describe("Watcher controller", func() { }, ), ) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(watcherTest.WatcherAPI.Namespace)) }) It("Should set DBReady Condition Status when DB is Created", func() { @@ -240,6 +250,9 @@ var _ = Describe("Watcher controller", func() { th.SimulateJobSuccess(watcherTest.WatcherDBSync) // We validate the full Watcher CR readiness status here // DB Ready + + // Simulate WatcherAPI deployment + th.SimulateDeploymentReplicaReady(watcherTest.WatcherAPIDeployment) th.ExpectCondition( watcherTest.Instance, ConditionGetterFunc(WatcherConditionGetter), @@ -291,6 +304,14 @@ var _ = Describe("Watcher controller", func() { corev1.ConditionTrue, ) + // Get WatcherAPI Ready condition + th.ExpectCondition( + watcherTest.Instance, + ConditionGetterFunc(WatcherConditionGetter), + watcherv1beta1.WatcherAPIReadyCondition, + corev1.ConditionTrue, + ) + // Global status Ready th.ExpectCondition( watcherTest.Instance, @@ -319,6 +340,22 @@ var _ = Describe("Watcher controller", func() { Expect(createdSecret.Data["WatcherPassword"]).To(Equal([]byte("password"))) Expect(createdSecret.Data["transport_url"]).To(Equal([]byte("rabbit://rabbitmq-secret/fake"))) + // Check WatcherAPI is created + WatcherAPI := GetWatcherAPI(watcherTest.WatcherAPI) + //Expect(WatcherAPI.Spec.Replicas).To(Equal(int(1))) + Expect(WatcherAPI.Spec.ContainerImage).To(Equal(watcherv1beta1.WatcherAPIContainerImage)) + Expect(WatcherAPI.Spec.Secret).To(Equal("watcher")) + Expect(WatcherAPI.Spec.ServiceAccount).To(Equal("watcher-watcher")) + Expect(int(*WatcherAPI.Spec.Replicas)).To(Equal(1)) + Expect(WatcherAPI.Spec.NodeSelector).To(BeNil()) + + // Assert that the watcher deployment is created + deployment := th.GetDeployment(watcherTest.WatcherAPIDeployment) + Expect(deployment.Spec.Template.Spec.ServiceAccountName).To(Equal("watcher-watcher")) + Expect(int(*deployment.Spec.Replicas)).To(Equal(1)) + Expect(deployment.Spec.Template.Spec.Volumes).To(HaveLen(3)) + Expect(deployment.Spec.Template.Spec.Containers).To(HaveLen(2)) + Expect(deployment.Spec.Selector.MatchLabels).To(Equal(map[string]string{"service": "watcher-api"})) }) It("Should fail to register watcher service to keystone when has not the expected secret", func() { @@ -498,6 +535,13 @@ var _ = Describe("Watcher controller", func() { BeforeEach(func() { DeferCleanup(th.DeleteInstance, CreateWatcher(watcherTest.Instance, GetNonDefaultWatcherSpec())) DeferCleanup(k8sClient.Delete, ctx, CreateWatcherMessageBusSecret(watcherTest.Instance.Namespace, "rabbitmq-secret")) + memcachedSpec := memcachedv1.MemcachedSpec{ + MemcachedSpecCore: memcachedv1.MemcachedSpecCore{ + Replicas: ptr.To(int32(1)), + }, + } + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(watcherTest.Watcher.Namespace, MemcachedInstance, memcachedSpec)) + infra.SimulateMemcachedReady(watcherTest.MemcachedNamespace) DeferCleanup( mariadb.DeleteDBService, mariadb.CreateDBService( @@ -508,6 +552,7 @@ var _ = Describe("Watcher controller", func() { }, ), ) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(watcherTest.WatcherAPI.Namespace)) }) It("should have the Spec fields with the expected values", func() { @@ -538,6 +583,10 @@ var _ = Describe("Watcher controller", func() { // Simulate dbsync success th.SimulateJobSuccess(watcherTest.WatcherDBSync) + + // Simulate WatcherAPI deployment + th.SimulateDeploymentReplicaReady(watcherTest.WatcherAPIDeployment) + // We validate the full Watcher CR readiness status here // DB Ready th.ExpectCondition( @@ -591,6 +640,14 @@ var _ = Describe("Watcher controller", func() { corev1.ConditionTrue, ) + // Get WatcherAPI Ready condition + th.ExpectCondition( + watcherTest.Instance, + ConditionGetterFunc(WatcherConditionGetter), + watcherv1beta1.WatcherAPIReadyCondition, + corev1.ConditionTrue, + ) + // Global status Ready th.ExpectCondition( watcherTest.Instance, @@ -623,6 +680,23 @@ var _ = Describe("Watcher controller", func() { Watcher := GetWatcher(watcherTest.Instance) Expect(Watcher.Status.Hash[watcherv1beta1.DbSyncHash]).ShouldNot(BeNil()) + // Check WatcherAPI is created with non-default values + WatcherAPI := GetWatcherAPI(watcherTest.WatcherAPI) + //Expect(WatcherAPI.Spec.Replicas).To(Equal(int(1))) + Expect(WatcherAPI.Spec.ContainerImage).To(Equal("fake-API-Container-URL")) + Expect(WatcherAPI.Spec.Secret).To(Equal("watcher")) + Expect(WatcherAPI.Spec.ServiceAccount).To(Equal("watcher-watcher")) + Expect(int(*WatcherAPI.Spec.Replicas)).To(Equal(2)) + Expect(*WatcherAPI.Spec.NodeSelector).To(Equal(map[string]string{"foo": "bar"})) + + // Assert that the watcher deployment is created + deployment := th.GetDeployment(watcherTest.WatcherAPIDeployment) + Expect(deployment.Spec.Template.Spec.ServiceAccountName).To(Equal("watcher-watcher")) + Expect(int(*deployment.Spec.Replicas)).To(Equal(2)) + Expect(deployment.Spec.Template.Spec.Volumes).To(HaveLen(3)) + Expect(deployment.Spec.Template.Spec.Containers).To(HaveLen(2)) + Expect(deployment.Spec.Selector.MatchLabels).To(Equal(map[string]string{"service": "watcher-api"})) + }) }) diff --git a/tests/kuttl/test-suites/default/common/deploy-with-defaults.yaml b/tests/kuttl/test-suites/default/common/deploy-with-defaults.yaml index 3b969bae..b008f3ff 100644 --- a/tests/kuttl/test-suites/default/common/deploy-with-defaults.yaml +++ b/tests/kuttl/test-suites/default/common/deploy-with-defaults.yaml @@ -5,3 +5,6 @@ metadata: namespace: watcher-kuttl-default spec: databaseInstance: "openstack" + apiContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-api:current-podified" + decisionengineContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-decision-engine:current-podified" + applierContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-applier:current-podified" diff --git a/tests/kuttl/test-suites/default/watcher-api/00-cleanup-watcher.yaml b/tests/kuttl/test-suites/default/watcher-api/00-cleanup-watcher.yaml deleted file mode 120000 index 92ed6e0b..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/00-cleanup-watcher.yaml +++ /dev/null @@ -1 +0,0 @@ -../common/cleanup-watcher.yaml \ No newline at end of file diff --git a/tests/kuttl/test-suites/default/watcher-api/01-cleanup-watcherapi.yaml b/tests/kuttl/test-suites/default/watcher-api/01-cleanup-watcherapi.yaml deleted file mode 100644 index f78c95e3..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/01-cleanup-watcherapi.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: kuttl.dev/v1beta1 -kind: TestStep -delete: -- apiVersion: watcher.openstack.org/v1beta1 - kind: WatcherAPI - name: watcherapi-kuttl -- apiVersion: v1 - kind: Secret - name: watcherapi-secret diff --git a/tests/kuttl/test-suites/default/watcher-api/02-deploy-with-defaults.yaml b/tests/kuttl/test-suites/default/watcher-api/02-deploy-with-defaults.yaml deleted file mode 120000 index ecf8d373..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/02-deploy-with-defaults.yaml +++ /dev/null @@ -1 +0,0 @@ -../common/deploy-with-defaults.yaml \ No newline at end of file diff --git a/tests/kuttl/test-suites/default/watcher-api/03-assert.yaml b/tests/kuttl/test-suites/default/watcher-api/03-assert.yaml deleted file mode 100644 index 4bda4a60..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/03-assert.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: watcher.openstack.org/v1beta1 -kind: WatcherAPI -metadata: - finalizers: - - openstack.org/watcherapi - name: watcherapi-kuttl -spec: - passwordSelectors: - service: WatcherPassword - secret: watcher-kuttl -status: - conditions: - - message: Setup complete - reason: Ready - status: "True" - type: Ready - - message: Deployment completed - reason: Ready - status: "True" - type: DeploymentReady - - message: Input data complete - reason: Ready - status: "True" - type: InputReady - - message: " Memcached instance has been provisioned" - reason: Ready - status: "True" - type: MemcachedReady - - message: Service config create completed - reason: Ready - status: "True" - type: ServiceConfigReady ---- -apiVersion: v1 -kind: Secret -metadata: - name: watcher-kuttl -type: Opaque ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: watcherapi-kuttl-api - labels: - service: watcher-api -spec: - replicas: 1 - template: - spec: - containers: - - name: watcherapi-kuttl-log - - name: watcher-api -status: - readyReplicas: 1 - replicas: 1 ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - service: watcher-api -spec: - containers: - - name: watcherapi-kuttl-log - - name: watcher-api -status: - phase: Running diff --git a/tests/kuttl/test-suites/default/watcher-api/03-deploy-watcher-api.yaml b/tests/kuttl/test-suites/default/watcher-api/03-deploy-watcher-api.yaml deleted file mode 100644 index fcd45859..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/03-deploy-watcher-api.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: watcher.openstack.org/v1beta1 -kind: WatcherAPI -metadata: - name: watcherapi-kuttl -spec: - secret: watcher-kuttl - memcachedInstance: "memcached" - containerImage: "quay.io/podified-master-centos9/openstack-watcher-api:current-podified" - serviceAccount: watcher-watcher-kuttl diff --git a/tests/kuttl/test-suites/default/watcher-api/04-cleanup-watcher.yaml b/tests/kuttl/test-suites/default/watcher-api/04-cleanup-watcher.yaml deleted file mode 120000 index 92ed6e0b..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/04-cleanup-watcher.yaml +++ /dev/null @@ -1 +0,0 @@ -../common/cleanup-watcher.yaml \ No newline at end of file diff --git a/tests/kuttl/test-suites/default/watcher-api/05-assert.yaml b/tests/kuttl/test-suites/default/watcher-api/05-assert.yaml deleted file mode 100644 index 6de52af3..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/05-assert.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -namespaced: true -commands: - - script: | - set -ex - oc get mariadbaccount -n ${NAMESPACE} --no-headers=true | [ $(grep -c ^watcher) == 0 ] - oc get mariadbdatabase -n ${NAMESPACE} --no-headers=true | [ $(grep -c ^watcher) == 0 ] - oc get secret -n ${NAMESPACE} --no-headers=true | [ $(grep -c ^watcher) == 0 ] diff --git a/tests/kuttl/test-suites/default/watcher-api/05-cleanup-watcherapi.yaml b/tests/kuttl/test-suites/default/watcher-api/05-cleanup-watcherapi.yaml deleted file mode 120000 index 40ff4783..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/05-cleanup-watcherapi.yaml +++ /dev/null @@ -1 +0,0 @@ -01-cleanup-watcherapi.yaml \ No newline at end of file diff --git a/tests/kuttl/test-suites/default/watcher-api/05-errors.yaml b/tests/kuttl/test-suites/default/watcher-api/05-errors.yaml deleted file mode 100644 index 672ed5d8..00000000 --- a/tests/kuttl/test-suites/default/watcher-api/05-errors.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: watcher.openstack.org/v1beta1 -kind: WatcherAPI -metadata: - name: watcherapi-kuttl ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: watcherapi-kuttl-api ---- -apiVersion: v1 -kind: Pod -metadata: - labels: - service: watcher-api diff --git a/tests/kuttl/test-suites/default/watcher/01-assert.yaml b/tests/kuttl/test-suites/default/watcher/01-assert.yaml index 4a3eeedb..8837c8ab 100644 --- a/tests/kuttl/test-suites/default/watcher/01-assert.yaml +++ b/tests/kuttl/test-suites/default/watcher/01-assert.yaml @@ -6,9 +6,9 @@ metadata: name: watcher-kuttl namespace: watcher-kuttl-default spec: - apiContainerImageURL: "quay.io/podified-antelope-centos9/openstack-watcher-api:current-podified" - decisionengineContainerImageURL: "quay.io/podified-antelope-centos9/openstack-watcher-decision-engine:current-podified" - applierContainerImageURL: "quay.io/podified-antelope-centos9/openstack-watcher-applier:current-podified" + apiContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-api:current-podified" + decisionengineContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-decision-engine:current-podified" + applierContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-applier:current-podified" databaseAccount: watcher databaseInstance: openstack passwordSelectors: @@ -17,7 +17,11 @@ spec: rabbitMqClusterName: rabbitmq secret: osp-secret serviceUser: watcher + apiServiceTemplate: + replicas: 1 + resources: {} status: + apiServiceReadyCount: 1 conditions: - message: Setup complete reason: Ready @@ -59,6 +63,10 @@ status: reason: Ready status: "True" type: ServiceConfigReady + - message: Setup complete + reason: Ready + status: "True" + type: WatcherAPIReady - message: WatcherRabbitMQTransportURL successfully created reason: Ready status: "True" @@ -173,6 +181,75 @@ kind: Secret metadata: name: watcher-kuttl-config-data --- +apiVersion: watcher.openstack.org/v1beta1 +kind: WatcherAPI +metadata: + finalizers: + - openstack.org/watcherapi + name: watcher-kuttl-api +spec: + containerImage: quay.io/podified-master-centos9/openstack-watcher-api:current-podified + memcachedInstance: memcached + passwordSelectors: + service: WatcherPassword + preserveJobs: false + replicas: 1 + resources: {} + secret: watcher-kuttl + serviceAccount: watcher-watcher-kuttl + serviceUser: watcher +status: + conditions: + - message: Setup complete + reason: Ready + status: "True" + type: Ready + - message: Deployment completed + reason: Ready + status: "True" + type: DeploymentReady + - message: Input data complete + reason: Ready + status: "True" + type: InputReady + - message: " Memcached instance has been provisioned" + reason: Ready + status: "True" + type: MemcachedReady + - message: Service config create completed + reason: Ready + status: "True" + type: ServiceConfigReady +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: watcher-kuttl-api-api + labels: + service: watcher-api +spec: + replicas: 1 + template: + spec: + containers: + - name: watcher-kuttl-api-log + - name: watcher-api +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: v1 +kind: Pod +metadata: + labels: + service: watcher-api +spec: + containers: + - name: watcher-kuttl-api-log + - name: watcher-api +status: + phase: Running +--- apiVersion: kuttl.dev/v1beta1 kind: TestAssert namespaced: true @@ -181,16 +258,9 @@ commands: set -euxo pipefail 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 ] + [ $(oc get -n watcher-kuttl-default keystoneservice watcher -o jsonpath={.status.serviceID}) == ${SERVICEID} ] [ -n "$(oc get -n watcher-kuttl-default watcher watcher-kuttl -o jsonpath={.status.hash.dbsync})" ] ---- -# Check for Container Image environment variables -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: - - script: | - set -euxo pipefail - # If we are running the container locally, skip this test + # If we are running the container locally, skip following test if [ "$(oc get pods -n openstack-operators -o name -l openstack.org/operator-name=watcher)" == "" ]; then exit 0 fi diff --git a/tests/kuttl/test-suites/default/watcher/04-assert.yaml b/tests/kuttl/test-suites/default/watcher/04-assert.yaml index d92296a0..fcbe128c 100644 --- a/tests/kuttl/test-suites/default/watcher/04-assert.yaml +++ b/tests/kuttl/test-suites/default/watcher/04-assert.yaml @@ -11,7 +11,11 @@ spec: passwordSelectors: service: WatcherPassword secret: osp-secret + apiServiceTemplate: + replicas: 2 + resources: {} status: + apiServiceReadyCount: 2 conditions: - message: Setup complete reason: Ready @@ -53,6 +57,10 @@ status: reason: Ready status: "True" type: ServiceConfigReady + - message: Setup complete + reason: Ready + status: "True" + type: WatcherAPIReady - message: WatcherRabbitMQTransportURL successfully created reason: Ready status: "True" @@ -154,3 +162,59 @@ kind: Secret metadata: name: watcher-kuttl-config-data --- +apiVersion: watcher.openstack.org/v1beta1 +kind: WatcherAPI +metadata: + finalizers: + - openstack.org/watcherapi + name: watcher-kuttl-api +spec: + containerImage: quay.io/podified-master-centos9/openstack-watcher-api:current-podified + memcachedInstance: memcached + passwordSelectors: + service: WatcherPassword + preserveJobs: false + replicas: 2 + resources: {} + secret: watcher-kuttl + serviceAccount: watcher-watcher-kuttl + serviceUser: watcher +status: + conditions: + - message: Setup complete + reason: Ready + status: "True" + type: Ready + - message: Deployment completed + reason: Ready + status: "True" + type: DeploymentReady + - message: Input data complete + reason: Ready + status: "True" + type: InputReady + - message: " Memcached instance has been provisioned" + reason: Ready + status: "True" + type: MemcachedReady + - message: Service config create completed + reason: Ready + status: "True" + type: ServiceConfigReady +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: watcher-kuttl-api-api + labels: + service: watcher-api +spec: + replicas: 2 + template: + spec: + containers: + - name: watcher-kuttl-api-log + - name: watcher-api +status: + readyReplicas: 2 + replicas: 2 diff --git a/tests/kuttl/test-suites/default/watcher/04-deploy-with-precreated-account.yaml b/tests/kuttl/test-suites/default/watcher/04-deploy-with-precreated-account.yaml index 6cda9cf9..87b740ec 100644 --- a/tests/kuttl/test-suites/default/watcher/04-deploy-with-precreated-account.yaml +++ b/tests/kuttl/test-suites/default/watcher/04-deploy-with-precreated-account.yaml @@ -6,3 +6,8 @@ metadata: spec: databaseInstance: "openstack" databaseAccount: watcher-precreated + apiContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-api:current-podified" + decisionengineContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-decision-engine:current-podified" + applierContainerImageURL: "quay.io/podified-master-centos9/openstack-watcher-applier:current-podified" + apiServiceTemplate: + replicas: 2 diff --git a/tests/kuttl/test-suites/default/watcher/05-errors.yaml b/tests/kuttl/test-suites/default/watcher/05-errors.yaml index 53702f0a..c891e5c6 100644 --- a/tests/kuttl/test-suites/default/watcher/05-errors.yaml +++ b/tests/kuttl/test-suites/default/watcher/05-errors.yaml @@ -49,3 +49,21 @@ apiVersion: watcher.openstack.org/v1beta1 kind: Watcher metadata: name: watcher-kuttl +--- +apiVersion: watcher.openstack.org/v1beta1 +kind: WatcherAPI +metadata: + finalizers: + - openstack.org/watcherapi + name: watcher-kuttl-api +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: watcher-kuttl-api-api +--- +apiVersion: v1 +kind: Pod +metadata: + labels: + service: watcher-api