diff --git a/apis/go.mod b/apis/go.mod index 24e16ce1..b2ca035f 100644 --- a/apis/go.mod +++ b/apis/go.mod @@ -3,6 +3,7 @@ module github.com/openstack-k8s-operators/infra-operator/apis go 1.19 require ( + github.com/go-logr/logr v1.2.4 github.com/onsi/ginkgo/v2 v2.12.1 github.com/onsi/gomega v1.27.10 github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 @@ -21,7 +22,6 @@ require ( github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/zapr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect @@ -38,11 +38,13 @@ require ( github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 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/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect diff --git a/apis/go.sum b/apis/go.sum index e1472bd0..312d22a0 100644 --- a/apis/go.sum +++ b/apis/go.sum @@ -195,6 +195,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 h1:VzM3TYHDgqPkettiP6I6q2jOeQFL4nrJM+UcAc4f6Fs= +github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0/go.mod h1:nqCI7aelBJU61wiBeeZWJ6oi4bJy5nrjkM6lWIMA4j0= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -228,6 +230,8 @@ github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= +github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 h1:n5QmZLJfPtKbNnPVqqSQkLU1X/NMmW3CbML3yjBUjyY= github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/apis/test/helpers/memcached.go b/apis/test/helpers/memcached.go new file mode 100644 index 00000000..1814ce07 --- /dev/null +++ b/apis/test/helpers/memcached.go @@ -0,0 +1,127 @@ +/* +Copyright 2023 Red Hat +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package helpers + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + t "github.com/onsi/gomega" + memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + corev1 "k8s.io/api/core/v1" + + base "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" +) + +// TestHelper is a collection of helpers for testing operators. It extends the +// generic TestHelper from modules/test. +type TestHelper struct { + *base.TestHelper +} + +// NewTestHelper returns a TestHelper +func NewTestHelper( + ctx context.Context, + k8sClient client.Client, + timeout time.Duration, + interval time.Duration, + logger logr.Logger, +) *TestHelper { + helper := &TestHelper{} + helper.TestHelper = base.NewTestHelper(ctx, k8sClient, timeout, interval, logger) + return helper +} + +// CreateMemcached creates a new Memcached instance with the specified namespace in the Kubernetes cluster. +func (tc *TestHelper) CreateMemcached(namespace string, memcachedName string, spec memcachedv1.MemcachedSpec) types.NamespacedName { + name := types.NamespacedName{ + Name: memcachedName, + Namespace: namespace, + } + + mc := &memcachedv1.Memcached{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "memcached.openstack.org/v1beta1", + Kind: "Memcached", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: memcachedName, + Namespace: namespace, + }, + Spec: spec, + } + + t.Expect(tc.K8sClient.Create(tc.Ctx, mc)).Should(t.Succeed()) + + return name +} + +// DeleteMemcached deletes a Memcached instance from the Kubernetes cluster. +func (tc *TestHelper) DeleteMemcached(name types.NamespacedName) { + t.Eventually(func(g t.Gomega) { + service := &corev1.Service{} + err := tc.K8sClient.Get(tc.Ctx, name, service) + // if it is already gone that is OK + if k8s_errors.IsNotFound(err) { + return + } + g.Expect(err).NotTo(t.HaveOccurred()) + + g.Expect(tc.K8sClient.Delete(tc.Ctx, service)).Should(t.Succeed()) + + err = tc.K8sClient.Get(tc.Ctx, name, service) + g.Expect(k8s_errors.IsNotFound(err)).To(t.BeTrue()) + }, tc.Timeout, tc.Interval).Should(t.Succeed()) +} + +// GetMemcached waits for and retrieves a Memcached instance from the Kubernetes cluster +func (tc *TestHelper) GetMemcached(name types.NamespacedName) *memcachedv1.Memcached { + mc := &memcachedv1.Memcached{} + t.Eventually(func(g t.Gomega) { + g.Expect(tc.K8sClient.Get(tc.Ctx, name, mc)).Should(t.Succeed()) + }, tc.Timeout, tc.Interval).Should(t.Succeed()) + return mc +} + +// SimulateMemcachedReady simulates a ready state for a Memcached instance in a Kubernetes cluster. +func (tc *TestHelper) SimulateMemcachedReady(name types.NamespacedName) { + t.Eventually(func(g t.Gomega) { + mc := tc.GetMemcached(name) + mc.Status.Conditions.MarkTrue(condition.ReadyCondition, condition.ReadyMessage) + mc.Status.ReadyCount = *mc.Spec.Replicas + + serverList := []string{} + serverListWithInet := []string{} + for i := 0; i < int(*mc.Spec.Replicas); i++ { + serverList = append(serverList, fmt.Sprintf("%s-%d.%s:11211", mc.Name, i, mc.Name)) + serverListWithInet = append(serverListWithInet, fmt.Sprintf("inet:[%s-%d.%s]:11211", mc.Name, i, mc.Name)) + } + mc.Status.ServerList = serverList + mc.Status.ServerListWithInet = serverListWithInet + + // This can return conflict so we have the t.Eventually block to retry + g.Expect(tc.K8sClient.Status().Update(tc.Ctx, mc)).To(t.Succeed()) + + }, tc.Timeout, tc.Interval).Should(t.Succeed()) + + tc.Logger.Info("Simulated memcached ready", "on", name) +} diff --git a/apis/test/helpers/transport.go b/apis/test/helpers/transport.go new file mode 100644 index 00000000..24ed923e --- /dev/null +++ b/apis/test/helpers/transport.go @@ -0,0 +1,60 @@ +/* +Copyright 2023 Red Hat +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package helpers + +import ( + "github.com/onsi/gomega" + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" +) + +// GetTransportURL retrieves a TransportURL resource with the specified name. +// +// Example usage: +// +// th.GetTransportURL(types.NamespacedName{Name: "test-transporturl", Namespace: "test-namespace"}) +func (tc *TestHelper) GetTransportURL(name types.NamespacedName) *rabbitmqv1.TransportURL { + instance := &rabbitmqv1.TransportURL{} + gomega.Eventually(func(g gomega.Gomega) { + g.Expect(tc.K8sClient.Get(tc.Ctx, name, instance)).Should(gomega.Succeed()) + }, tc.Timeout, tc.Interval).Should(gomega.Succeed()) + return instance +} + +// SimulateTransportURLReady function retrieves the TransportURL and +// simulates the readiness of a TransportURL resource. +// +// Example usage: +// +// th.SimulateTransportURLReady(types.NamespacedName{Name: "test-transporturl", Namespace: "test-namespace"}) +func (tc *TestHelper) SimulateTransportURLReady(name types.NamespacedName) { + gomega.Eventually(func(g gomega.Gomega) { + transport := tc.GetTransportURL(name) + transport.Status.SecretName = transport.Spec.RabbitmqClusterName + "-secret" + transport.Status.Conditions.MarkTrue("TransportURLReady", "Ready") + g.Expect(tc.K8sClient.Status().Update(tc.Ctx, transport)).To(gomega.Succeed()) + + }, tc.Timeout, tc.Interval).Should(gomega.Succeed()) + tc.Logger.Info("Simulated TransportURL ready", "on", name) +} + +// AssertTransportURLDoesNotExist ensures the TransportURL resource does not exist in a k8s cluster. +func (tc *TestHelper) AssertTransportURLDoesNotExist(name types.NamespacedName) { + instance := &rabbitmqv1.TransportURL{} + gomega.Eventually(func(g gomega.Gomega) { + err := tc.K8sClient.Get(tc.Ctx, name, instance) + g.Expect(k8s_errors.IsNotFound(err)).To(gomega.BeTrue()) + }, tc.Timeout, tc.Interval).Should(gomega.Succeed()) +}