diff --git a/api/bases/watcher.openstack.org_watcherapis.yaml b/api/bases/watcher.openstack.org_watcherapis.yaml index 4e57de24..7c40340f 100644 --- a/api/bases/watcher.openstack.org_watcherapis.yaml +++ b/api/bases/watcher.openstack.org_watcherapis.yaml @@ -55,6 +55,51 @@ spec: NodeSelector to target subset of worker nodes running this component. Setting here overrides any global NodeSelector settings within the Watcher CR. type: object + override: + description: |- + Override, provides the ability to override the generated manifest of + several child resources. + properties: + service: + additionalProperties: + description: MetalLBConfig to configure the MetalLB loadbalancer + service + properties: + ipAddressPool: + description: IPAddressPool expose VIP via MetalLB on the + IPAddressPool + minLength: 1 + type: string + loadBalancerIPs: + description: LoadBalancerIPs, request given IPs from the + pool if available. Using a list to allow dual stack (IPv4/IPv6) + support + items: + type: string + type: array + sharedIP: + default: true + description: SharedIP if true, VIP/VIPs get shared with + multiple services + type: boolean + sharedIPKey: + default: "" + description: |- + SharedIPKey specifies the sharing key which gets set as the annotation on the LoadBalancer service. + Services which share the same VIP must have the same SharedIPKey. Defaults to the IPAddressPool if + SharedIP is true, but no SharedIPKey specified. + type: string + required: + - ipAddressPool + type: object + description: |- + Override configuration for the Service created to serve traffic to + the cluster. + The key must be the endpoint type (public, internal) + temporarily use MetalLBConfig struct, later we'll switch to + service.RoutedOverrideSpec + type: object + type: object passwordSelectors: default: service: WatcherPassword diff --git a/api/bases/watcher.openstack.org_watchers.yaml b/api/bases/watcher.openstack.org_watchers.yaml index d22ad36a..949f895a 100644 --- a/api/bases/watcher.openstack.org_watchers.yaml +++ b/api/bases/watcher.openstack.org_watchers.yaml @@ -54,6 +54,51 @@ spec: NodeSelector to target subset of worker nodes running this component. Setting here overrides any global NodeSelector settings within the Watcher CR. type: object + override: + description: |- + Override, provides the ability to override the generated manifest of + several child resources. + properties: + service: + additionalProperties: + description: MetalLBConfig to configure the MetalLB loadbalancer + service + properties: + ipAddressPool: + description: IPAddressPool expose VIP via MetalLB on + the IPAddressPool + minLength: 1 + type: string + loadBalancerIPs: + description: LoadBalancerIPs, request given IPs from + the pool if available. Using a list to allow dual + stack (IPv4/IPv6) support + items: + type: string + type: array + sharedIP: + default: true + description: SharedIP if true, VIP/VIPs get shared with + multiple services + type: boolean + sharedIPKey: + default: "" + description: |- + SharedIPKey specifies the sharing key which gets set as the annotation on the LoadBalancer service. + Services which share the same VIP must have the same SharedIPKey. Defaults to the IPAddressPool if + SharedIP is true, but no SharedIPKey specified. + type: string + required: + - ipAddressPool + type: object + description: |- + Override configuration for the Service created to serve traffic to + the cluster. + The key must be the endpoint type (public, internal) + temporarily use MetalLBConfig struct, later we'll switch to + service.RoutedOverrideSpec + type: object + type: object replicas: default: 1 description: Replicas of Watcher service to run diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index f648a812..963acc6b 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -143,6 +143,30 @@ type WatcherSubCrsTemplate struct { NodeSelector *map[string]string `json:"nodeSelector,omitempty"` } +// MetalLBConfig to configure the MetalLB loadbalancer service +type MetalLBConfig struct { + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinLength=1 + // IPAddressPool expose VIP via MetalLB on the IPAddressPool + IPAddressPool string `json:"ipAddressPool"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=true + // SharedIP if true, VIP/VIPs get shared with multiple services + SharedIP bool `json:"sharedIP"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="" + // SharedIPKey specifies the sharing key which gets set as the annotation on the LoadBalancer service. + // Services which share the same VIP must have the same SharedIPKey. Defaults to the IPAddressPool if + // SharedIP is true, but no SharedIPKey specified. + SharedIPKey string `json:"sharedIPKey"` + + // +kubebuilder:validation:Optional + // LoadBalancerIPs, request given IPs from the pool if available. Using a list to allow dual stack (IPv4/IPv6) support + LoadBalancerIPs []string `json:"loadBalancerIPs"` +} + type WatcherImages struct { // +kubebuilder:validation:Required // APIContainerImageURL diff --git a/api/v1beta1/watcherapi_types.go b/api/v1beta1/watcherapi_types.go index c7e08173..7f9b8e72 100644 --- a/api/v1beta1/watcherapi_types.go +++ b/api/v1beta1/watcherapi_types.go @@ -18,6 +18,7 @@ package v1beta1 import ( "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -33,6 +34,11 @@ type WatcherAPISpec struct { Secret string `json:"secret"` WatcherSubCrsCommon `json:",inline"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of + // several child resources. + Override APIOverrideSpec `json:"override,omitempty"` } // WatcherAPIStatus defines the observed state of WatcherAPI @@ -53,10 +59,26 @@ type WatcherAPIStatus struct { Hash map[string]string `json:"hash,omitempty"` } +// APIOverrideSpec to override the generated manifest of several child +// resources. +type APIOverrideSpec struct { + // Override configuration for the Service created to serve traffic to + // the cluster. + // The key must be the endpoint type (public, internal) + // temporarily use MetalLBConfig struct, later we'll switch to + // service.RoutedOverrideSpec + Service map[service.Endpoint]MetalLBConfig `json:"service,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:validation:Optional + // Override, provides the ability to override the generated manifest of + // several child resources. + Override APIOverrideSpec `json:"override,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index e67c3232..911e8320 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -22,9 +22,52 @@ package v1beta1 import ( "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIOverrideSpec) DeepCopyInto(out *APIOverrideSpec) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = make(map[service.Endpoint]MetalLBConfig, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIOverrideSpec. +func (in *APIOverrideSpec) DeepCopy() *APIOverrideSpec { + if in == nil { + return nil + } + out := new(APIOverrideSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetalLBConfig) DeepCopyInto(out *MetalLBConfig) { + *out = *in + if in.LoadBalancerIPs != nil { + in, out := &in.LoadBalancerIPs, &out.LoadBalancerIPs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetalLBConfig. +func (in *MetalLBConfig) DeepCopy() *MetalLBConfig { + if in == nil { + return nil + } + out := new(MetalLBConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PasswordSelector) DeepCopyInto(out *PasswordSelector) { *out = *in @@ -131,6 +174,7 @@ func (in *WatcherAPISpec) DeepCopyInto(out *WatcherAPISpec) { *out = *in in.WatcherCommon.DeepCopyInto(&out.WatcherCommon) in.WatcherSubCrsCommon.DeepCopyInto(&out.WatcherSubCrsCommon) + in.Override.DeepCopyInto(&out.Override) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherAPISpec. @@ -176,6 +220,7 @@ func (in *WatcherAPIStatus) DeepCopy() *WatcherAPIStatus { func (in *WatcherAPITemplate) DeepCopyInto(out *WatcherAPITemplate) { *out = *in in.WatcherSubCrsTemplate.DeepCopyInto(&out.WatcherSubCrsTemplate) + in.Override.DeepCopyInto(&out.Override) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WatcherAPITemplate. diff --git a/config/crd/bases/watcher.openstack.org_watcherapis.yaml b/config/crd/bases/watcher.openstack.org_watcherapis.yaml index 4e57de24..7c40340f 100644 --- a/config/crd/bases/watcher.openstack.org_watcherapis.yaml +++ b/config/crd/bases/watcher.openstack.org_watcherapis.yaml @@ -55,6 +55,51 @@ spec: NodeSelector to target subset of worker nodes running this component. Setting here overrides any global NodeSelector settings within the Watcher CR. type: object + override: + description: |- + Override, provides the ability to override the generated manifest of + several child resources. + properties: + service: + additionalProperties: + description: MetalLBConfig to configure the MetalLB loadbalancer + service + properties: + ipAddressPool: + description: IPAddressPool expose VIP via MetalLB on the + IPAddressPool + minLength: 1 + type: string + loadBalancerIPs: + description: LoadBalancerIPs, request given IPs from the + pool if available. Using a list to allow dual stack (IPv4/IPv6) + support + items: + type: string + type: array + sharedIP: + default: true + description: SharedIP if true, VIP/VIPs get shared with + multiple services + type: boolean + sharedIPKey: + default: "" + description: |- + SharedIPKey specifies the sharing key which gets set as the annotation on the LoadBalancer service. + Services which share the same VIP must have the same SharedIPKey. Defaults to the IPAddressPool if + SharedIP is true, but no SharedIPKey specified. + type: string + required: + - ipAddressPool + type: object + description: |- + Override configuration for the Service created to serve traffic to + the cluster. + The key must be the endpoint type (public, internal) + temporarily use MetalLBConfig struct, later we'll switch to + service.RoutedOverrideSpec + type: object + type: object passwordSelectors: default: service: WatcherPassword diff --git a/config/crd/bases/watcher.openstack.org_watchers.yaml b/config/crd/bases/watcher.openstack.org_watchers.yaml index d22ad36a..949f895a 100644 --- a/config/crd/bases/watcher.openstack.org_watchers.yaml +++ b/config/crd/bases/watcher.openstack.org_watchers.yaml @@ -54,6 +54,51 @@ spec: NodeSelector to target subset of worker nodes running this component. Setting here overrides any global NodeSelector settings within the Watcher CR. type: object + override: + description: |- + Override, provides the ability to override the generated manifest of + several child resources. + properties: + service: + additionalProperties: + description: MetalLBConfig to configure the MetalLB loadbalancer + service + properties: + ipAddressPool: + description: IPAddressPool expose VIP via MetalLB on + the IPAddressPool + minLength: 1 + type: string + loadBalancerIPs: + description: LoadBalancerIPs, request given IPs from + the pool if available. Using a list to allow dual + stack (IPv4/IPv6) support + items: + type: string + type: array + sharedIP: + default: true + description: SharedIP if true, VIP/VIPs get shared with + multiple services + type: boolean + sharedIPKey: + default: "" + description: |- + SharedIPKey specifies the sharing key which gets set as the annotation on the LoadBalancer service. + Services which share the same VIP must have the same SharedIPKey. Defaults to the IPAddressPool if + SharedIP is true, but no SharedIPKey specified. + type: string + required: + - ipAddressPool + type: object + description: |- + Override configuration for the Service created to serve traffic to + the cluster. + The key must be the endpoint type (public, internal) + temporarily use MetalLBConfig struct, later we'll switch to + service.RoutedOverrideSpec + type: object + type: object replicas: default: 1 description: Replicas of Watcher service to run diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index f054a1f4..c1752ce0 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -188,6 +188,18 @@ rules: - patch - update - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - security.openshift.io resourceNames: diff --git a/controllers/watcher_controller.go b/controllers/watcher_controller.go index 79331a57..634960ae 100644 --- a/controllers/watcher_controller.go +++ b/controllers/watcher_controller.go @@ -765,6 +765,7 @@ func (r *WatcherReconciler) ensureAPI( Resources: instance.Spec.APIServiceTemplate.Resources, ServiceAccount: "watcher-" + instance.Name, }, + Override: instance.Spec.APIServiceTemplate.Override, } // If NodeSelector is not specified in Watcher APIServiceTemplate, the current diff --git a/controllers/watcherapi_controller.go b/controllers/watcherapi_controller.go index aa5c471d..0bac4e6d 100644 --- a/controllers/watcherapi_controller.go +++ b/controllers/watcherapi_controller.go @@ -19,6 +19,8 @@ package controllers import ( "context" "fmt" + "net/url" + "strings" "time" ctrl "sigs.k8s.io/controller-runtime" @@ -47,6 +49,7 @@ import ( "github.com/openstack-k8s-operators/watcher-operator/pkg/watcher" "github.com/openstack-k8s-operators/watcher-operator/pkg/watcherapi" + routev1 "github.com/openshift/api/route/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" @@ -69,12 +72,14 @@ func (r *WatcherAPIReconciler) GetLogger(ctx context.Context) logr.Logger { //+kubebuilder:rbac:groups=watcher.openstack.org,resources=watcherapis/status,verbs=get;update;patch //+kubebuilder:rbac:groups=watcher.openstack.org,resources=watcherapis/finalizers,verbs=update //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete; +//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneapis,verbs=get;list;watch; //+kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneservices,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneendpoints,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=memcached.openstack.org,resources=memcacheds,verbs=get;list;watch;update;patch //+kubebuilder:rbac:groups=memcached.openstack.org,resources=memcacheds/finalizers,verbs=update;patch //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete; +//+kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -221,6 +226,14 @@ func (r *WatcherAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) return result, err } + apiEndpoints, result, err := r.ensureServiceExposed(ctx, helper, instance) + if (err != nil || result != ctrl.Result{}) { + // We can ignore RequeueAfter as we are watching the Service resource + // but we have to return while waiting for the service to be exposed + return ctrl.Result{}, err + } + _ = apiEndpoints // we'll use the apiEndpoints later + // We reached the end of the Reconcile, update the Ready condition based on // the sub conditions if instance.Status.Conditions.AllSubConditionIsTrue() { @@ -228,6 +241,7 @@ func (r *WatcherAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) condition.ReadyCondition, condition.ReadyMessage) } + Log.Info(fmt.Sprintf("Successfully reconciled WatcherAPI instance '%s'", instance.Name)) return ctrl.Result{}, nil } @@ -355,6 +369,81 @@ func (r *WatcherAPIReconciler) createDeployment( return ctrl.Result{}, nil } +func (r *WatcherAPIReconciler) ensureServiceExposed( + ctx context.Context, + helper *helper.Helper, + instance *watcherv1beta1.WatcherAPI, +) (map[string]string, ctrl.Result, error) { + Log := r.GetLogger(ctx) + Log.Info(fmt.Sprintf("Defining WatcherAPI services '%s'", instance.Name)) + + ports := map[service.Endpoint]endpoint.Data{ + service.EndpointPublic: { + Port: watcher.WatcherPublicPort, + }, + service.EndpointInternal: { + Port: watcher.WatcherPublicPort, + }, + } + + for endpointType := range instance.Spec.Override.Service { + svcOverride := instance.Spec.Override.Service[endpointType] + portCfg := ports[endpointType] + portCfg.MetalLB = &endpoint.MetalLBData{ + IPAddressPool: svcOverride.IPAddressPool, + SharedIP: svcOverride.SharedIP, + SharedIPKey: svcOverride.SharedIPKey, + LoadBalancerIPs: svcOverride.LoadBalancerIPs, + } + + ports[endpointType] = portCfg + } + + apiEndpoints, ctrlResult, err := endpoint.ExposeEndpoints( + ctx, + helper, + watcher.ServiceName, + getAPIServiceLabels(), + ports, + r.RequeueTimeout, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ExposeServiceReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.ExposeServiceReadyErrorMessage, + err.Error(), + )) + return nil, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ExposeServiceReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.ExposeServiceReadyRunningMessage, + )) + return nil, ctrlResult, nil + } + + // fix wrongly formatted endpoint url gotten from the lib-common + // ExposeEndpoints function + for endpointType, endpointURL := range apiEndpoints { + // fix repeated '://' in endpoint url + endpointURL = strings.Replace(endpointURL, "://://", "://", 1) + if endpointType == string(service.EndpointPublic) { + // remove trailing port number + url, _ := url.Parse(endpointURL) + endpointURL = fmt.Sprintf("%s://%s", url.Scheme, url.Hostname()) + } + apiEndpoints[endpointType] = endpointURL + } + + instance.Status.Conditions.MarkTrue(condition.ExposeServiceReadyCondition, condition.ExposeServiceReadyMessage) + + return apiEndpoints, ctrl.Result{}, nil +} + func (r *WatcherAPIReconciler) reconcileDelete(ctx context.Context, instance *watcherv1beta1.WatcherAPI, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) Log.Info(fmt.Sprintf("Reconcile Service '%s' delete started", instance.Name)) @@ -387,9 +476,10 @@ func (r *WatcherAPIReconciler) initStatus(instance *watcherv1beta1.WatcherAPI) e // failure/in-progress operation condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage), condition.UnknownCondition(condition.InputReadyCondition, condition.InitReason, condition.InputReadyInitMessage), - condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyMessage), + condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), condition.UnknownCondition(condition.MemcachedReadyCondition, condition.InitReason, condition.MemcachedReadyInitMessage), - condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyMessage), + condition.UnknownCondition(condition.DeploymentReadyCondition, condition.InitReason, condition.DeploymentReadyInitMessage), + condition.UnknownCondition(condition.ExposeServiceReadyCondition, condition.InitReason, condition.ExposeServiceReadyInitMessage), ) instance.Status.Conditions.Init(&cl) @@ -422,6 +512,8 @@ func (r *WatcherAPIReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&watcherv1beta1.WatcherAPI{}). Owns(&corev1.Secret{}). Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). + Owns(&routev1.Route{}). Watches( &corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), diff --git a/go.mod b/go.mod index a350da60..e9625565 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/google/uuid v1.6.0 github.com/onsi/ginkgo/v2 v2.20.1 github.com/onsi/gomega v1.34.1 + github.com/openshift/api v3.9.0+incompatible github.com/openstack-k8s-operators/infra-operator/apis v0.5.0 github.com/openstack-k8s-operators/keystone-operator/api v0.5.1-0.20241023160107-bd8e671350e1 github.com/openstack-k8s-operators/lib-common/modules/common v0.5.1-0.20241029151503-4878b3fa3333 @@ -49,7 +50,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/openshift/api v3.9.0+incompatible // indirect github.com/openstack-k8s-operators/lib-common/modules/openstack v0.5.1-0.20241029151503-4878b3fa3333 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.18.0 // indirect diff --git a/main.go b/main.go index f8c4afaf..82adbc52 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ import ( "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" + routev1 "github.com/openshift/api/route/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -60,6 +61,7 @@ func init() { utilruntime.Must(rabbitmqv1.AddToScheme(scheme)) utilruntime.Must(keystonev1.AddToScheme(scheme)) utilruntime.Must(memcachedv1.AddToScheme(scheme)) + utilruntime.Must(routev1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/pkg/watcher/constants.go b/pkg/watcher/constants.go index fe9ff464..387a03d7 100644 --- a/pkg/watcher/constants.go +++ b/pkg/watcher/constants.go @@ -29,6 +29,9 @@ const ( // WatcherPublicPort - public port of watcher containers WatcherPublicPort int32 = 9322 + // WatcherPublicPort - internal port of watcher containers + WatcherInternalPort int32 = 9322 + // WatcherLogPath is the path used by WatcherAPI to stream/store its logs WatcherLogPath = "/var/log/watcher/" diff --git a/tests/functional/suite_test.go b/tests/functional/suite_test.go index 63d0e883..d17c5795 100644 --- a/tests/functional/suite_test.go +++ b/tests/functional/suite_test.go @@ -28,6 +28,7 @@ import ( metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + routev1 "github.com/openshift/api/route/v1" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" @@ -97,6 +98,8 @@ var _ = BeforeSuite(func() { keystoneCRDs, err := test.GetCRDDirFromModule( "github.com/openstack-k8s-operators/keystone-operator/api", "../../go.mod", "bases") Expect(err).ShouldNot(HaveOccurred()) + routev1CRDs, err := test.GetOpenShiftCRDDir("route/v1", "../../go.mod") + Expect(err).ShouldNot(HaveOccurred()) By("bootstrapping test environment") testEnv = &envtest.Environment{ @@ -105,6 +108,7 @@ var _ = BeforeSuite(func() { mariaDBCRDs, rabbitmqCRDs, keystoneCRDs, + routev1CRDs, }, ErrorIfCRDPathMissing: true, @@ -136,6 +140,8 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) err = memcachedv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = routev1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) logger = ctrl.Log.WithName("---Test---") //+kubebuilder:scaffold:scheme diff --git a/tests/functional/watcher_test_data.go b/tests/functional/watcher_test_data.go index c4d06dd7..b201e5c7 100644 --- a/tests/functional/watcher_test_data.go +++ b/tests/functional/watcher_test_data.go @@ -47,6 +47,10 @@ type WatcherTestData struct { RoleBindingName types.NamespacedName WatcherDBSync types.NamespacedName WatcherAPIDeployment types.NamespacedName + WatcherPublicServiceName types.NamespacedName + WatcherInternalServiceName types.NamespacedName + WatcherRouteName types.NamespacedName + WatcherInternalRouteName types.NamespacedName } // GetWatcherTestData is a function that initialize the WatcherTestData @@ -115,5 +119,21 @@ func GetWatcherTestData(watcherName types.NamespacedName) WatcherTestData { Namespace: watcherName.Namespace, Name: "watcher-api", }, + WatcherPublicServiceName: types.NamespacedName{ + Namespace: watcherName.Namespace, + Name: "watcher-public", + }, + WatcherInternalServiceName: types.NamespacedName{ + Namespace: watcherName.Namespace, + Name: "watcher-internal", + }, + WatcherRouteName: types.NamespacedName{ + Namespace: watcherName.Namespace, + Name: "watcher-public", + }, + WatcherInternalRouteName: types.NamespacedName{ + Namespace: watcherName.Namespace, + Name: "watcher-internal", + }, } } diff --git a/tests/functional/watcherapi_controller_test.go b/tests/functional/watcherapi_controller_test.go index 2e0ae8ce..52dd7fe6 100644 --- a/tests/functional/watcherapi_controller_test.go +++ b/tests/functional/watcherapi_controller_test.go @@ -10,7 +10,6 @@ import ( memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" - 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" "k8s.io/utils/ptr" @@ -114,14 +113,6 @@ var _ = Describe("WatcherAPI controller", func() { }, ) DeferCleanup(k8sClient.Delete, ctx, secret) - mariadb.CreateMariaDBDatabase(watcherTest.WatcherDatabaseName.Namespace, watcherTest.WatcherDatabaseName.Name, mariadbv1.MariaDBDatabaseSpec{}) - DeferCleanup(k8sClient.Delete, ctx, mariadb.GetMariaDBDatabase(watcherTest.WatcherDatabaseName)) - - mariadb.SimulateMariaDBTLSDatabaseCompleted(watcherTest.WatcherDatabaseName) - apiMariaDBAccount, apiMariaDBSecret := mariadb.CreateMariaDBAccountAndSecret( - watcherTest.WatcherDatabaseAccount, mariadbv1.MariaDBAccountSpec{}) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBAccount) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBSecret) DeferCleanup(th.DeleteInstance, CreateWatcherAPI(watcherTest.WatcherAPI, GetDefaultWatcherAPISpec())) DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(watcherTest.WatcherAPI.Namespace)) memcachedSpec := memcachedv1.MemcachedSpec{ @@ -183,6 +174,19 @@ var _ = Describe("WatcherAPI controller", func() { Expect(container.LivenessProbe.HTTPGet.Port.IntVal).To(Equal(int32(9322))) Expect(container.ReadinessProbe.HTTPGet.Port.IntVal).To(Equal(int32(9322))) }) + It("exposes the watcher-api service", func() { + th.ExpectCondition( + watcherTest.WatcherAPI, + ConditionGetterFunc(WatcherAPIConditionGetter), + condition.ExposeServiceReadyCondition, + corev1.ConditionTrue, + ) + public := th.GetService(watcherTest.WatcherPublicServiceName) + Expect(public.Labels["service"]).To(Equal("watcher-api")) + internal := th.GetService(watcherTest.WatcherInternalServiceName) + Expect(internal.Labels["service"]).To(Equal("watcher-api")) + th.AssertRouteExists(watcherTest.WatcherRouteName) + }) }) When("the secret is created but missing fields", func() { BeforeEach(func() { @@ -191,14 +195,6 @@ var _ = Describe("WatcherAPI controller", func() { map[string][]byte{}, ) DeferCleanup(k8sClient.Delete, ctx, secret) - mariadb.CreateMariaDBDatabase(watcherTest.WatcherDatabaseName.Namespace, watcherTest.WatcherDatabaseName.Name, mariadbv1.MariaDBDatabaseSpec{}) - DeferCleanup(k8sClient.Delete, ctx, mariadb.GetMariaDBDatabase(watcherTest.WatcherDatabaseName)) - - mariadb.SimulateMariaDBTLSDatabaseCompleted(watcherTest.WatcherDatabaseName) - apiMariaDBAccount, apiMariaDBSecret := mariadb.CreateMariaDBAccountAndSecret( - watcherTest.WatcherDatabaseAccount, mariadbv1.MariaDBAccountSpec{}) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBAccount) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBSecret) DeferCleanup(th.DeleteInstance, CreateWatcherAPI(watcherTest.WatcherAPI, GetDefaultWatcherAPISpec())) }) It("should have input false", func() { @@ -252,14 +248,7 @@ var _ = Describe("WatcherAPI controller", func() { }, ) DeferCleanup(k8sClient.Delete, ctx, secret) - mariadb.CreateMariaDBDatabase(watcherTest.WatcherDatabaseName.Namespace, watcherTest.WatcherDatabaseName.Name, mariadbv1.MariaDBDatabaseSpec{}) - DeferCleanup(k8sClient.Delete, ctx, mariadb.GetMariaDBDatabase(watcherTest.WatcherDatabaseName)) - mariadb.SimulateMariaDBTLSDatabaseCompleted(watcherTest.WatcherDatabaseName) - apiMariaDBAccount, apiMariaDBSecret := mariadb.CreateMariaDBAccountAndSecret( - watcherTest.WatcherDatabaseAccount, mariadbv1.MariaDBAccountSpec{}) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBAccount) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBSecret) DeferCleanup(th.DeleteInstance, CreateWatcherAPI(watcherTest.WatcherAPI, GetDefaultWatcherAPISpec())) }) It("should have input ready true", func() { @@ -294,14 +283,6 @@ var _ = Describe("WatcherAPI controller", func() { }, ) DeferCleanup(k8sClient.Delete, ctx, secret) - mariadb.CreateMariaDBDatabase(watcherTest.WatcherDatabaseName.Namespace, watcherTest.WatcherDatabaseName.Name, mariadbv1.MariaDBDatabaseSpec{}) - DeferCleanup(k8sClient.Delete, ctx, mariadb.GetMariaDBDatabase(watcherTest.WatcherDatabaseName)) - - mariadb.SimulateMariaDBTLSDatabaseCompleted(watcherTest.WatcherDatabaseName) - apiMariaDBAccount, apiMariaDBSecret := mariadb.CreateMariaDBAccountAndSecret( - watcherTest.WatcherDatabaseAccount, mariadbv1.MariaDBAccountSpec{}) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBAccount) - DeferCleanup(k8sClient.Delete, ctx, apiMariaDBSecret) memcachedSpec := memcachedv1.MemcachedSpec{ MemcachedSpecCore: memcachedv1.MemcachedSpecCore{ Replicas: ptr.To(int32(1)), @@ -343,4 +324,66 @@ var _ = Describe("WatcherAPI controller", func() { ) }) }) + When("WatcherAPI is created with service overrides", func() { + BeforeEach(func() { + secret := th.CreateSecret( + watcherTest.InternalTopLevelSecretName, + map[string][]byte{ + "WatcherPassword": []byte("service-password"), + "transport_url": []byte("url"), + }, + ) + DeferCleanup(k8sClient.Delete, ctx, secret) + spec := GetDefaultWatcherAPISpec() + apiOverrideSpec := map[string]interface{}{} + endpoint := map[string]interface{}{} + internalEndpoint := map[string]interface{}{} + endpoint["ipAddressPool"] = "osp-internalapi" + endpoint["loadBalancerIPs"] = []string{"internal-lb-ip-1", "internal-lb-ip-2"} + internalEndpoint["internal"] = endpoint + apiOverrideSpec["service"] = internalEndpoint + spec["override"] = apiOverrideSpec + DeferCleanup(th.DeleteInstance, CreateWatcherAPI(watcherTest.WatcherAPI, spec)) + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(watcherTest.WatcherAPI.Namespace)) + memcachedSpec := memcachedv1.MemcachedSpec{ + MemcachedSpecCore: memcachedv1.MemcachedSpecCore{ + Replicas: ptr.To(int32(1)), + }, + } + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(watcherTest.WatcherAPI.Namespace, MemcachedInstance, memcachedSpec)) + infra.SimulateMemcachedReady(watcherTest.MemcachedNamespace) + + }) + It("creates MetalLB service", func() { + th.SimulateDeploymentReplicaReady(watcherTest.WatcherAPIDeployment) + + // As the public endpoint is not mentioned in the service override + // a generic Service and a Route is created + public := th.GetService(watcherTest.WatcherPublicServiceName) + Expect(public.Annotations).NotTo(HaveKey("metallb.universe.tf/address-pool")) + Expect(public.Annotations).NotTo(HaveKey("metallb.universe.tf/allow-shared-ip")) + Expect(public.Annotations).NotTo(HaveKey("metallb.universe.tf/loadBalancerIPs")) + th.AssertRouteExists(watcherTest.WatcherRouteName) + + // As the internal endpoint is configure in the service override it + // does not get a Route but a Service with MetalLB annotations + // instead + internal := th.GetService(watcherTest.WatcherInternalServiceName) + Expect(internal.Annotations).To(HaveKeyWithValue("metallb.universe.tf/address-pool", "osp-internalapi")) + Expect(internal.Annotations).To(HaveKeyWithValue("metallb.universe.tf/allow-shared-ip", "osp-internalapi")) + Expect(internal.Annotations).To(HaveKeyWithValue("metallb.universe.tf/loadBalancerIPs", "internal-lb-ip-1,internal-lb-ip-2")) + th.AssertRouteNotExists(watcherTest.WatcherInternalRouteName) + + // simulate that the internal service got a LoadBalancerIP + // assigned + th.SimulateLoadBalancerServiceIP(watcherTest.WatcherInternalServiceName) + + th.ExpectCondition( + watcherTest.WatcherAPI, + ConditionGetterFunc(WatcherAPIConditionGetter), + condition.ReadyCondition, + corev1.ConditionTrue, + ) + }) + }) }) diff --git a/tests/kuttl/test-suites/default/watcher-api-service-override/00-cleanup-watcher.yaml b/tests/kuttl/test-suites/default/watcher-api-service-override/00-cleanup-watcher.yaml new file mode 120000 index 00000000..92ed6e0b --- /dev/null +++ b/tests/kuttl/test-suites/default/watcher-api-service-override/00-cleanup-watcher.yaml @@ -0,0 +1 @@ +../common/cleanup-watcher.yaml \ No newline at end of file diff --git a/tests/kuttl/test-suites/default/watcher-api-service-override/01-assert.yaml b/tests/kuttl/test-suites/default/watcher-api-service-override/01-assert.yaml new file mode 100644 index 00000000..f3e5b95b --- /dev/null +++ b/tests/kuttl/test-suites/default/watcher-api-service-override/01-assert.yaml @@ -0,0 +1,337 @@ +apiVersion: watcher.openstack.org/v1beta1 +kind: Watcher +metadata: + finalizers: + - openstack.org/watcher + name: watcher-kuttl + namespace: watcher-kuttl-default +spec: + 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: + service: WatcherPassword + preserveJobs: false + rabbitMqClusterName: rabbitmq + secret: osp-secret + serviceUser: watcher + apiServiceTemplate: + replicas: 1 + resources: {} +status: + apiServiceReadyCount: 1 + conditions: + - message: Setup complete + reason: Ready + status: "True" + type: Ready + - message: DB create completed + reason: Ready + status: "True" + type: DBReady + - message: DBsync completed + reason: Ready + status: "True" + type: DBSyncReady + - message: Input data complete + reason: Ready + status: "True" + type: InputReady + - message: Setup complete + reason: Ready + status: "True" + type: KeystoneServiceReady + - message: MariaDBAccount creation complete + reason: Ready + status: "True" + type: MariaDBAccountReady + - message: RoleBinding created + reason: Ready + status: "True" + type: RoleBindingReady + - message: Role created + reason: Ready + status: "True" + type: RoleReady + - message: ServiceAccount created + reason: Ready + status: "True" + type: ServiceAccountReady + - message: Service config create completed + reason: Ready + status: "True" + type: ServiceConfigReady + - message: Setup complete + reason: Ready + status: "True" + type: WatcherAPIReady + - message: WatcherRabbitMQTransportURL successfully created + reason: Ready + status: "True" + type: WatcherRabbitMQTransportURLReady +--- +apiVersion: v1 +kind: Secret +metadata: + name: watcher-db-secret + namespace: watcher-kuttl-default + finalizers: + - openstack.org/watcher +--- +apiVersion: mariadb.openstack.org/v1beta1 +kind: MariaDBAccount +metadata: + name: watcher + namespace: watcher-kuttl-default + finalizers: + - openstack.org/watcher + - openstack.org/mariadbaccount + labels: + mariaDBDatabaseName: watcher +--- +apiVersion: mariadb.openstack.org/v1beta1 +kind: MariaDBDatabase +metadata: + name: watcher + namespace: watcher-kuttl-default + finalizers: + - openstack.org/watcher + - openstack.org/mariadbdatabase + - openstack.org/mariadbaccount-watcher +--- +apiVersion: rabbitmq.openstack.org/v1beta1 +kind: TransportURL +metadata: + generation: 1 + labels: + service: watcher + name: watcher-kuttl-watcher-transport + namespace: watcher-kuttl-default +spec: + rabbitmqClusterName: rabbitmq +status: + conditions: + - message: Setup complete + reason: Ready + status: "True" + type: Ready + - message: TransportURL completed + reason: Ready + status: "True" + type: TransportURLReady + secretName: rabbitmq-transport-url-watcher-kuttl-watcher-transport +--- +apiVersion: v1 +kind: Secret +metadata: + name: rabbitmq-transport-url-watcher-kuttl-watcher-transport + namespace: watcher-kuttl-default +--- +apiVersion: v1 +kind: Secret +metadata: + name: watcher-kuttl +--- +apiVersion: keystone.openstack.org/v1beta1 +kind: KeystoneService +metadata: + name: watcher + finalizers: + - openstack.org/watcher + - openstack.org/keystoneservice +spec: + enabled: true + passwordSelector: WatcherPassword + secret: osp-secret + serviceDescription: Watcher Service + serviceName: watcher + serviceType: infra-optim + serviceUser: watcher +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: watcher-watcher-kuttl +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: watcher-watcher-kuttl-rolebinding + namespace: watcher-kuttl-default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: watcher-watcher-kuttl-role +subjects: +- kind: ServiceAccount + name: watcher-watcher-kuttl + namespace: watcher-kuttl-default +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + service: watcher + name: watcher-kuttl-db-sync +--- +apiVersion: v1 +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: Exposing service completed + reason: Ready + status: "True" + type: ExposeServiceReady + - 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 + 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: v1 +kind: Service +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public +spec: + ports: + - name: watcher-public + port: 9322 + protocol: TCP + targetPort: 9322 + selector: + service: watcher-api + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + internal: "true" + service: watcher-api + name: watcher-internal +spec: + allocateLoadBalancerNodePorts: true + ports: + - name: watcher-internal + port: 9322 + protocol: TCP + targetPort: 9322 + selector: + service: watcher-api + type: LoadBalancer +status: + loadBalancer: + ingress: + - ip: 172.17.0.82 +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public +spec: + port: + targetPort: watcher-public + to: + kind: Service + name: watcher-public +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +namespaced: true +commands: + - script: | + 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} ] + [ -n "$(oc get -n watcher-kuttl-default watcher watcher-kuttl -o jsonpath={.status.hash.dbsync})" ] + # 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 + env_variables=$(oc set env $(oc get pods -n openstack-operators -o name -l openstack.org/operator-name=watcher) -n openstack-operators --list) + counter=0 + for i in ${env_variables}; do + if echo ${i} | grep '_URL_DEFAULT' &> /dev/null; then + echo ${i} + counter=$((counter + 1)) + fi + done + if [ ${counter} -lt 3 ]; then + echo "Error: Less than 3 _URL_DEFAULT variables found." + exit 1 + else + echo "Success: ${counter} _URL_DEFAULT variables found." + fi diff --git a/tests/kuttl/test-suites/default/watcher-api-service-override/01-deploy-with-defaults.yaml b/tests/kuttl/test-suites/default/watcher-api-service-override/01-deploy-with-defaults.yaml new file mode 100644 index 00000000..bcd84992 --- /dev/null +++ b/tests/kuttl/test-suites/default/watcher-api-service-override/01-deploy-with-defaults.yaml @@ -0,0 +1,14 @@ +apiVersion: watcher.openstack.org/v1beta1 +kind: Watcher +metadata: + name: watcher-kuttl + namespace: watcher-kuttl-default +spec: + databaseInstance: "openstack" + apiServiceTemplate: + override: + service: + internal: + ipAddressPool: "internalapi" + loadBalancerIPs: + - 172.17.0.82 diff --git a/tests/kuttl/test-suites/default/watcher-api-service-override/05-assert.yaml b/tests/kuttl/test-suites/default/watcher-api-service-override/05-assert.yaml new file mode 100644 index 00000000..89371130 --- /dev/null +++ b/tests/kuttl/test-suites/default/watcher-api-service-override/05-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +namespaced: true +commands: + - script: | + set -ex + oc exec -n watcher-kuttl-default openstackclient -- openstack service list -f value -c Name -c Type | [ $(grep -c ^watcher) == 0 ] diff --git a/tests/kuttl/test-suites/default/watcher-api-service-override/05-cleanup-watcher.yaml b/tests/kuttl/test-suites/default/watcher-api-service-override/05-cleanup-watcher.yaml new file mode 120000 index 00000000..92ed6e0b --- /dev/null +++ b/tests/kuttl/test-suites/default/watcher-api-service-override/05-cleanup-watcher.yaml @@ -0,0 +1 @@ +../common/cleanup-watcher.yaml \ No newline at end of file diff --git a/tests/kuttl/test-suites/default/watcher-api-service-override/05-errors.yaml b/tests/kuttl/test-suites/default/watcher-api-service-override/05-errors.yaml new file mode 100644 index 00000000..633d9dd9 --- /dev/null +++ b/tests/kuttl/test-suites/default/watcher-api-service-override/05-errors.yaml @@ -0,0 +1,93 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: watcher-watcher-kuttl +--- +apiVersion: keystone.openstack.org/v1beta1 +kind: KeystoneService +metadata: + name: watcher +--- +apiVersion: v1 +kind: Secret +metadata: + name: rabbitmq-transport-url-watcher-kuttl-watcher-transport +--- +apiVersion: v1 +kind: Secret +metadata: + name: watcher-kuttl +--- +apiVersion: rabbitmq.openstack.org/v1beta1 +kind: TransportURL +metadata: + name: watcher-kuttl-watcher-transport +--- +apiVersion: mariadb.openstack.org/v1beta1 +kind: MariaDBDatabase +metadata: + name: watcher +--- +apiVersion: v1 +kind: Secret +metadata: + name: watcher-db-secret +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + service: watcher + name: watcher-kuttl-db-sync +--- +apiVersion: v1 +kind: Secret +metadata: + name: watcher-kuttl-config-data +--- +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 +--- +apiVersion: v1 +kind: Pod +metadata: + labels: + service: watcher-api +--- +apiVersion: v1 +kind: Service +metadata: + labels: + internal: "true" + service: watcher-api + name: watcher-internal +--- +apiVersion: v1 +kind: Service +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public diff --git a/tests/kuttl/test-suites/default/watcher/01-assert.yaml b/tests/kuttl/test-suites/default/watcher/01-assert.yaml index 7e6d2ea1..9ae80f76 100644 --- a/tests/kuttl/test-suites/default/watcher/01-assert.yaml +++ b/tests/kuttl/test-suites/default/watcher/01-assert.yaml @@ -208,6 +208,10 @@ status: reason: Ready status: "True" type: DeploymentReady + - message: Exposing service completed + reason: Ready + status: "True" + type: ExposeServiceReady - message: Input data complete reason: Ready status: "True" @@ -250,6 +254,54 @@ spec: status: phase: Running --- +apiVersion: v1 +kind: Service +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public +spec: + ports: + - name: watcher-public + port: 9322 + protocol: TCP + targetPort: 9322 + selector: + service: watcher-api + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + internal: "true" + service: watcher-api + name: watcher-internal +spec: + ports: + - name: watcher-internal + port: 9322 + protocol: TCP + targetPort: 9322 + selector: + service: watcher-api + type: ClusterIP +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public +spec: + port: + targetPort: watcher-public + to: + kind: Service + name: watcher-public +--- apiVersion: kuttl.dev/v1beta1 kind: TestAssert namespaced: true diff --git a/tests/kuttl/test-suites/default/watcher/04-assert.yaml b/tests/kuttl/test-suites/default/watcher/04-assert.yaml index 0f45843c..6bd0fd51 100644 --- a/tests/kuttl/test-suites/default/watcher/04-assert.yaml +++ b/tests/kuttl/test-suites/default/watcher/04-assert.yaml @@ -189,6 +189,10 @@ status: reason: Ready status: "True" type: DeploymentReady + - message: Exposing service completed + reason: Ready + status: "True" + type: ExposeServiceReady - message: Input data complete reason: Ready status: "True" diff --git a/tests/kuttl/test-suites/default/watcher/05-errors.yaml b/tests/kuttl/test-suites/default/watcher/05-errors.yaml index 26c20cfd..633d9dd9 100644 --- a/tests/kuttl/test-suites/default/watcher/05-errors.yaml +++ b/tests/kuttl/test-suites/default/watcher/05-errors.yaml @@ -67,3 +67,27 @@ kind: Pod metadata: labels: service: watcher-api +--- +apiVersion: v1 +kind: Service +metadata: + labels: + internal: "true" + service: watcher-api + name: watcher-internal +--- +apiVersion: v1 +kind: Service +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + public: "true" + service: watcher-api + name: watcher-public