diff --git a/api/go.mod b/api/go.mod index 493f25ba..26fcf507 100644 --- a/api/go.mod +++ b/api/go.mod @@ -31,7 +31,7 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 // indirect github.com/google/uuid v1.6.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/api/go.sum b/api/go.sum index 745550d5..94b3f6a8 100644 --- a/api/go.sum +++ b/api/go.sum @@ -2,9 +2,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -44,11 +41,10 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 h1:2XF1Vzq06X+inNqgJ9tRnGuw+ZVCB3FazXODD6JE1R8= +github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -126,7 +122,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/api/v1beta1/swiftring_types.go b/api/v1beta1/swiftring_types.go index a33510a4..34760abd 100644 --- a/api/v1beta1/swiftring_types.go +++ b/api/v1beta1/swiftring_types.go @@ -94,6 +94,15 @@ type SwiftRingList struct { Items []SwiftRing `json:"items"` } + +type SwiftDisk struct { + Device string `json:"device"` + Path string `json:"path"` + Weight int32 `json:"weight"` + Region int32 `json:"region"` + Zone int32 `json:"zone"` +} + func init() { SchemeBuilder.Register(&SwiftRing{}, &SwiftRingList{}) } diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 916e5ca8..aebae7db 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -106,6 +106,21 @@ func (in *SwiftDefaults) DeepCopy() *SwiftDefaults { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SwiftDisk) DeepCopyInto(out *SwiftDisk) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SwiftDisk. +func (in *SwiftDisk) DeepCopy() *SwiftDisk { + if in == nil { + return nil + } + out := new(SwiftDisk) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SwiftList) DeepCopyInto(out *SwiftList) { *out = *in diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index d19b8f23..eddab010 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -119,6 +119,14 @@ rules: - patch - update - watch +- apiGroups: + - dataplane.openstack.org + resources: + - openstackdataplanenodesets + verbs: + - get + - list + - watch - apiGroups: - k8s.cni.cncf.io resources: diff --git a/controllers/swiftring_controller.go b/controllers/swiftring_controller.go index a1bd616b..96caafba 100644 --- a/controllers/swiftring_controller.go +++ b/controllers/swiftring_controller.go @@ -35,6 +35,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/helper" "github.com/openstack-k8s-operators/lib-common/modules/common/job" + dataplanev1 "github.com/openstack-k8s-operators/dataplane-operator/api/v1beta1" swiftv1beta1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" "github.com/openstack-k8s-operators/swift-operator/pkg/swift" "github.com/openstack-k8s-operators/swift-operator/pkg/swiftring" @@ -151,31 +152,40 @@ func (r *SwiftRingReconciler) reconcileNormal(ctx context.Context, instance *swi serviceLabels := swiftring.Labels() - // Create a ConfigMap populated with content from templates/ - envVars := make(map[string]env.Setter) - tpl := swiftring.ConfigMapTemplates(instance, serviceLabels) - err := configmap.EnsureConfigMaps(ctx, helper, instance, tpl, &envVars) - if err != nil { - return ctrl.Result{}, err - } - // Swift ring init job - start if instance.Status.Hash == nil { instance.Status.Hash = map[string]string{} } - ringCreateHash := instance.Status.Hash[swiftv1beta1.RingCreateHash] - // Check if the device list ConfigMap did change and if so, delete the - // rebalance Job. This will result in a new Job that rebalances with - // the updated device list - _, deviceListHash, err := configmap.GetConfigMapAndHashWithName(ctx, helper, swift.DeviceConfigMapName, instance.Namespace) + deviceList, deviceListHash, err := swiftring.DeviceList(ctx, helper, instance) if err != nil { return ctrl.Result{}, err } + if instance.Status.Hash[swiftv1beta1.DeviceListHash] != deviceListHash { - if err := job.DeleteJob(ctx, helper, instance.Name+"-rebalance", instance.Namespace); err != nil { + // Create or update the devicelist ConfigMap + envVars := make(map[string]env.Setter) + tpl := swiftring.ConfigMapTemplates(instance, serviceLabels, deviceList) + err = configmap.EnsureConfigMaps(ctx, helper, instance, tpl, &envVars) + if err != nil { return ctrl.Result{}, err } + + // Delete a possibly still existing job that finished to re-run the job + j, err := job.GetJobWithName(ctx, helper, instance.Name+"-rebalance", instance.Namespace) + if err != nil { + if !apierrors.IsNotFound(err) { + return ctrl.Result{}, err + } + } else { + if j.Status.Active == 0 { + err = job.DeleteJob(ctx, helper, instance.Name+"-rebalance", instance.Namespace) + if err != nil { + return ctrl.Result{}, err + } + } + } + instance.Status.Hash[swiftv1beta1.RingCreateHash] = "" instance.Status.Hash[swiftv1beta1.DeviceListHash] = deviceListHash if err := r.Status().Update(ctx, instance); err != nil { @@ -183,7 +193,7 @@ func (r *SwiftRingReconciler) reconcileNormal(ctx context.Context, instance *swi } } - ringCreateJob := job.NewJob(swiftring.GetRingJob(instance, serviceLabels), swiftv1beta1.RingCreateHash, false, 5*time.Second, ringCreateHash) + ringCreateJob := job.NewJob(swiftring.GetRingJob(instance, serviceLabels), "rebalance", false, 5*time.Second, instance.Status.Hash[swiftv1beta1.RingCreateHash]) ctrlResult, err := ringCreateJob.DoJob(ctx, helper) if (ctrlResult != ctrl.Result{}) { instance.Status.Conditions.Set(condition.FalseCondition( @@ -191,9 +201,6 @@ func (r *SwiftRingReconciler) reconcileNormal(ctx context.Context, instance *swi condition.RequestedReason, condition.SeverityInfo, condition.ReadyInitMessage)) - if err := r.Status().Update(ctx, instance); err != nil { - return ctrl.Result{}, err - } return ctrlResult, nil } if err != nil { @@ -210,7 +217,6 @@ func (r *SwiftRingReconciler) reconcileNormal(ctx context.Context, instance *swi if ringCreateJob.HasChanged() { instance.Status.Hash[swiftv1beta1.RingCreateHash] = ringCreateJob.GetHash() - instance.Status.Hash[swiftv1beta1.DeviceListHash] = deviceListHash if err := r.Status().Update(ctx, instance); err != nil { return ctrl.Result{}, err } @@ -255,24 +261,20 @@ func (r *SwiftRingReconciler) reconcileDelete(ctx context.Context, instance *swi // SetupWithManager sets up the controller with the Manager. func (r *SwiftRingReconciler) SetupWithManager(mgr ctrl.Manager) error { - deviceConfigMapFilter := func(ctx context.Context, o client.Object) []reconcile.Request { + swiftRingFilter := func(ctx context.Context, o client.Object) []reconcile.Request { result := []reconcile.Request{} - if o.GetName() == swift.DeviceConfigMapName { - // There should be only one SwiftRing instance within - // the Namespace - that needs to be reconciled - swiftRings := &swiftv1beta1.SwiftRingList{} - listOpts := []client.ListOption{client.InNamespace(o.GetNamespace())} - err := r.Client.List(context.Background(), swiftRings, listOpts...) - if err != nil { - return nil - } - for _, cr := range swiftRings.Items { - name := client.ObjectKey{ - Namespace: o.GetNamespace(), - Name: cr.Name, - } - result = append(result, reconcile.Request{NamespacedName: name}) + swiftRings := &swiftv1beta1.SwiftRingList{} + listOpts := []client.ListOption{client.InNamespace(o.GetNamespace())} + err := r.Client.List(context.Background(), swiftRings, listOpts...) + if err != nil { + return nil + } + for _, cr := range swiftRings.Items { + name := client.ObjectKey{ + Namespace: o.GetNamespace(), + Name: cr.Name, } + result = append(result, reconcile.Request{NamespacedName: name}) } return result } @@ -283,6 +285,7 @@ func (r *SwiftRingReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ConfigMap{}). Owns(&rbacv1.ClusterRole{}). Owns(&rbacv1.ClusterRoleBinding{}). - Watches(&corev1.ConfigMap{}, handler.EnqueueRequestsFromMapFunc(deviceConfigMapFilter)). + Watches(&swiftv1beta1.SwiftStorage{}, handler.EnqueueRequestsFromMapFunc(swiftRingFilter)). + Watches(&dataplanev1.OpenStackDataPlaneNodeSet{}, handler.EnqueueRequestsFromMapFunc(swiftRingFilter)). Complete(r) } diff --git a/controllers/swiftstorage_controller.go b/controllers/swiftstorage_controller.go index b4304052..c7ce04be 100644 --- a/controllers/swiftstorage_controller.go +++ b/controllers/swiftstorage_controller.go @@ -41,7 +41,6 @@ import ( "k8s.io/client-go/kubernetes" swiftv1beta1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" - "github.com/openstack-k8s-operators/swift-operator/pkg/swift" "github.com/openstack-k8s-operators/swift-operator/pkg/swiftstorage" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" @@ -141,22 +140,6 @@ func (r *SwiftStorageReconciler) Reconcile(ctx context.Context, req ctrl.Request serviceLabels := swiftstorage.Labels() envVars := make(map[string]env.Setter) - // Check if there is already an existing ConfigMap and device list. If - // not, create an initial device list to bootstrap the cluster with The - // weights are simply set to the requested size, this will be changed - // once all StatefulSets are running - _, ctrlResult, err := configmap.GetConfigMap(ctx, helper, instance, swift.DeviceConfigMapName, 5*time.Second) - if err != nil { - return ctrlResult, err - } else if (ctrlResult != ctrl.Result{}) { - devices := swiftstorage.DeviceList(ctx, helper, instance) - tpl := swiftstorage.DeviceConfigMapTemplates(instance, devices) - err = configmap.EnsureConfigMaps(ctx, helper, instance, tpl, &envVars) - if err != nil { - return ctrl.Result{}, err - } - } - // Create a ConfigMap populated with content from templates/ tpl := swiftstorage.ConfigMapTemplates(instance, serviceLabels, instance.Spec.MemcachedServers) err = configmap.EnsureConfigMaps(ctx, helper, instance, tpl, &envVars) @@ -169,7 +152,7 @@ func (r *SwiftStorageReconciler) Reconcile(ctx context.Context, req ctrl.Request if err != nil { return ctrl.Result{}, err } - ctrlResult, err = svc.CreateOrPatch(ctx, helper) + ctrlResult, err := svc.CreateOrPatch(ctx, helper) if err != nil { return ctrlResult, err } else if (ctrlResult != ctrl.Result{}) { @@ -236,14 +219,6 @@ func (r *SwiftStorageReconciler) Reconcile(ctx context.Context, req ctrl.Request instance.Status.ReadyCount = sset.GetStatefulSet().Status.ReadyReplicas if instance.Status.ReadyCount == *instance.Spec.Replicas { - envVars := make(map[string]env.Setter) - devices := swiftstorage.DeviceList(ctx, helper, instance) - tpl = swiftstorage.DeviceConfigMapTemplates(instance, devices) - err = configmap.EnsureConfigMaps(ctx, helper, instance, tpl, &envVars) - if err != nil { - return ctrl.Result{}, err - } - // When the cluster is attached to an external network, create DNS record for every // cluster member so it can be resolved from outside cluster (edpm nodes) podList, err := pod.GetPodListWithLabel(ctx, helper, instance.Namespace, serviceLabels) diff --git a/go.mod b/go.mod index 5af9d9dd..2b0adebc 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 github.com/onsi/ginkgo/v2 v2.15.0 github.com/onsi/gomega v1.31.1 + github.com/openstack-k8s-operators/dataplane-operator/api v0.3.1-0.20240219024506-c76c45e69237 github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240218132212-ad757a2f5bab github.com/openstack-k8s-operators/keystone-operator/api v0.3.1-0.20240216173228-eec429bcc776 github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885 @@ -36,18 +37,22 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gophercloud/gophercloud v1.9.0 // indirect 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/mailru/easyjson v0.7.7 // indirect + github.com/metal3-io/baremetal-operator/apis v0.5.0 // indirect + github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.4.0 // 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/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885 // indirect + github.com/openstack-k8s-operators/lib-common/modules/storage v0.3.1-0.20240216173409-86913e6d5885 // indirect + github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.3.1-0.20240218185734-5d372a0fd380 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect diff --git a/go.sum b/go.sum index 872bdb45..8958563f 100644 --- a/go.sum +++ b/go.sum @@ -2,9 +2,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -44,13 +41,12 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 h1:2XF1Vzq06X+inNqgJ9tRnGuw+ZVCB3FazXODD6JE1R8= +github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gophercloud/gophercloud v1.9.0 h1:zKvmHOmHuaZlnx9d2DJpEgbMxrGt/+CJ/bKOKQh9Xzo= github.com/gophercloud/gophercloud v1.9.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -65,6 +61,10 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/metal3-io/baremetal-operator/apis v0.5.0 h1:IRaKMxRCkYfFAjvZdDyGC6bIaGQsV6MMNmGV8gqyYCc= +github.com/metal3-io/baremetal-operator/apis v0.5.0/go.mod h1:rjpH/E7MB7JWt/LPi6hDd5LpaBIiqaBnm3KtFjKxnUM= +github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.4.0 h1:AnA8XLLp3RKYjjlB4KI0fyPSDN/d5gb3ZtM2cVyxwOc= +github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.4.0/go.mod h1:399nvdaqoU9rTI25UdFw2EWcVjmJPpeZPIhfDAIx/XU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -78,6 +78,8 @@ github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxCMwNRnMjhhIDOWHJowi6q8G6koI= github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= +github.com/openstack-k8s-operators/dataplane-operator/api v0.3.1-0.20240219024506-c76c45e69237 h1:CBcDpN4bSG+qmssnMKYFnXqLCdAFnmVHeLRHQlo/HeY= +github.com/openstack-k8s-operators/dataplane-operator/api v0.3.1-0.20240219024506-c76c45e69237/go.mod h1:1M1q8AhjgB9P/Jxr4WodQxwv+C5AsVh9O6ZZlWiSEe4= github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240218132212-ad757a2f5bab h1:zdI202C0TtXz5UPB+uTdxZtIaFseEDV0HtQ/MmX6hQU= github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240218132212-ad757a2f5bab/go.mod h1:FGKwlmAIgTsvvz2+uusWqNYFKHNAm4uzVyKao+emeu0= github.com/openstack-k8s-operators/keystone-operator/api v0.3.1-0.20240216173228-eec429bcc776 h1:piRji0b//DM3h2KNXxphlIYTGeRw03QhfQ/+0JS5eVY= @@ -86,6 +88,10 @@ github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.2024021617 github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885/go.mod h1:bQwzyQtWCR9F0+IvWZ30J9d1lB6tcX3CNJ0Ten1smDw= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885 h1:g3wUugrt+GsQMtWdxQDzFXjY13U/FKLx1dVMMMJhQqA= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885/go.mod h1:8QsCFttAm+X6A8I8EQThGjNjeMAYt2hK7ivbvnR3434= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.3.1-0.20240216173409-86913e6d5885 h1:sMO+IYsZ91Nho0FV6y03J0NTGd8+ZWB4KmKJJU94gTU= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.3.1-0.20240216173409-86913e6d5885/go.mod h1:sK82mkh2UzITsbNa/y6AKTZftHQnsYigqRx+rFbfZM4= +github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.3.1-0.20240218185734-5d372a0fd380 h1:P6VpvKvGMy++Pwe/+4T0etjINGkMcUClTiReF3Ph9NU= +github.com/openstack-k8s-operators/openstack-baremetal-operator/api v0.3.1-0.20240218185734-5d372a0fd380/go.mod h1:BrNj++cLEqEJIw2p7SGGrFiuNXjiCDo7FKH0oB+qcMA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -140,7 +146,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/main.go b/main.go index e01058d9..d897ba49 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/util" networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + dataplanev1 "github.com/openstack-k8s-operators/dataplane-operator/api/v1beta1" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" infranetworkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1" swiftv1beta1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" @@ -62,6 +63,7 @@ func init() { utilruntime.Must(memcachedv1.AddToScheme(scheme)) utilruntime.Must(networkv1.AddToScheme(scheme)) utilruntime.Must(infranetworkv1.AddToScheme(scheme)) + utilruntime.Must(dataplanev1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/pkg/swiftring/const.go b/pkg/swiftring/const.go index bb975902..6755a540 100644 --- a/pkg/swiftring/const.go +++ b/pkg/swiftring/const.go @@ -17,5 +17,7 @@ package swiftring const ( // ComponentName - - ComponentName = "swift-ring" + ComponentName = "swift-ring" + DataplaneDisks = "edpm_swift_disks" + DataplaneDomain = "storage.example.com" ) diff --git a/pkg/swiftring/funcs.go b/pkg/swiftring/funcs.go index c4ae575d..b9b200f3 100644 --- a/pkg/swiftring/funcs.go +++ b/pkg/swiftring/funcs.go @@ -16,10 +16,123 @@ limitations under the License. package swiftring import ( + "context" + "encoding/json" + "fmt" + "path/filepath" + "sort" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/openstack-k8s-operators/lib-common/modules/common" + "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" + + dataplanev1 "github.com/openstack-k8s-operators/dataplane-operator/api/v1beta1" + swiftv1beta1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" "github.com/openstack-k8s-operators/swift-operator/pkg/swift" ) +//+kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch +//+kubebuilder:rbac:groups=dataplane.openstack.org,resources=openstackdataplanenodesets,verbs=get;list;watch + +func DeviceList(ctx context.Context, h *helper.Helper, instance *swiftv1beta1.SwiftRing) (string, string, error) { + // Returns a list of devices as CSV + devices := []string{} + + listOpts := []client.ListOption{client.InNamespace(instance.GetNamespace())} + + // Get all SwiftStorage instances + storages := &swiftv1beta1.SwiftStorageList{} + err := h.GetClient().List(context.Background(), storages, listOpts...) + if err != nil && !errors.IsNotFound(err) { + return "", "", err + } + for _, storageInstance := range storages.Items { + for replica := 0; replica < int(*storageInstance.Spec.Replicas); replica++ { + cn := fmt.Sprintf("%s-%s-%d", swift.ClaimName, storageInstance.Name, replica) + foundClaim := &corev1.PersistentVolumeClaim{} + err = h.GetClient().Get(ctx, types.NamespacedName{Name: cn, Namespace: storageInstance.Namespace}, foundClaim) + capacity := resource.MustParse(storageInstance.Spec.StorageRequest) + weight, _ := capacity.AsInt64() + if err == nil { + capacity := foundClaim.Status.Capacity["storage"] + weight, _ = capacity.AsInt64() + } else { + h.GetLogger().Info(fmt.Sprintf("Did not find PVC %s, assuming %s as capacity", cn, storageInstance.Spec.StorageRequest)) + } + weight = weight / (1000 * 1000 * 1000) // 10GiB gets a weight of 10 etc. + // CSV: region,zone,hostname,devicename,weight + devices = append(devices, fmt.Sprintf("1,1,%s-%d.%s,%s,%d\n", storageInstance.Name, replica, storageInstance.Name, "d1", weight)) + } + } + + // Get all OpenStackDataPlaneNodeSets that deploy the Swift service and + // their used Swift disks + nodeSets := &dataplanev1.OpenStackDataPlaneNodeSetList{} + err = h.GetClient().List(context.Background(), nodeSets, listOpts...) + if err != nil && !errors.IsNotFound(err) { + return "", "", err + } + for _, nodeSet := range nodeSets.Items { + for _, service := range nodeSet.Spec.Services { + if service == "swift" { + // Get the global disk vars first that are used for all + // nodes if not set otherwise per-node + globalDisks := make(map[string]swiftv1beta1.SwiftDisk) + if edpmSwiftDisks, found := nodeSet.Spec.NodeTemplate.Ansible.AnsibleVars[DataplaneDisks]; found { + var swiftDisks []swiftv1beta1.SwiftDisk + if err := json.Unmarshal(edpmSwiftDisks, &swiftDisks); err == nil { + for _, disk := range swiftDisks { + globalDisks[disk.Path] = disk + } + } + } + + for _, node := range nodeSet.Spec.Nodes { + hostName := fmt.Sprintf("%s.%s", node.HostName, DataplaneDomain) + hostDisks := make(map[string]swiftv1beta1.SwiftDisk) + for k, v := range globalDisks { + hostDisks[k] = v + } + // These overwrite the global vars if set + if edpmSwiftDisks, found := node.Ansible.AnsibleVars[DataplaneDisks]; found { + var swiftDisks []swiftv1beta1.SwiftDisk + if err := json.Unmarshal(edpmSwiftDisks, &swiftDisks); err == nil { + for _, disk := range swiftDisks { + hostDisks[disk.Path] = disk + } + } + } + for _, disk := range hostDisks { + devices = append(devices, fmt.Sprintf("%d,%d,%s,%s,%d\n", disk.Region, disk.Zone, hostName, filepath.Base(disk.Path), disk.Weight)) + } + } + } + } + } + + // Device list must be sorted to ensure hash does not change + sort.Strings(devices) + + var deviceList strings.Builder + for _, line := range devices { + deviceList.WriteString(line) + } + + deviceListHash, err := util.ObjectHash(deviceList.String()) + if err != nil { + return "", "", err + } + + return deviceList.String(), deviceListHash, nil +} + func Labels() map[string]string { return map[string]string{ common.AppSelector: swift.ServiceName, diff --git a/pkg/swiftring/templates.go b/pkg/swiftring/templates.go index eddeb638..1d0ff05c 100644 --- a/pkg/swiftring/templates.go +++ b/pkg/swiftring/templates.go @@ -20,10 +20,21 @@ import ( "fmt" "github.com/openstack-k8s-operators/lib-common/modules/common/util" swiftv1beta1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" + "github.com/openstack-k8s-operators/swift-operator/pkg/swift" ) -func ConfigMapTemplates(instance *swiftv1beta1.SwiftRing, labels map[string]string) []util.Template { +func ConfigMapTemplates(instance *swiftv1beta1.SwiftRing, labels map[string]string, devices string) []util.Template { + data := make(map[string]string) + data["devices.csv"] = devices + return []util.Template{ + { + Name: swift.DeviceConfigMapName, + Namespace: instance.Namespace, + Type: util.TemplateTypeNone, + InstanceType: instance.Kind, + CustomData: data, + }, { Name: fmt.Sprintf("%s-scripts", instance.Name), Namespace: instance.Namespace, diff --git a/pkg/swiftstorage/funcs.go b/pkg/swiftstorage/funcs.go index 0c4e8192..ec3debc8 100644 --- a/pkg/swiftstorage/funcs.go +++ b/pkg/swiftstorage/funcs.go @@ -17,48 +17,10 @@ limitations under the License. package swiftstorage import ( - "context" - "fmt" - "strings" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/types" - "github.com/openstack-k8s-operators/lib-common/modules/common" - "github.com/openstack-k8s-operators/lib-common/modules/common/helper" - - swiftv1beta1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" "github.com/openstack-k8s-operators/swift-operator/pkg/swift" ) -//+kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch - -func DeviceList(ctx context.Context, h *helper.Helper, instance *swiftv1beta1.SwiftStorage) string { - // Creates a CSV list of devices. If PVCs do not exist yet (because not - // all StatefulSets are up yet), it will just use the request capacity - // as value. - var devices strings.Builder - - foundClaim := &corev1.PersistentVolumeClaim{} - for replica := 0; replica < int(*instance.Spec.Replicas); replica++ { - cn := fmt.Sprintf("%s-%s-%d", swift.ClaimName, instance.Name, replica) - err := h.GetClient().Get(ctx, types.NamespacedName{Name: cn, Namespace: instance.Namespace}, foundClaim) - capacity := resource.MustParse(instance.Spec.StorageRequest) - weight, _ := capacity.AsInt64() - if err == nil { - capacity := foundClaim.Status.Capacity["storage"] - weight, _ = capacity.AsInt64() - } else { - h.GetLogger().Info(fmt.Sprintf("Did not find PVC %s, assuming %s as capacity", cn, instance.Spec.StorageRequest)) - } - weight = weight / (1000 * 1000 * 1000) // 10GiB gets a weight of 10 etc. - // CSV: region,zone,hostname,devicename,weight - devices.WriteString(fmt.Sprintf("1,1,%s-%d.%s.%s.svc,%s,%d\n", instance.Name, replica, instance.Name, instance.Namespace, "d1", weight)) - } - return devices.String() -} - func Labels() map[string]string { return map[string]string{ common.AppSelector: swift.ServiceName, diff --git a/pkg/swiftstorage/templates.go b/pkg/swiftstorage/templates.go index c01e9bc2..e2e8dfa6 100644 --- a/pkg/swiftstorage/templates.go +++ b/pkg/swiftstorage/templates.go @@ -20,7 +20,6 @@ import ( "fmt" "github.com/openstack-k8s-operators/lib-common/modules/common/util" swiftv1beta1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" - "github.com/openstack-k8s-operators/swift-operator/pkg/swift" ) func ConfigMapTemplates(instance *swiftv1beta1.SwiftStorage, labels map[string]string, memcachedServers string) []util.Template { @@ -52,18 +51,3 @@ func ConfigMapTemplates(instance *swiftv1beta1.SwiftStorage, labels map[string]s }, } } - -func DeviceConfigMapTemplates(instance *swiftv1beta1.SwiftStorage, devices string) []util.Template { - data := make(map[string]string) - data["devices.csv"] = devices - - return []util.Template{ - { - Name: swift.DeviceConfigMapName, - Namespace: instance.Namespace, - Type: util.TemplateTypeNone, - InstanceType: instance.Kind, - CustomData: data, - }, - } -} diff --git a/tests/kuttl/deps/dataplane.openstack.org_openstackdataplanenodesets.yaml b/tests/kuttl/deps/dataplane.openstack.org_openstackdataplanenodesets.yaml new file mode 100644 index 00000000..80de2948 --- /dev/null +++ b/tests/kuttl/deps/dataplane.openstack.org_openstackdataplanenodesets.yaml @@ -0,0 +1,1984 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: openstackdataplanenodesets.dataplane.openstack.org +spec: + group: dataplane.openstack.org + names: + kind: OpenStackDataPlaneNodeSet + listKind: OpenStackDataPlaneNodeSetList + plural: openstackdataplanenodesets + shortNames: + - osdpns + - osdpnodeset + - osdpnodesets + singular: openstackdataplanenodeset + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + baremetalSetTemplate: + properties: + agentImageUrl: + type: string + apacheImageUrl: + type: string + automatedCleaningMode: + default: metadata + enum: + - metadata + - disabled + type: string + baremetalHosts: + additionalProperties: + properties: + ctlPlaneIP: + type: string + networkData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + userData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + bmhLabelSelector: + additionalProperties: + type: string + type: object + bmhNamespace: + default: openshift-machine-api + type: string + bootstrapDns: + items: + type: string + type: array + cloudUserName: + default: cloud-admin + type: string + ctlplaneGateway: + type: string + ctlplaneInterface: + type: string + ctlplaneNetmask: + default: 255.255.255.0 + type: string + deploymentSSHSecret: + type: string + dnsSearchDomains: + items: + type: string + type: array + domainName: + type: string + hardwareReqs: + properties: + cpuReqs: + properties: + arch: + enum: + - x86_64 + - ppc64le + type: string + countReq: + properties: + count: + minimum: 1 + type: integer + exactMatch: + type: boolean + type: object + mhzReq: + properties: + exactMatch: + type: boolean + mhz: + minimum: 1 + type: integer + type: object + type: object + diskReqs: + properties: + gbReq: + properties: + exactMatch: + type: boolean + gb: + minimum: 1 + type: integer + type: object + ssdReq: + properties: + exactMatch: + type: boolean + ssd: + type: boolean + type: object + type: object + memReqs: + properties: + gbReq: + properties: + exactMatch: + type: boolean + gb: + minimum: 1 + type: integer + type: object + type: object + type: object + networkData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + osContainerImageUrl: + type: string + osImage: + type: string + passwordSecret: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + provisionServerName: + type: string + provisioningInterface: + type: string + userData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - cloudUserName + - ctlplaneInterface + - deploymentSSHSecret + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + networkAttachments: + items: + type: string + type: array + nodeTemplate: + properties: + ansible: + properties: + ansibleHost: + type: string + ansiblePort: + type: integer + ansibleUser: + type: string + ansibleVars: + x-kubernetes-preserve-unknown-fields: true + type: object + ansibleSSHPrivateKeySecret: + type: string + extraMounts: + items: + properties: + extraVolType: + type: string + mounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + propagation: + items: + type: string + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + required: + - mounts + - volumes + type: object + type: array + managementNetwork: + default: ctlplane + type: string + networkData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + networks: + items: + properties: + defaultRoute: + type: boolean + fixedIP: + type: string + name: + pattern: ^[a-zA-Z0-9][a-zA-Z0-9\-_]*[a-zA-Z0-9]$ + type: string + subnetName: + pattern: ^[a-zA-Z0-9][a-zA-Z0-9\-_]*[a-zA-Z0-9]$ + type: string + required: + - name + - subnetName + type: object + type: array + userData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - ansibleSSHPrivateKeySecret + type: object + nodes: + additionalProperties: + properties: + ansible: + properties: + ansibleHost: + type: string + ansiblePort: + type: integer + ansibleUser: + type: string + ansibleVars: + x-kubernetes-preserve-unknown-fields: true + type: object + extraMounts: + items: + properties: + extraVolType: + type: string + mounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + propagation: + items: + type: string + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + 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 + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + claims: + items: + properties: + name: + 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 + 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 + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + 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 + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + required: + - mounts + - volumes + type: object + type: array + hostName: + type: string + managementNetwork: + type: string + networkData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + networks: + items: + properties: + defaultRoute: + type: boolean + fixedIP: + type: string + name: + pattern: ^[a-zA-Z0-9][a-zA-Z0-9\-_]*[a-zA-Z0-9]$ + type: string + subnetName: + pattern: ^[a-zA-Z0-9][a-zA-Z0-9\-_]*[a-zA-Z0-9]$ + type: string + required: + - name + - subnetName + type: object + type: array + userData: + properties: + name: + type: string + namespace: + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + preProvisioned: + type: boolean + services: + default: + - download-cache + - bootstrap + - configure-network + - validate-network + - install-os + - configure-os + - run-os + - reboot-os + - install-certs + - ovn + - neutron-metadata + - libvirt + - nova + - telemetry + items: + type: string + type: array + tags: + items: + type: string + type: array + tlsEnabled: + default: false + type: boolean + required: + - nodeTemplate + - nodes + type: object + status: + properties: + allHostnames: + additionalProperties: + additionalProperties: + type: string + type: object + type: object + allIPs: + additionalProperties: + additionalProperties: + type: string + type: object + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + severity: + type: string + status: + type: string + type: + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + configHash: + type: string + configMapHashes: + additionalProperties: + type: string + type: object + ctlplaneSearchDomain: + type: string + deployed: + type: boolean + deployedConfigHash: + type: string + deploymentStatuses: + additionalProperties: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + severity: + type: string + status: + type: string + type: + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + type: object + dnsClusterAddresses: + items: + type: string + type: array + secretHashes: + additionalProperties: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/tests/kuttl/tests/basic-deploy/00-deps.yaml b/tests/kuttl/tests/basic-deploy/00-deps.yaml new file mode 100644 index 00000000..1f725683 --- /dev/null +++ b/tests/kuttl/tests/basic-deploy/00-deps.yaml @@ -0,0 +1,7 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + if ! [[ `oc get crd openstackdataplanenodesets.dataplane.openstack.org` ]]; then + oc apply -f ../../deps/dataplane.openstack.org_openstackdataplanenodesets.yaml + fi diff --git a/tests/kuttl/tests/basic-deploy_tls/00-deps.yaml b/tests/kuttl/tests/basic-deploy_tls/00-deps.yaml new file mode 100644 index 00000000..1f725683 --- /dev/null +++ b/tests/kuttl/tests/basic-deploy_tls/00-deps.yaml @@ -0,0 +1,7 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + if ! [[ `oc get crd openstackdataplanenodesets.dataplane.openstack.org` ]]; then + oc apply -f ../../deps/dataplane.openstack.org_openstackdataplanenodesets.yaml + fi