From e2ca1e1b3bfcabebfb6321ce0a407ecb408a9b93 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Sat, 22 Nov 2025 02:16:04 +0000 Subject: [PATCH 1/6] Trunk resource: add Trunk resource controller implementation --- Makefile | 2 +- api/v1alpha1/trunk_type.go | 158 ++++++ cmd/manager/main.go | 2 + cmd/resource-generator/main.go | 4 + config/crd/kustomization.yaml | 1 + config/samples/kustomization.yaml | 1 + config/samples/openstack_v1alpha1_trunk.yaml | 26 + .../kustomizeconfig/kustomizeconfig.yaml | 12 + internal/controllers/trunk/actuator.go | 449 ++++++++++++++++++ internal/controllers/trunk/actuator_test.go | 202 ++++++++ internal/controllers/trunk/controller.go | 185 ++++++++ internal/controllers/trunk/status.go | 90 ++++ .../tests/trunk-create-full/00-assert.yaml | 64 +++ .../trunk-create-full/00-create-resource.yaml | 108 +++++ .../trunk/tests/trunk-create-full/README.md | 18 + .../tests/trunk-create-minimal/00-assert.yaml | 30 ++ .../00-create-resource.yaml | 13 + .../00-prerequisites.yaml | 44 ++ .../tests/trunk-create-minimal/01-assert.yaml | 12 + .../01-delete-secret.yaml | 6 + .../tests/trunk-create-minimal/README.md | 16 + .../trunk/tests/trunk-update/00-assert.yaml | 13 + .../trunk-update/00-minimal-resource.yaml | 14 + .../tests/trunk-update/00-prerequisites.yaml | 44 ++ .../trunk/tests/trunk-update/01-assert.yaml | 15 + .../trunk-update/01-updated-resource.yaml | 19 + .../trunk/tests/trunk-update/02-assert.yaml | 14 + .../trunk-update/02-reverted-resource.yaml | 14 + .../trunk/tests/trunk-update/README.md | 26 + internal/osclients/networking.go | 25 + kuttl-test.yaml | 1 + 31 files changed, 1627 insertions(+), 1 deletion(-) create mode 100644 api/v1alpha1/trunk_type.go create mode 100644 config/samples/openstack_v1alpha1_trunk.yaml create mode 100644 internal/controllers/trunk/actuator.go create mode 100644 internal/controllers/trunk/actuator_test.go create mode 100644 internal/controllers/trunk/controller.go create mode 100644 internal/controllers/trunk/status.go create mode 100644 internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-full/README.md create mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/README.md create mode 100644 internal/controllers/trunk/tests/trunk-update/00-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml create mode 100644 internal/controllers/trunk/tests/trunk-update/01-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-update/02-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-update/README.md diff --git a/Makefile b/Makefile index 37b4913a1..31d24744d 100644 --- a/Makefile +++ b/Makefile @@ -316,7 +316,7 @@ CONTROLLER_TOOLS_VERSION ?= v0.17.1 ENVTEST_VERSION ?= release-0.22 GOLANGCI_LINT_VERSION ?= v2.7.2 KAL_VERSION ?= v0.0.0-20250924094418-502783c08f9d -MOCKGEN_VERSION ?= v0.5.0 +MOCKGEN_VERSION ?= v0.6.0 KUTTL_VERSION ?= v0.23.0 GOVULNCHECK_VERSION ?= v1.1.4 OPERATOR_SDK_VERSION ?= v1.41.1 diff --git a/api/v1alpha1/trunk_type.go b/api/v1alpha1/trunk_type.go new file mode 100644 index 000000000..883394be7 --- /dev/null +++ b/api/v1alpha1/trunk_type.go @@ -0,0 +1,158 @@ +/* +Copyright 2025 The ORC Authors. + +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 v1alpha1 + +// TrunkFilter specifies a filter to select a trunk. At least one parameter must be specified. +// +kubebuilder:validation:MinProperties:=1 +type TrunkFilter struct { + // name of the existing resource + // +optional + Name *OpenStackName `json:"name,omitempty"` + + // description of the existing resource + // +optional + Description *NeutronDescription `json:"description,omitempty"` + + // portRef is a reference to the ORC Port which this trunk is associated with. + // +optional + PortRef *KubernetesNameRef `json:"portRef,omitempty"` + + // projectRef is a reference to the ORC Project this resource is associated with. + // Typically, only used by admin. + // +optional + ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` + + FilterByNeutronTags `json:",inline"` +} + +// Subport represents a subport that will be attached to a trunk. +type Subport struct { + // portRef is a reference to the ORC Port which will be used as a subport. + // +required + PortRef KubernetesNameRef `json:"portRef"` + + // segmentationType is the type of segmentation to use (e.g., "vlan"). + // +required + // +kubebuilder:validation:MaxLength=64 + SegmentationType string `json:"segmentationType"` + + // segmentationID is the segmentation identifier (e.g., VLAN ID). + // +required + SegmentationID int32 `json:"segmentationID"` +} + +// SubportStatus represents the observed state of a subport. +type SubportStatus struct { + // portID is the ID of the port used as a subport. + // +kubebuilder:validation:MaxLength=1024 + // +optional + PortID string `json:"portID,omitempty"` + + // segmentationType is the type of segmentation used. + // +kubebuilder:validation:MaxLength=1024 + // +optional + SegmentationType string `json:"segmentationType,omitempty"` + + // segmentationID is the segmentation identifier. + // +optional + SegmentationID *int32 `json:"segmentationID,omitempty"` +} + +// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="portRef is immutable" +type TrunkResourceSpec struct { + // name is a human-readable name of the trunk. If not set, the object's name will be used. + // +optional + Name *OpenStackName `json:"name,omitempty"` + + // description is a human-readable description for the resource. + // +optional + Description *NeutronDescription `json:"description,omitempty"` + + // portRef is a reference to the ORC Port which will be used as the parent port for this trunk. + // +required + PortRef KubernetesNameRef `json:"portRef"` + + // tags is a list of tags which will be applied to the trunk. + // +kubebuilder:validation:MaxItems:=64 + // +listType=set + // +optional + Tags []NeutronTag `json:"tags,omitempty"` + + // adminStateUp is the administrative state of the trunk, which is up (true) or down (false). + // +optional + AdminStateUp *bool `json:"adminStateUp,omitempty"` + + // subports are the subports that will be attached to this trunk. + // +kubebuilder:validation:MaxItems:=128 + // +listType=atomic + // +optional + Subports []Subport `json:"subports,omitempty"` + + // projectRef is a reference to the ORC Project this resource is associated with. + // Typically, only used by admin. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="projectRef is immutable" + ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` +} + +type TrunkResourceStatus struct { + // name is the human-readable name of the resource. Might not be unique. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Name string `json:"name,omitempty"` + + // description is a human-readable description for the resource. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Description string `json:"description,omitempty"` + + // portID is the ID of the parent port. + // +kubebuilder:validation:MaxLength=1024 + // +optional + PortID string `json:"portID,omitempty"` + + // projectID is the project owner of the resource. + // +kubebuilder:validation:MaxLength=1024 + // +optional + ProjectID string `json:"projectID,omitempty"` + + // status indicates the current status of the resource. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Status string `json:"status,omitempty"` + + // tags is the list of tags on the resource. + // +kubebuilder:validation:MaxItems=64 + // +kubebuilder:validation:items:MaxLength=1024 + // +listType=atomic + // +optional + Tags []string `json:"tags,omitempty"` + + // adminStateUp is the administrative state of the trunk, + // which is up (true) or down (false). + // +optional + AdminStateUp *bool `json:"adminStateUp,omitempty"` + + // subports is a list of subports attached to this trunk. + // +kubebuilder:validation:MaxItems=128 + // +listType=atomic + // +optional + Subports []SubportStatus `json:"subports,omitempty"` + + NeutronStatusMetadata `json:",inline"` +} + diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 293aa2bab..bf9bc38ca 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -45,6 +45,7 @@ import ( "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/servergroup" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/service" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/subnet" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/trunk" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/volume" "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/volumetype" internalmanager "github.com/k-orc/openstack-resource-controller/v2/internal/manager" @@ -113,6 +114,7 @@ func main() { router.New(scopeFactory), routerinterface.New(scopeFactory), port.New(scopeFactory), + trunk.New(scopeFactory), floatingip.New(scopeFactory), flavor.New(scopeFactory), securitygroup.New(scopeFactory), diff --git a/cmd/resource-generator/main.go b/cmd/resource-generator/main.go index 6848b155f..c1540600d 100644 --- a/cmd/resource-generator/main.go +++ b/cmd/resource-generator/main.go @@ -146,6 +146,10 @@ var resources []templateFields = []templateFields{ Name: "Subnet", ExistingOSClient: true, }, + { + Name: "Trunk", + ExistingOSClient: true, + }, { Name: "Volume", }, diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 33b8c85e2..b73dcac05 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -20,6 +20,7 @@ resources: - bases/openstack.k-orc.cloud_servergroups.yaml - bases/openstack.k-orc.cloud_services.yaml - bases/openstack.k-orc.cloud_subnets.yaml +- bases/openstack.k-orc.cloud_trunks.yaml - bases/openstack.k-orc.cloud_volumes.yaml - bases/openstack.k-orc.cloud_volumetypes.yaml # +kubebuilder:scaffold:crdkustomizeresource diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index dac467c69..aa2a75e43 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -18,6 +18,7 @@ resources: - openstack_v1alpha1_servergroup.yaml - openstack_v1alpha1_service.yaml - openstack_v1alpha1_subnet.yaml +- openstack_v1alpha1_trunk.yaml - openstack_v1alpha1_volume.yaml - openstack_v1alpha1_volumetype.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/openstack_v1alpha1_trunk.yaml b/config/samples/openstack_v1alpha1_trunk.yaml new file mode 100644 index 000000000..1b77e9e02 --- /dev/null +++ b/config/samples/openstack_v1alpha1_trunk.yaml @@ -0,0 +1,26 @@ +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-sample +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-sample + description: Sample Trunk + portRef: port-sample + tags: + - tag1 + - tag2 + adminStateUp: true + subports: + - portRef: port-sample-subport1 + segmentationType: vlan + segmentationID: 100 + - portRef: port-sample-subport2 + segmentationType: vlan + segmentationID: 200 + projectRef: project-sample + diff --git a/examples/components/kustomizeconfig/kustomizeconfig.yaml b/examples/components/kustomizeconfig/kustomizeconfig.yaml index 90866d4e5..c8439d33b 100644 --- a/examples/components/kustomizeconfig/kustomizeconfig.yaml +++ b/examples/components/kustomizeconfig/kustomizeconfig.yaml @@ -25,6 +25,8 @@ nameReference: kind: Subnet - path: spec/cloudCredentialsRef/secretName kind: KeyPair + - path: spec/cloudCredentialsRef/secretName + kind: Trunk - kind: Network fieldSpecs: @@ -77,6 +79,12 @@ nameReference: kind: FloatingIP - path: spec/resource/ports[]/portRef kind: Server + - path: spec/resource/portRef + kind: Trunk + - path: spec/resource/subports[]/portRef + kind: Trunk + - path: spec/import/filter/portRef + kind: Trunk - kind: Project fieldSpecs: @@ -90,3 +98,7 @@ nameReference: kind: Port - path: spec/resource/projectRef kind: SecurityGroup + - path: spec/resource/projectRef + kind: Trunk + - path: spec/import/filter/projectRef + kind: Trunk diff --git a/internal/controllers/trunk/actuator.go b/internal/controllers/trunk/actuator.go new file mode 100644 index 000000000..12830bdf0 --- /dev/null +++ b/internal/controllers/trunk/actuator.go @@ -0,0 +1,449 @@ +/* +Copyright 2025 The ORC Authors. + +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 trunk + +import ( + "context" + "fmt" + "iter" + "slices" + "time" + + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" + "github.com/k-orc/openstack-resource-controller/v2/internal/logging" + osclients "github.com/k-orc/openstack-resource-controller/v2/internal/osclients" + orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors" + "github.com/k-orc/openstack-resource-controller/v2/internal/util/tags" +) + +type ( + osResourceT = trunks.Trunk + + createResourceActuator = interfaces.CreateResourceActuator[orcObjectPT, orcObjectT, filterT, osResourceT] + deleteResourceActuator = interfaces.DeleteResourceActuator[orcObjectPT, orcObjectT, osResourceT] + reconcileResourceActuator = interfaces.ReconcileResourceActuator[orcObjectPT, osResourceT] + resourceReconciler = interfaces.ResourceReconciler[orcObjectPT, osResourceT] + helperFactory = interfaces.ResourceHelperFactory[orcObjectPT, orcObjectT, resourceSpecT, filterT, osResourceT] + trunkIterator = iter.Seq2[*osResourceT, error] +) + +type trunkActuator struct { + osClient osclients.NetworkClient + k8sClient client.Client +} + +var _ createResourceActuator = trunkActuator{} +var _ deleteResourceActuator = trunkActuator{} + +func (trunkActuator) GetResourceID(osResource *osResourceT) string { + return osResource.ID +} + +func (actuator trunkActuator) GetOSResourceByID(ctx context.Context, id string) (*osResourceT, progress.ReconcileStatus) { + trunk, err := actuator.osClient.GetTrunk(ctx, id) + if err != nil { + return nil, progress.WrapError(err) + } + return trunk, nil +} + +func (actuator trunkActuator) ListOSResourcesForAdoption(ctx context.Context, obj *orcv1alpha1.Trunk) (trunkIterator, bool) { + if obj.Spec.Resource == nil { + return nil, false + } + + listOpts := trunks.ListOpts{Name: getResourceName(obj)} + trunks, err := actuator.osClient.ListTrunk(ctx, listOpts) + if err != nil { + return func(yield func(*osResourceT, error) bool) { + yield(nil, err) + }, true + } + return func(yield func(*osResourceT, error) bool) { + for i := range trunks { + if !yield(&trunks[i], nil) { + return + } + } + }, true +} + +func (actuator trunkActuator) ListOSResourcesForImport(ctx context.Context, obj orcObjectPT, filter filterT) (iter.Seq2[*osResourceT, error], progress.ReconcileStatus) { + var reconcileStatus progress.ReconcileStatus + + port := &orcv1alpha1.Port{} + if filter.PortRef != nil { + portKey := client.ObjectKey{Name: string(*filter.PortRef), Namespace: obj.Namespace} + if err := actuator.k8sClient.Get(ctx, portKey, port); err != nil { + if apierrors.IsNotFound(err) { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Port", portKey.Name, progress.WaitingOnCreation)) + } else { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WrapError(fmt.Errorf("fetching port %s: %w", portKey.Name, err))) + } + } else { + if !orcv1alpha1.IsAvailable(port) || port.Status.ID == nil { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Port", portKey.Name, progress.WaitingOnReady)) + } + } + } + + project := &orcv1alpha1.Project{} + if filter.ProjectRef != nil { + projectKey := client.ObjectKey{Name: string(*filter.ProjectRef), Namespace: obj.Namespace} + if err := actuator.k8sClient.Get(ctx, projectKey, project); err != nil { + if apierrors.IsNotFound(err) { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Project", projectKey.Name, progress.WaitingOnCreation)) + } else { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WrapError(fmt.Errorf("fetching project %s: %w", projectKey.Name, err))) + } + } else { + if !orcv1alpha1.IsAvailable(project) || project.Status.ID == nil { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Project", projectKey.Name, progress.WaitingOnReady)) + } + } + } + + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return nil, reconcileStatus + } + + listOpts := trunks.ListOpts{ + Name: string(ptr.Deref(filter.Name, "")), + Description: string(ptr.Deref(filter.Description, "")), + PortID: ptr.Deref(port.Status.ID, ""), + ProjectID: ptr.Deref(project.Status.ID, ""), + Tags: tags.Join(filter.Tags), + TagsAny: tags.Join(filter.TagsAny), + NotTags: tags.Join(filter.NotTags), + NotTagsAny: tags.Join(filter.NotTagsAny), + } + + trunksList, err := actuator.osClient.ListTrunk(ctx, listOpts) + if err != nil { + return func(yield func(*osResourceT, error) bool) { + yield(nil, err) + }, nil + } + return func(yield func(*osResourceT, error) bool) { + for i := range trunksList { + if !yield(&trunksList[i], nil) { + return + } + } + }, nil +} + +func (actuator trunkActuator) CreateResource(ctx context.Context, obj *orcv1alpha1.Trunk) (*osResourceT, progress.ReconcileStatus) { + resource := obj.Spec.Resource + if resource == nil { + // Should have been caught by API validation + return nil, progress.WrapError(orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Creation requested, but spec.resource is not set")) + } + + // Fetch all dependencies and ensure they have our finalizer + port, portDepRS := portDependency.GetDependency( + ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Port) bool { + return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil + }, + ) + portMap, subportDepRS := subportDependency.GetDependencies( + ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Port) bool { + return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil + }, + ) + reconcileStatus := progress.NewReconcileStatus(). + WithReconcileStatus(portDepRS). + WithReconcileStatus(subportDepRS) + + var projectID string + if resource.ProjectRef != nil { + project, projectDepRS := projectDependency.GetDependency( + ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Project) bool { + return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil + }, + ) + reconcileStatus = reconcileStatus.WithReconcileStatus(projectDepRS) + if project != nil { + projectID = ptr.Deref(project.Status.ID, "") + } + } + + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return nil, reconcileStatus + } + + createOpts := trunks.CreateOpts{ + PortID: *port.Status.ID, + Name: getResourceName(obj), + Description: string(ptr.Deref(resource.Description, "")), + AdminStateUp: resource.AdminStateUp, + ProjectID: projectID, + } + + // Convert subports from spec to OpenStack format + if len(resource.Subports) > 0 { + createOpts.Subports = make([]trunks.Subport, len(resource.Subports)) + for i := range resource.Subports { + portName := string(resource.Subports[i].PortRef) + subportPort, ok := portMap[portName] + if !ok { + // Programming error + return nil, progress.WrapError(fmt.Errorf("subport port %s was not returned by GetDependencies", portName)) + } + createOpts.Subports[i] = trunks.Subport{ + PortID: *subportPort.Status.ID, + SegmentationType: resource.Subports[i].SegmentationType, + SegmentationID: int(resource.Subports[i].SegmentationID), + } + } + } + + osResource, err := actuator.osClient.CreateTrunk(ctx, createOpts) + if err != nil { + // We should require the spec to be updated before retrying a create which returned a conflict + if orcerrors.IsConflict(err) { + err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration creating resource: "+err.Error(), err) + } + return nil, progress.WrapError(err) + } + + return osResource, nil +} + +func (actuator trunkActuator) DeleteResource(ctx context.Context, _ *orcv1alpha1.Trunk, osResource *osResourceT) progress.ReconcileStatus { + return progress.WrapError(actuator.osClient.DeleteTrunk(ctx, osResource.ID)) +} + +var _ reconcileResourceActuator = trunkActuator{} + +func (actuator trunkActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller interfaces.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) { + return []resourceReconciler{ + tags.ReconcileTags[orcObjectPT, osResourceT](orcObject.Spec.Resource.Tags, osResource.Tags, tags.NewNeutronTagReplacer(actuator.osClient, "trunks", osResource.ID)), + actuator.updateResource, + actuator.reconcileSubports, + }, nil +} + +func (actuator trunkActuator) updateResource(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus { + log := ctrl.LoggerFrom(ctx) + resource := obj.Spec.Resource + if resource == nil { + return progress.WrapError( + orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Update requested, but spec.resource is not set")) + } + + var updateOpts trunks.UpdateOpts + needsUpdate := false + + // Handle name update + name := getResourceName(obj) + if osResource.Name != name { + updateOpts.Name = &name + needsUpdate = true + } + + // Handle description update + description := string(ptr.Deref(resource.Description, "")) + if osResource.Description != description { + updateOpts.Description = &description + needsUpdate = true + } + + // Handle adminStateUp update + if resource.AdminStateUp != nil && *resource.AdminStateUp != osResource.AdminStateUp { + updateOpts.AdminStateUp = resource.AdminStateUp + needsUpdate = true + } + + if !needsUpdate { + log.V(logging.Debug).Info("No changes") + return nil + } + + updateOpts.RevisionNumber = &osResource.RevisionNumber + + _, err := actuator.osClient.UpdateTrunk(ctx, osResource.ID, updateOpts) + + // We should require the spec to be updated before retrying an update which returned a conflict + if orcerrors.IsConflict(err) { + err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration updating resource: "+err.Error(), err) + } + + if err != nil { + return progress.WrapError(err) + } + + return progress.NeedsRefresh() +} + +func (actuator trunkActuator) reconcileSubports(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus { + log := ctrl.LoggerFrom(ctx) + resource := obj.Spec.Resource + if resource == nil { + return nil + } + + // Get current subports from OpenStack + currentSubports, err := actuator.osClient.ListTrunkSubports(ctx, osResource.ID) + if err != nil { + return progress.WrapError(fmt.Errorf("failed to list trunk subports: %w", err)) + } + + // Get desired subports from spec + portMap, subportDepRS := subportDependency.GetDependencies( + ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Port) bool { + return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil + }, + ) + if needsReschedule, _ := subportDepRS.NeedsReschedule(); needsReschedule { + return subportDepRS + } + + desiredSubports := make([]trunks.Subport, 0, len(resource.Subports)) + for i := range resource.Subports { + portName := string(resource.Subports[i].PortRef) + subportPort, ok := portMap[portName] + if !ok { + // Port not ready yet, will be retried + continue + } + desiredSubports = append(desiredSubports, trunks.Subport{ + PortID: *subportPort.Status.ID, + SegmentationType: resource.Subports[i].SegmentationType, + SegmentationID: int(resource.Subports[i].SegmentationID), + }) + } + + // Build maps for comparison + currentMap := make(map[string]trunks.Subport) + for _, subport := range currentSubports { + currentMap[subport.PortID] = subport + } + + desiredMap := make(map[string]trunks.Subport) + for _, subport := range desiredSubports { + desiredMap[subport.PortID] = subport + } + + // Find subports to add + toAdd := []trunks.Subport{} + for portID, desired := range desiredMap { + if current, exists := currentMap[portID]; !exists { + toAdd = append(toAdd, desired) + } else if current.SegmentationType != desired.SegmentationType || current.SegmentationID != desired.SegmentationID { + // Subport exists but with different segmentation, need to remove and re-add + // First remove the old one + removeOpts := trunks.RemoveSubportsOpts{ + Subports: []trunks.RemoveSubport{{PortID: portID}}, + } + if err := actuator.osClient.RemoveSubports(ctx, osResource.ID, removeOpts); err != nil { + return progress.WrapError(fmt.Errorf("failed to remove subport %s: %w", portID, err)) + } + toAdd = append(toAdd, desired) + } + } + + // Find subports to remove + toRemove := []trunks.RemoveSubport{} + for portID := range currentMap { + if _, exists := desiredMap[portID]; !exists { + toRemove = append(toRemove, trunks.RemoveSubport{PortID: portID}) + } + } + + // Apply changes + if len(toRemove) > 0 { + removeOpts := trunks.RemoveSubportsOpts{Subports: toRemove} + if err := actuator.osClient.RemoveSubports(ctx, osResource.ID, removeOpts); err != nil { + return progress.WrapError(fmt.Errorf("failed to remove subports: %w", err)) + } + log.V(logging.Debug).Info("Removed subports", "count", len(toRemove)) + } + + if len(toAdd) > 0 { + addOpts := trunks.AddSubportsOpts{Subports: toAdd} + if _, err := actuator.osClient.AddSubports(ctx, osResource.ID, addOpts); err != nil { + return progress.WrapError(fmt.Errorf("failed to add subports: %w", err)) + } + log.V(logging.Debug).Info("Added subports", "count", len(toAdd)) + } + + if len(toAdd) > 0 || len(toRemove) > 0 { + return progress.NeedsRefresh() + } + + return nil +} + +type trunkHelperFactory struct{} + +var _ helperFactory = trunkHelperFactory{} + +func (trunkHelperFactory) NewAPIObjectAdapter(obj orcObjectPT) adapterI { + return trunkAdapter{obj} +} + +func (trunkHelperFactory) NewCreateActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (createResourceActuator, progress.ReconcileStatus) { + return newActuator(ctx, controller, orcObject) +} + +func (trunkHelperFactory) NewDeleteActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (deleteResourceActuator, progress.ReconcileStatus) { + return newActuator(ctx, controller, orcObject) +} + +func newActuator(ctx context.Context, controller interfaces.ResourceController, orcObject *orcv1alpha1.Trunk) (trunkActuator, progress.ReconcileStatus) { + if orcObject == nil { + return trunkActuator{}, progress.WrapError(fmt.Errorf("orcObject may not be nil")) + } + + // Ensure credential secrets exist and have our finalizer + _, reconcileStatus := credentialsDependency.GetDependencies(ctx, controller.GetK8sClient(), orcObject, func(*corev1.Secret) bool { return true }) + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return trunkActuator{}, reconcileStatus + } + + log := ctrl.LoggerFrom(ctx) + clientScope, err := controller.GetScopeFactory().NewClientScopeFromObject(ctx, controller.GetK8sClient(), log, orcObject) + if err != nil { + return trunkActuator{}, progress.WrapError(err) + } + osClient, err := clientScope.NewNetworkClient() + if err != nil { + return trunkActuator{}, progress.WrapError(err) + } + + return trunkActuator{ + osClient: osClient, + k8sClient: controller.GetK8sClient(), + }, nil +} + diff --git a/internal/controllers/trunk/actuator_test.go b/internal/controllers/trunk/actuator_test.go new file mode 100644 index 000000000..a764793a1 --- /dev/null +++ b/internal/controllers/trunk/actuator_test.go @@ -0,0 +1,202 @@ +/* +Copyright 2025 The ORC Authors. + +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 trunk + +import ( + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "k8s.io/utils/ptr" +) + +func TestNeedsUpdate(t *testing.T) { + testCases := []struct { + name string + updateOpts trunks.UpdateOpts + expectChange bool + }{ + { + name: "Empty base opts", + updateOpts: trunks.UpdateOpts{}, + expectChange: false, + }, + { + name: "Empty base opts with revision number", + updateOpts: trunks.UpdateOpts{RevisionNumber: ptr.To(4)}, + expectChange: false, + }, + { + name: "Updated opts with name", + updateOpts: trunks.UpdateOpts{Name: ptr.To("updated")}, + expectChange: true, + }, + { + name: "Updated opts with description", + updateOpts: trunks.UpdateOpts{Description: ptr.To("new description")}, + expectChange: true, + }, + { + name: "Updated opts with adminStateUp", + updateOpts: trunks.UpdateOpts{AdminStateUp: ptr.To(false)}, + expectChange: true, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + got := needsUpdate(tt.updateOpts) + if got != tt.expectChange { + t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) + } + }) + } +} + +func TestHandleNameUpdate(t *testing.T) { + ptrToName := ptr.To[orcv1alpha1.OpenStackName] + testCases := []struct { + name string + newValue *orcv1alpha1.OpenStackName + existingValue string + expectChange bool + }{ + {name: "Identical", newValue: ptrToName("name"), existingValue: "name", expectChange: false}, + {name: "Different", newValue: ptrToName("new-name"), existingValue: "name", expectChange: true}, + {name: "No value provided, existing is identical to object name", newValue: nil, existingValue: "object-name", expectChange: false}, + {name: "No value provided, existing is different from object name", newValue: nil, existingValue: "different-from-object-name", expectChange: true}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + resource := &orcv1alpha1.Trunk{} + resource.Name = "object-name" + resource.Spec = orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{Name: tt.newValue}, + } + osResource := &trunks.Trunk{Name: tt.existingValue} + + updateOpts := trunks.UpdateOpts{} + handleNameUpdate(&updateOpts, resource, osResource) + + got := needsUpdate(updateOpts) + if got != tt.expectChange { + t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) + } + }) + } +} + +func TestHandleDescriptionUpdate(t *testing.T) { + ptrToDescription := ptr.To[orcv1alpha1.NeutronDescription] + testCases := []struct { + name string + newValue *orcv1alpha1.NeutronDescription + existingValue string + expectChange bool + }{ + {name: "Identical", newValue: ptrToDescription("desc"), existingValue: "desc", expectChange: false}, + {name: "Different", newValue: ptrToDescription("new-desc"), existingValue: "desc", expectChange: true}, + {name: "No value provided, existing is set", newValue: nil, existingValue: "desc", expectChange: true}, + {name: "No value provided, existing is empty", newValue: nil, existingValue: "", expectChange: false}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + resource := &orcv1alpha1.TrunkResourceSpec{Description: tt.newValue} + osResource := &trunks.Trunk{Description: tt.existingValue} + + updateOpts := trunks.UpdateOpts{} + handleDescriptionUpdate(&updateOpts, resource, osResource) + + got := needsUpdate(updateOpts) + if got != tt.expectChange { + t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) + } + }) + } +} + +func TestHandleAdminStateUpUpdate(t *testing.T) { + ptrToBool := ptr.To[bool] + testCases := []struct { + name string + newValue *bool + existingValue bool + expectChange bool + }{ + {name: "Identical", newValue: ptrToBool(true), existingValue: true, expectChange: false}, + {name: "Different", newValue: ptrToBool(true), existingValue: false, expectChange: true}, + {name: "No value provided, existing is set", newValue: nil, existingValue: false, expectChange: false}, + {name: "No value provided, existing is default", newValue: nil, existingValue: true, expectChange: false}, + {name: "False when already false", newValue: ptrToBool(false), existingValue: false, expectChange: false}, + {name: "False when was true", newValue: ptrToBool(false), existingValue: true, expectChange: true}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + resource := &orcv1alpha1.TrunkResourceSpec{AdminStateUp: tt.newValue} + osResource := &trunks.Trunk{AdminStateUp: tt.existingValue} + + updateOpts := trunks.UpdateOpts{} + handleAdminStateUpUpdate(&updateOpts, resource, osResource) + + got := needsUpdate(updateOpts) + if got != tt.expectChange { + t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) + } + }) + } +} + +// needsUpdate checks if the updateOpts contains any changes that require an update +func needsUpdate(updateOpts trunks.UpdateOpts) bool { + return updateOpts.Name != nil || updateOpts.Description != nil || updateOpts.AdminStateUp != nil +} + +// handleNameUpdate updates the updateOpts if the name needs to be changed +func handleNameUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alpha1.Trunk, osResource *trunks.Trunk) { + name := getResourceName(resource) + if osResource.Name != name { + updateOpts.Name = &name + } +} + +// handleDescriptionUpdate updates the updateOpts if the description needs to be changed +func handleDescriptionUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alpha1.TrunkResourceSpec, osResource *trunks.Trunk) { + description := string(ptr.Deref(resource.Description, "")) + if osResource.Description != description { + updateOpts.Description = &description + } +} + +// handleAdminStateUpUpdate updates the updateOpts if the adminStateUp needs to be changed +func handleAdminStateUpUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alpha1.TrunkResourceSpec, osResource *trunks.Trunk) { + if resource.AdminStateUp != nil && *resource.AdminStateUp != osResource.AdminStateUp { + updateOpts.AdminStateUp = resource.AdminStateUp + } +} + +// getResourceName returns the name of the OpenStack resource we should use. +// This is a test helper that mirrors the function in the generated adapter file. +func getResourceName(orcObject *orcv1alpha1.Trunk) string { + if orcObject.Spec.Resource != nil && orcObject.Spec.Resource.Name != nil { + return string(*orcObject.Spec.Resource.Name) + } + return orcObject.Name +} + diff --git a/internal/controllers/trunk/controller.go b/internal/controllers/trunk/controller.go new file mode 100644 index 000000000..e52a04df1 --- /dev/null +++ b/internal/controllers/trunk/controller.go @@ -0,0 +1,185 @@ +/* +Copyright 2024 The ORC Authors. + +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 trunk + +import ( + "context" + "errors" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/controller" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/pkg/predicates" + + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/reconciler" + "github.com/k-orc/openstack-resource-controller/v2/internal/scope" + "github.com/k-orc/openstack-resource-controller/v2/internal/util/credentials" + "github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency" +) + +// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=trunks,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=trunks/status,verbs=get;update;patch + +const controllerName = "trunk" + +var ( + portDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( + "spec.resource.portRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Resource + if resource == nil { + return nil + } + return []string{string(resource.PortRef)} + }, + finalizer, externalObjectFieldOwner, + ) + + portImportDependency = dependency.NewDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( + "spec.import.filter.portRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Import + if resource == nil || resource.Filter == nil || resource.Filter.PortRef == nil { + return nil + } + return []string{string(*resource.Filter.PortRef)} + }, + ) + + subportDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( + "spec.resource.subports[].portRef", + func(trunk *orcv1alpha1.Trunk) []string { + if trunk.Spec.Resource == nil { + return nil + } + ports := make([]string, len(trunk.Spec.Resource.Subports)) + for i := range trunk.Spec.Resource.Subports { + ports[i] = string(trunk.Spec.Resource.Subports[i].PortRef) + } + return ports + }, + finalizer, externalObjectFieldOwner, + ) + + projectDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Project]( + "spec.resource.projectRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Resource + if resource == nil || resource.ProjectRef == nil { + return nil + } + return []string{string(*resource.ProjectRef)} + }, + finalizer, externalObjectFieldOwner, + ) + + projectImportDependency = dependency.NewDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Project]( + "spec.import.filter.projectRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Import + if resource == nil || resource.Filter == nil || resource.Filter.ProjectRef == nil { + return nil + } + return []string{string(*resource.Filter.ProjectRef)} + }, + ) +) + +type trunkReconcilerConstructor struct { + scopeFactory scope.Factory +} + +func New(scopeFactory scope.Factory) interfaces.Controller { + return trunkReconcilerConstructor{ + scopeFactory: scopeFactory, + } +} + +func (trunkReconcilerConstructor) GetName() string { + return controllerName +} + +// SetupWithManager sets up the controller with the Manager. +func (c trunkReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { + log := ctrl.LoggerFrom(ctx) + k8sClient := mgr.GetClient() + + portWatchEventHandler, err := portDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + portImportWatchEventHandler, err := portImportDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + subportWatchEventHandler, err := subportDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + projectWatchEventHandler, err := projectDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + projectImportWatchEventHandler, err := projectImportDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + builder := ctrl.NewControllerManagedBy(mgr). + WithOptions(options). + For(&orcv1alpha1.Trunk{}). + Watches(&orcv1alpha1.Port{}, portWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), + ). + // A second watch is necessary because we need a different handler that omits deletion guards + Watches(&orcv1alpha1.Port{}, portImportWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), + ). + Watches(&orcv1alpha1.Port{}, subportWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), + ). + Watches(&orcv1alpha1.Project{}, projectWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Project{})), + ). + // A second watch is necessary because we need a different handler that omits deletion guards + Watches(&orcv1alpha1.Project{}, projectImportWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Project{})), + ) + + if err := errors.Join( + portDependency.AddToManager(ctx, mgr), + portImportDependency.AddToManager(ctx, mgr), + subportDependency.AddToManager(ctx, mgr), + projectDependency.AddToManager(ctx, mgr), + projectImportDependency.AddToManager(ctx, mgr), + credentialsDependency.AddToManager(ctx, mgr), + credentials.AddCredentialsWatch(log, mgr.GetClient(), builder, credentialsDependency), + ); err != nil { + return err + } + + r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, trunkHelperFactory{}, trunkStatusWriter{}) + return builder.Complete(&r) +} + diff --git a/internal/controllers/trunk/status.go b/internal/controllers/trunk/status.go new file mode 100644 index 000000000..7fe50906f --- /dev/null +++ b/internal/controllers/trunk/status.go @@ -0,0 +1,90 @@ +/* +Copyright 2024 The ORC Authors. + +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 trunk + +import ( + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" + orcapplyconfigv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" +) + +const ( + TrunkStatusActive = "ACTIVE" + TrunkStatusDown = "DOWN" +) + +type objectApplyPT = *orcapplyconfigv1alpha1.TrunkApplyConfiguration +type statusApplyPT = *orcapplyconfigv1alpha1.TrunkStatusApplyConfiguration + +type trunkStatusWriter struct{} + +var _ interfaces.ResourceStatusWriter[orcObjectPT, *osResourceT, objectApplyPT, statusApplyPT] = trunkStatusWriter{} + +func (trunkStatusWriter) GetApplyConfig(name, namespace string) objectApplyPT { + return orcapplyconfigv1alpha1.Trunk(name, namespace) +} + +func (trunkStatusWriter) ResourceAvailableStatus(orcObject orcObjectPT, osResource *osResourceT) (metav1.ConditionStatus, progress.ReconcileStatus) { + if osResource == nil { + if orcObject.Status.ID == nil { + return metav1.ConditionFalse, nil + } else { + return metav1.ConditionUnknown, nil + } + } + + // Both active and down trunks are Available + if osResource.Status == TrunkStatusActive || osResource.Status == TrunkStatusDown { + return metav1.ConditionTrue, nil + } + return metav1.ConditionFalse, nil +} + +func (trunkStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *osResourceT, statusApply statusApplyPT) { + resourceStatus := orcapplyconfigv1alpha1.TrunkResourceStatus(). + WithName(osResource.Name). + WithAdminStateUp(osResource.AdminStateUp). + WithStatus(osResource.Status). + WithProjectID(osResource.ProjectID). + WithPortID(osResource.PortID). + WithTags(osResource.Tags...). + WithRevisionNumber(int64(osResource.RevisionNumber)). + WithCreatedAt(metav1.NewTime(osResource.CreatedAt)). + WithUpdatedAt(metav1.NewTime(osResource.UpdatedAt)) + + if osResource.Description != "" { + resourceStatus.WithDescription(osResource.Description) + } + + if len(osResource.Subports) > 0 { + subports := make([]*orcapplyconfigv1alpha1.SubportStatusApplyConfiguration, len(osResource.Subports)) + for i := range osResource.Subports { + subportStatus := orcapplyconfigv1alpha1.SubportStatus(). + WithPortID(osResource.Subports[i].PortID). + WithSegmentationType(osResource.Subports[i].SegmentationType). + WithSegmentationID(int32(osResource.Subports[i].SegmentationID)) + subports[i] = subportStatus + } + resourceStatus.WithSubports(subports...) + } + + statusApply.WithResource(resourceStatus) +} + diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml new file mode 100644 index 000000000..6091e47f9 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml @@ -0,0 +1,64 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-full +status: + resource: + name: trunk-create-full-override + description: Trunk from "create full" test + adminStateUp: true + status: ACTIVE + tags: + - tag1 + - tag2 + subports: + - portID: "" + segmentationType: vlan + segmentationID: 100 + - portID: "" + segmentationType: vlan + segmentationID: 200 +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: trunk + name: trunk-create-full + ref: trunk + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: port + name: trunk-create-full + ref: port + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: port + name: trunk-create-full-subport1 + ref: subport1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: port + name: trunk-create-full-subport2 + ref: subport2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: project + name: trunk-create-full + ref: project +assertAll: + - celExpr: "trunk.status.id != ''" + - celExpr: "trunk.status.resource.createdAt != ''" + - celExpr: "trunk.status.resource.updatedAt != ''" + - celExpr: "trunk.status.resource.portID == port.status.id" + - celExpr: "trunk.status.resource.name == 'trunk-create-full-override'" + - celExpr: "trunk.status.resource.description == 'Trunk from \"create full\" test'" + - celExpr: "trunk.status.resource.adminStateUp == true" + - celExpr: "trunk.status.resource.projectID == project.status.id" + - celExpr: "size(trunk.status.resource.subports) == 2" + - celExpr: "trunk.status.resource.subports[0].portID == subport1.status.id" + - celExpr: "trunk.status.resource.subports[0].segmentationType == 'vlan'" + - celExpr: "trunk.status.resource.subports[0].segmentationID == 100" + - celExpr: "trunk.status.resource.subports[1].portID == subport2.status.id" + - celExpr: "trunk.status.resource.subports[1].segmentationType == 'vlan'" + - celExpr: "trunk.status.resource.subports[1].segmentationID == 200" + - celExpr: "'tag1' in trunk.status.resource.tags" + - celExpr: "'tag2' in trunk.status.resource.tags" + diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml new file mode 100644 index 000000000..118756c2f --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml @@ -0,0 +1,108 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Project +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + projectRef: trunk-create-full +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-create-full + ipVersion: 4 + cidr: 192.168.157.0/24 + projectRef: trunk-create-full +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-create-full-port + description: Parent port for trunk-create-full + networkRef: trunk-create-full + projectRef: trunk-create-full +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-create-full-subport1 +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-create-full-subport1 + networkRef: trunk-create-full + projectRef: trunk-create-full +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-create-full-subport2 +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-create-full-subport2 + networkRef: trunk-create-full + projectRef: trunk-create-full +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-create-full-override + description: Trunk from "create full" test + portRef: trunk-create-full + adminStateUp: true + tags: + - tag1 + - tag2 + subports: + - portRef: trunk-create-full-subport1 + segmentationType: vlan + segmentationID: 100 + - portRef: trunk-create-full-subport2 + segmentationType: vlan + segmentationID: 200 + projectRef: trunk-create-full + diff --git a/internal/controllers/trunk/tests/trunk-create-full/README.md b/internal/controllers/trunk/tests/trunk-create-full/README.md new file mode 100644 index 000000000..ad105a3a0 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-full/README.md @@ -0,0 +1,18 @@ +# Create a trunk with all options + +## Step 00 + +Create a trunk with all fields set: +- Custom name +- Description +- Admin state +- Tags +- Subports with VLAN segmentation +- Project reference + +Verify that all fields are correctly reflected in the observed state. + +## Reference + +https://k-orc.cloud/development/writing-tests/#create-full + diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml new file mode 100644 index 000000000..98f62359e --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-minimal +status: + resource: + name: trunk-create-minimal + adminStateUp: true + status: ACTIVE +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: trunk + name: trunk-create-minimal + ref: trunk + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: port + name: trunk-create-minimal + ref: port +assertAll: + - celExpr: "trunk.status.id != ''" + - celExpr: "trunk.status.resource.createdAt != ''" + - celExpr: "trunk.status.resource.updatedAt != ''" + - celExpr: "trunk.status.resource.portID == port.status.id" + - celExpr: "trunk.status.resource.name == 'trunk-create-minimal'" + - celExpr: "trunk.status.resource.adminStateUp == true" + diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml new file mode 100644 index 000000000..33ef84722 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + portRef: trunk-create-minimal + diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml new file mode 100644 index 000000000..4b9042767 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml @@ -0,0 +1,44 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-create-minimal +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-create-minimal + ipVersion: 4 + cidr: 192.168.156.0/24 +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-create-minimal + diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml new file mode 100644 index 000000000..251abc20d --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: v1 + kind: Secret + name: openstack-clouds + ref: secret +assertAll: + - celExpr: "secret.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/trunk' in secret.metadata.finalizers" + diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml new file mode 100644 index 000000000..a4cb499df --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl delete secret openstack-clouds --wait=false + namespaced: true + diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/README.md b/internal/controllers/trunk/tests/trunk-create-minimal/README.md new file mode 100644 index 000000000..f3fa33767 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-minimal/README.md @@ -0,0 +1,16 @@ +# Create a trunk with the minimum options + +## Step 00 + +Create a minimal trunk, that sets only the required fields (portRef), and verify that the observed state corresponds to the spec. + +Also validate that the OpenStack resource uses the name of the ORC object when it is not specified. + +## Step 01 + +Try deleting the secret and ensure that it is not deleted thanks to the finalizer. + +## Reference + +https://k-orc.cloud/development/writing-tests/#create-minimal + diff --git a/internal/controllers/trunk/tests/trunk-update/00-assert.yaml b/internal/controllers/trunk/tests/trunk-update/00-assert.yaml new file mode 100644 index 000000000..c3b738632 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/00-assert.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: trunk + name: trunk-update + ref: trunk +assertAll: + - celExpr: "trunk.status.id != ''" + - celExpr: "trunk.status.resource.name == 'trunk-update'" + - celExpr: "trunk.status.resource.adminStateUp == true" + diff --git a/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml new file mode 100644 index 000000000..990eab8f3 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + portRef: trunk-update + adminStateUp: true + diff --git a/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml new file mode 100644 index 000000000..de6f33262 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml @@ -0,0 +1,44 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-update +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-update + ipVersion: 4 + cidr: 192.168.158.0/24 +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-update + diff --git a/internal/controllers/trunk/tests/trunk-update/01-assert.yaml b/internal/controllers/trunk/tests/trunk-update/01-assert.yaml new file mode 100644 index 000000000..b0c7694a9 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/01-assert.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: trunk + name: trunk-update + ref: trunk +assertAll: + - celExpr: "trunk.status.resource.name == 'trunk-update-updated'" + - celExpr: "trunk.status.resource.description == 'trunk-update-updated'" + - celExpr: "trunk.status.resource.adminStateUp == false" + - celExpr: "'tag1' in trunk.status.resource.tags" + - celExpr: "'tag2' in trunk.status.resource.tags" + diff --git a/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml b/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml new file mode 100644 index 000000000..0dbc6956a --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-update-updated + description: trunk-update-updated + portRef: trunk-update + adminStateUp: false + tags: + - tag1 + - tag2 + diff --git a/internal/controllers/trunk/tests/trunk-update/02-assert.yaml b/internal/controllers/trunk/tests/trunk-update/02-assert.yaml new file mode 100644 index 000000000..bd433e88a --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/02-assert.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: trunk + name: trunk-update + ref: trunk +assertAll: + - celExpr: "trunk.status.resource.name == 'trunk-update'" + - celExpr: "trunk.status.resource.description == ''" + - celExpr: "trunk.status.resource.adminStateUp == true" + - celExpr: "size(trunk.status.resource.tags) == 0" + diff --git a/internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml b/internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml new file mode 100644 index 000000000..990eab8f3 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + portRef: trunk-update + adminStateUp: true + diff --git a/internal/controllers/trunk/tests/trunk-update/README.md b/internal/controllers/trunk/tests/trunk-update/README.md new file mode 100644 index 000000000..f8056b138 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/README.md @@ -0,0 +1,26 @@ +# Update a trunk + +This test verifies that mutable fields of a trunk can be updated. + +## Step 00 + +Create a minimal trunk and verify its initial state. + +## Step 01 + +Update the trunk with: +- New name +- Description +- Admin state (false) +- Tags + +Verify that the changes are reflected in the observed status. + +## Step 02 + +Revert the changes back to the minimal configuration and verify that the resource status is similar to the one we had in step 00. + +## Reference + +https://k-orc.cloud/development/writing-tests/#update + diff --git a/internal/osclients/networking.go b/internal/osclients/networking.go index 99156d64e..2885f94d7 100644 --- a/internal/osclients/networking.go +++ b/internal/osclients/networking.go @@ -102,6 +102,15 @@ type NetworkClient interface { GetSubnet(ctx context.Context, id string) (*subnets.Subnet, error) UpdateSubnet(ctx context.Context, id string, opts subnets.UpdateOptsBuilder) (*subnets.Subnet, error) + ListTrunk(ctx context.Context, opts trunks.ListOptsBuilder) ([]trunks.Trunk, error) + GetTrunk(ctx context.Context, id string) (*trunks.Trunk, error) + CreateTrunk(ctx context.Context, opts trunks.CreateOptsBuilder) (*trunks.Trunk, error) + UpdateTrunk(ctx context.Context, id string, opts trunks.UpdateOptsBuilder) (*trunks.Trunk, error) + DeleteTrunk(ctx context.Context, id string) error + ListTrunkSubports(ctx context.Context, trunkID string) ([]trunks.Subport, error) + AddSubports(ctx context.Context, id string, opts trunks.AddSubportsOptsBuilder) (*trunks.Trunk, error) + RemoveSubports(ctx context.Context, id string, opts trunks.RemoveSubportsOptsBuilder) error + ReplaceAllAttributesTags(ctx context.Context, resourceType string, resourceID string, opts attributestags.ReplaceAllOptsBuilder) ([]string, error) } @@ -214,10 +223,18 @@ func (c networkClient) UpdatePort(ctx context.Context, id string, opts ports.Upd return &portExt, nil } +func (c networkClient) GetTrunk(ctx context.Context, id string) (*trunks.Trunk, error) { + return trunks.Get(ctx, c.serviceClient, id).Extract() +} + func (c networkClient) CreateTrunk(ctx context.Context, opts trunks.CreateOptsBuilder) (*trunks.Trunk, error) { return trunks.Create(ctx, c.serviceClient, opts).Extract() } +func (c networkClient) UpdateTrunk(ctx context.Context, id string, opts trunks.UpdateOptsBuilder) (*trunks.Trunk, error) { + return trunks.Update(ctx, c.serviceClient, id, opts).Extract() +} + func (c networkClient) DeleteTrunk(ctx context.Context, id string) error { return trunks.Delete(ctx, c.serviceClient, id).ExtractErr() } @@ -239,6 +256,14 @@ func (c networkClient) ListTrunk(ctx context.Context, opts trunks.ListOptsBuilde return trunks.ExtractTrunks(allPages) } +func (c networkClient) AddSubports(ctx context.Context, id string, opts trunks.AddSubportsOptsBuilder) (*trunks.Trunk, error) { + return trunks.AddSubports(ctx, c.serviceClient, id, opts).Extract() +} + +func (c networkClient) RemoveSubports(ctx context.Context, id string, opts trunks.RemoveSubportsOptsBuilder) error { + return trunks.RemoveSubports(ctx, c.serviceClient, id, opts).ExtractErr() +} + func (c networkClient) CreateRouter(ctx context.Context, opts routers.CreateOptsBuilder) (*routers.Router, error) { return routers.Create(ctx, c.serviceClient, opts).Extract() } diff --git a/kuttl-test.yaml b/kuttl-test.yaml index d499782e6..67828d420 100644 --- a/kuttl-test.yaml +++ b/kuttl-test.yaml @@ -19,6 +19,7 @@ testDirs: - ./internal/controllers/servergroup/tests/ - ./internal/controllers/service/tests/ - ./internal/controllers/subnet/tests/ +- ./internal/controllers/trunk/tests/ - ./internal/controllers/volume/tests/ - ./internal/controllers/volumetype/tests/ timeout: 240 From 29faf6696b82c5b8f650ac9482a4e97ac4503c93 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Sat, 22 Nov 2025 05:00:18 +0000 Subject: [PATCH 2/6] Trunk resource: auto-generated files + fixing some bugs --- PROJECT | 8 + README.md | 1 + api/v1alpha1/zz_generated.deepcopy.go | 296 ++++++++ api/v1alpha1/zz_generated.trunk-resource.go | 177 +++++ cmd/models-schema/zz_generated.openapi.go | 636 ++++++++++++++++++ .../bases/openstack.k-orc.cloud_trunks.yaml | 485 +++++++++++++ config/rbac/role.yaml | 2 + internal/controllers/trunk/actuator.go | 2 - internal/controllers/trunk/actuator_test.go | 8 - internal/controllers/trunk/controller.go | 3 +- internal/controllers/trunk/status.go | 2 +- .../controllers/trunk/zz_generated.adapter.go | 88 +++ .../trunk/zz_generated.controller.go | 45 ++ internal/osclients/mock/networking.go | 119 ++++ internal/osclients/networking.go | 7 +- .../api/v1alpha1/subport.go | 61 ++ .../api/v1alpha1/subportstatus.go | 57 ++ .../applyconfiguration/api/v1alpha1/trunk.go | 281 ++++++++ .../api/v1alpha1/trunkfilter.go | 111 +++ .../api/v1alpha1/trunkimport.go | 48 ++ .../api/v1alpha1/trunkresourcespec.go | 104 +++ .../api/v1alpha1/trunkresourcestatus.go | 138 ++++ .../api/v1alpha1/trunkspec.go | 79 +++ .../api/v1alpha1/trunkstatus.go | 66 ++ .../applyconfiguration/internal/internal.go | 205 ++++++ pkg/clients/applyconfiguration/utils.go | 18 + .../typed/api/v1alpha1/api_client.go | 5 + .../api/v1alpha1/fake/fake_api_client.go | 4 + .../typed/api/v1alpha1/fake/fake_trunk.go | 49 ++ .../typed/api/v1alpha1/generated_expansion.go | 2 + .../clientset/typed/api/v1alpha1/trunk.go | 74 ++ .../api/v1alpha1/interface.go | 7 + .../externalversions/api/v1alpha1/trunk.go | 102 +++ .../informers/externalversions/generic.go | 2 + .../api/v1alpha1/expansion_generated.go | 8 + pkg/clients/listers/api/v1alpha1/trunk.go | 70 ++ website/docs/crd-reference.md | 200 ++++++ 37 files changed, 3553 insertions(+), 17 deletions(-) create mode 100644 api/v1alpha1/zz_generated.trunk-resource.go create mode 100644 config/crd/bases/openstack.k-orc.cloud_trunks.yaml create mode 100644 internal/controllers/trunk/zz_generated.adapter.go create mode 100644 internal/controllers/trunk/zz_generated.controller.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/subport.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunk.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunkimport.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunkspec.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunkstatus.go create mode 100644 pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_trunk.go create mode 100644 pkg/clients/clientset/clientset/typed/api/v1alpha1/trunk.go create mode 100644 pkg/clients/informers/externalversions/api/v1alpha1/trunk.go create mode 100644 pkg/clients/listers/api/v1alpha1/trunk.go diff --git a/PROJECT b/PROJECT index 8d6e2c12d..3d09e3c30 100644 --- a/PROJECT +++ b/PROJECT @@ -144,6 +144,14 @@ resources: kind: Subnet path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: k-orc.cloud + group: openstack + kind: Trunk + path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 + version: v1alpha1 - api: crdVersion: v1 namespaced: true diff --git a/README.md b/README.md index 26c737373..4060d87f7 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ kubectl delete -f $ORC_RELEASE | server group | | ✔ | ✔ | | service | | ✔ | ✔ | | subnet | | ◐ | ◐ | +| trunk | | ✔ | ✔ | | volume | | ◐ | ◐ | | volume type | | ◐ | ◐ | diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 67bfeab8a..377613005 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -4959,6 +4959,302 @@ func (in *SubnetStatus) DeepCopy() *SubnetStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Subport) DeepCopyInto(out *Subport) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subport. +func (in *Subport) DeepCopy() *Subport { + if in == nil { + return nil + } + out := new(Subport) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubportStatus) DeepCopyInto(out *SubportStatus) { + *out = *in + if in.SegmentationID != nil { + in, out := &in.SegmentationID, &out.SegmentationID + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubportStatus. +func (in *SubportStatus) DeepCopy() *SubportStatus { + if in == nil { + return nil + } + out := new(SubportStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Trunk) DeepCopyInto(out *Trunk) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trunk. +func (in *Trunk) DeepCopy() *Trunk { + if in == nil { + return nil + } + out := new(Trunk) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Trunk) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkFilter) DeepCopyInto(out *TrunkFilter) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(OpenStackName) + **out = **in + } + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(NeutronDescription) + **out = **in + } + if in.PortRef != nil { + in, out := &in.PortRef, &out.PortRef + *out = new(KubernetesNameRef) + **out = **in + } + if in.ProjectRef != nil { + in, out := &in.ProjectRef, &out.ProjectRef + *out = new(KubernetesNameRef) + **out = **in + } + in.FilterByNeutronTags.DeepCopyInto(&out.FilterByNeutronTags) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkFilter. +func (in *TrunkFilter) DeepCopy() *TrunkFilter { + if in == nil { + return nil + } + out := new(TrunkFilter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkImport) DeepCopyInto(out *TrunkImport) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Filter != nil { + in, out := &in.Filter, &out.Filter + *out = new(TrunkFilter) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkImport. +func (in *TrunkImport) DeepCopy() *TrunkImport { + if in == nil { + return nil + } + out := new(TrunkImport) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkList) DeepCopyInto(out *TrunkList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Trunk, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkList. +func (in *TrunkList) DeepCopy() *TrunkList { + if in == nil { + return nil + } + out := new(TrunkList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TrunkList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkResourceSpec) DeepCopyInto(out *TrunkResourceSpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(OpenStackName) + **out = **in + } + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(NeutronDescription) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]NeutronTag, len(*in)) + copy(*out, *in) + } + if in.AdminStateUp != nil { + in, out := &in.AdminStateUp, &out.AdminStateUp + *out = new(bool) + **out = **in + } + if in.Subports != nil { + in, out := &in.Subports, &out.Subports + *out = make([]Subport, len(*in)) + copy(*out, *in) + } + if in.ProjectRef != nil { + in, out := &in.ProjectRef, &out.ProjectRef + *out = new(KubernetesNameRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkResourceSpec. +func (in *TrunkResourceSpec) DeepCopy() *TrunkResourceSpec { + if in == nil { + return nil + } + out := new(TrunkResourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkResourceStatus) DeepCopyInto(out *TrunkResourceStatus) { + *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AdminStateUp != nil { + in, out := &in.AdminStateUp, &out.AdminStateUp + *out = new(bool) + **out = **in + } + if in.Subports != nil { + in, out := &in.Subports, &out.Subports + *out = make([]SubportStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.NeutronStatusMetadata.DeepCopyInto(&out.NeutronStatusMetadata) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkResourceStatus. +func (in *TrunkResourceStatus) DeepCopy() *TrunkResourceStatus { + if in == nil { + return nil + } + out := new(TrunkResourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkSpec) DeepCopyInto(out *TrunkSpec) { + *out = *in + if in.Import != nil { + in, out := &in.Import, &out.Import + *out = new(TrunkImport) + (*in).DeepCopyInto(*out) + } + if in.Resource != nil { + in, out := &in.Resource, &out.Resource + *out = new(TrunkResourceSpec) + (*in).DeepCopyInto(*out) + } + if in.ManagedOptions != nil { + in, out := &in.ManagedOptions, &out.ManagedOptions + *out = new(ManagedOptions) + **out = **in + } + out.CloudCredentialsRef = in.CloudCredentialsRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkSpec. +func (in *TrunkSpec) DeepCopy() *TrunkSpec { + if in == nil { + return nil + } + out := new(TrunkSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkStatus) DeepCopyInto(out *TrunkStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Resource != nil { + in, out := &in.Resource, &out.Resource + *out = new(TrunkResourceStatus) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkStatus. +func (in *TrunkStatus) DeepCopy() *TrunkStatus { + if in == nil { + return nil + } + out := new(TrunkStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserDataSpec) DeepCopyInto(out *UserDataSpec) { *out = *in diff --git a/api/v1alpha1/zz_generated.trunk-resource.go b/api/v1alpha1/zz_generated.trunk-resource.go new file mode 100644 index 000000000..c92b7e118 --- /dev/null +++ b/api/v1alpha1/zz_generated.trunk-resource.go @@ -0,0 +1,177 @@ +// Code generated by resource-generator. DO NOT EDIT. +/* +Copyright 2025 The ORC Authors. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TrunkImport specifies an existing resource which will be imported instead of +// creating a new one +// +kubebuilder:validation:MinProperties:=1 +// +kubebuilder:validation:MaxProperties:=1 +type TrunkImport struct { + // id contains the unique identifier of an existing OpenStack resource. Note + // that when specifying an import by ID, the resource MUST already exist. + // The ORC object will enter an error state if the resource does not exist. + // +optional + // +kubebuilder:validation:Format:=uuid + ID *string `json:"id,omitempty"` + + // filter contains a resource query which is expected to return a single + // result. The controller will continue to retry if filter returns no + // results. If filter returns multiple results the controller will set an + // error state and will not continue to retry. + // +optional + Filter *TrunkFilter `json:"filter,omitempty"` +} + +// TrunkSpec defines the desired state of an ORC object. +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'managed' ? has(self.resource) : true",message="resource must be specified when policy is managed" +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'managed' ? !has(self.__import__) : true",message="import may not be specified when policy is managed" +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'unmanaged' ? !has(self.resource) : true",message="resource may not be specified when policy is unmanaged" +// +kubebuilder:validation:XValidation:rule="self.managementPolicy == 'unmanaged' ? has(self.__import__) : true",message="import must be specified when policy is unmanaged" +// +kubebuilder:validation:XValidation:rule="has(self.managedOptions) ? self.managementPolicy == 'managed' : true",message="managedOptions may only be provided when policy is managed" +type TrunkSpec struct { + // import refers to an existing OpenStack resource which will be imported instead of + // creating a new one. + // +optional + Import *TrunkImport `json:"import,omitempty"` + + // resource specifies the desired state of the resource. + // + // resource may not be specified if the management policy is `unmanaged`. + // + // resource must be specified if the management policy is `managed`. + // +optional + Resource *TrunkResourceSpec `json:"resource,omitempty"` + + // managementPolicy defines how ORC will treat the object. Valid values are + // `managed`: ORC will create, update, and delete the resource; `unmanaged`: + // ORC will import an existing resource, and will not apply updates to it or + // delete it. + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="managementPolicy is immutable" + // +kubebuilder:default:=managed + // +optional + ManagementPolicy ManagementPolicy `json:"managementPolicy,omitempty"` + + // managedOptions specifies options which may be applied to managed objects. + // +optional + ManagedOptions *ManagedOptions `json:"managedOptions,omitempty"` + + // cloudCredentialsRef points to a secret containing OpenStack credentials + // +required + CloudCredentialsRef CloudCredentialsReference `json:"cloudCredentialsRef"` +} + +// TrunkStatus defines the observed state of an ORC resource. +type TrunkStatus struct { + // conditions represents the observed status of the object. + // Known .status.conditions.type are: "Available", "Progressing" + // + // Available represents the availability of the OpenStack resource. If it is + // true then the resource is ready for use. + // + // Progressing indicates whether the controller is still attempting to + // reconcile the current state of the OpenStack resource to the desired + // state. Progressing will be False either because the desired state has + // been achieved, or because some terminal error prevents it from ever being + // achieved and the controller is no longer attempting to reconcile. If + // Progressing is True, an observer waiting on the resource should continue + // to wait. + // + // +kubebuilder:validation:MaxItems:=32 + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // id is the unique identifier of the OpenStack resource. + // +optional + ID *string `json:"id,omitempty"` + + // resource contains the observed state of the OpenStack resource. + // +optional + Resource *TrunkResourceStatus `json:"resource,omitempty"` +} + +var _ ObjectWithConditions = &Trunk{} + +func (i *Trunk) GetConditions() []metav1.Condition { + return i.Status.Conditions +} + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:resource:categories=openstack +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="ID",type="string",JSONPath=".status.id",description="Resource ID" +// +kubebuilder:printcolumn:name="Available",type="string",JSONPath=".status.conditions[?(@.type=='Available')].status",description="Availability status of resource" +// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[?(@.type=='Progressing')].message",description="Message describing current progress status" + +// Trunk is the Schema for an ORC resource. +type Trunk struct { + metav1.TypeMeta `json:",inline"` + + // metadata contains the object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec specifies the desired state of the resource. + // +optional + Spec TrunkSpec `json:"spec,omitempty"` + + // status defines the observed state of the resource. + // +optional + Status TrunkStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// TrunkList contains a list of Trunk. +type TrunkList struct { + metav1.TypeMeta `json:",inline"` + + // metadata contains the list metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // items contains a list of Trunk. + // +required + Items []Trunk `json:"items"` +} + +func (l *TrunkList) GetItems() []Trunk { + return l.Items +} + +func init() { + SchemeBuilder.Register(&Trunk{}, &TrunkList{}) +} + +func (i *Trunk) GetCloudCredentialsRef() (*string, *CloudCredentialsReference) { + if i == nil { + return nil, nil + } + + return &i.Namespace, &i.Spec.CloudCredentialsRef +} + +var _ CloudCredentialsRefProvider = &Trunk{} diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go index 5b739dfef..b18a88f56 100644 --- a/cmd/models-schema/zz_generated.openapi.go +++ b/cmd/models-schema/zz_generated.openapi.go @@ -199,6 +199,16 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetResourceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetResourceStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetSpec": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetSpec(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetStatus": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetStatus(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Subport": schema_openstack_resource_controller_v2_api_v1alpha1_Subport(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubportStatus": schema_openstack_resource_controller_v2_api_v1alpha1_SubportStatus(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Trunk": schema_openstack_resource_controller_v2_api_v1alpha1_Trunk(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkFilter": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkFilter(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkImport": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkImport(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkList": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkList(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkResourceSpec": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceSpec(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkResourceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceStatus(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSpec": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkSpec(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkStatus": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.UserDataSpec": schema_openstack_resource_controller_v2_api_v1alpha1_UserDataSpec(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Volume": schema_openstack_resource_controller_v2_api_v1alpha1_Volume(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeAttachmentStatus": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeAttachmentStatus(ref), @@ -9590,6 +9600,632 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_SubnetStatus(ref commo } } +func schema_openstack_resource_controller_v2_api_v1alpha1_Subport(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Subport represents a subport that will be attached to a trunk.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "portRef": { + SchemaProps: spec.SchemaProps{ + Description: "portRef is a reference to the ORC Port which will be used as a subport.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "segmentationType": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationType is the type of segmentation to use (e.g., \"vlan\").", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "segmentationID": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationID is the segmentation identifier (e.g., VLAN ID).", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"portRef", "segmentationType", "segmentationID"}, + }, + }, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_SubportStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SubportStatus represents the observed state of a subport.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "portID": { + SchemaProps: spec.SchemaProps{ + Description: "portID is the ID of the port used as a subport.", + Type: []string{"string"}, + Format: "", + }, + }, + "segmentationType": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationType is the type of segmentation used.", + Type: []string{"string"}, + Format: "", + }, + }, + "segmentationID": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationID is the segmentation identifier.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_Trunk(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Trunk is the Schema for an ORC resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata contains the object metadata", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "spec specifies the desired state of the resource.", + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status defines the observed state of the resource.", + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSpec", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkFilter(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TrunkFilter specifies a filter to select a trunk. At least one parameter must be specified.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name of the existing resource", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description of the existing resource", + Type: []string{"string"}, + Format: "", + }, + }, + "portRef": { + SchemaProps: spec.SchemaProps{ + Description: "portRef is a reference to the ORC Port which this trunk is associated with.", + Type: []string{"string"}, + Format: "", + }, + }, + "projectRef": { + SchemaProps: spec.SchemaProps{ + Description: "projectRef is a reference to the ORC Project this resource is associated with. Typically, only used by admin.", + Type: []string{"string"}, + Format: "", + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tags is a list of tags to filter by. If specified, the resource must have all of the tags specified to be included in the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "tagsAny": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tagsAny is a list of tags to filter by. If specified, the resource must have at least one of the tags specified to be included in the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "notTags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "notTags is a list of tags to filter by. If specified, resources which contain all of the given tags will be excluded from the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "notTagsAny": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "notTagsAny is a list of tags to filter by. If specified, resources which contain any of the given tags will be excluded from the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkImport(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TrunkImport specifies an existing resource which will be imported instead of creating a new one", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Description: "id contains the unique identifier of an existing OpenStack resource. Note that when specifying an import by ID, the resource MUST already exist. The ORC object will enter an error state if the resource does not exist.", + Type: []string{"string"}, + Format: "", + }, + }, + "filter": { + SchemaProps: spec.SchemaProps{ + Description: "filter contains a resource query which is expected to return a single result. The controller will continue to retry if filter returns no results. If filter returns multiple results the controller will set an error state and will not continue to retry.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkFilter"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkFilter"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TrunkList contains a list of Trunk.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "metadata contains the list metadata", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "items contains a list of Trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Trunk"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Trunk", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is a human-readable name of the trunk. If not set, the object's name will be used.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description is a human-readable description for the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "portRef": { + SchemaProps: spec.SchemaProps{ + Description: "portRef is a reference to the ORC Port which will be used as the parent port for this trunk.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tags is a list of tags which will be applied to the trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "adminStateUp is the administrative state of the trunk, which is up (true) or down (false).", + Type: []string{"boolean"}, + Format: "", + }, + }, + "subports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "subports are the subports that will be attached to this trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Subport"), + }, + }, + }, + }, + }, + "projectRef": { + SchemaProps: spec.SchemaProps{ + Description: "projectRef is a reference to the ORC Project this resource is associated with. Typically, only used by admin.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"portRef"}, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Subport"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the human-readable name of the resource. Might not be unique.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description is a human-readable description for the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "portID": { + SchemaProps: spec.SchemaProps{ + Description: "portID is the ID of the parent port.", + Type: []string{"string"}, + Format: "", + }, + }, + "projectID": { + SchemaProps: spec.SchemaProps{ + Description: "projectID is the project owner of the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status indicates the current status of the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tags is the list of tags on the resource.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "adminStateUp is the administrative state of the trunk, which is up (true) or down (false).", + Type: []string{"boolean"}, + Format: "", + }, + }, + "subports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "subports is a list of subports attached to this trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubportStatus"), + }, + }, + }, + }, + }, + "createdAt": { + SchemaProps: spec.SchemaProps{ + Description: "createdAt shows the date and time when the resource was created. The date and time stamp format is ISO 8601", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "updatedAt": { + SchemaProps: spec.SchemaProps{ + Description: "updatedAt shows the date and time when the resource was updated. The date and time stamp format is ISO 8601", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "revisionNumber": { + SchemaProps: spec.SchemaProps{ + Description: "revisionNumber optionally set via extensions/standard-attr-revisions", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubportStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TrunkSpec defines the desired state of an ORC object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "import": { + SchemaProps: spec.SchemaProps{ + Description: "import refers to an existing OpenStack resource which will be imported instead of creating a new one.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkImport"), + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "resource specifies the desired state of the resource.\n\nresource may not be specified if the management policy is `unmanaged`.\n\nresource must be specified if the management policy is `managed`.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkResourceSpec"), + }, + }, + "managementPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "managementPolicy defines how ORC will treat the object. Valid values are `managed`: ORC will create, update, and delete the resource; `unmanaged`: ORC will import an existing resource, and will not apply updates to it or delete it.", + Type: []string{"string"}, + Format: "", + }, + }, + "managedOptions": { + SchemaProps: spec.SchemaProps{ + Description: "managedOptions specifies options which may be applied to managed objects.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ManagedOptions"), + }, + }, + "cloudCredentialsRef": { + SchemaProps: spec.SchemaProps{ + Description: "cloudCredentialsRef points to a secret containing OpenStack credentials", + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.CloudCredentialsReference"), + }, + }, + }, + Required: []string{"cloudCredentialsRef"}, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.CloudCredentialsReference", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ManagedOptions", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkImport", "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkResourceSpec"}, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TrunkStatus defines the observed state of an ORC resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "type", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "conditions represents the observed status of the object. Known .status.conditions.type are: \"Available\", \"Progressing\"\n\nAvailable represents the availability of the OpenStack resource. If it is true then the resource is ready for use.\n\nProgressing indicates whether the controller is still attempting to reconcile the current state of the OpenStack resource to the desired state. Progressing will be False either because the desired state has been achieved, or because some terminal error prevents it from ever being achieved and the controller is no longer attempting to reconcile. If Progressing is True, an observer waiting on the resource should continue to wait.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + "id": { + SchemaProps: spec.SchemaProps{ + Description: "id is the unique identifier of the OpenStack resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "resource contains the observed state of the OpenStack resource.", + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkResourceStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkResourceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + } +} + func schema_openstack_resource_controller_v2_api_v1alpha1_UserDataSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/config/crd/bases/openstack.k-orc.cloud_trunks.yaml b/config/crd/bases/openstack.k-orc.cloud_trunks.yaml new file mode 100644 index 000000000..5daf493e7 --- /dev/null +++ b/config/crd/bases/openstack.k-orc.cloud_trunks.yaml @@ -0,0 +1,485 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.1 + name: trunks.openstack.k-orc.cloud +spec: + group: openstack.k-orc.cloud + names: + categories: + - openstack + kind: Trunk + listKind: TrunkList + plural: trunks + singular: trunk + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Resource ID + jsonPath: .status.id + name: ID + type: string + - description: Availability status of resource + jsonPath: .status.conditions[?(@.type=='Available')].status + name: Available + type: string + - description: Message describing current progress status + jsonPath: .status.conditions[?(@.type=='Progressing')].message + name: Message + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Trunk is the Schema for an ORC resource. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec specifies the desired state of the resource. + properties: + cloudCredentialsRef: + description: cloudCredentialsRef points to a secret containing OpenStack + credentials + properties: + cloudName: + description: cloudName specifies the name of the entry in the + clouds.yaml file to use. + maxLength: 256 + minLength: 1 + type: string + secretName: + description: |- + secretName is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain a key named `clouds.yaml` which contains an OpenStack clouds.yaml file. + The secret may optionally contain a key named `cacert` containing a PEM-encoded CA certificate. + maxLength: 253 + minLength: 1 + type: string + required: + - cloudName + - secretName + type: object + import: + description: |- + import refers to an existing OpenStack resource which will be imported instead of + creating a new one. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: |- + filter contains a resource query which is expected to return a single + result. The controller will continue to retry if filter returns no + results. If filter returns multiple results the controller will set an + error state and will not continue to retry. + minProperties: 1 + properties: + description: + description: description of the existing resource + maxLength: 255 + minLength: 1 + type: string + name: + description: name of the existing resource + maxLength: 255 + minLength: 1 + pattern: ^[^,]+$ + type: string + notTags: + description: |- + notTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + notTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + portRef: + description: portRef is a reference to the ORC Port which + this trunk is associated with. + maxLength: 253 + minLength: 1 + type: string + projectRef: + description: |- + projectRef is a reference to the ORC Project this resource is associated with. + Typically, only used by admin. + maxLength: 253 + minLength: 1 + type: string + tags: + description: |- + tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + tagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + type: object + id: + description: |- + id contains the unique identifier of an existing OpenStack resource. Note + that when specifying an import by ID, the resource MUST already exist. + The ORC object will enter an error state if the resource does not exist. + format: uuid + type: string + type: object + managedOptions: + description: managedOptions specifies options which may be applied + to managed objects. + properties: + onDelete: + default: delete + description: |- + onDelete specifies the behaviour of the controller when the ORC + object is deleted. Options are `delete` - delete the OpenStack resource; + `detach` - do not delete the OpenStack resource. If not specified, the + default is `delete`. + enum: + - delete + - detach + type: string + type: object + managementPolicy: + default: managed + description: |- + managementPolicy defines how ORC will treat the object. Valid values are + `managed`: ORC will create, update, and delete the resource; `unmanaged`: + ORC will import an existing resource, and will not apply updates to it or + delete it. + enum: + - managed + - unmanaged + type: string + x-kubernetes-validations: + - message: managementPolicy is immutable + rule: self == oldSelf + resource: + description: |- + resource specifies the desired state of the resource. + + resource may not be specified if the management policy is `unmanaged`. + + resource must be specified if the management policy is `managed`. + properties: + adminStateUp: + description: adminStateUp is the administrative state of the trunk, + which is up (true) or down (false). + type: boolean + description: + description: description is a human-readable description for the + resource. + maxLength: 255 + minLength: 1 + type: string + name: + description: name is a human-readable name of the trunk. If not + set, the object's name will be used. + maxLength: 255 + minLength: 1 + pattern: ^[^,]+$ + type: string + portRef: + description: portRef is a reference to the ORC Port which will + be used as the parent port for this trunk. + maxLength: 253 + minLength: 1 + type: string + projectRef: + description: |- + projectRef is a reference to the ORC Project this resource is associated with. + Typically, only used by admin. + maxLength: 253 + minLength: 1 + type: string + x-kubernetes-validations: + - message: projectRef is immutable + rule: self == oldSelf + subports: + description: subports are the subports that will be attached to + this trunk. + items: + description: Subport represents a subport that will be attached + to a trunk. + properties: + portRef: + description: portRef is a reference to the ORC Port which + will be used as a subport. + maxLength: 253 + minLength: 1 + type: string + segmentationID: + description: segmentationID is the segmentation identifier + (e.g., VLAN ID). + format: int32 + type: integer + segmentationType: + description: segmentationType is the type of segmentation + to use (e.g., "vlan"). + maxLength: 64 + type: string + required: + - portRef + - segmentationID + - segmentationType + type: object + maxItems: 128 + type: array + x-kubernetes-list-type: atomic + tags: + description: tags is a list of tags which will be applied to the + trunk. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + required: + - portRef + type: object + x-kubernetes-validations: + - message: portRef is immutable + rule: self == oldSelf + required: + - cloudCredentialsRef + type: object + x-kubernetes-validations: + - message: resource must be specified when policy is managed + rule: 'self.managementPolicy == ''managed'' ? has(self.resource) : true' + - message: import may not be specified when policy is managed + rule: 'self.managementPolicy == ''managed'' ? !has(self.__import__) + : true' + - message: resource may not be specified when policy is unmanaged + rule: 'self.managementPolicy == ''unmanaged'' ? !has(self.resource) + : true' + - message: import must be specified when policy is unmanaged + rule: 'self.managementPolicy == ''unmanaged'' ? has(self.__import__) + : true' + - message: managedOptions may only be provided when policy is managed + rule: 'has(self.managedOptions) ? self.managementPolicy == ''managed'' + : true' + status: + description: status defines the observed state of the resource. + properties: + conditions: + description: |- + conditions represents the observed status of the object. + Known .status.conditions.type are: "Available", "Progressing" + + Available represents the availability of the OpenStack resource. If it is + true then the resource is ready for use. + + Progressing indicates whether the controller is still attempting to + reconcile the current state of the OpenStack resource to the desired + state. Progressing will be False either because the desired state has + been achieved, or because some terminal error prevents it from ever being + achieved and the controller is no longer attempting to reconcile. If + Progressing is True, an observer waiting on the resource should continue + to wait. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + id: + description: id is the unique identifier of the OpenStack resource. + type: string + resource: + description: resource contains the observed state of the OpenStack + resource. + properties: + adminStateUp: + description: |- + adminStateUp is the administrative state of the trunk, + which is up (true) or down (false). + type: boolean + createdAt: + description: createdAt shows the date and time when the resource + was created. The date and time stamp format is ISO 8601 + format: date-time + type: string + description: + description: description is a human-readable description for the + resource. + maxLength: 1024 + type: string + name: + description: name is the human-readable name of the resource. + Might not be unique. + maxLength: 1024 + type: string + portID: + description: portID is the ID of the parent port. + maxLength: 1024 + type: string + projectID: + description: projectID is the project owner of the resource. + maxLength: 1024 + type: string + revisionNumber: + description: revisionNumber optionally set via extensions/standard-attr-revisions + format: int64 + type: integer + status: + description: status indicates the current status of the resource. + maxLength: 1024 + type: string + subports: + description: subports is a list of subports attached to this trunk. + items: + description: SubportStatus represents the observed state of + a subport. + properties: + portID: + description: portID is the ID of the port used as a subport. + maxLength: 1024 + type: string + segmentationID: + description: segmentationID is the segmentation identifier. + format: int32 + type: integer + segmentationType: + description: segmentationType is the type of segmentation + used. + maxLength: 1024 + type: string + type: object + maxItems: 128 + type: array + x-kubernetes-list-type: atomic + tags: + description: tags is the list of tags on the resource. + items: + maxLength: 1024 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: atomic + updatedAt: + description: updatedAt shows the date and time when the resource + was updated. The date and time stamp format is ISO 8601 + format: date-time + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 5a0a7443b..5545c52a6 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -34,6 +34,7 @@ rules: - servers - services - subnets + - trunks - volumes - volumetypes verbs: @@ -64,6 +65,7 @@ rules: - servers/status - services/status - subnets/status + - trunks/status - volumes/status - volumetypes/status verbs: diff --git a/internal/controllers/trunk/actuator.go b/internal/controllers/trunk/actuator.go index 12830bdf0..91d742ce5 100644 --- a/internal/controllers/trunk/actuator.go +++ b/internal/controllers/trunk/actuator.go @@ -20,8 +20,6 @@ import ( "context" "fmt" "iter" - "slices" - "time" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" corev1 "k8s.io/api/core/v1" diff --git a/internal/controllers/trunk/actuator_test.go b/internal/controllers/trunk/actuator_test.go index a764793a1..6eaec4345 100644 --- a/internal/controllers/trunk/actuator_test.go +++ b/internal/controllers/trunk/actuator_test.go @@ -191,12 +191,4 @@ func handleAdminStateUpUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alph } } -// getResourceName returns the name of the OpenStack resource we should use. -// This is a test helper that mirrors the function in the generated adapter file. -func getResourceName(orcObject *orcv1alpha1.Trunk) string { - if orcObject.Spec.Resource != nil && orcObject.Spec.Resource.Name != nil { - return string(*orcObject.Spec.Resource.Name) - } - return orcObject.Name -} diff --git a/internal/controllers/trunk/controller.go b/internal/controllers/trunk/controller.go index e52a04df1..5fead1d3e 100644 --- a/internal/controllers/trunk/controller.go +++ b/internal/controllers/trunk/controller.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The ORC Authors. +Copyright 2025 The ORC Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -76,6 +76,7 @@ var ( return ports }, finalizer, externalObjectFieldOwner, + dependency.OverrideDependencyName("subport"), ) projectDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Project]( diff --git a/internal/controllers/trunk/status.go b/internal/controllers/trunk/status.go index 7fe50906f..cfdc32e19 100644 --- a/internal/controllers/trunk/status.go +++ b/internal/controllers/trunk/status.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The ORC Authors. +Copyright 2025 The ORC Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/internal/controllers/trunk/zz_generated.adapter.go b/internal/controllers/trunk/zz_generated.adapter.go new file mode 100644 index 000000000..9e6bef3b1 --- /dev/null +++ b/internal/controllers/trunk/zz_generated.adapter.go @@ -0,0 +1,88 @@ +// Code generated by resource-generator. DO NOT EDIT. +/* +Copyright 2025 The ORC Authors. + +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 trunk + +import ( + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" +) + +// Fundamental types +type ( + orcObjectT = orcv1alpha1.Trunk + orcObjectListT = orcv1alpha1.TrunkList + resourceSpecT = orcv1alpha1.TrunkResourceSpec + filterT = orcv1alpha1.TrunkFilter +) + +// Derived types +type ( + orcObjectPT = *orcObjectT + adapterI = interfaces.APIObjectAdapter[orcObjectPT, resourceSpecT, filterT] + adapterT = trunkAdapter +) + +type trunkAdapter struct { + *orcv1alpha1.Trunk +} + +var _ adapterI = &adapterT{} + +func (f adapterT) GetObject() orcObjectPT { + return f.Trunk +} + +func (f adapterT) GetManagementPolicy() orcv1alpha1.ManagementPolicy { + return f.Spec.ManagementPolicy +} + +func (f adapterT) GetManagedOptions() *orcv1alpha1.ManagedOptions { + return f.Spec.ManagedOptions +} + +func (f adapterT) GetStatusID() *string { + return f.Status.ID +} + +func (f adapterT) GetResourceSpec() *resourceSpecT { + return f.Spec.Resource +} + +func (f adapterT) GetImportID() *string { + if f.Spec.Import == nil { + return nil + } + return f.Spec.Import.ID +} + +func (f adapterT) GetImportFilter() *filterT { + if f.Spec.Import == nil { + return nil + } + return f.Spec.Import.Filter +} + +// getResourceName returns the name of the OpenStack resource we should use. +// This method is not implemented as part of APIObjectAdapter as it is intended +// to be used by resource actuators, which don't use the adapter. +func getResourceName(orcObject orcObjectPT) string { + if orcObject.Spec.Resource.Name != nil { + return string(*orcObject.Spec.Resource.Name) + } + return orcObject.Name +} diff --git a/internal/controllers/trunk/zz_generated.controller.go b/internal/controllers/trunk/zz_generated.controller.go new file mode 100644 index 000000000..e99ea3d01 --- /dev/null +++ b/internal/controllers/trunk/zz_generated.controller.go @@ -0,0 +1,45 @@ +// Code generated by resource-generator. DO NOT EDIT. +/* +Copyright 2025 The ORC Authors. + +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 trunk + +import ( + corev1 "k8s.io/api/core/v1" + + "github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency" + orcstrings "github.com/k-orc/openstack-resource-controller/v2/internal/util/strings" +) + +var ( + // NOTE: controllerName must be defined in any controller using this template + + // finalizer is the string this controller adds to an object's Finalizers + finalizer = orcstrings.GetFinalizerName(controllerName) + + // externalObjectFieldOwner is the field owner we use when using + // server-side-apply on objects we don't control + externalObjectFieldOwner = orcstrings.GetSSAFieldOwner(controllerName) + + credentialsDependency = dependency.NewDeletionGuardDependency[*orcObjectListT, *corev1.Secret]( + "spec.cloudCredentialsRef.secretName", + func(obj orcObjectPT) []string { + return []string{obj.Spec.CloudCredentialsRef.SecretName} + }, + finalizer, externalObjectFieldOwner, + dependency.OverrideDependencyName("credentials"), + ) +) diff --git a/internal/osclients/mock/networking.go b/internal/osclients/mock/networking.go index 9b5e25046..41baf525f 100644 --- a/internal/osclients/mock/networking.go +++ b/internal/osclients/mock/networking.go @@ -34,6 +34,7 @@ import ( routers "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" groups "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" rules "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" + trunks "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" networks "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" ports "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" subnets "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" @@ -80,6 +81,21 @@ func (mr *MockNetworkClientMockRecorder) AddRouterInterface(ctx, id, opts any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRouterInterface", reflect.TypeOf((*MockNetworkClient)(nil).AddRouterInterface), ctx, id, opts) } +// AddSubports mocks base method. +func (m *MockNetworkClient) AddSubports(ctx context.Context, id string, opts trunks.AddSubportsOptsBuilder) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddSubports", ctx, id, opts) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddSubports indicates an expected call of AddSubports. +func (mr *MockNetworkClientMockRecorder) AddSubports(ctx, id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubports", reflect.TypeOf((*MockNetworkClient)(nil).AddSubports), ctx, id, opts) +} + // CreateFloatingIP mocks base method. func (m *MockNetworkClient) CreateFloatingIP(ctx context.Context, opts floatingips.CreateOptsBuilder) (*floatingips.FloatingIP, error) { m.ctrl.T.Helper() @@ -185,6 +201,21 @@ func (mr *MockNetworkClientMockRecorder) CreateSubnet(ctx, opts any) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSubnet", reflect.TypeOf((*MockNetworkClient)(nil).CreateSubnet), ctx, opts) } +// CreateTrunk mocks base method. +func (m *MockNetworkClient) CreateTrunk(ctx context.Context, opts trunks.CreateOptsBuilder) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateTrunk", ctx, opts) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateTrunk indicates an expected call of CreateTrunk. +func (mr *MockNetworkClientMockRecorder) CreateTrunk(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTrunk", reflect.TypeOf((*MockNetworkClient)(nil).CreateTrunk), ctx, opts) +} + // DeleteFloatingIP mocks base method. func (m *MockNetworkClient) DeleteFloatingIP(ctx context.Context, id string) error { m.ctrl.T.Helper() @@ -283,6 +314,20 @@ func (mr *MockNetworkClientMockRecorder) DeleteSubnet(ctx, id any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSubnet", reflect.TypeOf((*MockNetworkClient)(nil).DeleteSubnet), ctx, id) } +// DeleteTrunk mocks base method. +func (m *MockNetworkClient) DeleteTrunk(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteTrunk", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteTrunk indicates an expected call of DeleteTrunk. +func (mr *MockNetworkClientMockRecorder) DeleteTrunk(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTrunk", reflect.TypeOf((*MockNetworkClient)(nil).DeleteTrunk), ctx, id) +} + // GetFloatingIP mocks base method. func (m *MockNetworkClient) GetFloatingIP(ctx context.Context, id string) (*floatingips.FloatingIP, error) { m.ctrl.T.Helper() @@ -388,6 +433,21 @@ func (mr *MockNetworkClientMockRecorder) GetSubnet(ctx, id any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnet", reflect.TypeOf((*MockNetworkClient)(nil).GetSubnet), ctx, id) } +// GetTrunk mocks base method. +func (m *MockNetworkClient) GetTrunk(ctx context.Context, id string) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTrunk", ctx, id) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTrunk indicates an expected call of GetTrunk. +func (mr *MockNetworkClientMockRecorder) GetTrunk(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTrunk", reflect.TypeOf((*MockNetworkClient)(nil).GetTrunk), ctx, id) +} + // ListFloatingIP mocks base method. func (m *MockNetworkClient) ListFloatingIP(ctx context.Context, opts floatingips.ListOptsBuilder) iter.Seq2[*floatingips.FloatingIP, error] { m.ctrl.T.Helper() @@ -487,6 +547,36 @@ func (mr *MockNetworkClientMockRecorder) ListSubnet(ctx, opts any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubnet", reflect.TypeOf((*MockNetworkClient)(nil).ListSubnet), ctx, opts) } +// ListTrunk mocks base method. +func (m *MockNetworkClient) ListTrunk(ctx context.Context, opts trunks.ListOptsBuilder) ([]trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListTrunk", ctx, opts) + ret0, _ := ret[0].([]trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListTrunk indicates an expected call of ListTrunk. +func (mr *MockNetworkClientMockRecorder) ListTrunk(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTrunk", reflect.TypeOf((*MockNetworkClient)(nil).ListTrunk), ctx, opts) +} + +// ListTrunkSubports mocks base method. +func (m *MockNetworkClient) ListTrunkSubports(ctx context.Context, trunkID string) ([]trunks.Subport, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListTrunkSubports", ctx, trunkID) + ret0, _ := ret[0].([]trunks.Subport) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListTrunkSubports indicates an expected call of ListTrunkSubports. +func (mr *MockNetworkClientMockRecorder) ListTrunkSubports(ctx, trunkID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTrunkSubports", reflect.TypeOf((*MockNetworkClient)(nil).ListTrunkSubports), ctx, trunkID) +} + // RemoveRouterInterface mocks base method. func (m *MockNetworkClient) RemoveRouterInterface(ctx context.Context, id string, opts routers.RemoveInterfaceOptsBuilder) (*routers.InterfaceInfo, error) { m.ctrl.T.Helper() @@ -502,6 +592,20 @@ func (mr *MockNetworkClientMockRecorder) RemoveRouterInterface(ctx, id, opts any return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveRouterInterface", reflect.TypeOf((*MockNetworkClient)(nil).RemoveRouterInterface), ctx, id, opts) } +// RemoveSubports mocks base method. +func (m *MockNetworkClient) RemoveSubports(ctx context.Context, id string, opts trunks.RemoveSubportsOptsBuilder) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveSubports", ctx, id, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveSubports indicates an expected call of RemoveSubports. +func (mr *MockNetworkClientMockRecorder) RemoveSubports(ctx, id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveSubports", reflect.TypeOf((*MockNetworkClient)(nil).RemoveSubports), ctx, id, opts) +} + // ReplaceAllAttributesTags mocks base method. func (m *MockNetworkClient) ReplaceAllAttributesTags(ctx context.Context, resourceType, resourceID string, opts attributestags.ReplaceAllOptsBuilder) ([]string, error) { m.ctrl.T.Helper() @@ -606,3 +710,18 @@ func (mr *MockNetworkClientMockRecorder) UpdateSubnet(ctx, id, opts any) *gomock mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSubnet", reflect.TypeOf((*MockNetworkClient)(nil).UpdateSubnet), ctx, id, opts) } + +// UpdateTrunk mocks base method. +func (m *MockNetworkClient) UpdateTrunk(ctx context.Context, id string, opts trunks.UpdateOptsBuilder) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTrunk", ctx, id, opts) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTrunk indicates an expected call of UpdateTrunk. +func (mr *MockNetworkClientMockRecorder) UpdateTrunk(ctx, id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTrunk", reflect.TypeOf((*MockNetworkClient)(nil).UpdateTrunk), ctx, id, opts) +} diff --git a/internal/osclients/networking.go b/internal/osclients/networking.go index 2885f94d7..74e99798d 100644 --- a/internal/osclients/networking.go +++ b/internal/osclients/networking.go @@ -243,10 +243,6 @@ func (c networkClient) ListTrunkSubports(ctx context.Context, trunkID string) ([ return trunks.GetSubports(ctx, c.serviceClient, trunkID).Extract() } -func (c networkClient) RemoveSubports(ctx context.Context, id string, opts trunks.RemoveSubportsOpts) error { - _, err := trunks.RemoveSubports(ctx, c.serviceClient, id, opts).Extract() - return err -} func (c networkClient) ListTrunk(ctx context.Context, opts trunks.ListOptsBuilder) ([]trunks.Trunk, error) { allPages, err := trunks.List(c.serviceClient, opts).AllPages(ctx) @@ -261,7 +257,8 @@ func (c networkClient) AddSubports(ctx context.Context, id string, opts trunks.A } func (c networkClient) RemoveSubports(ctx context.Context, id string, opts trunks.RemoveSubportsOptsBuilder) error { - return trunks.RemoveSubports(ctx, c.serviceClient, id, opts).ExtractErr() + _, err := trunks.RemoveSubports(ctx, c.serviceClient, id, opts).Extract() + return err } func (c networkClient) CreateRouter(ctx context.Context, opts routers.CreateOptsBuilder) (*routers.Router, error) { diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/subport.go b/pkg/clients/applyconfiguration/api/v1alpha1/subport.go new file mode 100644 index 000000000..41d035e28 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/subport.go @@ -0,0 +1,61 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// SubportApplyConfiguration represents a declarative configuration of the Subport type for use +// with apply. +type SubportApplyConfiguration struct { + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + SegmentationType *string `json:"segmentationType,omitempty"` + SegmentationID *int32 `json:"segmentationID,omitempty"` +} + +// SubportApplyConfiguration constructs a declarative configuration of the Subport type for use with +// apply. +func Subport() *SubportApplyConfiguration { + return &SubportApplyConfiguration{} +} + +// WithPortRef sets the PortRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PortRef field is set to the value of the last call. +func (b *SubportApplyConfiguration) WithPortRef(value apiv1alpha1.KubernetesNameRef) *SubportApplyConfiguration { + b.PortRef = &value + return b +} + +// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationType field is set to the value of the last call. +func (b *SubportApplyConfiguration) WithSegmentationType(value string) *SubportApplyConfiguration { + b.SegmentationType = &value + return b +} + +// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationID field is set to the value of the last call. +func (b *SubportApplyConfiguration) WithSegmentationID(value int32) *SubportApplyConfiguration { + b.SegmentationID = &value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go new file mode 100644 index 000000000..7f5982148 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go @@ -0,0 +1,57 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// SubportStatusApplyConfiguration represents a declarative configuration of the SubportStatus type for use +// with apply. +type SubportStatusApplyConfiguration struct { + PortID *string `json:"portID,omitempty"` + SegmentationType *string `json:"segmentationType,omitempty"` + SegmentationID *int32 `json:"segmentationID,omitempty"` +} + +// SubportStatusApplyConfiguration constructs a declarative configuration of the SubportStatus type for use with +// apply. +func SubportStatus() *SubportStatusApplyConfiguration { + return &SubportStatusApplyConfiguration{} +} + +// WithPortID sets the PortID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PortID field is set to the value of the last call. +func (b *SubportStatusApplyConfiguration) WithPortID(value string) *SubportStatusApplyConfiguration { + b.PortID = &value + return b +} + +// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationType field is set to the value of the last call. +func (b *SubportStatusApplyConfiguration) WithSegmentationType(value string) *SubportStatusApplyConfiguration { + b.SegmentationType = &value + return b +} + +// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationID field is set to the value of the last call. +func (b *SubportStatusApplyConfiguration) WithSegmentationID(value int32) *SubportStatusApplyConfiguration { + b.SegmentationID = &value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunk.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunk.go new file mode 100644 index 000000000..948d40e2d --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunk.go @@ -0,0 +1,281 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + internal "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/internal" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// TrunkApplyConfiguration represents a declarative configuration of the Trunk type for use +// with apply. +type TrunkApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *TrunkSpecApplyConfiguration `json:"spec,omitempty"` + Status *TrunkStatusApplyConfiguration `json:"status,omitempty"` +} + +// Trunk constructs a declarative configuration of the Trunk type for use with +// apply. +func Trunk(name, namespace string) *TrunkApplyConfiguration { + b := &TrunkApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("Trunk") + b.WithAPIVersion("openstack.k-orc.cloud/v1alpha1") + return b +} + +// ExtractTrunk extracts the applied configuration owned by fieldManager from +// trunk. If no managedFields are found in trunk for fieldManager, a +// TrunkApplyConfiguration is returned with only the Name, Namespace (if applicable), +// APIVersion and Kind populated. It is possible that no managed fields were found for because other +// field managers have taken ownership of all the fields previously owned by fieldManager, or because +// the fieldManager never owned fields any fields. +// trunk must be a unmodified Trunk API object that was retrieved from the Kubernetes API. +// ExtractTrunk provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +// Experimental! +func ExtractTrunk(trunk *apiv1alpha1.Trunk, fieldManager string) (*TrunkApplyConfiguration, error) { + return extractTrunk(trunk, fieldManager, "") +} + +// ExtractTrunkStatus is the same as ExtractTrunk except +// that it extracts the status subresource applied configuration. +// Experimental! +func ExtractTrunkStatus(trunk *apiv1alpha1.Trunk, fieldManager string) (*TrunkApplyConfiguration, error) { + return extractTrunk(trunk, fieldManager, "status") +} + +func extractTrunk(trunk *apiv1alpha1.Trunk, fieldManager string, subresource string) (*TrunkApplyConfiguration, error) { + b := &TrunkApplyConfiguration{} + err := managedfields.ExtractInto(trunk, internal.Parser().Type("com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Trunk"), fieldManager, b, subresource) + if err != nil { + return nil, err + } + b.WithName(trunk.Name) + b.WithNamespace(trunk.Namespace) + + b.WithKind("Trunk") + b.WithAPIVersion("openstack.k-orc.cloud/v1alpha1") + return b, nil +} +func (b TrunkApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithKind(value string) *TrunkApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithAPIVersion(value string) *TrunkApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithName(value string) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithGenerateName(value string) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithNamespace(value string) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithUID(value types.UID) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithResourceVersion(value string) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithGeneration(value int64) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithCreationTimestamp(value metav1.Time) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *TrunkApplyConfiguration) WithLabels(entries map[string]string) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *TrunkApplyConfiguration) WithAnnotations(entries map[string]string) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *TrunkApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *TrunkApplyConfiguration) WithFinalizers(values ...string) *TrunkApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *TrunkApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithSpec(value *TrunkSpecApplyConfiguration) *TrunkApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *TrunkApplyConfiguration) WithStatus(value *TrunkStatusApplyConfiguration) *TrunkApplyConfiguration { + b.Status = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *TrunkApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *TrunkApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *TrunkApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *TrunkApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go new file mode 100644 index 000000000..3ab2b232a --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go @@ -0,0 +1,111 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// TrunkFilterApplyConfiguration represents a declarative configuration of the TrunkFilter type for use +// with apply. +type TrunkFilterApplyConfiguration struct { + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` + FilterByNeutronTagsApplyConfiguration `json:",inline"` +} + +// TrunkFilterApplyConfiguration constructs a declarative configuration of the TrunkFilter type for use with +// apply. +func TrunkFilter() *TrunkFilterApplyConfiguration { + return &TrunkFilterApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TrunkFilterApplyConfiguration) WithName(value apiv1alpha1.OpenStackName) *TrunkFilterApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *TrunkFilterApplyConfiguration) WithDescription(value apiv1alpha1.NeutronDescription) *TrunkFilterApplyConfiguration { + b.Description = &value + return b +} + +// WithPortRef sets the PortRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PortRef field is set to the value of the last call. +func (b *TrunkFilterApplyConfiguration) WithPortRef(value apiv1alpha1.KubernetesNameRef) *TrunkFilterApplyConfiguration { + b.PortRef = &value + return b +} + +// WithProjectRef sets the ProjectRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ProjectRef field is set to the value of the last call. +func (b *TrunkFilterApplyConfiguration) WithProjectRef(value apiv1alpha1.KubernetesNameRef) *TrunkFilterApplyConfiguration { + b.ProjectRef = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *TrunkFilterApplyConfiguration) WithTags(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.Tags = append(b.FilterByNeutronTagsApplyConfiguration.Tags, values[i]) + } + return b +} + +// WithTagsAny adds the given value to the TagsAny field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the TagsAny field. +func (b *TrunkFilterApplyConfiguration) WithTagsAny(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.TagsAny = append(b.FilterByNeutronTagsApplyConfiguration.TagsAny, values[i]) + } + return b +} + +// WithNotTags adds the given value to the NotTags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the NotTags field. +func (b *TrunkFilterApplyConfiguration) WithNotTags(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.NotTags = append(b.FilterByNeutronTagsApplyConfiguration.NotTags, values[i]) + } + return b +} + +// WithNotTagsAny adds the given value to the NotTagsAny field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the NotTagsAny field. +func (b *TrunkFilterApplyConfiguration) WithNotTagsAny(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.NotTagsAny = append(b.FilterByNeutronTagsApplyConfiguration.NotTagsAny, values[i]) + } + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkimport.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkimport.go new file mode 100644 index 000000000..5b482302f --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkimport.go @@ -0,0 +1,48 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TrunkImportApplyConfiguration represents a declarative configuration of the TrunkImport type for use +// with apply. +type TrunkImportApplyConfiguration struct { + ID *string `json:"id,omitempty"` + Filter *TrunkFilterApplyConfiguration `json:"filter,omitempty"` +} + +// TrunkImportApplyConfiguration constructs a declarative configuration of the TrunkImport type for use with +// apply. +func TrunkImport() *TrunkImportApplyConfiguration { + return &TrunkImportApplyConfiguration{} +} + +// WithID sets the ID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ID field is set to the value of the last call. +func (b *TrunkImportApplyConfiguration) WithID(value string) *TrunkImportApplyConfiguration { + b.ID = &value + return b +} + +// WithFilter sets the Filter field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Filter field is set to the value of the last call. +func (b *TrunkImportApplyConfiguration) WithFilter(value *TrunkFilterApplyConfiguration) *TrunkImportApplyConfiguration { + b.Filter = value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go new file mode 100644 index 000000000..1e0fc7711 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go @@ -0,0 +1,104 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// TrunkResourceSpecApplyConfiguration represents a declarative configuration of the TrunkResourceSpec type for use +// with apply. +type TrunkResourceSpecApplyConfiguration struct { + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + Tags []apiv1alpha1.NeutronTag `json:"tags,omitempty"` + AdminStateUp *bool `json:"adminStateUp,omitempty"` + Subports []SubportApplyConfiguration `json:"subports,omitempty"` + ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` +} + +// TrunkResourceSpecApplyConfiguration constructs a declarative configuration of the TrunkResourceSpec type for use with +// apply. +func TrunkResourceSpec() *TrunkResourceSpecApplyConfiguration { + return &TrunkResourceSpecApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TrunkResourceSpecApplyConfiguration) WithName(value apiv1alpha1.OpenStackName) *TrunkResourceSpecApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *TrunkResourceSpecApplyConfiguration) WithDescription(value apiv1alpha1.NeutronDescription) *TrunkResourceSpecApplyConfiguration { + b.Description = &value + return b +} + +// WithPortRef sets the PortRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PortRef field is set to the value of the last call. +func (b *TrunkResourceSpecApplyConfiguration) WithPortRef(value apiv1alpha1.KubernetesNameRef) *TrunkResourceSpecApplyConfiguration { + b.PortRef = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *TrunkResourceSpecApplyConfiguration) WithTags(values ...apiv1alpha1.NeutronTag) *TrunkResourceSpecApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *TrunkResourceSpecApplyConfiguration) WithAdminStateUp(value bool) *TrunkResourceSpecApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithSubports adds the given value to the Subports field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Subports field. +func (b *TrunkResourceSpecApplyConfiguration) WithSubports(values ...*SubportApplyConfiguration) *TrunkResourceSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSubports") + } + b.Subports = append(b.Subports, *values[i]) + } + return b +} + +// WithProjectRef sets the ProjectRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ProjectRef field is set to the value of the last call. +func (b *TrunkResourceSpecApplyConfiguration) WithProjectRef(value apiv1alpha1.KubernetesNameRef) *TrunkResourceSpecApplyConfiguration { + b.ProjectRef = &value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go new file mode 100644 index 000000000..9f997f571 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go @@ -0,0 +1,138 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TrunkResourceStatusApplyConfiguration represents a declarative configuration of the TrunkResourceStatus type for use +// with apply. +type TrunkResourceStatusApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + PortID *string `json:"portID,omitempty"` + ProjectID *string `json:"projectID,omitempty"` + Status *string `json:"status,omitempty"` + Tags []string `json:"tags,omitempty"` + AdminStateUp *bool `json:"adminStateUp,omitempty"` + Subports []SubportStatusApplyConfiguration `json:"subports,omitempty"` + NeutronStatusMetadataApplyConfiguration `json:",inline"` +} + +// TrunkResourceStatusApplyConfiguration constructs a declarative configuration of the TrunkResourceStatus type for use with +// apply. +func TrunkResourceStatus() *TrunkResourceStatusApplyConfiguration { + return &TrunkResourceStatusApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithName(value string) *TrunkResourceStatusApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithDescription(value string) *TrunkResourceStatusApplyConfiguration { + b.Description = &value + return b +} + +// WithPortID sets the PortID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PortID field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithPortID(value string) *TrunkResourceStatusApplyConfiguration { + b.PortID = &value + return b +} + +// WithProjectID sets the ProjectID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ProjectID field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithProjectID(value string) *TrunkResourceStatusApplyConfiguration { + b.ProjectID = &value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithStatus(value string) *TrunkResourceStatusApplyConfiguration { + b.Status = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *TrunkResourceStatusApplyConfiguration) WithTags(values ...string) *TrunkResourceStatusApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithAdminStateUp(value bool) *TrunkResourceStatusApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithSubports adds the given value to the Subports field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Subports field. +func (b *TrunkResourceStatusApplyConfiguration) WithSubports(values ...*SubportStatusApplyConfiguration) *TrunkResourceStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSubports") + } + b.Subports = append(b.Subports, *values[i]) + } + return b +} + +// WithCreatedAt sets the CreatedAt field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreatedAt field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithCreatedAt(value v1.Time) *TrunkResourceStatusApplyConfiguration { + b.NeutronStatusMetadataApplyConfiguration.CreatedAt = &value + return b +} + +// WithUpdatedAt sets the UpdatedAt field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UpdatedAt field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithUpdatedAt(value v1.Time) *TrunkResourceStatusApplyConfiguration { + b.NeutronStatusMetadataApplyConfiguration.UpdatedAt = &value + return b +} + +// WithRevisionNumber sets the RevisionNumber field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RevisionNumber field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithRevisionNumber(value int64) *TrunkResourceStatusApplyConfiguration { + b.NeutronStatusMetadataApplyConfiguration.RevisionNumber = &value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkspec.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkspec.go new file mode 100644 index 000000000..2eaef7903 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkspec.go @@ -0,0 +1,79 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// TrunkSpecApplyConfiguration represents a declarative configuration of the TrunkSpec type for use +// with apply. +type TrunkSpecApplyConfiguration struct { + Import *TrunkImportApplyConfiguration `json:"import,omitempty"` + Resource *TrunkResourceSpecApplyConfiguration `json:"resource,omitempty"` + ManagementPolicy *apiv1alpha1.ManagementPolicy `json:"managementPolicy,omitempty"` + ManagedOptions *ManagedOptionsApplyConfiguration `json:"managedOptions,omitempty"` + CloudCredentialsRef *CloudCredentialsReferenceApplyConfiguration `json:"cloudCredentialsRef,omitempty"` +} + +// TrunkSpecApplyConfiguration constructs a declarative configuration of the TrunkSpec type for use with +// apply. +func TrunkSpec() *TrunkSpecApplyConfiguration { + return &TrunkSpecApplyConfiguration{} +} + +// WithImport sets the Import field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Import field is set to the value of the last call. +func (b *TrunkSpecApplyConfiguration) WithImport(value *TrunkImportApplyConfiguration) *TrunkSpecApplyConfiguration { + b.Import = value + return b +} + +// WithResource sets the Resource field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Resource field is set to the value of the last call. +func (b *TrunkSpecApplyConfiguration) WithResource(value *TrunkResourceSpecApplyConfiguration) *TrunkSpecApplyConfiguration { + b.Resource = value + return b +} + +// WithManagementPolicy sets the ManagementPolicy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ManagementPolicy field is set to the value of the last call. +func (b *TrunkSpecApplyConfiguration) WithManagementPolicy(value apiv1alpha1.ManagementPolicy) *TrunkSpecApplyConfiguration { + b.ManagementPolicy = &value + return b +} + +// WithManagedOptions sets the ManagedOptions field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ManagedOptions field is set to the value of the last call. +func (b *TrunkSpecApplyConfiguration) WithManagedOptions(value *ManagedOptionsApplyConfiguration) *TrunkSpecApplyConfiguration { + b.ManagedOptions = value + return b +} + +// WithCloudCredentialsRef sets the CloudCredentialsRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CloudCredentialsRef field is set to the value of the last call. +func (b *TrunkSpecApplyConfiguration) WithCloudCredentialsRef(value *CloudCredentialsReferenceApplyConfiguration) *TrunkSpecApplyConfiguration { + b.CloudCredentialsRef = value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkstatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkstatus.go new file mode 100644 index 000000000..ccf85550d --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkstatus.go @@ -0,0 +1,66 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// TrunkStatusApplyConfiguration represents a declarative configuration of the TrunkStatus type for use +// with apply. +type TrunkStatusApplyConfiguration struct { + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` + ID *string `json:"id,omitempty"` + Resource *TrunkResourceStatusApplyConfiguration `json:"resource,omitempty"` +} + +// TrunkStatusApplyConfiguration constructs a declarative configuration of the TrunkStatus type for use with +// apply. +func TrunkStatus() *TrunkStatusApplyConfiguration { + return &TrunkStatusApplyConfiguration{} +} + +// WithConditions adds the given value to the Conditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Conditions field. +func (b *TrunkStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *TrunkStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} + +// WithID sets the ID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ID field is set to the value of the last call. +func (b *TrunkStatusApplyConfiguration) WithID(value string) *TrunkStatusApplyConfiguration { + b.ID = &value + return b +} + +// WithResource sets the Resource field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Resource field is set to the value of the last call. +func (b *TrunkStatusApplyConfiguration) WithResource(value *TrunkResourceStatusApplyConfiguration) *TrunkStatusApplyConfiguration { + b.Resource = value + return b +} diff --git a/pkg/clients/applyconfiguration/internal/internal.go b/pkg/clients/applyconfiguration/internal/internal.go index 92ad4e797..a06668cdc 100644 --- a/pkg/clients/applyconfiguration/internal/internal.go +++ b/pkg/clients/applyconfiguration/internal/internal.go @@ -2849,6 +2849,211 @@ var schemaYAML = typed.YAMLObject(`types: - name: resource type: namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.SubnetResourceStatus +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Subport + map: + fields: + - name: portRef + type: + scalar: string + default: "" + - name: segmentationID + type: + scalar: numeric + default: 0 + - name: segmentationType + type: + scalar: string + default: "" +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.SubportStatus + map: + fields: + - name: portID + type: + scalar: string + - name: segmentationID + type: + scalar: numeric + - name: segmentationType + type: + scalar: string +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Trunk + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + default: {} + - name: spec + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSpec + default: {} + - name: status + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkStatus + default: {} +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkFilter + map: + fields: + - name: description + type: + scalar: string + - name: name + type: + scalar: string + - name: notTags + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: notTagsAny + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: portRef + type: + scalar: string + - name: projectRef + type: + scalar: string + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: tagsAny + type: + list: + elementType: + scalar: string + elementRelationship: associative +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkImport + map: + fields: + - name: filter + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkFilter + - name: id + type: + scalar: string +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceSpec + map: + fields: + - name: adminStateUp + type: + scalar: boolean + - name: description + type: + scalar: string + - name: name + type: + scalar: string + - name: portRef + type: + scalar: string + default: "" + - name: projectRef + type: + scalar: string + - name: subports + type: + list: + elementType: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Subport + elementRelationship: atomic + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: associative +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceStatus + map: + fields: + - name: adminStateUp + type: + scalar: boolean + - name: createdAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: description + type: + scalar: string + - name: name + type: + scalar: string + - name: portID + type: + scalar: string + - name: projectID + type: + scalar: string + - name: revisionNumber + type: + scalar: numeric + - name: status + type: + scalar: string + - name: subports + type: + list: + elementType: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.SubportStatus + elementRelationship: atomic + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: updatedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSpec + map: + fields: + - name: cloudCredentialsRef + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.CloudCredentialsReference + default: {} + - name: import + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkImport + - name: managedOptions + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ManagedOptions + - name: managementPolicy + type: + scalar: string + - name: resource + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceSpec +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Condition + elementRelationship: associative + keys: + - type + - name: id + type: + scalar: string + - name: resource + type: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceStatus - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.UserDataSpec map: fields: diff --git a/pkg/clients/applyconfiguration/utils.go b/pkg/clients/applyconfiguration/utils.go index e3166fefe..bbe791f6c 100644 --- a/pkg/clients/applyconfiguration/utils.go +++ b/pkg/clients/applyconfiguration/utils.go @@ -336,6 +336,24 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.SubnetSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SubnetStatus"): return &apiv1alpha1.SubnetStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Subport"): + return &apiv1alpha1.SubportApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("SubportStatus"): + return &apiv1alpha1.SubportStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Trunk"): + return &apiv1alpha1.TrunkApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkFilter"): + return &apiv1alpha1.TrunkFilterApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkImport"): + return &apiv1alpha1.TrunkImportApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkResourceSpec"): + return &apiv1alpha1.TrunkResourceSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkResourceStatus"): + return &apiv1alpha1.TrunkResourceStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkSpec"): + return &apiv1alpha1.TrunkSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkStatus"): + return &apiv1alpha1.TrunkStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("UserDataSpec"): return &apiv1alpha1.UserDataSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Volume"): diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go index 4d2f93b0d..d269b4c08 100644 --- a/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/api_client.go @@ -45,6 +45,7 @@ type OpenstackV1alpha1Interface interface { ServerGroupsGetter ServicesGetter SubnetsGetter + TrunksGetter VolumesGetter VolumeTypesGetter } @@ -122,6 +123,10 @@ func (c *OpenstackV1alpha1Client) Subnets(namespace string) SubnetInterface { return newSubnets(c, namespace) } +func (c *OpenstackV1alpha1Client) Trunks(namespace string) TrunkInterface { + return newTrunks(c, namespace) +} + func (c *OpenstackV1alpha1Client) Volumes(namespace string) VolumeInterface { return newVolumes(c, namespace) } diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go index 44feeb45c..a59d50e53 100644 --- a/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_api_client.go @@ -96,6 +96,10 @@ func (c *FakeOpenstackV1alpha1) Subnets(namespace string) v1alpha1.SubnetInterfa return newFakeSubnets(c, namespace) } +func (c *FakeOpenstackV1alpha1) Trunks(namespace string) v1alpha1.TrunkInterface { + return newFakeTrunks(c, namespace) +} + func (c *FakeOpenstackV1alpha1) Volumes(namespace string) v1alpha1.VolumeInterface { return newFakeVolumes(c, namespace) } diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_trunk.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_trunk.go new file mode 100644 index 000000000..c66994f9e --- /dev/null +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/fake/fake_trunk.go @@ -0,0 +1,49 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" + typedapiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/clientset/clientset/typed/api/v1alpha1" + gentype "k8s.io/client-go/gentype" +) + +// fakeTrunks implements TrunkInterface +type fakeTrunks struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.Trunk, *v1alpha1.TrunkList, *apiv1alpha1.TrunkApplyConfiguration] + Fake *FakeOpenstackV1alpha1 +} + +func newFakeTrunks(fake *FakeOpenstackV1alpha1, namespace string) typedapiv1alpha1.TrunkInterface { + return &fakeTrunks{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.Trunk, *v1alpha1.TrunkList, *apiv1alpha1.TrunkApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("trunks"), + v1alpha1.SchemeGroupVersion.WithKind("Trunk"), + func() *v1alpha1.Trunk { return &v1alpha1.Trunk{} }, + func() *v1alpha1.TrunkList { return &v1alpha1.TrunkList{} }, + func(dst, src *v1alpha1.TrunkList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.TrunkList) []*v1alpha1.Trunk { return gentype.ToPointerSlice(list.Items) }, + func(list *v1alpha1.TrunkList, items []*v1alpha1.Trunk) { list.Items = gentype.FromPointerSlice(items) }, + ), + fake, + } +} diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go index 56550a99f..026081a53 100644 --- a/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/generated_expansion.go @@ -52,6 +52,8 @@ type ServiceExpansion interface{} type SubnetExpansion interface{} +type TrunkExpansion interface{} + type VolumeExpansion interface{} type VolumeTypeExpansion interface{} diff --git a/pkg/clients/clientset/clientset/typed/api/v1alpha1/trunk.go b/pkg/clients/clientset/clientset/typed/api/v1alpha1/trunk.go new file mode 100644 index 000000000..48c8406bc --- /dev/null +++ b/pkg/clients/clientset/clientset/typed/api/v1alpha1/trunk.go @@ -0,0 +1,74 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + context "context" + + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + applyconfigurationapiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" + scheme "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/clientset/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// TrunksGetter has a method to return a TrunkInterface. +// A group's client should implement this interface. +type TrunksGetter interface { + Trunks(namespace string) TrunkInterface +} + +// TrunkInterface has methods to work with Trunk resources. +type TrunkInterface interface { + Create(ctx context.Context, trunk *apiv1alpha1.Trunk, opts v1.CreateOptions) (*apiv1alpha1.Trunk, error) + Update(ctx context.Context, trunk *apiv1alpha1.Trunk, opts v1.UpdateOptions) (*apiv1alpha1.Trunk, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, trunk *apiv1alpha1.Trunk, opts v1.UpdateOptions) (*apiv1alpha1.Trunk, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*apiv1alpha1.Trunk, error) + List(ctx context.Context, opts v1.ListOptions) (*apiv1alpha1.TrunkList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *apiv1alpha1.Trunk, err error) + Apply(ctx context.Context, trunk *applyconfigurationapiv1alpha1.TrunkApplyConfiguration, opts v1.ApplyOptions) (result *apiv1alpha1.Trunk, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, trunk *applyconfigurationapiv1alpha1.TrunkApplyConfiguration, opts v1.ApplyOptions) (result *apiv1alpha1.Trunk, err error) + TrunkExpansion +} + +// trunks implements TrunkInterface +type trunks struct { + *gentype.ClientWithListAndApply[*apiv1alpha1.Trunk, *apiv1alpha1.TrunkList, *applyconfigurationapiv1alpha1.TrunkApplyConfiguration] +} + +// newTrunks returns a Trunks +func newTrunks(c *OpenstackV1alpha1Client, namespace string) *trunks { + return &trunks{ + gentype.NewClientWithListAndApply[*apiv1alpha1.Trunk, *apiv1alpha1.TrunkList, *applyconfigurationapiv1alpha1.TrunkApplyConfiguration]( + "trunks", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *apiv1alpha1.Trunk { return &apiv1alpha1.Trunk{} }, + func() *apiv1alpha1.TrunkList { return &apiv1alpha1.TrunkList{} }, + ), + } +} diff --git a/pkg/clients/informers/externalversions/api/v1alpha1/interface.go b/pkg/clients/informers/externalversions/api/v1alpha1/interface.go index 1b4497815..c4c4d137a 100644 --- a/pkg/clients/informers/externalversions/api/v1alpha1/interface.go +++ b/pkg/clients/informers/externalversions/api/v1alpha1/interface.go @@ -58,6 +58,8 @@ type Interface interface { Services() ServiceInformer // Subnets returns a SubnetInformer. Subnets() SubnetInformer + // Trunks returns a TrunkInformer. + Trunks() TrunkInformer // Volumes returns a VolumeInformer. Volumes() VolumeInformer // VolumeTypes returns a VolumeTypeInformer. @@ -160,6 +162,11 @@ func (v *version) Subnets() SubnetInformer { return &subnetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Trunks returns a TrunkInformer. +func (v *version) Trunks() TrunkInformer { + return &trunkInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // Volumes returns a VolumeInformer. func (v *version) Volumes() VolumeInformer { return &volumeInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/clients/informers/externalversions/api/v1alpha1/trunk.go b/pkg/clients/informers/externalversions/api/v1alpha1/trunk.go new file mode 100644 index 000000000..a0c0a293c --- /dev/null +++ b/pkg/clients/informers/externalversions/api/v1alpha1/trunk.go @@ -0,0 +1,102 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + context "context" + time "time" + + v2apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + clientset "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/clientset/clientset" + internalinterfaces "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/informers/externalversions/internalinterfaces" + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/listers/api/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// TrunkInformer provides access to a shared informer and lister for +// Trunks. +type TrunkInformer interface { + Informer() cache.SharedIndexInformer + Lister() apiv1alpha1.TrunkLister +} + +type trunkInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewTrunkInformer constructs a new informer for Trunk type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewTrunkInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredTrunkInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredTrunkInformer constructs a new informer for Trunk type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredTrunkInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Trunks(namespace).List(context.Background(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Trunks(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Trunks(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OpenstackV1alpha1().Trunks(namespace).Watch(ctx, options) + }, + }, + &v2apiv1alpha1.Trunk{}, + resyncPeriod, + indexers, + ) +} + +func (f *trunkInformer) defaultInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredTrunkInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *trunkInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&v2apiv1alpha1.Trunk{}, f.defaultInformer) +} + +func (f *trunkInformer) Lister() apiv1alpha1.TrunkLister { + return apiv1alpha1.NewTrunkLister(f.Informer().GetIndexer()) +} diff --git a/pkg/clients/informers/externalversions/generic.go b/pkg/clients/informers/externalversions/generic.go index 30911d11f..a61f25af9 100644 --- a/pkg/clients/informers/externalversions/generic.go +++ b/pkg/clients/informers/externalversions/generic.go @@ -87,6 +87,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().Services().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("subnets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().Subnets().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("trunks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().Trunks().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("volumes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Openstack().V1alpha1().Volumes().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("volumetypes"): diff --git a/pkg/clients/listers/api/v1alpha1/expansion_generated.go b/pkg/clients/listers/api/v1alpha1/expansion_generated.go index ba2888731..a46ae3384 100644 --- a/pkg/clients/listers/api/v1alpha1/expansion_generated.go +++ b/pkg/clients/listers/api/v1alpha1/expansion_generated.go @@ -154,6 +154,14 @@ type SubnetListerExpansion interface{} // SubnetNamespaceLister. type SubnetNamespaceListerExpansion interface{} +// TrunkListerExpansion allows custom methods to be added to +// TrunkLister. +type TrunkListerExpansion interface{} + +// TrunkNamespaceListerExpansion allows custom methods to be added to +// TrunkNamespaceLister. +type TrunkNamespaceListerExpansion interface{} + // VolumeListerExpansion allows custom methods to be added to // VolumeLister. type VolumeListerExpansion interface{} diff --git a/pkg/clients/listers/api/v1alpha1/trunk.go b/pkg/clients/listers/api/v1alpha1/trunk.go new file mode 100644 index 000000000..6dd678340 --- /dev/null +++ b/pkg/clients/listers/api/v1alpha1/trunk.go @@ -0,0 +1,70 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" +) + +// TrunkLister helps list Trunks. +// All objects returned here must be treated as read-only. +type TrunkLister interface { + // List lists all Trunks in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*apiv1alpha1.Trunk, err error) + // Trunks returns an object that can list and get Trunks. + Trunks(namespace string) TrunkNamespaceLister + TrunkListerExpansion +} + +// trunkLister implements the TrunkLister interface. +type trunkLister struct { + listers.ResourceIndexer[*apiv1alpha1.Trunk] +} + +// NewTrunkLister returns a new TrunkLister. +func NewTrunkLister(indexer cache.Indexer) TrunkLister { + return &trunkLister{listers.New[*apiv1alpha1.Trunk](indexer, apiv1alpha1.Resource("trunk"))} +} + +// Trunks returns an object that can list and get Trunks. +func (s *trunkLister) Trunks(namespace string) TrunkNamespaceLister { + return trunkNamespaceLister{listers.NewNamespaced[*apiv1alpha1.Trunk](s.ResourceIndexer, namespace)} +} + +// TrunkNamespaceLister helps list and get Trunks. +// All objects returned here must be treated as read-only. +type TrunkNamespaceLister interface { + // List lists all Trunks in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*apiv1alpha1.Trunk, err error) + // Get retrieves the Trunk from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*apiv1alpha1.Trunk, error) + TrunkNamespaceListerExpansion +} + +// trunkNamespaceLister implements the TrunkNamespaceLister +// interface. +type trunkNamespaceLister struct { + listers.ResourceIndexer[*apiv1alpha1.Trunk] +} diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md index 36f357a3c..0b95b0a44 100644 --- a/website/docs/crd-reference.md +++ b/website/docs/crd-reference.md @@ -27,6 +27,7 @@ Package v1alpha1 contains API Schema definitions for the openstack v1alpha1 API - [ServerGroup](#servergroup) - [Service](#service) - [Subnet](#subnet) +- [Trunk](#trunk) - [Volume](#volume) - [VolumeType](#volumetype) @@ -179,6 +180,7 @@ _Appears in:_ - [ServerSpec](#serverspec) - [ServiceSpec](#servicespec) - [SubnetSpec](#subnetspec) +- [TrunkSpec](#trunkspec) - [VolumeSpec](#volumespec) - [VolumeTypeSpec](#volumetypespec) @@ -419,6 +421,7 @@ _Appears in:_ - [RouterFilter](#routerfilter) - [SecurityGroupFilter](#securitygroupfilter) - [SubnetFilter](#subnetfilter) +- [TrunkFilter](#trunkfilter) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -1632,6 +1635,9 @@ _Appears in:_ - [ServerVolumeSpec](#servervolumespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) +- [Subport](#subport) +- [TrunkFilter](#trunkfilter) +- [TrunkResourceSpec](#trunkresourcespec) - [UserDataSpec](#userdataspec) - [VolumeResourceSpec](#volumeresourcespec) @@ -1692,6 +1698,7 @@ _Appears in:_ - [ServerSpec](#serverspec) - [ServiceSpec](#servicespec) - [SubnetSpec](#subnetspec) +- [TrunkSpec](#trunkspec) - [VolumeSpec](#volumespec) - [VolumeTypeSpec](#volumetypespec) @@ -1726,6 +1733,7 @@ _Appears in:_ - [ServerSpec](#serverspec) - [ServiceSpec](#servicespec) - [SubnetSpec](#subnetspec) +- [TrunkSpec](#trunkspec) - [VolumeSpec](#volumespec) - [VolumeTypeSpec](#volumetypespec) @@ -1918,6 +1926,8 @@ _Appears in:_ - [SecurityGroupRule](#securitygrouprule) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) +- [TrunkFilter](#trunkfilter) +- [TrunkResourceSpec](#trunkresourcespec) @@ -1935,6 +1945,7 @@ _Appears in:_ - [PortResourceStatus](#portresourcestatus) - [SecurityGroupResourceStatus](#securitygroupresourcestatus) - [SubnetResourceStatus](#subnetresourcestatus) +- [TrunkResourceStatus](#trunkresourcestatus) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -1968,6 +1979,8 @@ _Appears in:_ - [SecurityGroupResourceSpec](#securitygroupresourcespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) +- [TrunkFilter](#trunkfilter) +- [TrunkResourceSpec](#trunkresourcespec) @@ -2025,6 +2038,8 @@ _Appears in:_ - [ServiceResourceSpec](#serviceresourcespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) +- [TrunkFilter](#trunkfilter) +- [TrunkResourceSpec](#trunkresourcespec) - [VolumeFilter](#volumefilter) - [VolumeResourceSpec](#volumeresourcespec) - [VolumeTypeFilter](#volumetypefilter) @@ -3777,6 +3792,191 @@ _Appears in:_ | `resource` _[SubnetResourceStatus](#subnetresourcestatus)_ | resource contains the observed state of the OpenStack resource. | | | +#### Subport + + + +Subport represents a subport that will be attached to a trunk. + + + +_Appears in:_ +- [TrunkResourceSpec](#trunkresourcespec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which will be used as a subport. | | MaxLength: 253
MinLength: 1
| +| `segmentationType` _string_ | segmentationType is the type of segmentation to use (e.g., "vlan"). | | MaxLength: 64
| +| `segmentationID` _integer_ | segmentationID is the segmentation identifier (e.g., VLAN ID). | | | + + +#### SubportStatus + + + +SubportStatus represents the observed state of a subport. + + + +_Appears in:_ +- [TrunkResourceStatus](#trunkresourcestatus) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `portID` _string_ | portID is the ID of the port used as a subport. | | MaxLength: 1024
| +| `segmentationType` _string_ | segmentationType is the type of segmentation used. | | MaxLength: 1024
| +| `segmentationID` _integer_ | segmentationID is the segmentation identifier. | | | + + +#### Trunk + + + +Trunk is the Schema for an ORC resource. + + + + + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `apiVersion` _string_ | `openstack.k-orc.cloud/v1alpha1` | | | +| `kind` _string_ | `Trunk` | | | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[TrunkSpec](#trunkspec)_ | spec specifies the desired state of the resource. | | | +| `status` _[TrunkStatus](#trunkstatus)_ | status defines the observed state of the resource. | | | + + +#### TrunkFilter + + + +TrunkFilter specifies a filter to select a trunk. At least one parameter must be specified. + +_Validation:_ +- MinProperties: 1 + +_Appears in:_ +- [TrunkImport](#trunkimport) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `name` _[OpenStackName](#openstackname)_ | name of the existing resource | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
| +| `description` _[NeutronDescription](#neutrondescription)_ | description of the existing resource | | MaxLength: 255
MinLength: 1
| +| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which this trunk is associated with. | | MaxLength: 253
MinLength: 1
| +| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project this resource is associated with.
Typically, only used by admin. | | MaxLength: 253
MinLength: 1
| +| `tags` _[NeutronTag](#neutrontag) array_ | tags is a list of tags to filter by. If specified, the resource must
have all of the tags specified to be included in the result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| +| `tagsAny` _[NeutronTag](#neutrontag) array_ | tagsAny is a list of tags to filter by. If specified, the resource
must have at least one of the tags specified to be included in the
result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| +| `notTags` _[NeutronTag](#neutrontag) array_ | notTags is a list of tags to filter by. If specified, resources which
contain all of the given tags will be excluded from the result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| +| `notTagsAny` _[NeutronTag](#neutrontag) array_ | notTagsAny is a list of tags to filter by. If specified, resources
which contain any of the given tags will be excluded from the result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| + + +#### TrunkImport + + + +TrunkImport specifies an existing resource which will be imported instead of +creating a new one + +_Validation:_ +- MaxProperties: 1 +- MinProperties: 1 + +_Appears in:_ +- [TrunkSpec](#trunkspec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `id` _string_ | id contains the unique identifier of an existing OpenStack resource. Note
that when specifying an import by ID, the resource MUST already exist.
The ORC object will enter an error state if the resource does not exist. | | Format: uuid
| +| `filter` _[TrunkFilter](#trunkfilter)_ | filter contains a resource query which is expected to return a single
result. The controller will continue to retry if filter returns no
results. If filter returns multiple results the controller will set an
error state and will not continue to retry. | | MinProperties: 1
| + + +#### TrunkResourceSpec + + + + + + + +_Appears in:_ +- [TrunkSpec](#trunkspec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `name` _[OpenStackName](#openstackname)_ | name is a human-readable name of the trunk. If not set, the object's name will be used. | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
| +| `description` _[NeutronDescription](#neutrondescription)_ | description is a human-readable description for the resource. | | MaxLength: 255
MinLength: 1
| +| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which will be used as the parent port for this trunk. | | MaxLength: 253
MinLength: 1
| +| `tags` _[NeutronTag](#neutrontag) array_ | tags is a list of tags which will be applied to the trunk. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| +| `adminStateUp` _boolean_ | adminStateUp is the administrative state of the trunk, which is up (true) or down (false). | | | +| `subports` _[Subport](#subport) array_ | subports are the subports that will be attached to this trunk. | | MaxItems: 128
| +| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project this resource is associated with.
Typically, only used by admin. | | MaxLength: 253
MinLength: 1
| + + +#### TrunkResourceStatus + + + + + + + +_Appears in:_ +- [TrunkStatus](#trunkstatus) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `name` _string_ | name is the human-readable name of the resource. Might not be unique. | | MaxLength: 1024
| +| `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 1024
| +| `portID` _string_ | portID is the ID of the parent port. | | MaxLength: 1024
| +| `projectID` _string_ | projectID is the project owner of the resource. | | MaxLength: 1024
| +| `status` _string_ | status indicates the current status of the resource. | | MaxLength: 1024
| +| `tags` _string array_ | tags is the list of tags on the resource. | | MaxItems: 64
items:MaxLength: 1024
| +| `adminStateUp` _boolean_ | adminStateUp is the administrative state of the trunk,
which is up (true) or down (false). | | | +| `subports` _[SubportStatus](#subportstatus) array_ | subports is a list of subports attached to this trunk. | | MaxItems: 128
| +| `createdAt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#time-v1-meta)_ | createdAt shows the date and time when the resource was created. The date and time stamp format is ISO 8601 | | | +| `updatedAt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#time-v1-meta)_ | updatedAt shows the date and time when the resource was updated. The date and time stamp format is ISO 8601 | | | +| `revisionNumber` _integer_ | revisionNumber optionally set via extensions/standard-attr-revisions | | | + + +#### TrunkSpec + + + +TrunkSpec defines the desired state of an ORC object. + + + +_Appears in:_ +- [Trunk](#trunk) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `import` _[TrunkImport](#trunkimport)_ | import refers to an existing OpenStack resource which will be imported instead of
creating a new one. | | MaxProperties: 1
MinProperties: 1
| +| `resource` _[TrunkResourceSpec](#trunkresourcespec)_ | resource specifies the desired state of the resource.
resource may not be specified if the management policy is `unmanaged`.
resource must be specified if the management policy is `managed`. | | | +| `managementPolicy` _[ManagementPolicy](#managementpolicy)_ | managementPolicy defines how ORC will treat the object. Valid values are
`managed`: ORC will create, update, and delete the resource; `unmanaged`:
ORC will import an existing resource, and will not apply updates to it or
delete it. | managed | Enum: [managed unmanaged]
| +| `managedOptions` _[ManagedOptions](#managedoptions)_ | managedOptions specifies options which may be applied to managed objects. | | | +| `cloudCredentialsRef` _[CloudCredentialsReference](#cloudcredentialsreference)_ | cloudCredentialsRef points to a secret containing OpenStack credentials | | | + + +#### TrunkStatus + + + +TrunkStatus defines the observed state of an ORC resource. + + + +_Appears in:_ +- [Trunk](#trunk) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#condition-v1-meta) array_ | conditions represents the observed status of the object.
Known .status.conditions.type are: "Available", "Progressing"
Available represents the availability of the OpenStack resource. If it is
true then the resource is ready for use.
Progressing indicates whether the controller is still attempting to
reconcile the current state of the OpenStack resource to the desired
state. Progressing will be False either because the desired state has
been achieved, or because some terminal error prevents it from ever being
achieved and the controller is no longer attempting to reconcile. If
Progressing is True, an observer waiting on the resource should continue
to wait. | | MaxItems: 32
| +| `id` _string_ | id is the unique identifier of the OpenStack resource. | | | +| `resource` _[TrunkResourceStatus](#trunkresourcestatus)_ | resource contains the observed state of the OpenStack resource. | | | + + #### UserDataSpec From ba267265b0cc9746ba35d2bf8f985e74ffe30c18 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Sat, 22 Nov 2025 17:00:40 +0000 Subject: [PATCH 3/6] Trunk resource: increase test coverage + fix bugs --- api/v1alpha1/trunk_type.go | 45 +- internal/controllers/trunk/actuator.go | 42 +- internal/controllers/trunk/actuator_test.go | 489 +++++++++++++++++++- internal/controllers/trunk/status.go | 2 +- website/docs/crd-reference.md | 3 +- 5 files changed, 557 insertions(+), 24 deletions(-) diff --git a/api/v1alpha1/trunk_type.go b/api/v1alpha1/trunk_type.go index 883394be7..6945c86b8 100644 --- a/api/v1alpha1/trunk_type.go +++ b/api/v1alpha1/trunk_type.go @@ -27,15 +27,39 @@ type TrunkFilter struct { // +optional Description *NeutronDescription `json:"description,omitempty"` - // portRef is a reference to the ORC Port which this trunk is associated with. + // admin state of the trunk (UP/DOWN) // +optional - PortRef *KubernetesNameRef `json:"portRef,omitempty"` + AdminStateUp *bool `json:"adminStateUp,omitempty"` + + // status of the trunk (ACTIVE/BUILD/ERROR/DELETED/DOWN/any other Neutron-supported status) + // +optional + Status *string `json:"status,omitempty"` // projectRef is a reference to the ORC Project this resource is associated with. // Typically, only used by admin. // +optional ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` + // projectID is the OpenStack project (tenant) ID for this trunk. + // +optional + ProjectID *string `json:"projectID,omitempty"` + + // tenantID is the OpenStack tenant ID for this trunk. + // +optional + TenantID *string `json:"tenantID,omitempty"` + + // portRef is a reference to the ORC Port which this trunk is associated with. + // +optional + PortRef *KubernetesNameRef `json:"portRef,omitempty"` + + // portID is the OpenStack port ID the trunk is associated with. + // +optional + PortID *string `json:"portID,omitempty"` + + // revisionNumber is the revision number of the trunk. + // +optional + RevisionNumber *int64 `json:"revisionNumber,omitempty"` + FilterByNeutronTags `json:",inline"` } @@ -43,15 +67,18 @@ type TrunkFilter struct { type Subport struct { // portRef is a reference to the ORC Port which will be used as a subport. // +required - PortRef KubernetesNameRef `json:"portRef"` - - // segmentationType is the type of segmentation to use (e.g., "vlan"). + PortRef KubernetesNameRef `json:"portRef,omitempty"` + + // segmentationType is the type of segmentation to use. + // Possible values are "vlan" or "inherit". // +required - // +kubebuilder:validation:MaxLength=64 + // +kubebuilder:validation:MaxLength=32 SegmentationType string `json:"segmentationType"` // segmentationID is the segmentation identifier (e.g., VLAN ID). + // If segmentationType is "vlan", must be between 1 and 4094 inclusive. // +required + // +kubebuilder:validation:XValidation:rule="self.segmentationType != 'vlan' || (self.segmentationID >= 1 && self.segmentationID <= 4094)" SegmentationID int32 `json:"segmentationID"` } @@ -63,7 +90,7 @@ type SubportStatus struct { PortID string `json:"portID,omitempty"` // segmentationType is the type of segmentation used. - // +kubebuilder:validation:MaxLength=1024 + // +kubebuilder:validation:MaxLength=32 // +optional SegmentationType string `json:"segmentationType,omitempty"` @@ -84,8 +111,7 @@ type TrunkResourceSpec struct { // portRef is a reference to the ORC Port which will be used as the parent port for this trunk. // +required - PortRef KubernetesNameRef `json:"portRef"` - + PortRef KubernetesNameRef `json:"portRef,omitempty"` // tags is a list of tags which will be applied to the trunk. // +kubebuilder:validation:MaxItems:=64 // +listType=set @@ -155,4 +181,3 @@ type TrunkResourceStatus struct { NeutronStatusMetadata `json:",inline"` } - diff --git a/internal/controllers/trunk/actuator.go b/internal/controllers/trunk/actuator.go index 91d742ce5..f607c07df 100644 --- a/internal/controllers/trunk/actuator.go +++ b/internal/controllers/trunk/actuator.go @@ -300,9 +300,15 @@ func (actuator trunkActuator) updateResource(ctx context.Context, obj orcObjectP return progress.WrapError(err) } + // Refresh is needed to get the updated resource state from OpenStack after modifications. + // This ensures the status accurately reflects the current state before the next reconciliation cycle. return progress.NeedsRefresh() } +// reconcileSubports ensures the trunk's subports match the desired state from the spec. +// It handles adding new subports, removing deleted ones, and updating existing subports. +// Note: OpenStack trunk API does not support in-place updates of subport segmentation, +// so changes require removing and re-adding the subport, which may cause brief network interruption. func (actuator trunkActuator) reconcileSubports(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus { log := ctrl.LoggerFrom(ctx) resource := obj.Spec.Resource @@ -332,6 +338,7 @@ func (actuator trunkActuator) reconcileSubports(ctx context.Context, obj orcObje subportPort, ok := portMap[portName] if !ok { // Port not ready yet, will be retried + log.V(logging.Debug).Info("Skipping subport: port not ready", "portRef", portName) continue } desiredSubports = append(desiredSubports, trunks.Subport{ @@ -341,23 +348,19 @@ func (actuator trunkActuator) reconcileSubports(ctx context.Context, obj orcObje }) } - // Build maps for comparison - currentMap := make(map[string]trunks.Subport) - for _, subport := range currentSubports { - currentMap[subport.PortID] = subport - } - - desiredMap := make(map[string]trunks.Subport) - for _, subport := range desiredSubports { - desiredMap[subport.PortID] = subport - } + // Build maps for comparison (keyed by PortID for efficient lookup) + currentMap := buildSubportMap(currentSubports) + desiredMap := buildSubportMap(desiredSubports) // Find subports to add + // Note: OpenStack trunk API does not support in-place updates of subport segmentation. + // When segmentation type or ID changes, we must remove the old subport and add it back + // with the new segmentation. This may cause a brief network interruption for that subport. toAdd := []trunks.Subport{} for portID, desired := range desiredMap { if current, exists := currentMap[portID]; !exists { toAdd = append(toAdd, desired) - } else if current.SegmentationType != desired.SegmentationType || current.SegmentationID != desired.SegmentationID { + } else if subportNeedsUpdate(current, desired) { // Subport exists but with different segmentation, need to remove and re-add // First remove the old one removeOpts := trunks.RemoveSubportsOpts{ @@ -396,12 +399,29 @@ func (actuator trunkActuator) reconcileSubports(ctx context.Context, obj orcObje } if len(toAdd) > 0 || len(toRemove) > 0 { + // Refresh is needed to get the updated subport list from OpenStack after modifications. + // This ensures the status accurately reflects the current state before the next reconciliation. return progress.NeedsRefresh() } return nil } +// buildSubportMap creates a map of subports keyed by PortID for efficient comparison. +func buildSubportMap(subports []trunks.Subport) map[string]trunks.Subport { + result := make(map[string]trunks.Subport, len(subports)) + for _, subport := range subports { + result[subport.PortID] = subport + } + return result +} + +// subportNeedsUpdate checks if a subport needs to be updated by comparing segmentation parameters. +// OpenStack trunk API does not support in-place updates, so any change requires remove+re-add. +func subportNeedsUpdate(current, desired trunks.Subport) bool { + return current.SegmentationType != desired.SegmentationType || current.SegmentationID != desired.SegmentationID +} + type trunkHelperFactory struct{} var _ helperFactory = trunkHelperFactory{} diff --git a/internal/controllers/trunk/actuator_test.go b/internal/controllers/trunk/actuator_test.go index 6eaec4345..91ccc9420 100644 --- a/internal/controllers/trunk/actuator_test.go +++ b/internal/controllers/trunk/actuator_test.go @@ -17,11 +17,23 @@ limitations under the License. package trunk import ( + "context" + "errors" "testing" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" - orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + osclientsmock "github.com/k-orc/openstack-resource-controller/v2/internal/osclients/mock" + orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors" + "go.uber.org/mock/gomock" ) func TestNeedsUpdate(t *testing.T) { @@ -191,4 +203,479 @@ func handleAdminStateUpUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alph } } +func TestGetResourceID(t *testing.T) { + actuator := trunkActuator{} + osResource := &trunks.Trunk{ID: "test-id-123"} + + got := actuator.GetResourceID(osResource) + if got != "test-id-123" { + t.Errorf("Expected ID 'test-id-123', got '%s'", got) + } +} + +func TestGetOSResourceByID(t *testing.T) { + ctx := context.Background() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) + actuator := trunkActuator{osClient: mockClient} + + t.Run("success", func(t *testing.T) { + expectedTrunk := &trunks.Trunk{ID: "test-id", Name: "test-trunk"} + mockClient.EXPECT().GetTrunk(ctx, "test-id").Return(expectedTrunk, nil) + + got, status := actuator.GetOSResourceByID(ctx, "test-id") + if status != nil { + t.Errorf("Expected nil status, got %v", status) + } + if got.ID != expectedTrunk.ID { + t.Errorf("Expected ID '%s', got '%s'", expectedTrunk.ID, got.ID) + } + }) + + t.Run("error", func(t *testing.T) { + expectedErr := errors.New("not found") + mockClient.EXPECT().GetTrunk(ctx, "test-id").Return(nil, expectedErr) + + got, status := actuator.GetOSResourceByID(ctx, "test-id") + if got != nil { + t.Errorf("Expected nil, got %v", got) + } + if status == nil { + t.Fatal("Expected non-nil status") + } + needsReschedule, err := status.NeedsReschedule() + if !needsReschedule { + t.Error("Expected needsReschedule to be true") + } + if err == nil { + t.Error("Expected error in status") + } + }) +} + +func TestListOSResourcesForAdoption(t *testing.T) { + ctx := context.Background() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) + actuator := trunkActuator{osClient: mockClient} + + t.Run("no resource spec", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + Spec: orcv1alpha1.TrunkSpec{Resource: nil}, + } + iter, ok := actuator.ListOSResourcesForAdoption(ctx, obj) + if ok { + t.Error("Expected ok to be false when resource spec is nil") + } + if iter != nil { + t.Error("Expected nil iterator when resource spec is nil") + } + }) + + t.Run("success", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{}, + }, + } + expectedTrunks := []trunks.Trunk{ + {ID: "id1", Name: "test-trunk"}, + {ID: "id2", Name: "test-trunk"}, + } + mockClient.EXPECT().ListTrunk(ctx, gomock.Any()).Return(expectedTrunks, nil) + + iter, ok := actuator.ListOSResourcesForAdoption(ctx, obj) + if !ok { + t.Error("Expected ok to be true") + } + + var results []*trunks.Trunk + for trunk, err := range iter { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + results = append(results, trunk) + } + + if len(results) != 2 { + t.Errorf("Expected 2 trunks, got %d", len(results)) + } + }) + + t.Run("error", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{}, + }, + } + expectedErr := errors.New("list error") + mockClient.EXPECT().ListTrunk(ctx, gomock.Any()).Return(nil, expectedErr) + + iter, ok := actuator.ListOSResourcesForAdoption(ctx, obj) + if !ok { + t.Error("Expected ok to be true even on error") + } + + var gotErr error + for _, err := range iter { + gotErr = err + break + } + + if gotErr != expectedErr { + t.Errorf("Expected error '%v', got '%v'", expectedErr, gotErr) + } + }) +} + +func TestDeleteResource(t *testing.T) { + ctx := context.Background() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) + actuator := trunkActuator{osClient: mockClient} + + t.Run("success", func(t *testing.T) { + osResource := &trunks.Trunk{ID: "test-id"} + mockClient.EXPECT().DeleteTrunk(ctx, "test-id").Return(nil) + + status := actuator.DeleteResource(ctx, nil, osResource) + if status != nil { + needsReschedule, err := status.NeedsReschedule() + if needsReschedule || err != nil { + t.Errorf("Expected nil status, got needsReschedule=%v, err=%v", needsReschedule, err) + } + } + }) + + t.Run("error", func(t *testing.T) { + osResource := &trunks.Trunk{ID: "test-id"} + expectedErr := errors.New("delete error") + mockClient.EXPECT().DeleteTrunk(ctx, "test-id").Return(expectedErr) + + status := actuator.DeleteResource(ctx, nil, osResource) + if status == nil { + t.Fatal("Expected non-nil status on error") + } + needsReschedule, err := status.NeedsReschedule() + if !needsReschedule { + t.Error("Expected needsReschedule to be true") + } + if err == nil { + t.Error("Expected error in status") + } + }) +} + +func TestUpdateResource(t *testing.T) { + ctx := ctrl.LoggerInto(context.Background(), ctrl.Log) + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) + actuator := trunkActuator{osClient: mockClient} + + t.Run("no changes", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Name: ptr.To(orcv1alpha1.OpenStackName("test-trunk")), + }, + }, + } + osResource := &trunks.Trunk{ + ID: "test-id", + Name: "test-trunk", + Description: "", + AdminStateUp: true, + RevisionNumber: 1, + } + + status := actuator.updateResource(ctx, obj, osResource) + if status != nil { + needsReschedule, _ := status.NeedsReschedule() + if needsReschedule { + t.Error("Expected no status when no changes") + } + } + }) + + t.Run("name change", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Name: ptr.To(orcv1alpha1.OpenStackName("new-name")), + }, + }, + } + osResource := &trunks.Trunk{ + ID: "test-id", + Name: "old-name", + RevisionNumber: 1, + } + + expectedTrunk := &trunks.Trunk{ID: "test-id", Name: "new-name"} + mockClient.EXPECT().UpdateTrunk(ctx, "test-id", gomock.Any()).Return(expectedTrunk, nil) + + status := actuator.updateResource(ctx, obj, osResource) + if status == nil { + t.Fatal("Expected non-nil status after update") + } + // Status should indicate refresh is needed (status will have a refresh message) + }) + + t.Run("description change", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Description: ptr.To(orcv1alpha1.NeutronDescription("new desc")), + }, + }, + } + osResource := &trunks.Trunk{ + ID: "test-id", + Name: "test-trunk", + Description: "old desc", + RevisionNumber: 1, + } + + expectedTrunk := &trunks.Trunk{ID: "test-id", Description: "new desc"} + mockClient.EXPECT().UpdateTrunk(ctx, "test-id", gomock.Any()).Return(expectedTrunk, nil) + + status := actuator.updateResource(ctx, obj, osResource) + if status == nil { + t.Fatal("Expected non-nil status after update") + } + }) + + t.Run("conflict error", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Name: ptr.To(orcv1alpha1.OpenStackName("new-name")), + }, + }, + } + osResource := &trunks.Trunk{ + ID: "test-id", + Name: "old-name", + RevisionNumber: 1, + } + + conflictErr := gophercloud.ErrUnexpectedResponseCode{Actual: 409} + mockClient.EXPECT().UpdateTrunk(ctx, "test-id", gomock.Any()).Return(nil, conflictErr) + + status := actuator.updateResource(ctx, obj, osResource) + if status == nil { + t.Fatal("Expected non-nil status on error") + } + needsReschedule, err := status.NeedsReschedule() + if !needsReschedule { + t.Error("Expected needsReschedule to be true on conflict") + } + if err == nil { + t.Error("Expected error in status") + } + // Check that it's a terminal error + var terminalError *orcerrors.TerminalError + if !errors.As(err, &terminalError) { + t.Error("Expected terminal error on conflict") + } + }) +} + +func TestReconcileSubports(t *testing.T) { + ctx := ctrl.LoggerInto(context.Background(), ctrl.Log) + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + scheme := runtime.NewScheme() + orcv1alpha1.AddToScheme(scheme) + corev1.AddToScheme(scheme) + + mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) + k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() + actuator := trunkActuator{osClient: mockClient, k8sClient: k8sClient} + + t.Run("no resource spec", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + Spec: orcv1alpha1.TrunkSpec{Resource: nil}, + } + osResource := &trunks.Trunk{ID: "test-id"} + + status := actuator.reconcileSubports(ctx, obj, osResource) + if status != nil { + t.Errorf("Expected nil status, got %v", status) + } + }) + + t.Run("no changes needed", func(t *testing.T) { + // Create a fresh k8sClient for this test to avoid state from previous tests + testK8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() + testActuator := trunkActuator{osClient: mockClient, k8sClient: testK8sClient} + + port1 := &orcv1alpha1.Port{ + ObjectMeta: metav1.ObjectMeta{ + Name: "port1", + Namespace: "default", + Finalizers: []string{finalizer}, + }, + Status: orcv1alpha1.PortStatus{ + ID: ptr.To("port-id-1"), + Conditions: []metav1.Condition{ + {Type: "Available", Status: metav1.ConditionTrue}, + }, + }, + } + testK8sClient.Create(ctx, port1) + + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Subports: []orcv1alpha1.Subport{ + { + PortRef: "port1", + SegmentationType: "vlan", + SegmentationID: 100, + }, + }, + }, + }, + } + osResource := &trunks.Trunk{ + ID: "trunk-id", + } + + currentSubports := []trunks.Subport{ + { + PortID: "port-id-1", + SegmentationType: "vlan", + SegmentationID: 100, + }, + } + + mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return(currentSubports, nil) + + status := testActuator.reconcileSubports(ctx, obj, osResource) + if status != nil { + t.Errorf("Expected nil status when no changes, got %v", status) + } + }) + + t.Run("add subport", func(t *testing.T) { + // Create a fresh k8sClient for this test to avoid state from previous tests + testK8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() + testActuator := trunkActuator{osClient: mockClient, k8sClient: testK8sClient} + + port1 := &orcv1alpha1.Port{ + ObjectMeta: metav1.ObjectMeta{ + Name: "port1", + Namespace: "default", + Finalizers: []string{finalizer}, + }, + Status: orcv1alpha1.PortStatus{ + ID: ptr.To("port-id-1"), + Conditions: []metav1.Condition{ + {Type: "Available", Status: metav1.ConditionTrue}, + }, + }, + } + testK8sClient.Create(ctx, port1) + + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Subports: []orcv1alpha1.Subport{ + { + PortRef: "port1", + SegmentationType: "vlan", + SegmentationID: 100, + }, + }, + }, + }, + } + osResource := &trunks.Trunk{ID: "trunk-id"} + + mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return([]trunks.Subport{}, nil) + expectedTrunk := &trunks.Trunk{ID: "trunk-id"} + mockClient.EXPECT().AddSubports(ctx, "trunk-id", gomock.Any()).Return(expectedTrunk, nil) + + status := testActuator.reconcileSubports(ctx, obj, osResource) + if status == nil { + t.Fatal("Expected non-nil status after adding subport") + } + // Status should indicate refresh is needed (status will have a refresh message) + }) + + t.Run("remove subport", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Subports: []orcv1alpha1.Subport{}, + }, + }, + } + osResource := &trunks.Trunk{ID: "trunk-id"} + + currentSubports := []trunks.Subport{ + { + PortID: "port-id-1", + SegmentationType: "vlan", + SegmentationID: 100, + }, + } + + mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return(currentSubports, nil) + mockClient.EXPECT().RemoveSubports(ctx, "trunk-id", gomock.Any()).Return(nil) + + status := actuator.reconcileSubports(ctx, obj, osResource) + if status == nil { + t.Fatal("Expected non-nil status after removing subport") + } + // Status should indicate refresh is needed (status will have a refresh message) + }) + + t.Run("list subports error", func(t *testing.T) { + obj := &orcv1alpha1.Trunk{ + ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, + Spec: orcv1alpha1.TrunkSpec{ + Resource: &orcv1alpha1.TrunkResourceSpec{ + Subports: []orcv1alpha1.Subport{}, + }, + }, + } + osResource := &trunks.Trunk{ID: "trunk-id"} + + expectedErr := errors.New("list error") + mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return(nil, expectedErr) + + status := actuator.reconcileSubports(ctx, obj, osResource) + if status == nil { + t.Fatal("Expected non-nil status on error") + } + needsReschedule, err := status.NeedsReschedule() + if !needsReschedule { + t.Error("Expected needsReschedule to be true on error") + } + if err == nil { + t.Error("Expected error in status") + } + }) +} + diff --git a/internal/controllers/trunk/status.go b/internal/controllers/trunk/status.go index cfdc32e19..38e12f6ec 100644 --- a/internal/controllers/trunk/status.go +++ b/internal/controllers/trunk/status.go @@ -70,7 +70,7 @@ func (trunkStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *osReso WithUpdatedAt(metav1.NewTime(osResource.UpdatedAt)) if osResource.Description != "" { - resourceStatus.WithDescription(osResource.Description) + resourceStatus = resourceStatus.WithDescription(osResource.Description) } if len(osResource.Subports) > 0 { diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md index 0b95b0a44..176622d41 100644 --- a/website/docs/crd-reference.md +++ b/website/docs/crd-reference.md @@ -3952,7 +3952,8 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `import` _[TrunkImport](#trunkimport)_ | import refers to an existing OpenStack resource which will be imported instead of
creating a new one. | | MaxProperties: 1
MinProperties: 1
| +| `import` _[TrunkImport]provider.go +(#trunkimport)_ | import refers to an existing OpenStack resource which will be imported instead of
creating a new one. | | MaxProperties: 1
MinProperties: 1
| | `resource` _[TrunkResourceSpec](#trunkresourcespec)_ | resource specifies the desired state of the resource.
resource may not be specified if the management policy is `unmanaged`.
resource must be specified if the management policy is `managed`. | | | | `managementPolicy` _[ManagementPolicy](#managementpolicy)_ | managementPolicy defines how ORC will treat the object. Valid values are
`managed`: ORC will create, update, and delete the resource; `unmanaged`:
ORC will import an existing resource, and will not apply updates to it or
delete it. | managed | Enum: [managed unmanaged]
| | `managedOptions` _[ManagedOptions](#managedoptions)_ | managedOptions specifies options which may be applied to managed objects. | | | From 8fe327aa977f55288eb4e6c613ca06631b4e502d Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Wed, 17 Dec 2025 13:50:31 +0000 Subject: [PATCH 4/6] Trunk resource: add improvments + fix bugs --- PROJECT | 8 - api/v1alpha1/trunk_type.go | 183 ---- api/v1alpha1/trunk_types.go | 102 +++ api/v1alpha1/zz_generated.deepcopy.go | 75 +- cmd/models-schema/zz_generated.openapi.go | 301 +------ cmd/resource-generator/main.go | 4 - .../bases/openstack.k-orc.cloud_trunks.yaml | 196 +---- config/crd/kustomization.yaml | 1 - config/samples/kustomization.yaml | 1 - config/samples/openstack_v1alpha1_trunk.yaml | 20 +- .../tests/domain-create-full/00-assert.yaml | 10 - .../domain-create-minimal/00-assert.yaml | 1 - .../domain/tests/domain-import/02-assert.yaml | 14 - .../domain/tests/domain-update/01-assert.yaml | 8 - .../domain/tests/domain-update/02-assert.yaml | 1 - .../flavor/tests/flavor-import/02-assert.yaml | 14 - .../floatingip-create-full/00-assert.yaml | 29 - .../floatingip-create-minimal/00-assert.yaml | 15 - .../00-assert.yaml | 14 - .../02-assert.yaml | 29 - .../03-assert.yaml | 10 - .../tests/floatingip-import/02-assert.yaml | 19 - .../tests/floatingip-update/00-assert.yaml | 2 - .../tests/floatingip-update/01-assert.yaml | 15 - .../tests/floatingip-update/02-assert.yaml | 2 - .../image/tests/image-import/02-assert.yaml | 14 - .../tests/network-create-full/00-assert.yaml | 4 - .../network-import-dependency/02-assert.yaml | 19 - .../network-import-dependency/03-assert.yaml | 6 - .../tests/network-import/02-assert.yaml | 14 - .../tests/network-update/01-assert.yaml | 8 - .../tests/port-create-full/00-assert.yaml | 7 - .../tests/port-create-minimal/00-assert.yaml | 4 - .../tests/port-create-sriov/00-assert.yaml | 5 - .../port-import-dependency/02-assert.yaml | 24 - .../port-import-dependency/03-assert.yaml | 8 - .../port/tests/port-import/02-assert.yaml | 14 - .../port/tests/port-update/00-assert.yaml | 4 - .../port/tests/port-update/01-assert.yaml | 25 - .../port/tests/port-update/02-assert.yaml | 4 - .../tests/project-import/02-assert.yaml | 14 - .../tests/project-update/01-assert.yaml | 8 - .../tests/router-create-full/00-assert.yaml | 23 - .../router-import-dependency/02-assert.yaml | 19 - .../router-import-dependency/03-assert.yaml | 6 - .../router/tests/router-import/02-assert.yaml | 14 - .../00-assert.yaml | 10 - .../securitygroup-create-full/00-assert.yaml | 18 - .../02-assert.yaml | 19 - .../03-assert.yaml | 6 - .../tests/securitygroup-import/02-assert.yaml | 14 - .../tests/securitygroup-update/01-assert.yaml | 8 - .../tests/server-create-full/00-assert.yaml | 15 - .../server-create-minimal/00-assert.yaml | 10 - .../server/tests/server-import/02-assert.yaml | 14 - .../server/tests/server-update/00-assert.yaml | 4 - .../server/tests/server-update/01-assert.yaml | 12 - .../server/tests/server-update/02-assert.yaml | 5 - .../tests/servergroup-import/02-assert.yaml | 14 - .../subnet-create-full-v4/00-assert.yaml | 3 - .../subnet-create-full-v6/00-assert.yaml | 23 - .../subnet-create-minimal-v4/00-assert.yaml | 1 - .../subnet-create-minimal-v6/00-assert.yaml | 1 - .../subnet-import-dependency/02-assert.yaml | 24 - .../subnet-import-dependency/03-assert.yaml | 8 - .../subnet/tests/subnet-import/02-assert.yaml | 19 - .../subnet/tests/subnet-update/00-assert.yaml | 1 - .../subnet/tests/subnet-update/01-assert.yaml | 14 - .../subnet/tests/subnet-update/02-assert.yaml | 1 - internal/controllers/trunk/actuator.go | 784 +++++++----------- internal/controllers/trunk/actuator_test.go | 582 +------------ internal/controllers/trunk/controller.go | 342 ++++---- internal/controllers/trunk/status.go | 155 ++-- .../tests/trunk-create-full/00-assert.yaml | 102 +-- .../trunk-create-full/00-create-resource.yaml | 151 +--- .../tests/trunk-create-full/00-secret.yaml | 6 + .../trunk/tests/trunk-create-full/README.md | 29 +- .../tests/trunk-create-minimal/00-assert.yaml | 62 +- .../00-create-resource.yaml | 41 +- .../tests/trunk-create-minimal/00-secret.yaml | 6 + .../tests/trunk-create-minimal/01-assert.yaml | 23 +- .../01-delete-secret.yaml | 13 +- .../tests/trunk-create-minimal/README.md | 31 +- .../tests/trunk-dependency/00-assert.yaml | 45 + .../00-create-resources-missing-deps.yaml | 56 ++ .../tests/trunk-dependency/00-secret.yaml | 6 + .../tests/trunk-dependency/01-assert.yaml | 45 + .../01-create-dependencies.yaml | 32 + .../tests/trunk-dependency/02-assert.yaml | 23 + .../02-delete-dependencies.yaml | 11 + .../tests/trunk-dependency/03-assert.yaml | 11 + .../trunk-dependency/03-delete-resources.yaml | 13 + .../trunk/tests/trunk-dependency/README.md | 21 + .../trunk-import-dependency/00-assert.yaml | 19 + .../00-import-resource.yaml | 40 + .../trunk-import-dependency/00-secret.yaml | 6 + .../trunk-import-dependency/01-assert.yaml | 34 + .../01-create-trap-resource.yaml | 56 ++ .../trunk-import-dependency/02-assert.yaml | 39 + .../02-create-resource.yaml | 55 ++ .../trunk-import-dependency/03-assert.yaml | 8 + .../03-delete-import-dependencies.yaml | 9 + .../trunk-import-dependency/04-assert.yaml | 6 + .../04-delete-resource.yaml | 7 + .../tests/trunk-import-dependency/README.md | 29 + .../tests/trunk-import-error/00-assert.yaml | 30 + .../00-create-resources.yaml | 43 + .../tests/trunk-import-error/00-secret.yaml | 6 + .../tests/trunk-import-error/01-assert.yaml | 15 + .../01-import-resource.yaml | 13 + .../trunk/tests/trunk-import-error/README.md | 13 + .../trunk/tests/trunk-import/00-assert.yaml | 15 + .../trunk-import/00-import-resource.yaml | 15 + .../trunk/tests/trunk-import/00-secret.yaml | 6 + .../trunk/tests/trunk-import/01-assert.yaml | 34 + .../trunk-import/01-create-trap-resource.yaml | 31 + .../trunk/tests/trunk-import/02-assert.yaml | 33 + .../trunk-import/02-create-resource.yaml | 28 + .../trunk/tests/trunk-import/README.md | 18 + .../trunk/tests/trunk-update/00-assert.yaml | 39 +- .../trunk-update/00-minimal-resource.yaml | 42 +- .../tests/trunk-update/00-prerequisites.yaml | 50 +- .../trunk/tests/trunk-update/01-assert.yaml | 32 +- .../trunk-update/01-updated-resource.yaml | 29 +- .../trunk/tests/trunk-update/02-assert.yaml | 40 +- .../trunk-update/02-reverted-resource.yaml | 21 +- .../trunk/tests/trunk-update/README.md | 43 +- .../tests/volume-create-full/00-assert.yaml | 19 - .../volume-create-minimal/00-assert.yaml | 6 - .../tests/volume-dependency/03-assert.yaml | 9 - .../volume/tests/volume-import/02-assert.yaml | 14 - .../volumetype-create-full/00-assert.yaml | 10 - .../volumetype-create-minimal/00-assert.yaml | 10 - .../tests/volumetype-import/02-assert.yaml | 14 - internal/osclients/trunk.go | 104 +++ kuttl-test.yaml | 1 - .../api/v1alpha1/subport.go | 61 -- .../api/v1alpha1/subportstatus.go | 57 -- .../api/v1alpha1/trunkfilter.go | 51 +- .../api/v1alpha1/trunkresourcespec.go | 44 +- .../api/v1alpha1/trunkresourcestatus.go | 80 +- .../applyconfiguration/internal/internal.go | 94 --- pkg/clients/applyconfiguration/utils.go | 4 - website/docs/crd-reference.md | 86 +- 144 files changed, 2026 insertions(+), 3694 deletions(-) delete mode 100644 api/v1alpha1/trunk_type.go create mode 100644 api/v1alpha1/trunk_types.go create mode 100644 internal/controllers/trunk/tests/trunk-create-full/00-secret.yaml create mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/00-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/00-secret.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/01-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/02-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/02-delete-dependencies.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/03-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/03-delete-resources.yaml create mode 100644 internal/controllers/trunk/tests/trunk-dependency/README.md create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/00-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/00-import-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/00-secret.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/01-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/02-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/03-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/03-delete-import-dependencies.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/04-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/04-delete-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-dependency/README.md create mode 100644 internal/controllers/trunk/tests/trunk-import-error/00-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-error/00-secret.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-error/01-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-error/01-import-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import-error/README.md create mode 100644 internal/controllers/trunk/tests/trunk-import/00-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import/00-secret.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import/01-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import/02-assert.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml create mode 100644 internal/controllers/trunk/tests/trunk-import/README.md create mode 100644 internal/osclients/trunk.go delete mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/subport.go delete mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go diff --git a/PROJECT b/PROJECT index 3d09e3c30..8d6e2c12d 100644 --- a/PROJECT +++ b/PROJECT @@ -144,14 +144,6 @@ resources: kind: Subnet path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - domain: k-orc.cloud - group: openstack - kind: Trunk - path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 - version: v1alpha1 - api: crdVersion: v1 namespaced: true diff --git a/api/v1alpha1/trunk_type.go b/api/v1alpha1/trunk_type.go deleted file mode 100644 index 6945c86b8..000000000 --- a/api/v1alpha1/trunk_type.go +++ /dev/null @@ -1,183 +0,0 @@ -/* -Copyright 2025 The ORC Authors. - -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 v1alpha1 - -// TrunkFilter specifies a filter to select a trunk. At least one parameter must be specified. -// +kubebuilder:validation:MinProperties:=1 -type TrunkFilter struct { - // name of the existing resource - // +optional - Name *OpenStackName `json:"name,omitempty"` - - // description of the existing resource - // +optional - Description *NeutronDescription `json:"description,omitempty"` - - // admin state of the trunk (UP/DOWN) - // +optional - AdminStateUp *bool `json:"adminStateUp,omitempty"` - - // status of the trunk (ACTIVE/BUILD/ERROR/DELETED/DOWN/any other Neutron-supported status) - // +optional - Status *string `json:"status,omitempty"` - - // projectRef is a reference to the ORC Project this resource is associated with. - // Typically, only used by admin. - // +optional - ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` - - // projectID is the OpenStack project (tenant) ID for this trunk. - // +optional - ProjectID *string `json:"projectID,omitempty"` - - // tenantID is the OpenStack tenant ID for this trunk. - // +optional - TenantID *string `json:"tenantID,omitempty"` - - // portRef is a reference to the ORC Port which this trunk is associated with. - // +optional - PortRef *KubernetesNameRef `json:"portRef,omitempty"` - - // portID is the OpenStack port ID the trunk is associated with. - // +optional - PortID *string `json:"portID,omitempty"` - - // revisionNumber is the revision number of the trunk. - // +optional - RevisionNumber *int64 `json:"revisionNumber,omitempty"` - - FilterByNeutronTags `json:",inline"` -} - -// Subport represents a subport that will be attached to a trunk. -type Subport struct { - // portRef is a reference to the ORC Port which will be used as a subport. - // +required - PortRef KubernetesNameRef `json:"portRef,omitempty"` - - // segmentationType is the type of segmentation to use. - // Possible values are "vlan" or "inherit". - // +required - // +kubebuilder:validation:MaxLength=32 - SegmentationType string `json:"segmentationType"` - - // segmentationID is the segmentation identifier (e.g., VLAN ID). - // If segmentationType is "vlan", must be between 1 and 4094 inclusive. - // +required - // +kubebuilder:validation:XValidation:rule="self.segmentationType != 'vlan' || (self.segmentationID >= 1 && self.segmentationID <= 4094)" - SegmentationID int32 `json:"segmentationID"` -} - -// SubportStatus represents the observed state of a subport. -type SubportStatus struct { - // portID is the ID of the port used as a subport. - // +kubebuilder:validation:MaxLength=1024 - // +optional - PortID string `json:"portID,omitempty"` - - // segmentationType is the type of segmentation used. - // +kubebuilder:validation:MaxLength=32 - // +optional - SegmentationType string `json:"segmentationType,omitempty"` - - // segmentationID is the segmentation identifier. - // +optional - SegmentationID *int32 `json:"segmentationID,omitempty"` -} - -// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="portRef is immutable" -type TrunkResourceSpec struct { - // name is a human-readable name of the trunk. If not set, the object's name will be used. - // +optional - Name *OpenStackName `json:"name,omitempty"` - - // description is a human-readable description for the resource. - // +optional - Description *NeutronDescription `json:"description,omitempty"` - - // portRef is a reference to the ORC Port which will be used as the parent port for this trunk. - // +required - PortRef KubernetesNameRef `json:"portRef,omitempty"` - // tags is a list of tags which will be applied to the trunk. - // +kubebuilder:validation:MaxItems:=64 - // +listType=set - // +optional - Tags []NeutronTag `json:"tags,omitempty"` - - // adminStateUp is the administrative state of the trunk, which is up (true) or down (false). - // +optional - AdminStateUp *bool `json:"adminStateUp,omitempty"` - - // subports are the subports that will be attached to this trunk. - // +kubebuilder:validation:MaxItems:=128 - // +listType=atomic - // +optional - Subports []Subport `json:"subports,omitempty"` - - // projectRef is a reference to the ORC Project this resource is associated with. - // Typically, only used by admin. - // +optional - // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="projectRef is immutable" - ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` -} - -type TrunkResourceStatus struct { - // name is the human-readable name of the resource. Might not be unique. - // +kubebuilder:validation:MaxLength=1024 - // +optional - Name string `json:"name,omitempty"` - - // description is a human-readable description for the resource. - // +kubebuilder:validation:MaxLength=1024 - // +optional - Description string `json:"description,omitempty"` - - // portID is the ID of the parent port. - // +kubebuilder:validation:MaxLength=1024 - // +optional - PortID string `json:"portID,omitempty"` - - // projectID is the project owner of the resource. - // +kubebuilder:validation:MaxLength=1024 - // +optional - ProjectID string `json:"projectID,omitempty"` - - // status indicates the current status of the resource. - // +kubebuilder:validation:MaxLength=1024 - // +optional - Status string `json:"status,omitempty"` - - // tags is the list of tags on the resource. - // +kubebuilder:validation:MaxItems=64 - // +kubebuilder:validation:items:MaxLength=1024 - // +listType=atomic - // +optional - Tags []string `json:"tags,omitempty"` - - // adminStateUp is the administrative state of the trunk, - // which is up (true) or down (false). - // +optional - AdminStateUp *bool `json:"adminStateUp,omitempty"` - - // subports is a list of subports attached to this trunk. - // +kubebuilder:validation:MaxItems=128 - // +listType=atomic - // +optional - Subports []SubportStatus `json:"subports,omitempty"` - - NeutronStatusMetadata `json:",inline"` -} diff --git a/api/v1alpha1/trunk_types.go b/api/v1alpha1/trunk_types.go new file mode 100644 index 000000000..400373ce4 --- /dev/null +++ b/api/v1alpha1/trunk_types.go @@ -0,0 +1,102 @@ +/* +Copyright 2025 The ORC Authors. + +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 v1alpha1 + +// TrunkResourceSpec contains the desired state of the resource. +type TrunkResourceSpec struct { + // name will be the name of the created resource. If not specified, the + // name of the ORC object will be used. + // +optional + Name *OpenStackName `json:"name,omitempty"` + + // description is a human-readable description for the resource. + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=255 + // +optional + Description *string `json:"description,omitempty"` + + // portRef is a reference to the ORC Port which this resource is associated with. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="portRef is immutable" + PortRef KubernetesNameRef `json:"portRef,omitempty"` + + // projectRef is a reference to the ORC Project which this resource is associated with. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="projectRef is immutable" + ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` + + // TODO(scaffolding): Add more types. + // To see what is supported, you can take inspiration from the CreateOpts structure from + // github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks + // + // Until you have implemented mutability for the field, you must add a CEL validation + // preventing the field being modified: + // `// +kubebuilder:validation:XValidation:rule="self == oldSelf",message=" is immutable"` +} + +// TrunkFilter defines an existing resource by its properties +// +kubebuilder:validation:MinProperties:=1 +type TrunkFilter struct { + // name of the existing resource + // +optional + Name *OpenStackName `json:"name,omitempty"` + + // description of the existing resource + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=255 + // +optional + Description *string `json:"description,omitempty"` + + // portRef is a reference to the ORC Port which this resource is associated with. + // +optional + PortRef *KubernetesNameRef `json:"portRef,omitempty"` + + // projectRef is a reference to the ORC Project which this resource is associated with. + // +optional + ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` + + // TODO(scaffolding): Add more types. + // To see what is supported, you can take inspiration from the ListOpts structure from + // github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks +} + +// TrunkResourceStatus represents the observed state of the resource. +type TrunkResourceStatus struct { + // name is a Human-readable name for the resource. Might not be unique. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Name string `json:"name,omitempty"` + + // description is a human-readable description for the resource. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Description string `json:"description,omitempty"` + + // portID is the ID of the Port to which the resource is associated. + // +kubebuilder:validation:MaxLength=1024 + // +optional + PortID string `json:"portID,omitempty"` + + // projectID is the ID of the Project to which the resource is associated. + // +kubebuilder:validation:MaxLength=1024 + // +optional + ProjectID string `json:"projectID,omitempty"` + + // TODO(scaffolding): Add more types. + // To see what is supported, you can take inspiration from the Trunk structure from + // github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 377613005..19f25803d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -4959,41 +4959,6 @@ func (in *SubnetStatus) DeepCopy() *SubnetStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Subport) DeepCopyInto(out *Subport) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subport. -func (in *Subport) DeepCopy() *Subport { - if in == nil { - return nil - } - out := new(Subport) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubportStatus) DeepCopyInto(out *SubportStatus) { - *out = *in - if in.SegmentationID != nil { - in, out := &in.SegmentationID, &out.SegmentationID - *out = new(int32) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubportStatus. -func (in *SubportStatus) DeepCopy() *SubportStatus { - if in == nil { - return nil - } - out := new(SubportStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Trunk) DeepCopyInto(out *Trunk) { *out = *in @@ -5031,7 +4996,7 @@ func (in *TrunkFilter) DeepCopyInto(out *TrunkFilter) { } if in.Description != nil { in, out := &in.Description, &out.Description - *out = new(NeutronDescription) + *out = new(string) **out = **in } if in.PortRef != nil { @@ -5044,7 +5009,6 @@ func (in *TrunkFilter) DeepCopyInto(out *TrunkFilter) { *out = new(KubernetesNameRef) **out = **in } - in.FilterByNeutronTags.DeepCopyInto(&out.FilterByNeutronTags) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkFilter. @@ -5124,24 +5088,9 @@ func (in *TrunkResourceSpec) DeepCopyInto(out *TrunkResourceSpec) { } if in.Description != nil { in, out := &in.Description, &out.Description - *out = new(NeutronDescription) - **out = **in - } - if in.Tags != nil { - in, out := &in.Tags, &out.Tags - *out = make([]NeutronTag, len(*in)) - copy(*out, *in) - } - if in.AdminStateUp != nil { - in, out := &in.AdminStateUp, &out.AdminStateUp - *out = new(bool) + *out = new(string) **out = **in } - if in.Subports != nil { - in, out := &in.Subports, &out.Subports - *out = make([]Subport, len(*in)) - copy(*out, *in) - } if in.ProjectRef != nil { in, out := &in.ProjectRef, &out.ProjectRef *out = new(KubernetesNameRef) @@ -5162,24 +5111,6 @@ func (in *TrunkResourceSpec) DeepCopy() *TrunkResourceSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrunkResourceStatus) DeepCopyInto(out *TrunkResourceStatus) { *out = *in - if in.Tags != nil { - in, out := &in.Tags, &out.Tags - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.AdminStateUp != nil { - in, out := &in.AdminStateUp, &out.AdminStateUp - *out = new(bool) - **out = **in - } - if in.Subports != nil { - in, out := &in.Subports, &out.Subports - *out = make([]SubportStatus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - in.NeutronStatusMetadata.DeepCopyInto(&out.NeutronStatusMetadata) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkResourceStatus. @@ -5241,7 +5172,7 @@ func (in *TrunkStatus) DeepCopyInto(out *TrunkStatus) { if in.Resource != nil { in, out := &in.Resource, &out.Resource *out = new(TrunkResourceStatus) - (*in).DeepCopyInto(*out) + **out = **in } } diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go index b18a88f56..eb4943f25 100644 --- a/cmd/models-schema/zz_generated.openapi.go +++ b/cmd/models-schema/zz_generated.openapi.go @@ -199,8 +199,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetResourceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetResourceStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetSpec": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetSpec(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubnetStatus": schema_openstack_resource_controller_v2_api_v1alpha1_SubnetStatus(ref), - "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Subport": schema_openstack_resource_controller_v2_api_v1alpha1_Subport(ref), - "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubportStatus": schema_openstack_resource_controller_v2_api_v1alpha1_SubportStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Trunk": schema_openstack_resource_controller_v2_api_v1alpha1_Trunk(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkFilter": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkFilter(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkImport": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkImport(ref), @@ -9600,78 +9598,6 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_SubnetStatus(ref commo } } -func schema_openstack_resource_controller_v2_api_v1alpha1_Subport(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Subport represents a subport that will be attached to a trunk.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "portRef": { - SchemaProps: spec.SchemaProps{ - Description: "portRef is a reference to the ORC Port which will be used as a subport.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "segmentationType": { - SchemaProps: spec.SchemaProps{ - Description: "segmentationType is the type of segmentation to use (e.g., \"vlan\").", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "segmentationID": { - SchemaProps: spec.SchemaProps{ - Description: "segmentationID is the segmentation identifier (e.g., VLAN ID).", - Default: 0, - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"portRef", "segmentationType", "segmentationID"}, - }, - }, - } -} - -func schema_openstack_resource_controller_v2_api_v1alpha1_SubportStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SubportStatus represents the observed state of a subport.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "portID": { - SchemaProps: spec.SchemaProps{ - Description: "portID is the ID of the port used as a subport.", - Type: []string{"string"}, - Format: "", - }, - }, - "segmentationType": { - SchemaProps: spec.SchemaProps{ - Description: "segmentationType is the type of segmentation used.", - Type: []string{"string"}, - Format: "", - }, - }, - "segmentationID": { - SchemaProps: spec.SchemaProps{ - Description: "segmentationID is the segmentation identifier.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - }, - }, - } -} - func schema_openstack_resource_controller_v2_api_v1alpha1_Trunk(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -9726,7 +9652,7 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkFilter(ref common return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "TrunkFilter specifies a filter to select a trunk. At least one parameter must be specified.", + Description: "TrunkFilter defines an existing resource by its properties", Type: []string{"object"}, Properties: map[string]spec.Schema{ "name": { @@ -9745,98 +9671,18 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkFilter(ref common }, "portRef": { SchemaProps: spec.SchemaProps{ - Description: "portRef is a reference to the ORC Port which this trunk is associated with.", + Description: "portRef is a reference to the ORC Port which this resource is associated with.", Type: []string{"string"}, Format: "", }, }, "projectRef": { SchemaProps: spec.SchemaProps{ - Description: "projectRef is a reference to the ORC Project this resource is associated with. Typically, only used by admin.", + Description: "projectRef is a reference to the ORC Project which this resource is associated with.", Type: []string{"string"}, Format: "", }, }, - "tags": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "tags is a list of tags to filter by. If specified, the resource must have all of the tags specified to be included in the result.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "tagsAny": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "tagsAny is a list of tags to filter by. If specified, the resource must have at least one of the tags specified to be included in the result.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "notTags": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "notTags is a list of tags to filter by. If specified, resources which contain all of the given tags will be excluded from the result.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "notTagsAny": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "notTagsAny is a list of tags to filter by. If specified, resources which contain any of the given tags will be excluded from the result.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, }, }, }, @@ -9926,11 +9772,12 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceSpec(ref return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, + Description: "TrunkResourceSpec contains the desired state of the resource.", + Type: []string{"object"}, Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ - Description: "name is a human-readable name of the trunk. If not set, the object's name will be used.", + Description: "name will be the name of the created resource. If not specified, the name of the ORC object will be used.", Type: []string{"string"}, Format: "", }, @@ -9944,61 +9791,14 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceSpec(ref }, "portRef": { SchemaProps: spec.SchemaProps{ - Description: "portRef is a reference to the ORC Port which will be used as the parent port for this trunk.", - Default: "", + Description: "portRef is a reference to the ORC Port which this resource is associated with.", Type: []string{"string"}, Format: "", }, }, - "tags": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "tags is a list of tags which will be applied to the trunk.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "adminStateUp": { - SchemaProps: spec.SchemaProps{ - Description: "adminStateUp is the administrative state of the trunk, which is up (true) or down (false).", - Type: []string{"boolean"}, - Format: "", - }, - }, - "subports": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "atomic", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "subports are the subports that will be attached to this trunk.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Subport"), - }, - }, - }, - }, - }, "projectRef": { SchemaProps: spec.SchemaProps{ - Description: "projectRef is a reference to the ORC Project this resource is associated with. Typically, only used by admin.", + Description: "projectRef is a reference to the ORC Project which this resource is associated with.", Type: []string{"string"}, Format: "", }, @@ -10007,8 +9807,6 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceSpec(ref Required: []string{"portRef"}, }, }, - Dependencies: []string{ - "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Subport"}, } } @@ -10016,11 +9814,12 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceStatus(re return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, + Description: "TrunkResourceStatus represents the observed state of the resource.", + Type: []string{"object"}, Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ - Description: "name is the human-readable name of the resource. Might not be unique.", + Description: "name is a Human-readable name for the resource. Might not be unique.", Type: []string{"string"}, Format: "", }, @@ -10034,95 +9833,21 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceStatus(re }, "portID": { SchemaProps: spec.SchemaProps{ - Description: "portID is the ID of the parent port.", + Description: "portID is the ID of the Port to which the resource is associated.", Type: []string{"string"}, Format: "", }, }, "projectID": { SchemaProps: spec.SchemaProps{ - Description: "projectID is the project owner of the resource.", + Description: "projectID is the ID of the Project to which the resource is associated.", Type: []string{"string"}, Format: "", }, }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "status indicates the current status of the resource.", - Type: []string{"string"}, - Format: "", - }, - }, - "tags": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "atomic", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "tags is the list of tags on the resource.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "adminStateUp": { - SchemaProps: spec.SchemaProps{ - Description: "adminStateUp is the administrative state of the trunk, which is up (true) or down (false).", - Type: []string{"boolean"}, - Format: "", - }, - }, - "subports": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "atomic", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "subports is a list of subports attached to this trunk.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubportStatus"), - }, - }, - }, - }, - }, - "createdAt": { - SchemaProps: spec.SchemaProps{ - Description: "createdAt shows the date and time when the resource was created. The date and time stamp format is ISO 8601", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "updatedAt": { - SchemaProps: spec.SchemaProps{ - Description: "updatedAt shows the date and time when the resource was updated. The date and time stamp format is ISO 8601", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "revisionNumber": { - SchemaProps: spec.SchemaProps{ - Description: "revisionNumber optionally set via extensions/standard-attr-revisions", - Type: []string{"integer"}, - Format: "int64", - }, - }, }, }, }, - Dependencies: []string{ - "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.SubportStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } diff --git a/cmd/resource-generator/main.go b/cmd/resource-generator/main.go index c1540600d..6848b155f 100644 --- a/cmd/resource-generator/main.go +++ b/cmd/resource-generator/main.go @@ -146,10 +146,6 @@ var resources []templateFields = []templateFields{ Name: "Subnet", ExistingOSClient: true, }, - { - Name: "Trunk", - ExistingOSClient: true, - }, { Name: "Volume", }, diff --git a/config/crd/bases/openstack.k-orc.cloud_trunks.yaml b/config/crd/bases/openstack.k-orc.cloud_trunks.yaml index 5daf493e7..a5f3db488 100644 --- a/config/crd/bases/openstack.k-orc.cloud_trunks.yaml +++ b/config/crd/bases/openstack.k-orc.cloud_trunks.yaml @@ -102,76 +102,18 @@ spec: minLength: 1 pattern: ^[^,]+$ type: string - notTags: - description: |- - notTags is a list of tags to filter by. If specified, resources which - contain all of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - maxLength: 255 - minLength: 1 - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - notTagsAny: - description: |- - notTagsAny is a list of tags to filter by. If specified, resources - which contain any of the given tags will be excluded from the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - maxLength: 255 - minLength: 1 - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set portRef: description: portRef is a reference to the ORC Port which - this trunk is associated with. + this resource is associated with. maxLength: 253 minLength: 1 type: string projectRef: - description: |- - projectRef is a reference to the ORC Project this resource is associated with. - Typically, only used by admin. + description: projectRef is a reference to the ORC Project + which this resource is associated with. maxLength: 253 minLength: 1 type: string - tags: - description: |- - tags is a list of tags to filter by. If specified, the resource must - have all of the tags specified to be included in the result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - maxLength: 255 - minLength: 1 - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - tagsAny: - description: |- - tagsAny is a list of tags to filter by. If specified, the resource - must have at least one of the tags specified to be included in the - result. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - maxLength: 255 - minLength: 1 - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set type: object id: description: |- @@ -219,10 +161,6 @@ spec: resource must be specified if the management policy is `managed`. properties: - adminStateUp: - description: adminStateUp is the administrative state of the trunk, - which is up (true) or down (false). - type: boolean description: description: description is a human-readable description for the resource. @@ -230,78 +168,34 @@ spec: minLength: 1 type: string name: - description: name is a human-readable name of the trunk. If not - set, the object's name will be used. + description: |- + name will be the name of the created resource. If not specified, the + name of the ORC object will be used. maxLength: 255 minLength: 1 pattern: ^[^,]+$ type: string portRef: - description: portRef is a reference to the ORC Port which will - be used as the parent port for this trunk. + description: portRef is a reference to the ORC Port which this + resource is associated with. maxLength: 253 minLength: 1 type: string + x-kubernetes-validations: + - message: portRef is immutable + rule: self == oldSelf projectRef: - description: |- - projectRef is a reference to the ORC Project this resource is associated with. - Typically, only used by admin. + description: projectRef is a reference to the ORC Project which + this resource is associated with. maxLength: 253 minLength: 1 type: string x-kubernetes-validations: - message: projectRef is immutable rule: self == oldSelf - subports: - description: subports are the subports that will be attached to - this trunk. - items: - description: Subport represents a subport that will be attached - to a trunk. - properties: - portRef: - description: portRef is a reference to the ORC Port which - will be used as a subport. - maxLength: 253 - minLength: 1 - type: string - segmentationID: - description: segmentationID is the segmentation identifier - (e.g., VLAN ID). - format: int32 - type: integer - segmentationType: - description: segmentationType is the type of segmentation - to use (e.g., "vlan"). - maxLength: 64 - type: string - required: - - portRef - - segmentationID - - segmentationType - type: object - maxItems: 128 - type: array - x-kubernetes-list-type: atomic - tags: - description: tags is a list of tags which will be applied to the - trunk. - items: - description: |- - NeutronTag represents a tag on a Neutron resource. - It may not be empty and may not contain commas. - maxLength: 255 - minLength: 1 - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set required: - portRef type: object - x-kubernetes-validations: - - message: portRef is immutable - rule: self == oldSelf required: - cloudCredentialsRef type: object @@ -404,78 +298,26 @@ spec: description: resource contains the observed state of the OpenStack resource. properties: - adminStateUp: - description: |- - adminStateUp is the administrative state of the trunk, - which is up (true) or down (false). - type: boolean - createdAt: - description: createdAt shows the date and time when the resource - was created. The date and time stamp format is ISO 8601 - format: date-time - type: string description: description: description is a human-readable description for the resource. maxLength: 1024 type: string name: - description: name is the human-readable name of the resource. - Might not be unique. + description: name is a Human-readable name for the resource. Might + not be unique. maxLength: 1024 type: string portID: - description: portID is the ID of the parent port. + description: portID is the ID of the Port to which the resource + is associated. maxLength: 1024 type: string projectID: - description: projectID is the project owner of the resource. - maxLength: 1024 - type: string - revisionNumber: - description: revisionNumber optionally set via extensions/standard-attr-revisions - format: int64 - type: integer - status: - description: status indicates the current status of the resource. + description: projectID is the ID of the Project to which the resource + is associated. maxLength: 1024 type: string - subports: - description: subports is a list of subports attached to this trunk. - items: - description: SubportStatus represents the observed state of - a subport. - properties: - portID: - description: portID is the ID of the port used as a subport. - maxLength: 1024 - type: string - segmentationID: - description: segmentationID is the segmentation identifier. - format: int32 - type: integer - segmentationType: - description: segmentationType is the type of segmentation - used. - maxLength: 1024 - type: string - type: object - maxItems: 128 - type: array - x-kubernetes-list-type: atomic - tags: - description: tags is the list of tags on the resource. - items: - maxLength: 1024 - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: atomic - updatedAt: - description: updatedAt shows the date and time when the resource - was updated. The date and time stamp format is ISO 8601 - format: date-time - type: string type: object type: object type: object diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index b73dcac05..33b8c85e2 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -20,7 +20,6 @@ resources: - bases/openstack.k-orc.cloud_servergroups.yaml - bases/openstack.k-orc.cloud_services.yaml - bases/openstack.k-orc.cloud_subnets.yaml -- bases/openstack.k-orc.cloud_trunks.yaml - bases/openstack.k-orc.cloud_volumes.yaml - bases/openstack.k-orc.cloud_volumetypes.yaml # +kubebuilder:scaffold:crdkustomizeresource diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index aa2a75e43..dac467c69 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -18,7 +18,6 @@ resources: - openstack_v1alpha1_servergroup.yaml - openstack_v1alpha1_service.yaml - openstack_v1alpha1_subnet.yaml -- openstack_v1alpha1_trunk.yaml - openstack_v1alpha1_volume.yaml - openstack_v1alpha1_volumetype.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/openstack_v1alpha1_trunk.yaml b/config/samples/openstack_v1alpha1_trunk.yaml index 1b77e9e02..f663b0346 100644 --- a/config/samples/openstack_v1alpha1_trunk.yaml +++ b/config/samples/openstack_v1alpha1_trunk.yaml @@ -1,26 +1,14 @@ +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk metadata: name: trunk-sample spec: cloudCredentialsRef: - cloudName: openstack-admin + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: - name: trunk-sample description: Sample Trunk - portRef: port-sample - tags: - - tag1 - - tag2 - adminStateUp: true - subports: - - portRef: port-sample-subport1 - segmentationType: vlan - segmentationID: 100 - - portRef: port-sample-subport2 - segmentationType: vlan - segmentationID: 200 - projectRef: project-sample - + # TODO(scaffolding): Add all fields the resource supports diff --git a/internal/controllers/domain/tests/domain-create-full/00-assert.yaml b/internal/controllers/domain/tests/domain-create-full/00-assert.yaml index 31550542a..f9a6b6c4a 100644 --- a/internal/controllers/domain/tests/domain-create-full/00-assert.yaml +++ b/internal/controllers/domain/tests/domain-create-full/00-assert.yaml @@ -15,13 +15,3 @@ status: - type: Progressing status: "False" reason: Success ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Domain - name: domain-create-full - ref: domain -assertAll: - - celExpr: "domain.status.id != ''" diff --git a/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml b/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml index 864c9bac6..5dbc7e293 100644 --- a/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml +++ b/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml @@ -22,5 +22,4 @@ resourceRefs: name: domain-create-minimal ref: domain assertAll: - - celExpr: "domain.status.id != ''" - celExpr: "!has(domain.status.resource.description)" \ No newline at end of file diff --git a/internal/controllers/domain/tests/domain-import/02-assert.yaml b/internal/controllers/domain/tests/domain-import/02-assert.yaml index 69cfc4319..62a6ba634 100644 --- a/internal/controllers/domain/tests/domain-import/02-assert.yaml +++ b/internal/controllers/domain/tests/domain-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Domain - name: domain-import-external - ref: domain1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Domain - name: domain-import-external-not-this-one - ref: domain2 -assertAll: - - celExpr: "domain1.status.id != domain2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Domain metadata: diff --git a/internal/controllers/domain/tests/domain-update/01-assert.yaml b/internal/controllers/domain/tests/domain-update/01-assert.yaml index b00b0270d..017267dee 100644 --- a/internal/controllers/domain/tests/domain-update/01-assert.yaml +++ b/internal/controllers/domain/tests/domain-update/01-assert.yaml @@ -1,12 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Domain - name: domain-update - ref: domain ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Domain metadata: diff --git a/internal/controllers/domain/tests/domain-update/02-assert.yaml b/internal/controllers/domain/tests/domain-update/02-assert.yaml index 3e931f326..e109195f6 100644 --- a/internal/controllers/domain/tests/domain-update/02-assert.yaml +++ b/internal/controllers/domain/tests/domain-update/02-assert.yaml @@ -7,7 +7,6 @@ resourceRefs: name: domain-update ref: domain assertAll: - - celExpr: "domain.status.id != ''" - celExpr: "!has(domain.status.resource.description)" --- diff --git a/internal/controllers/flavor/tests/flavor-import/02-assert.yaml b/internal/controllers/flavor/tests/flavor-import/02-assert.yaml index 26745be04..a76b2d2e6 100644 --- a/internal/controllers/flavor/tests/flavor-import/02-assert.yaml +++ b/internal/controllers/flavor/tests/flavor-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Flavor - name: flavor-import-external - ref: flavor1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Flavor - name: flavor-import-external-not-this-one - ref: flavor2 -assertAll: - - celExpr: "flavor1.status.id != flavor2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Flavor metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml index cda640f2d..7f7d44f47 100644 --- a/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml @@ -1,33 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-create-full - ref: floatingIP - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: floatingip-create-full-external - ref: network - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Router - name: floatingip-create-full - ref: router - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Port - name: floatingip-create-full - ref: port - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: project - name: floatingip-create-full - ref: project -assertAll: - - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" - - celExpr: "floatingIP.status.resource.portID == port.status.id" - - celExpr: "floatingIP.status.resource.routerID == router.status.id" - - celExpr: "floatingIP.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml index fa451d399..e69de29bb 100644 --- a/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml @@ -1,15 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-create-minimal - ref: floatingIP - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: floatingip-create-minimal - ref: network -assertAll: - - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" - - celExpr: "floatingIP.status.resource.floatingIP != ''" diff --git a/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml index 9e997be07..37839a10b 100644 --- a/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-create-subnet-ref - ref: floatingIP - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: floatingip-create-subnet-ref - ref: network -assertAll: - - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml b/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml index 3bd12c0a4..48b2f3dba 100644 --- a/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml @@ -1,33 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-import-dependency - ref: fip1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-import-dependency-not-this-one - ref: fip2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: floatingip-import-dependency - ref: network - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Port - name: floatingip-import-dependency - ref: port - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: floatingip-import-dependency - ref: project -assertAll: - - celExpr: "fip1.status.id != fip2.status.id" - - celExpr: "fip1.status.resource.floatingNetworkID == network.status.id" - - celExpr: "fip1.status.resource.portID == port.status.id" - - celExpr: "fip1.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml b/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml index 6025dacfd..e69de29bb 100644 --- a/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml @@ -1,10 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: -- script: "! kubectl get network floatingip-import-dependency --namespace $NAMESPACE" - skipLogOutput: true -- script: "! kubectl get port floatingip-import-dependency --namespace $NAMESPACE" - skipLogOutput: true -- script: "! kubectl get project floatingip-import-dependency --namespace $NAMESPACE" - skipLogOutput: true diff --git a/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml b/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml index 18e15169a..33cc17cc1 100644 --- a/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml @@ -1,23 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-import - ref: fip1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-import-not-this-one - ref: fip2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-import-external - ref: fipExternal -assertAll: - - celExpr: "fip1.status.id != fip2.status.id" - - celExpr: "fip1.status.resource.floatingIP == fipExternal.status.resource.floatingIP" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml index ccaa21f97..309a64678 100644 --- a/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml @@ -11,8 +11,6 @@ resourceRefs: name: floatingip-update ref: network assertAll: - - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" - - celExpr: "floatingIP.status.resource.floatingIP != ''" - celExpr: "!has(floatingIP.status.resource.tags)" - celExpr: "!has(floatingIP.status.resource.description)" --- diff --git a/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml b/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml index 84c932ff6..12e07f10c 100644 --- a/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml @@ -1,19 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: FloatingIP - name: floatingip-update - ref: floatingIP - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: floatingip-update - ref: network -assertAll: - - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" - - celExpr: "floatingIP.status.resource.floatingIP != ''" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml b/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml index ccaa21f97..309a64678 100644 --- a/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml @@ -11,8 +11,6 @@ resourceRefs: name: floatingip-update ref: network assertAll: - - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" - - celExpr: "floatingIP.status.resource.floatingIP != ''" - celExpr: "!has(floatingIP.status.resource.tags)" - celExpr: "!has(floatingIP.status.resource.description)" --- diff --git a/internal/controllers/image/tests/image-import/02-assert.yaml b/internal/controllers/image/tests/image-import/02-assert.yaml index 2f6bf271c..d6a29d5a0 100644 --- a/internal/controllers/image/tests/image-import/02-assert.yaml +++ b/internal/controllers/image/tests/image-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Image - name: image-import-external - ref: image1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Image - name: image-import-external-not-this-one - ref: image2 -assertAll: - - celExpr: "image1.status.id != image2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Image metadata: diff --git a/internal/controllers/network/tests/network-create-full/00-assert.yaml b/internal/controllers/network/tests/network-create-full/00-assert.yaml index 81d65cf81..ff2b57bb2 100644 --- a/internal/controllers/network/tests/network-create-full/00-assert.yaml +++ b/internal/controllers/network/tests/network-create-full/00-assert.yaml @@ -31,8 +31,4 @@ resourceRefs: name: network-create-full ref: project assertAll: - - celExpr: "network.status.id != ''" - - celExpr: "network.status.resource.createdAt != ''" - - celExpr: "network.status.resource.updatedAt != ''" - celExpr: "network.status.resource.revisionNumber > 0" - - celExpr: "network.status.resource.projectID == project.status.id" diff --git a/internal/controllers/network/tests/network-import-dependency/02-assert.yaml b/internal/controllers/network/tests/network-import-dependency/02-assert.yaml index d16f0596a..4800215c7 100644 --- a/internal/controllers/network/tests/network-import-dependency/02-assert.yaml +++ b/internal/controllers/network/tests/network-import-dependency/02-assert.yaml @@ -1,23 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: network-import-dependency - ref: network1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: network-import-dependency-external-not-this-one - ref: network2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: network-import-dependency - ref: project -assertAll: - - celExpr: "network1.status.id != network2.status.id" - - celExpr: "network1.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Network metadata: diff --git a/internal/controllers/network/tests/network-import-dependency/03-assert.yaml b/internal/controllers/network/tests/network-import-dependency/03-assert.yaml index 0a5312ae1..e69de29bb 100644 --- a/internal/controllers/network/tests/network-import-dependency/03-assert.yaml +++ b/internal/controllers/network/tests/network-import-dependency/03-assert.yaml @@ -1,6 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: -- script: "! kubectl get project network-import-dependency --namespace $NAMESPACE" - skipLogOutput: true diff --git a/internal/controllers/network/tests/network-import/02-assert.yaml b/internal/controllers/network/tests/network-import/02-assert.yaml index 77bcbec7c..5fd10f506 100644 --- a/internal/controllers/network/tests/network-import/02-assert.yaml +++ b/internal/controllers/network/tests/network-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: network-import-external - ref: network1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: network-import-external-not-this-one - ref: network2 -assertAll: - - celExpr: "network1.status.id != network2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Network metadata: diff --git a/internal/controllers/network/tests/network-update/01-assert.yaml b/internal/controllers/network/tests/network-update/01-assert.yaml index e7fbd943e..94cfb4bc2 100644 --- a/internal/controllers/network/tests/network-update/01-assert.yaml +++ b/internal/controllers/network/tests/network-update/01-assert.yaml @@ -1,12 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: network-update - ref: network ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Network metadata: diff --git a/internal/controllers/port/tests/port-create-full/00-assert.yaml b/internal/controllers/port/tests/port-create-full/00-assert.yaml index b8340a6fe..b28910518 100644 --- a/internal/controllers/port/tests/port-create-full/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-full/00-assert.yaml @@ -38,12 +38,5 @@ resourceRefs: name: port-create-full ref: project assertAll: - - celExpr: "port.status.id != ''" - - celExpr: "port.status.resource.createdAt != ''" - - celExpr: "port.status.resource.updatedAt != ''" - - celExpr: "port.status.resource.macAddress != ''" - celExpr: "port.status.resource.revisionNumber > 0" - - celExpr: "port.status.resource.fixedIPs[0].subnetID == subnet.status.id" - celExpr: "port.status.resource.fixedIPs[0].ip == '192.168.155.122'" - - celExpr: "port.status.resource.securityGroups[0] == sg.status.id" - - celExpr: "port.status.resource.projectID == project.status.id" diff --git a/internal/controllers/port/tests/port-create-minimal/00-assert.yaml b/internal/controllers/port/tests/port-create-minimal/00-assert.yaml index 9c6d861fc..cfc645688 100644 --- a/internal/controllers/port/tests/port-create-minimal/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-minimal/00-assert.yaml @@ -21,8 +21,4 @@ resourceRefs: name: port-create-minimal ref: port assertAll: - - celExpr: "port.status.id != ''" - - celExpr: "port.status.resource.createdAt != ''" - - celExpr: "port.status.resource.updatedAt != ''" - - celExpr: "port.status.resource.macAddress != ''" - celExpr: "!has(port.status.resource.fixedIPs)" diff --git a/internal/controllers/port/tests/port-create-sriov/00-assert.yaml b/internal/controllers/port/tests/port-create-sriov/00-assert.yaml index 3277dfaea..6af78c05c 100644 --- a/internal/controllers/port/tests/port-create-sriov/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-sriov/00-assert.yaml @@ -27,12 +27,7 @@ resourceRefs: name: port-create-sriov ref: subnet assertAll: - - celExpr: "port.status.id != ''" - - celExpr: "port.status.resource.createdAt != ''" - - celExpr: "port.status.resource.updatedAt != ''" - - celExpr: "port.status.resource.macAddress != ''" - celExpr: "port.status.resource.revisionNumber > 0" - - celExpr: "port.status.resource.fixedIPs[0].subnetID == subnet.status.id" - celExpr: "port.status.resource.fixedIPs[0].ip == '192.168.155.122'" - celExpr: "!has(port.status.resource.allowedAddressPairs)" - celExpr: "!has(port.status.resource.securityGroups)" \ No newline at end of file diff --git a/internal/controllers/port/tests/port-import-dependency/02-assert.yaml b/internal/controllers/port/tests/port-import-dependency/02-assert.yaml index f0524a0bb..f266a9d75 100644 --- a/internal/controllers/port/tests/port-import-dependency/02-assert.yaml +++ b/internal/controllers/port/tests/port-import-dependency/02-assert.yaml @@ -1,28 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Port - name: port-import-dependency - ref: port1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Port - name: port-import-dependency-external-not-this-one - ref: port2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: port-import-dependency - ref: network - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: port-import-dependency - ref: project -assertAll: - - celExpr: "port1.status.id != port2.status.id" - - celExpr: "port1.status.resource.networkID == network.status.id" - - celExpr: "port1.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: diff --git a/internal/controllers/port/tests/port-import-dependency/03-assert.yaml b/internal/controllers/port/tests/port-import-dependency/03-assert.yaml index 7fffe080d..e69de29bb 100644 --- a/internal/controllers/port/tests/port-import-dependency/03-assert.yaml +++ b/internal/controllers/port/tests/port-import-dependency/03-assert.yaml @@ -1,8 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: -- script: "! kubectl get network port-import-dependency --namespace $NAMESPACE" - skipLogOutput: true -- script: "! kubectl get project port-import-dependency --namespace $NAMESPACE" - skipLogOutput: true diff --git a/internal/controllers/port/tests/port-import/02-assert.yaml b/internal/controllers/port/tests/port-import/02-assert.yaml index 1d1486451..56f3b3cb2 100644 --- a/internal/controllers/port/tests/port-import/02-assert.yaml +++ b/internal/controllers/port/tests/port-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Port - name: port-import-external - ref: port1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Port - name: port-import-external-not-this-one - ref: port2 -assertAll: - - celExpr: "port1.status.id != port2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: diff --git a/internal/controllers/port/tests/port-update/00-assert.yaml b/internal/controllers/port/tests/port-update/00-assert.yaml index fef380932..d17ac94bb 100644 --- a/internal/controllers/port/tests/port-update/00-assert.yaml +++ b/internal/controllers/port/tests/port-update/00-assert.yaml @@ -7,10 +7,6 @@ resourceRefs: name: port-update ref: port assertAll: - - celExpr: "port.status.id != ''" - - celExpr: "port.status.resource.createdAt != ''" - - celExpr: "port.status.resource.updatedAt != ''" - - celExpr: "port.status.resource.macAddress != ''" - celExpr: "!has(port.status.resource.fixedIPs)" - celExpr: "!has(port.status.resource.description)" --- diff --git a/internal/controllers/port/tests/port-update/01-assert.yaml b/internal/controllers/port/tests/port-update/01-assert.yaml index ef7850e61..83fd3c0b5 100644 --- a/internal/controllers/port/tests/port-update/01-assert.yaml +++ b/internal/controllers/port/tests/port-update/01-assert.yaml @@ -1,28 +1,3 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Port - name: port-update - ref: port - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Subnet - name: port-update - ref: subnet - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: port-update - ref: network - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: SecurityGroup - name: port-update - ref: securitygroup -assertAll: - - celExpr: "port.status.resource.networkID == network.status.id" - - celExpr: "subnet.status.resource.networkID == network.status.id" - - celExpr: "port.status.resource.securityGroups[0] == securitygroup.status.id" - --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port diff --git a/internal/controllers/port/tests/port-update/02-assert.yaml b/internal/controllers/port/tests/port-update/02-assert.yaml index 314caa5b0..838604d25 100644 --- a/internal/controllers/port/tests/port-update/02-assert.yaml +++ b/internal/controllers/port/tests/port-update/02-assert.yaml @@ -7,10 +7,6 @@ resourceRefs: name: port-update ref: port assertAll: - - celExpr: "port.status.id != ''" - - celExpr: "port.status.resource.createdAt != ''" - - celExpr: "port.status.resource.updatedAt != ''" - - celExpr: "port.status.resource.macAddress != ''" - celExpr: "!has(port.status.resource.fixedIPs)" - celExpr: "!has(port.status.resource.description)" - celExpr: "!has(port.status.resource.securityGroups)" diff --git a/internal/controllers/project/tests/project-import/02-assert.yaml b/internal/controllers/project/tests/project-import/02-assert.yaml index 270a3d060..1774bdf96 100644 --- a/internal/controllers/project/tests/project-import/02-assert.yaml +++ b/internal/controllers/project/tests/project-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: project-import-external - ref: project1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: project-import-external-not-this-one - ref: project2 -assertAll: - - celExpr: "project1.status.id != project2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project metadata: diff --git a/internal/controllers/project/tests/project-update/01-assert.yaml b/internal/controllers/project/tests/project-update/01-assert.yaml index c893540b0..0d180969f 100644 --- a/internal/controllers/project/tests/project-update/01-assert.yaml +++ b/internal/controllers/project/tests/project-update/01-assert.yaml @@ -1,12 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: project-update - ref: project ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project metadata: diff --git a/internal/controllers/router/tests/router-create-full/00-assert.yaml b/internal/controllers/router/tests/router-create-full/00-assert.yaml index 4213dfced..d2faadbbb 100644 --- a/internal/controllers/router/tests/router-create-full/00-assert.yaml +++ b/internal/controllers/router/tests/router-create-full/00-assert.yaml @@ -13,26 +13,3 @@ status: - tag1 - tag2 adminStateUp: false ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: router - name: router-create-full - ref: router - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: network - name: router-external-network - ref: network - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: project - name: router-create-full - ref: project -assertAll: - - celExpr: "router.status.id != ''" - # - celExpr: "router.status.resource.createdAt != ''" - # - celExpr: "router.status.resource.updatedAt != ''" - # - celExpr: "router.status.resource.revisionNumber > 0" - - celExpr: "router.status.resource.externalGateways[0].networkID == network.status.id" - - celExpr: "router.status.resource.projectID == project.status.id" diff --git a/internal/controllers/router/tests/router-import-dependency/02-assert.yaml b/internal/controllers/router/tests/router-import-dependency/02-assert.yaml index ddd39fbcb..c708fad01 100644 --- a/internal/controllers/router/tests/router-import-dependency/02-assert.yaml +++ b/internal/controllers/router/tests/router-import-dependency/02-assert.yaml @@ -1,23 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Router - name: router-import-dependency - ref: router1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Router - name: router-import-dependency-external-not-this-one - ref: router2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: router-import-dependency - ref: project -assertAll: - - celExpr: "router1.status.id != router2.status.id" - - celExpr: "router1.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Router metadata: diff --git a/internal/controllers/router/tests/router-import-dependency/03-assert.yaml b/internal/controllers/router/tests/router-import-dependency/03-assert.yaml index b143ec99c..e69de29bb 100644 --- a/internal/controllers/router/tests/router-import-dependency/03-assert.yaml +++ b/internal/controllers/router/tests/router-import-dependency/03-assert.yaml @@ -1,6 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: -- script: "! kubectl get project router-import-dependency --namespace $NAMESPACE" - skipLogOutput: true diff --git a/internal/controllers/router/tests/router-import/02-assert.yaml b/internal/controllers/router/tests/router-import/02-assert.yaml index 91112be50..da639473b 100644 --- a/internal/controllers/router/tests/router-import/02-assert.yaml +++ b/internal/controllers/router/tests/router-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Router - name: router-import-external - ref: router1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Router - name: router-import-external-not-this-one - ref: router2 -assertAll: - - celExpr: "router1.status.id != router2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Router metadata: diff --git a/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml b/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml index 17a0aa42d..0af8a590b 100644 --- a/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml +++ b/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml @@ -13,13 +13,3 @@ status: message: OpenStack resource is up to date status: "False" reason: Success ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: routerinterface - name: routerinterface-create-minimal - ref: routerinterface -assertAll: - - celExpr: "routerinterface.status.id != ''" \ No newline at end of file diff --git a/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml index 6d6cbecbb..633417899 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml @@ -20,21 +20,3 @@ status: tags: - tag1 - tag2 ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: securitygroup - name: securitygroup-create-full - ref: sg - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: project - name: securitygroup-create-full - ref: project -assertAll: - - celExpr: "sg.status.id != ''" - - celExpr: "sg.status.resource.createdAt != ''" - - celExpr: "sg.status.resource.updatedAt != ''" - # - celExpr: "sg.status.resource.revisionNumber > 0" - - celExpr: "sg.status.resource.projectID == project.status.id" diff --git a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml index 5e427fdda..419c878a8 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml @@ -1,23 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: SecurityGroup - name: securitygroup-import-dependency - ref: securitygroup1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: SecurityGroup - name: securitygroup-import-dependency-external-not-this-one - ref: securitygroup2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: securitygroup-import-dependency - ref: project -assertAll: - - celExpr: "securitygroup1.status.id != securitygroup2.status.id" - - celExpr: "securitygroup1.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: SecurityGroup metadata: diff --git a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml index 035006903..e69de29bb 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml @@ -1,6 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: -- script: "! kubectl get project securitygroup-import-dependency --namespace $NAMESPACE" - skipLogOutput: true diff --git a/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml index 03c0a13af..945b1f77d 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: SecurityGroup - name: securitygroup-import-external - ref: securitygroup1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: SecurityGroup - name: securitygroup-import-external-not-this-one - ref: securitygroup2 -assertAll: - - celExpr: "securitygroup1.status.id != securitygroup2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: SecurityGroup metadata: diff --git a/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml index dd2e51ea3..aabc0f186 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml @@ -1,12 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: SecurityGroup - name: securitygroup-update - ref: secGroup ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: SecurityGroup metadata: diff --git a/internal/controllers/server/tests/server-create-full/00-assert.yaml b/internal/controllers/server/tests/server-create-full/00-assert.yaml index 5f351d6d1..841846187 100644 --- a/internal/controllers/server/tests/server-create-full/00-assert.yaml +++ b/internal/controllers/server/tests/server-create-full/00-assert.yaml @@ -35,25 +35,10 @@ resourceRefs: name: server-create-full ref: subnet assertAll: - - celExpr: "server.status.resource.hostID != ''" - celExpr: "server.status.resource.availabilityZone == 'nova'" - - celExpr: "server.status.resource.imageID == image.status.id" - - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" - - celExpr: "server.status.resource.volumes[0].id == volume.status.id" - - celExpr: "volume.status.resource.attachments[0].attachmentID != ''" - - celExpr: "volume.status.resource.attachments[0].serverID == server.status.id" - - celExpr: "volume.status.resource.attachments[0].device != ''" - - celExpr: "volume.status.resource.attachments[0].attachedAt != ''" - - celExpr: "port.status.resource.deviceID == server.status.id" - celExpr: "port.status.resource.status == 'ACTIVE'" - celExpr: "size(server.status.resource.interfaces) == 1" - - celExpr: "server.status.resource.interfaces[0].portID == port.status.id" - - celExpr: "server.status.resource.interfaces[0].netID == network.status.id" - - celExpr: "server.status.resource.interfaces[0].macAddr != ''" - - celExpr: "server.status.resource.interfaces[0].portState != ''" - celExpr: "size(server.status.resource.interfaces[0].fixedIPs) >= 1" - - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].ipAddress != ''" - - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].subnetID == subnet.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-create-minimal/00-assert.yaml b/internal/controllers/server/tests/server-create-minimal/00-assert.yaml index e01b2f813..14d9d90d4 100644 --- a/internal/controllers/server/tests/server-create-minimal/00-assert.yaml +++ b/internal/controllers/server/tests/server-create-minimal/00-assert.yaml @@ -23,19 +23,9 @@ resourceRefs: name: server-create-minimal ref: subnet assertAll: - - celExpr: "server.status.resource.hostID != ''" - - celExpr: "server.status.resource.availabilityZone != ''" - - celExpr: "server.status.resource.imageID == image.status.id" - - celExpr: "port.status.resource.deviceID == server.status.id" - celExpr: "port.status.resource.status == 'ACTIVE'" - celExpr: "size(server.status.resource.interfaces) == 1" - - celExpr: "server.status.resource.interfaces[0].portID == port.status.id" - - celExpr: "server.status.resource.interfaces[0].netID == network.status.id" - - celExpr: "server.status.resource.interfaces[0].macAddr != ''" - - celExpr: "server.status.resource.interfaces[0].portState != ''" - celExpr: "size(server.status.resource.interfaces[0].fixedIPs) >= 1" - - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].ipAddress != ''" - - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].subnetID == subnet.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-import/02-assert.yaml b/internal/controllers/server/tests/server-import/02-assert.yaml index 877c14b58..5a5b65a12 100644 --- a/internal/controllers/server/tests/server-import/02-assert.yaml +++ b/internal/controllers/server/tests/server-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Server - name: server-import-external - ref: server1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Server - name: server-import-external-not-this-one - ref: server2 -assertAll: - - celExpr: "server1.status.id != server2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server metadata: diff --git a/internal/controllers/server/tests/server-update/00-assert.yaml b/internal/controllers/server/tests/server-update/00-assert.yaml index 551244650..7256bcfc5 100644 --- a/internal/controllers/server/tests/server-update/00-assert.yaml +++ b/internal/controllers/server/tests/server-update/00-assert.yaml @@ -19,13 +19,9 @@ resourceRefs: name: server-update ref: sg assertAll: - - celExpr: "server.status.resource.hostID != ''" - - celExpr: "server.status.resource.imageID == image.status.id" - - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" - celExpr: "!has(server.status.resource.tags)" - celExpr: "!has(server.status.resource.volumes)" - celExpr: "size(server.status.resource.interfaces) == 1" - - celExpr: "server.status.resource.interfaces[0].portID == port.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-update/01-assert.yaml b/internal/controllers/server/tests/server-update/01-assert.yaml index 473aecab0..b9a5ad237 100644 --- a/internal/controllers/server/tests/server-update/01-assert.yaml +++ b/internal/controllers/server/tests/server-update/01-assert.yaml @@ -27,21 +27,9 @@ resourceRefs: name: server-update ref: volume assertAll: - - celExpr: "server.status.resource.hostID != ''" - - celExpr: "server.status.resource.imageID == image.status.id" - - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" - - celExpr: "server.status.resource.volumes[0].id == volume.status.id" - - celExpr: "port1.status.resource.deviceID == server.status.id" - celExpr: "port1.status.resource.status == 'ACTIVE'" - - celExpr: "port2.status.resource.deviceID == server.status.id" - celExpr: "port2.status.resource.status == 'ACTIVE'" - celExpr: "size(server.status.resource.interfaces) == 2" - - celExpr: "server.status.resource.interfaces.exists(i, i.portID == port1.status.id)" - - celExpr: "server.status.resource.interfaces.exists(i, i.portID == port2.status.id)" - - celExpr: "volume.status.resource.attachments[0].attachmentID != ''" - - celExpr: "volume.status.resource.attachments[0].serverID == server.status.id" - - celExpr: "volume.status.resource.attachments[0].device != ''" - - celExpr: "volume.status.resource.attachments[0].attachedAt != ''" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-update/02-assert.yaml b/internal/controllers/server/tests/server-update/02-assert.yaml index 68beeb722..88263e513 100644 --- a/internal/controllers/server/tests/server-update/02-assert.yaml +++ b/internal/controllers/server/tests/server-update/02-assert.yaml @@ -27,18 +27,13 @@ resourceRefs: name: server-update ref: volume assertAll: - - celExpr: "server.status.resource.hostID != ''" - - celExpr: "server.status.resource.imageID == image.status.id" - - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" - celExpr: "!has(server.status.resource.tags)" - celExpr: "!has(server.status.resource.volumes)" - celExpr: "!has(volume.status.resource.attachments)" - - celExpr: "port1.status.resource.deviceID == server.status.id" - celExpr: "port1.status.resource.status == 'ACTIVE'" - celExpr: "!has(port2.status.resource.deviceID)" - celExpr: "port2.status.resource.status == 'DOWN'" - celExpr: "size(server.status.resource.interfaces) == 1" - - celExpr: "server.status.resource.interfaces[0].portID == port1.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml b/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml index 594f9fcae..ccbf63267 100644 --- a/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml +++ b/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: ServerGroup - name: servergroup-import-external - ref: sg1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: ServerGroup - name: servergroup-import-external-not-this-one - ref: sg2 -assertAll: - - celExpr: "sg1.status.id != sg2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: ServerGroup metadata: diff --git a/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml index 78eb9c1ab..a1e8452a8 100644 --- a/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml @@ -15,12 +15,9 @@ resourceRefs: name: subnet-create-full-v4 ref: project assertAll: - - celExpr: "subnet.status.id != ''" # - celExpr: "subnet.status.resource.createdAt != ''" # - celExpr: "subnet.status.resource.updatedAt != ''" # - celExpr: "subnet.status.resource.revisionNumber > 0" - - celExpr: "subnet.status.resource.networkID == network.status.id" - - celExpr: "subnet.status.resource.projectID == project.status.id" - celExpr: "'ipv6AddressMode' in subnet.status.resource == false" - celExpr: "'ipv6RAMode' in subnet.status.resource == false" --- diff --git a/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml index 8d812d6a1..7907aad4c 100644 --- a/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml @@ -1,27 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Subnet - name: subnet-create-full-v6 - ref: subnet - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: subnet-create-full-v6 - ref: network - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: project - name: subnet-create-full-v6 - ref: project -assertAll: - - celExpr: "subnet.status.id != ''" - # - celExpr: "subnet.status.resource.createdAt != ''" - # - celExpr: "subnet.status.resource.updatedAt != ''" - # - celExpr: "subnet.status.resource.revisionNumber > 0" - - celExpr: "subnet.status.resource.networkID == network.status.id" - - celExpr: "subnet.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml index a063a5836..5fa907a5a 100644 --- a/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml @@ -11,7 +11,6 @@ resourceRefs: name: subnet-create-minimal-v4 ref: network assertAll: - - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "'description' in subnet.status.resource == false" - celExpr: "'ipv6AddressMode' in subnet.status.resource == false" - celExpr: "'ipv6RAMode' in subnet.status.resource == false" diff --git a/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml index d0460c9b9..dfe3431b3 100644 --- a/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml @@ -11,7 +11,6 @@ resourceRefs: name: subnet-create-minimal-v6 ref: network assertAll: - - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "'description' in subnet.status.resource == false" - celExpr: "'ipv6AddressMode' in subnet.status.resource == false" - celExpr: "'ipv6RAMode' in subnet.status.resource == false" diff --git a/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml b/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml index fd416c9d3..cbd0d3cc0 100644 --- a/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml @@ -1,28 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Subnet - name: subnet-import-dependency - ref: subnet1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Subnet - name: subnet-import-dependency-external-not-this-one - ref: subnet2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: subnet-import-dependency - ref: network - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: subnet-import-dependency - ref: project -assertAll: - - celExpr: "subnet1.status.id != subnet2.status.id" - - celExpr: "subnet1.status.resource.networkID == network.status.id" - - celExpr: "subnet1.status.resource.projectID == project.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml b/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml index 7c736b10f..e69de29bb 100644 --- a/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml @@ -1,8 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: -- script: "! kubectl get network subnet-import-dependency --namespace $NAMESPACE" - skipLogOutput: true -- script: "! kubectl get project subnet-import-dependency --namespace $NAMESPACE" - skipLogOutput: true diff --git a/internal/controllers/subnet/tests/subnet-import/02-assert.yaml b/internal/controllers/subnet/tests/subnet-import/02-assert.yaml index 7533a1c4f..d9e366a08 100644 --- a/internal/controllers/subnet/tests/subnet-import/02-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-import/02-assert.yaml @@ -1,23 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Subnet - name: subnet-import-external - ref: subnet1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Subnet - name: subnet-import-external-not-this-one - ref: subnet2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: subnet-import - ref: network -assertAll: - - celExpr: "subnet1.status.id != subnet2.status.id" - - celExpr: "subnet1.status.resource.networkID == network.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-update/00-assert.yaml b/internal/controllers/subnet/tests/subnet-update/00-assert.yaml index d0f2c3de9..69f633bdc 100644 --- a/internal/controllers/subnet/tests/subnet-update/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-update/00-assert.yaml @@ -11,7 +11,6 @@ resourceRefs: name: subnet-update ref: network assertAll: - - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "!has(subnet.status.resource.description)" - celExpr: "!has(subnet.status.resource.tags)" - celExpr: "!has(subnet.status.resource.hostRoutes)" diff --git a/internal/controllers/subnet/tests/subnet-update/01-assert.yaml b/internal/controllers/subnet/tests/subnet-update/01-assert.yaml index 65207d2f3..2a62c1d82 100644 --- a/internal/controllers/subnet/tests/subnet-update/01-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-update/01-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Subnet - name: subnet-update - ref: subnet - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Network - name: subnet-update - ref: network -assertAll: - - celExpr: "subnet.status.resource.networkID == network.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-update/02-assert.yaml b/internal/controllers/subnet/tests/subnet-update/02-assert.yaml index 3602f09c8..e51f0d639 100644 --- a/internal/controllers/subnet/tests/subnet-update/02-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-update/02-assert.yaml @@ -11,7 +11,6 @@ resourceRefs: name: subnet-update ref: network assertAll: - - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "!has(subnet.status.resource.description)" - celExpr: "!has(subnet.status.resource.tags)" - celExpr: "!has(subnet.status.resource.hostRoutes)" diff --git a/internal/controllers/trunk/actuator.go b/internal/controllers/trunk/actuator.go index f607c07df..d8f1a3eca 100644 --- a/internal/controllers/trunk/actuator.go +++ b/internal/controllers/trunk/actuator.go @@ -1,467 +1,317 @@ -/* -Copyright 2025 The ORC Authors. - -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 trunk - -import ( - "context" - "fmt" - "iter" - - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/utils/ptr" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" - "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" - "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" - "github.com/k-orc/openstack-resource-controller/v2/internal/logging" - osclients "github.com/k-orc/openstack-resource-controller/v2/internal/osclients" - orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors" - "github.com/k-orc/openstack-resource-controller/v2/internal/util/tags" -) - -type ( - osResourceT = trunks.Trunk - - createResourceActuator = interfaces.CreateResourceActuator[orcObjectPT, orcObjectT, filterT, osResourceT] - deleteResourceActuator = interfaces.DeleteResourceActuator[orcObjectPT, orcObjectT, osResourceT] - reconcileResourceActuator = interfaces.ReconcileResourceActuator[orcObjectPT, osResourceT] - resourceReconciler = interfaces.ResourceReconciler[orcObjectPT, osResourceT] - helperFactory = interfaces.ResourceHelperFactory[orcObjectPT, orcObjectT, resourceSpecT, filterT, osResourceT] - trunkIterator = iter.Seq2[*osResourceT, error] -) - -type trunkActuator struct { - osClient osclients.NetworkClient - k8sClient client.Client -} - -var _ createResourceActuator = trunkActuator{} -var _ deleteResourceActuator = trunkActuator{} - -func (trunkActuator) GetResourceID(osResource *osResourceT) string { - return osResource.ID -} - -func (actuator trunkActuator) GetOSResourceByID(ctx context.Context, id string) (*osResourceT, progress.ReconcileStatus) { - trunk, err := actuator.osClient.GetTrunk(ctx, id) - if err != nil { - return nil, progress.WrapError(err) - } - return trunk, nil -} - -func (actuator trunkActuator) ListOSResourcesForAdoption(ctx context.Context, obj *orcv1alpha1.Trunk) (trunkIterator, bool) { - if obj.Spec.Resource == nil { - return nil, false - } - - listOpts := trunks.ListOpts{Name: getResourceName(obj)} - trunks, err := actuator.osClient.ListTrunk(ctx, listOpts) - if err != nil { - return func(yield func(*osResourceT, error) bool) { - yield(nil, err) - }, true - } - return func(yield func(*osResourceT, error) bool) { - for i := range trunks { - if !yield(&trunks[i], nil) { - return - } - } - }, true -} - -func (actuator trunkActuator) ListOSResourcesForImport(ctx context.Context, obj orcObjectPT, filter filterT) (iter.Seq2[*osResourceT, error], progress.ReconcileStatus) { - var reconcileStatus progress.ReconcileStatus - - port := &orcv1alpha1.Port{} - if filter.PortRef != nil { - portKey := client.ObjectKey{Name: string(*filter.PortRef), Namespace: obj.Namespace} - if err := actuator.k8sClient.Get(ctx, portKey, port); err != nil { - if apierrors.IsNotFound(err) { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WaitingOnObject("Port", portKey.Name, progress.WaitingOnCreation)) - } else { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WrapError(fmt.Errorf("fetching port %s: %w", portKey.Name, err))) - } - } else { - if !orcv1alpha1.IsAvailable(port) || port.Status.ID == nil { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WaitingOnObject("Port", portKey.Name, progress.WaitingOnReady)) - } - } - } - - project := &orcv1alpha1.Project{} - if filter.ProjectRef != nil { - projectKey := client.ObjectKey{Name: string(*filter.ProjectRef), Namespace: obj.Namespace} - if err := actuator.k8sClient.Get(ctx, projectKey, project); err != nil { - if apierrors.IsNotFound(err) { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WaitingOnObject("Project", projectKey.Name, progress.WaitingOnCreation)) - } else { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WrapError(fmt.Errorf("fetching project %s: %w", projectKey.Name, err))) - } - } else { - if !orcv1alpha1.IsAvailable(project) || project.Status.ID == nil { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WaitingOnObject("Project", projectKey.Name, progress.WaitingOnReady)) - } - } - } - - if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { - return nil, reconcileStatus - } - - listOpts := trunks.ListOpts{ - Name: string(ptr.Deref(filter.Name, "")), - Description: string(ptr.Deref(filter.Description, "")), - PortID: ptr.Deref(port.Status.ID, ""), - ProjectID: ptr.Deref(project.Status.ID, ""), - Tags: tags.Join(filter.Tags), - TagsAny: tags.Join(filter.TagsAny), - NotTags: tags.Join(filter.NotTags), - NotTagsAny: tags.Join(filter.NotTagsAny), - } - - trunksList, err := actuator.osClient.ListTrunk(ctx, listOpts) - if err != nil { - return func(yield func(*osResourceT, error) bool) { - yield(nil, err) - }, nil - } - return func(yield func(*osResourceT, error) bool) { - for i := range trunksList { - if !yield(&trunksList[i], nil) { - return - } - } - }, nil -} - -func (actuator trunkActuator) CreateResource(ctx context.Context, obj *orcv1alpha1.Trunk) (*osResourceT, progress.ReconcileStatus) { - resource := obj.Spec.Resource - if resource == nil { - // Should have been caught by API validation - return nil, progress.WrapError(orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Creation requested, but spec.resource is not set")) - } - - // Fetch all dependencies and ensure they have our finalizer - port, portDepRS := portDependency.GetDependency( - ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Port) bool { - return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil - }, - ) - portMap, subportDepRS := subportDependency.GetDependencies( - ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Port) bool { - return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil - }, - ) - reconcileStatus := progress.NewReconcileStatus(). - WithReconcileStatus(portDepRS). - WithReconcileStatus(subportDepRS) - - var projectID string - if resource.ProjectRef != nil { - project, projectDepRS := projectDependency.GetDependency( - ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Project) bool { - return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil - }, - ) - reconcileStatus = reconcileStatus.WithReconcileStatus(projectDepRS) - if project != nil { - projectID = ptr.Deref(project.Status.ID, "") - } - } - - if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { - return nil, reconcileStatus - } - - createOpts := trunks.CreateOpts{ - PortID: *port.Status.ID, - Name: getResourceName(obj), - Description: string(ptr.Deref(resource.Description, "")), - AdminStateUp: resource.AdminStateUp, - ProjectID: projectID, - } - - // Convert subports from spec to OpenStack format - if len(resource.Subports) > 0 { - createOpts.Subports = make([]trunks.Subport, len(resource.Subports)) - for i := range resource.Subports { - portName := string(resource.Subports[i].PortRef) - subportPort, ok := portMap[portName] - if !ok { - // Programming error - return nil, progress.WrapError(fmt.Errorf("subport port %s was not returned by GetDependencies", portName)) - } - createOpts.Subports[i] = trunks.Subport{ - PortID: *subportPort.Status.ID, - SegmentationType: resource.Subports[i].SegmentationType, - SegmentationID: int(resource.Subports[i].SegmentationID), - } - } - } - - osResource, err := actuator.osClient.CreateTrunk(ctx, createOpts) - if err != nil { - // We should require the spec to be updated before retrying a create which returned a conflict - if orcerrors.IsConflict(err) { - err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration creating resource: "+err.Error(), err) - } - return nil, progress.WrapError(err) - } - - return osResource, nil -} - -func (actuator trunkActuator) DeleteResource(ctx context.Context, _ *orcv1alpha1.Trunk, osResource *osResourceT) progress.ReconcileStatus { - return progress.WrapError(actuator.osClient.DeleteTrunk(ctx, osResource.ID)) -} - -var _ reconcileResourceActuator = trunkActuator{} - -func (actuator trunkActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller interfaces.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) { - return []resourceReconciler{ - tags.ReconcileTags[orcObjectPT, osResourceT](orcObject.Spec.Resource.Tags, osResource.Tags, tags.NewNeutronTagReplacer(actuator.osClient, "trunks", osResource.ID)), - actuator.updateResource, - actuator.reconcileSubports, - }, nil -} - -func (actuator trunkActuator) updateResource(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus { - log := ctrl.LoggerFrom(ctx) - resource := obj.Spec.Resource - if resource == nil { - return progress.WrapError( - orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Update requested, but spec.resource is not set")) - } - - var updateOpts trunks.UpdateOpts - needsUpdate := false - - // Handle name update - name := getResourceName(obj) - if osResource.Name != name { - updateOpts.Name = &name - needsUpdate = true - } - - // Handle description update - description := string(ptr.Deref(resource.Description, "")) - if osResource.Description != description { - updateOpts.Description = &description - needsUpdate = true - } - - // Handle adminStateUp update - if resource.AdminStateUp != nil && *resource.AdminStateUp != osResource.AdminStateUp { - updateOpts.AdminStateUp = resource.AdminStateUp - needsUpdate = true - } - - if !needsUpdate { - log.V(logging.Debug).Info("No changes") - return nil - } - - updateOpts.RevisionNumber = &osResource.RevisionNumber - - _, err := actuator.osClient.UpdateTrunk(ctx, osResource.ID, updateOpts) - - // We should require the spec to be updated before retrying an update which returned a conflict - if orcerrors.IsConflict(err) { - err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration updating resource: "+err.Error(), err) - } - - if err != nil { - return progress.WrapError(err) - } - - // Refresh is needed to get the updated resource state from OpenStack after modifications. - // This ensures the status accurately reflects the current state before the next reconciliation cycle. - return progress.NeedsRefresh() -} - -// reconcileSubports ensures the trunk's subports match the desired state from the spec. -// It handles adding new subports, removing deleted ones, and updating existing subports. -// Note: OpenStack trunk API does not support in-place updates of subport segmentation, -// so changes require removing and re-adding the subport, which may cause brief network interruption. -func (actuator trunkActuator) reconcileSubports(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus { - log := ctrl.LoggerFrom(ctx) - resource := obj.Spec.Resource - if resource == nil { - return nil - } - - // Get current subports from OpenStack - currentSubports, err := actuator.osClient.ListTrunkSubports(ctx, osResource.ID) - if err != nil { - return progress.WrapError(fmt.Errorf("failed to list trunk subports: %w", err)) - } - - // Get desired subports from spec - portMap, subportDepRS := subportDependency.GetDependencies( - ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Port) bool { - return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil - }, - ) - if needsReschedule, _ := subportDepRS.NeedsReschedule(); needsReschedule { - return subportDepRS - } - - desiredSubports := make([]trunks.Subport, 0, len(resource.Subports)) - for i := range resource.Subports { - portName := string(resource.Subports[i].PortRef) - subportPort, ok := portMap[portName] - if !ok { - // Port not ready yet, will be retried - log.V(logging.Debug).Info("Skipping subport: port not ready", "portRef", portName) - continue - } - desiredSubports = append(desiredSubports, trunks.Subport{ - PortID: *subportPort.Status.ID, - SegmentationType: resource.Subports[i].SegmentationType, - SegmentationID: int(resource.Subports[i].SegmentationID), - }) - } - - // Build maps for comparison (keyed by PortID for efficient lookup) - currentMap := buildSubportMap(currentSubports) - desiredMap := buildSubportMap(desiredSubports) - - // Find subports to add - // Note: OpenStack trunk API does not support in-place updates of subport segmentation. - // When segmentation type or ID changes, we must remove the old subport and add it back - // with the new segmentation. This may cause a brief network interruption for that subport. - toAdd := []trunks.Subport{} - for portID, desired := range desiredMap { - if current, exists := currentMap[portID]; !exists { - toAdd = append(toAdd, desired) - } else if subportNeedsUpdate(current, desired) { - // Subport exists but with different segmentation, need to remove and re-add - // First remove the old one - removeOpts := trunks.RemoveSubportsOpts{ - Subports: []trunks.RemoveSubport{{PortID: portID}}, - } - if err := actuator.osClient.RemoveSubports(ctx, osResource.ID, removeOpts); err != nil { - return progress.WrapError(fmt.Errorf("failed to remove subport %s: %w", portID, err)) - } - toAdd = append(toAdd, desired) - } - } - - // Find subports to remove - toRemove := []trunks.RemoveSubport{} - for portID := range currentMap { - if _, exists := desiredMap[portID]; !exists { - toRemove = append(toRemove, trunks.RemoveSubport{PortID: portID}) - } - } - - // Apply changes - if len(toRemove) > 0 { - removeOpts := trunks.RemoveSubportsOpts{Subports: toRemove} - if err := actuator.osClient.RemoveSubports(ctx, osResource.ID, removeOpts); err != nil { - return progress.WrapError(fmt.Errorf("failed to remove subports: %w", err)) - } - log.V(logging.Debug).Info("Removed subports", "count", len(toRemove)) - } - - if len(toAdd) > 0 { - addOpts := trunks.AddSubportsOpts{Subports: toAdd} - if _, err := actuator.osClient.AddSubports(ctx, osResource.ID, addOpts); err != nil { - return progress.WrapError(fmt.Errorf("failed to add subports: %w", err)) - } - log.V(logging.Debug).Info("Added subports", "count", len(toAdd)) - } - - if len(toAdd) > 0 || len(toRemove) > 0 { - // Refresh is needed to get the updated subport list from OpenStack after modifications. - // This ensures the status accurately reflects the current state before the next reconciliation. - return progress.NeedsRefresh() - } - - return nil -} - -// buildSubportMap creates a map of subports keyed by PortID for efficient comparison. -func buildSubportMap(subports []trunks.Subport) map[string]trunks.Subport { - result := make(map[string]trunks.Subport, len(subports)) - for _, subport := range subports { - result[subport.PortID] = subport - } - return result -} - -// subportNeedsUpdate checks if a subport needs to be updated by comparing segmentation parameters. -// OpenStack trunk API does not support in-place updates, so any change requires remove+re-add. -func subportNeedsUpdate(current, desired trunks.Subport) bool { - return current.SegmentationType != desired.SegmentationType || current.SegmentationID != desired.SegmentationID -} - -type trunkHelperFactory struct{} - -var _ helperFactory = trunkHelperFactory{} - -func (trunkHelperFactory) NewAPIObjectAdapter(obj orcObjectPT) adapterI { - return trunkAdapter{obj} -} - -func (trunkHelperFactory) NewCreateActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (createResourceActuator, progress.ReconcileStatus) { - return newActuator(ctx, controller, orcObject) -} - -func (trunkHelperFactory) NewDeleteActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (deleteResourceActuator, progress.ReconcileStatus) { - return newActuator(ctx, controller, orcObject) -} - -func newActuator(ctx context.Context, controller interfaces.ResourceController, orcObject *orcv1alpha1.Trunk) (trunkActuator, progress.ReconcileStatus) { - if orcObject == nil { - return trunkActuator{}, progress.WrapError(fmt.Errorf("orcObject may not be nil")) - } - - // Ensure credential secrets exist and have our finalizer - _, reconcileStatus := credentialsDependency.GetDependencies(ctx, controller.GetK8sClient(), orcObject, func(*corev1.Secret) bool { return true }) - if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { - return trunkActuator{}, reconcileStatus - } - - log := ctrl.LoggerFrom(ctx) - clientScope, err := controller.GetScopeFactory().NewClientScopeFromObject(ctx, controller.GetK8sClient(), log, orcObject) - if err != nil { - return trunkActuator{}, progress.WrapError(err) - } - osClient, err := clientScope.NewNetworkClient() - if err != nil { - return trunkActuator{}, progress.WrapError(err) - } - - return trunkActuator{ - osClient: osClient, - k8sClient: controller.GetK8sClient(), - }, nil -} - +/* +Copyright 2025 The ORC Authors. + +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 trunk + +import ( + "context" + "iter" + + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" + "github.com/k-orc/openstack-resource-controller/v2/internal/logging" + "github.com/k-orc/openstack-resource-controller/v2/internal/osclients" + orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors" +) + +// OpenStack resource types +type ( + osResourceT = trunks.Trunk + + createResourceActuator = interfaces.CreateResourceActuator[orcObjectPT, orcObjectT, filterT, osResourceT] + deleteResourceActuator = interfaces.DeleteResourceActuator[orcObjectPT, orcObjectT, osResourceT] + resourceReconciler = interfaces.ResourceReconciler[orcObjectPT, osResourceT] + helperFactory = interfaces.ResourceHelperFactory[orcObjectPT, orcObjectT, resourceSpecT, filterT, osResourceT] +) + +type trunkActuator struct { + osClient osclients.TrunkClient + k8sClient client.Client +} + +var _ createResourceActuator = trunkActuator{} +var _ deleteResourceActuator = trunkActuator{} + +func (trunkActuator) GetResourceID(osResource *osResourceT) string { + return osResource.ID +} + +func (actuator trunkActuator) GetOSResourceByID(ctx context.Context, id string) (*osResourceT, progress.ReconcileStatus) { + resource, err := actuator.osClient.GetTrunk(ctx, id) + if err != nil { + return nil, progress.WrapError(err) + } + return resource, nil +} + +func (actuator trunkActuator) ListOSResourcesForAdoption(ctx context.Context, orcObject orcObjectPT) (iter.Seq2[*osResourceT, error], bool) { + resourceSpec := orcObject.Spec.Resource + if resourceSpec == nil { + return nil, false + } + + // TODO(scaffolding) If you need to filter resources on fields that the List() function + // of gophercloud does not support, it's possible to perform client-side filtering. + // Check osclients.ResourceFilter + + listOpts := trunks.ListOpts{ + Name: getResourceName(orcObject), + Description: ptr.Deref(resourceSpec.Description, ""), + } + + return actuator.osClient.ListTrunks(ctx, listOpts), true +} + +func (actuator trunkActuator) ListOSResourcesForImport(ctx context.Context, obj orcObjectPT, filter filterT) (iter.Seq2[*osResourceT, error], progress.ReconcileStatus) { + // TODO(scaffolding) If you need to filter resources on fields that the List() function + // of gophercloud does not support, it's possible to perform client-side filtering. + // Check osclients.ResourceFilter + var reconcileStatus progress.ReconcileStatus + + port := &orcv1alpha1.Port{} + if filter.PortRef != nil { + portKey := client.ObjectKey{Name: string(*filter.PortRef), Namespace: obj.Namespace} + if err := actuator.k8sClient.Get(ctx, portKey, port); err != nil { + if apierrors.IsNotFound(err) { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Port", portKey.Name, progress.WaitingOnCreation)) + } else { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WrapError(fmt.Errorf("fetching port %s: %w", portKey.Name, err))) + } + } else { + if !orcv1alpha1.IsAvailable(port) || port.Status.ID == nil { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Port", portKey.Name, progress.WaitingOnReady)) + } + } + } + + project := &orcv1alpha1.Project{} + if filter.ProjectRef != nil { + projectKey := client.ObjectKey{Name: string(*filter.ProjectRef), Namespace: obj.Namespace} + if err := actuator.k8sClient.Get(ctx, projectKey, project); err != nil { + if apierrors.IsNotFound(err) { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Project", projectKey.Name, progress.WaitingOnCreation)) + } else { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WrapError(fmt.Errorf("fetching project %s: %w", projectKey.Name, err))) + } + } else { + if !orcv1alpha1.IsAvailable(project) || project.Status.ID == nil { + reconcileStatus = reconcileStatus.WithReconcileStatus( + progress.WaitingOnObject("Project", projectKey.Name, progress.WaitingOnReady)) + } + } + } + + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return nil, reconcileStatus + } + + listOpts := trunks.ListOpts{ + Name: string(ptr.Deref(filter.Name, "")), + Description: string(ptr.Deref(filter.Description, "")), + Port: ptr.Deref(port.Status.ID, ""), + Project: ptr.Deref(project.Status.ID, ""), + // TODO(scaffolding): Add more import filters + } + + return actuator.osClient.ListTrunks(ctx, listOpts), nil +} + +func (actuator trunkActuator) CreateResource(ctx context.Context, obj orcObjectPT) (*osResourceT, progress.ReconcileStatus) { + resource := obj.Spec.Resource + + if resource == nil { + // Should have been caught by API validation + return nil, progress.WrapError( + orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Creation requested, but spec.resource is not set")) + } + var reconcileStatus progress.ReconcileStatus + + var portID string + port, portDepRS := portDependency.GetDependency( + ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Port) bool { + return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil + }, + ) + reconcileStatus = reconcileStatus.WithReconcileStatus(portDepRS) + if port != nil { + portID = ptr.Deref(port.Status.ID, "") + } + + var projectID string + if resource.ProjectRef != nil { + project, projectDepRS := projectDependency.GetDependency( + ctx, actuator.k8sClient, obj, func(dep *orcv1alpha1.Project) bool { + return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil + }, + ) + reconcileStatus = reconcileStatus.WithReconcileStatus(projectDepRS) + if project != nil { + projectID = ptr.Deref(project.Status.ID, "") + } + } + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return nil, reconcileStatus + } + createOpts := trunks.CreateOpts{ + Name: getResourceName(obj), + Description: ptr.Deref(resource.Description, ""), + PortID: portID, + ProjectID: projectID, + // TODO(scaffolding): Add more fields + } + + osResource, err := actuator.osClient.CreateTrunk(ctx, createOpts) + if err != nil { + // We should require the spec to be updated before retrying a create which returned a conflict + if !orcerrors.IsRetryable(err) { + err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration creating resource: "+err.Error(), err) + } + return nil, progress.WrapError(err) + } + + return osResource, nil +} + +func (actuator trunkActuator) DeleteResource(ctx context.Context, _ orcObjectPT, resource *osResourceT) progress.ReconcileStatus { + return progress.WrapError(actuator.osClient.DeleteTrunk(ctx, resource.ID)) +} + +func (actuator trunkActuator) updateResource(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus { + log := ctrl.LoggerFrom(ctx) + resource := obj.Spec.Resource + if resource == nil { + // Should have been caught by API validation + return progress.WrapError( + orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Update requested, but spec.resource is not set")) + } + + updateOpts := trunks.UpdateOpts{} + + handleNameUpdate(&updateOpts, obj, osResource) + handleDescriptionUpdate(&updateOpts, resource, osResource) + + // TODO(scaffolding): add handler for all fields supporting mutability + + needsUpdate, err := needsUpdate(updateOpts) + if err != nil { + return progress.WrapError( + orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration updating resource: "+err.Error(), err)) + } + if !needsUpdate { + log.V(logging.Debug).Info("No changes") + return nil + } + + _, err = actuator.osClient.UpdateTrunk(ctx, osResource.ID, updateOpts) + + // We should require the spec to be updated before retrying an update which returned a conflict + if orcerrors.IsConflict(err) { + err = orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "invalid configuration updating resource: "+err.Error(), err) + } + + if err != nil { + return progress.WrapError(err) + } + + return progress.NeedsRefresh() +} + +func needsUpdate(updateOpts trunks.UpdateOpts) (bool, error) { + updateOptsMap, err := updateOpts.ToTrunkUpdateMap() + if err != nil { + return false, err + } + + updateMap, ok := updateOptsMap["trunk"].(map[string]any) + if !ok { + updateMap = make(map[string]any) + } + + return len(updateMap) > 0, nil +} + +func handleNameUpdate(updateOpts *trunks.UpdateOpts, obj orcObjectPT, osResource *osResourceT) { + name := getResourceName(obj) + if osResource.Name != name { + updateOpts.Name = &name + } +} + +func handleDescriptionUpdate(updateOpts *trunks.UpdateOpts, resource *resourceSpecT, osResource *osResourceT) { + description := ptr.Deref(resource.Description, "") + if osResource.Description != description { + updateOpts.Description = &description + } +} + +func (actuator trunkActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller interfaces.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) { + return []resourceReconciler{ + actuator.updateResource, + }, nil +} + +type trunkHelperFactory struct{} + +var _ helperFactory = trunkHelperFactory{} + +func newActuator(ctx context.Context, orcObject *orcv1alpha1.Trunk, controller interfaces.ResourceController) (trunkActuator, progress.ReconcileStatus) { + log := ctrl.LoggerFrom(ctx) + + // Ensure credential secrets exist and have our finalizer + _, reconcileStatus := credentialsDependency.GetDependencies(ctx, controller.GetK8sClient(), orcObject, func(*corev1.Secret) bool { return true }) + if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { + return trunkActuator{}, reconcileStatus + } + + clientScope, err := controller.GetScopeFactory().NewClientScopeFromObject(ctx, controller.GetK8sClient(), log, orcObject) + if err != nil { + return trunkActuator{}, progress.WrapError(err) + } + osClient, err := clientScope.NewTrunkClient() + if err != nil { + return trunkActuator{}, progress.WrapError(err) + } + + return trunkActuator{ + osClient: osClient, + k8sClient: controller.GetK8sClient(), + }, nil +} + +func (trunkHelperFactory) NewAPIObjectAdapter(obj orcObjectPT) adapterI { + return trunkAdapter{obj} +} + +func (trunkHelperFactory) NewCreateActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (createResourceActuator, progress.ReconcileStatus) { + return newActuator(ctx, orcObject, controller) +} + +func (trunkHelperFactory) NewDeleteActuator(ctx context.Context, orcObject orcObjectPT, controller interfaces.ResourceController) (deleteResourceActuator, progress.ReconcileStatus) { + return newActuator(ctx, orcObject, controller) +} diff --git a/internal/controllers/trunk/actuator_test.go b/internal/controllers/trunk/actuator_test.go index 91ccc9420..8bc6a7075 100644 --- a/internal/controllers/trunk/actuator_test.go +++ b/internal/controllers/trunk/actuator_test.go @@ -17,23 +17,11 @@ limitations under the License. package trunk import ( - "context" - "errors" "testing" - "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/utils/ptr" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" - osclientsmock "github.com/k-orc/openstack-resource-controller/v2/internal/osclients/mock" - orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors" - "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" ) func TestNeedsUpdate(t *testing.T) { @@ -48,30 +36,15 @@ func TestNeedsUpdate(t *testing.T) { expectChange: false, }, { - name: "Empty base opts with revision number", - updateOpts: trunks.UpdateOpts{RevisionNumber: ptr.To(4)}, - expectChange: false, - }, - { - name: "Updated opts with name", + name: "Updated opts", updateOpts: trunks.UpdateOpts{Name: ptr.To("updated")}, expectChange: true, }, - { - name: "Updated opts with description", - updateOpts: trunks.UpdateOpts{Description: ptr.To("new description")}, - expectChange: true, - }, - { - name: "Updated opts with adminStateUp", - updateOpts: trunks.UpdateOpts{AdminStateUp: ptr.To(false)}, - expectChange: true, - }, } for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { - got := needsUpdate(tt.updateOpts) + got, _ := needsUpdate(tt.updateOpts) if got != tt.expectChange { t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) } @@ -100,24 +73,25 @@ func TestHandleNameUpdate(t *testing.T) { resource.Spec = orcv1alpha1.TrunkSpec{ Resource: &orcv1alpha1.TrunkResourceSpec{Name: tt.newValue}, } - osResource := &trunks.Trunk{Name: tt.existingValue} + osResource := &osResourceT{Name: tt.existingValue} updateOpts := trunks.UpdateOpts{} handleNameUpdate(&updateOpts, resource, osResource) - got := needsUpdate(updateOpts) + got, _ := needsUpdate(updateOpts) if got != tt.expectChange { t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) } }) + } } func TestHandleDescriptionUpdate(t *testing.T) { - ptrToDescription := ptr.To[orcv1alpha1.NeutronDescription] + ptrToDescription := ptr.To[string] testCases := []struct { name string - newValue *orcv1alpha1.NeutronDescription + newValue *string existingValue string expectChange bool }{ @@ -130,552 +104,16 @@ func TestHandleDescriptionUpdate(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { resource := &orcv1alpha1.TrunkResourceSpec{Description: tt.newValue} - osResource := &trunks.Trunk{Description: tt.existingValue} + osResource := &osResourceT{Description: tt.existingValue} updateOpts := trunks.UpdateOpts{} handleDescriptionUpdate(&updateOpts, resource, osResource) - got := needsUpdate(updateOpts) + got, _ := needsUpdate(updateOpts) if got != tt.expectChange { t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) } }) - } -} -func TestHandleAdminStateUpUpdate(t *testing.T) { - ptrToBool := ptr.To[bool] - testCases := []struct { - name string - newValue *bool - existingValue bool - expectChange bool - }{ - {name: "Identical", newValue: ptrToBool(true), existingValue: true, expectChange: false}, - {name: "Different", newValue: ptrToBool(true), existingValue: false, expectChange: true}, - {name: "No value provided, existing is set", newValue: nil, existingValue: false, expectChange: false}, - {name: "No value provided, existing is default", newValue: nil, existingValue: true, expectChange: false}, - {name: "False when already false", newValue: ptrToBool(false), existingValue: false, expectChange: false}, - {name: "False when was true", newValue: ptrToBool(false), existingValue: true, expectChange: true}, } - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - resource := &orcv1alpha1.TrunkResourceSpec{AdminStateUp: tt.newValue} - osResource := &trunks.Trunk{AdminStateUp: tt.existingValue} - - updateOpts := trunks.UpdateOpts{} - handleAdminStateUpUpdate(&updateOpts, resource, osResource) - - got := needsUpdate(updateOpts) - if got != tt.expectChange { - t.Errorf("Expected change: %v, got: %v", tt.expectChange, got) - } - }) - } -} - -// needsUpdate checks if the updateOpts contains any changes that require an update -func needsUpdate(updateOpts trunks.UpdateOpts) bool { - return updateOpts.Name != nil || updateOpts.Description != nil || updateOpts.AdminStateUp != nil -} - -// handleNameUpdate updates the updateOpts if the name needs to be changed -func handleNameUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alpha1.Trunk, osResource *trunks.Trunk) { - name := getResourceName(resource) - if osResource.Name != name { - updateOpts.Name = &name - } -} - -// handleDescriptionUpdate updates the updateOpts if the description needs to be changed -func handleDescriptionUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alpha1.TrunkResourceSpec, osResource *trunks.Trunk) { - description := string(ptr.Deref(resource.Description, "")) - if osResource.Description != description { - updateOpts.Description = &description - } -} - -// handleAdminStateUpUpdate updates the updateOpts if the adminStateUp needs to be changed -func handleAdminStateUpUpdate(updateOpts *trunks.UpdateOpts, resource *orcv1alpha1.TrunkResourceSpec, osResource *trunks.Trunk) { - if resource.AdminStateUp != nil && *resource.AdminStateUp != osResource.AdminStateUp { - updateOpts.AdminStateUp = resource.AdminStateUp - } -} - -func TestGetResourceID(t *testing.T) { - actuator := trunkActuator{} - osResource := &trunks.Trunk{ID: "test-id-123"} - - got := actuator.GetResourceID(osResource) - if got != "test-id-123" { - t.Errorf("Expected ID 'test-id-123', got '%s'", got) - } -} - -func TestGetOSResourceByID(t *testing.T) { - ctx := context.Background() - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) - actuator := trunkActuator{osClient: mockClient} - - t.Run("success", func(t *testing.T) { - expectedTrunk := &trunks.Trunk{ID: "test-id", Name: "test-trunk"} - mockClient.EXPECT().GetTrunk(ctx, "test-id").Return(expectedTrunk, nil) - - got, status := actuator.GetOSResourceByID(ctx, "test-id") - if status != nil { - t.Errorf("Expected nil status, got %v", status) - } - if got.ID != expectedTrunk.ID { - t.Errorf("Expected ID '%s', got '%s'", expectedTrunk.ID, got.ID) - } - }) - - t.Run("error", func(t *testing.T) { - expectedErr := errors.New("not found") - mockClient.EXPECT().GetTrunk(ctx, "test-id").Return(nil, expectedErr) - - got, status := actuator.GetOSResourceByID(ctx, "test-id") - if got != nil { - t.Errorf("Expected nil, got %v", got) - } - if status == nil { - t.Fatal("Expected non-nil status") - } - needsReschedule, err := status.NeedsReschedule() - if !needsReschedule { - t.Error("Expected needsReschedule to be true") - } - if err == nil { - t.Error("Expected error in status") - } - }) } - -func TestListOSResourcesForAdoption(t *testing.T) { - ctx := context.Background() - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) - actuator := trunkActuator{osClient: mockClient} - - t.Run("no resource spec", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - Spec: orcv1alpha1.TrunkSpec{Resource: nil}, - } - iter, ok := actuator.ListOSResourcesForAdoption(ctx, obj) - if ok { - t.Error("Expected ok to be false when resource spec is nil") - } - if iter != nil { - t.Error("Expected nil iterator when resource spec is nil") - } - }) - - t.Run("success", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{}, - }, - } - expectedTrunks := []trunks.Trunk{ - {ID: "id1", Name: "test-trunk"}, - {ID: "id2", Name: "test-trunk"}, - } - mockClient.EXPECT().ListTrunk(ctx, gomock.Any()).Return(expectedTrunks, nil) - - iter, ok := actuator.ListOSResourcesForAdoption(ctx, obj) - if !ok { - t.Error("Expected ok to be true") - } - - var results []*trunks.Trunk - for trunk, err := range iter { - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - results = append(results, trunk) - } - - if len(results) != 2 { - t.Errorf("Expected 2 trunks, got %d", len(results)) - } - }) - - t.Run("error", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{}, - }, - } - expectedErr := errors.New("list error") - mockClient.EXPECT().ListTrunk(ctx, gomock.Any()).Return(nil, expectedErr) - - iter, ok := actuator.ListOSResourcesForAdoption(ctx, obj) - if !ok { - t.Error("Expected ok to be true even on error") - } - - var gotErr error - for _, err := range iter { - gotErr = err - break - } - - if gotErr != expectedErr { - t.Errorf("Expected error '%v', got '%v'", expectedErr, gotErr) - } - }) -} - -func TestDeleteResource(t *testing.T) { - ctx := context.Background() - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) - actuator := trunkActuator{osClient: mockClient} - - t.Run("success", func(t *testing.T) { - osResource := &trunks.Trunk{ID: "test-id"} - mockClient.EXPECT().DeleteTrunk(ctx, "test-id").Return(nil) - - status := actuator.DeleteResource(ctx, nil, osResource) - if status != nil { - needsReschedule, err := status.NeedsReschedule() - if needsReschedule || err != nil { - t.Errorf("Expected nil status, got needsReschedule=%v, err=%v", needsReschedule, err) - } - } - }) - - t.Run("error", func(t *testing.T) { - osResource := &trunks.Trunk{ID: "test-id"} - expectedErr := errors.New("delete error") - mockClient.EXPECT().DeleteTrunk(ctx, "test-id").Return(expectedErr) - - status := actuator.DeleteResource(ctx, nil, osResource) - if status == nil { - t.Fatal("Expected non-nil status on error") - } - needsReschedule, err := status.NeedsReschedule() - if !needsReschedule { - t.Error("Expected needsReschedule to be true") - } - if err == nil { - t.Error("Expected error in status") - } - }) -} - -func TestUpdateResource(t *testing.T) { - ctx := ctrl.LoggerInto(context.Background(), ctrl.Log) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) - actuator := trunkActuator{osClient: mockClient} - - t.Run("no changes", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Name: ptr.To(orcv1alpha1.OpenStackName("test-trunk")), - }, - }, - } - osResource := &trunks.Trunk{ - ID: "test-id", - Name: "test-trunk", - Description: "", - AdminStateUp: true, - RevisionNumber: 1, - } - - status := actuator.updateResource(ctx, obj, osResource) - if status != nil { - needsReschedule, _ := status.NeedsReschedule() - if needsReschedule { - t.Error("Expected no status when no changes") - } - } - }) - - t.Run("name change", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Name: ptr.To(orcv1alpha1.OpenStackName("new-name")), - }, - }, - } - osResource := &trunks.Trunk{ - ID: "test-id", - Name: "old-name", - RevisionNumber: 1, - } - - expectedTrunk := &trunks.Trunk{ID: "test-id", Name: "new-name"} - mockClient.EXPECT().UpdateTrunk(ctx, "test-id", gomock.Any()).Return(expectedTrunk, nil) - - status := actuator.updateResource(ctx, obj, osResource) - if status == nil { - t.Fatal("Expected non-nil status after update") - } - // Status should indicate refresh is needed (status will have a refresh message) - }) - - t.Run("description change", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Description: ptr.To(orcv1alpha1.NeutronDescription("new desc")), - }, - }, - } - osResource := &trunks.Trunk{ - ID: "test-id", - Name: "test-trunk", - Description: "old desc", - RevisionNumber: 1, - } - - expectedTrunk := &trunks.Trunk{ID: "test-id", Description: "new desc"} - mockClient.EXPECT().UpdateTrunk(ctx, "test-id", gomock.Any()).Return(expectedTrunk, nil) - - status := actuator.updateResource(ctx, obj, osResource) - if status == nil { - t.Fatal("Expected non-nil status after update") - } - }) - - t.Run("conflict error", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "test-trunk"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Name: ptr.To(orcv1alpha1.OpenStackName("new-name")), - }, - }, - } - osResource := &trunks.Trunk{ - ID: "test-id", - Name: "old-name", - RevisionNumber: 1, - } - - conflictErr := gophercloud.ErrUnexpectedResponseCode{Actual: 409} - mockClient.EXPECT().UpdateTrunk(ctx, "test-id", gomock.Any()).Return(nil, conflictErr) - - status := actuator.updateResource(ctx, obj, osResource) - if status == nil { - t.Fatal("Expected non-nil status on error") - } - needsReschedule, err := status.NeedsReschedule() - if !needsReschedule { - t.Error("Expected needsReschedule to be true on conflict") - } - if err == nil { - t.Error("Expected error in status") - } - // Check that it's a terminal error - var terminalError *orcerrors.TerminalError - if !errors.As(err, &terminalError) { - t.Error("Expected terminal error on conflict") - } - }) -} - -func TestReconcileSubports(t *testing.T) { - ctx := ctrl.LoggerInto(context.Background(), ctrl.Log) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - scheme := runtime.NewScheme() - orcv1alpha1.AddToScheme(scheme) - corev1.AddToScheme(scheme) - - mockClient := osclientsmock.NewMockNetworkClient(mockCtrl) - k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() - actuator := trunkActuator{osClient: mockClient, k8sClient: k8sClient} - - t.Run("no resource spec", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - Spec: orcv1alpha1.TrunkSpec{Resource: nil}, - } - osResource := &trunks.Trunk{ID: "test-id"} - - status := actuator.reconcileSubports(ctx, obj, osResource) - if status != nil { - t.Errorf("Expected nil status, got %v", status) - } - }) - - t.Run("no changes needed", func(t *testing.T) { - // Create a fresh k8sClient for this test to avoid state from previous tests - testK8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() - testActuator := trunkActuator{osClient: mockClient, k8sClient: testK8sClient} - - port1 := &orcv1alpha1.Port{ - ObjectMeta: metav1.ObjectMeta{ - Name: "port1", - Namespace: "default", - Finalizers: []string{finalizer}, - }, - Status: orcv1alpha1.PortStatus{ - ID: ptr.To("port-id-1"), - Conditions: []metav1.Condition{ - {Type: "Available", Status: metav1.ConditionTrue}, - }, - }, - } - testK8sClient.Create(ctx, port1) - - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Subports: []orcv1alpha1.Subport{ - { - PortRef: "port1", - SegmentationType: "vlan", - SegmentationID: 100, - }, - }, - }, - }, - } - osResource := &trunks.Trunk{ - ID: "trunk-id", - } - - currentSubports := []trunks.Subport{ - { - PortID: "port-id-1", - SegmentationType: "vlan", - SegmentationID: 100, - }, - } - - mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return(currentSubports, nil) - - status := testActuator.reconcileSubports(ctx, obj, osResource) - if status != nil { - t.Errorf("Expected nil status when no changes, got %v", status) - } - }) - - t.Run("add subport", func(t *testing.T) { - // Create a fresh k8sClient for this test to avoid state from previous tests - testK8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() - testActuator := trunkActuator{osClient: mockClient, k8sClient: testK8sClient} - - port1 := &orcv1alpha1.Port{ - ObjectMeta: metav1.ObjectMeta{ - Name: "port1", - Namespace: "default", - Finalizers: []string{finalizer}, - }, - Status: orcv1alpha1.PortStatus{ - ID: ptr.To("port-id-1"), - Conditions: []metav1.Condition{ - {Type: "Available", Status: metav1.ConditionTrue}, - }, - }, - } - testK8sClient.Create(ctx, port1) - - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Subports: []orcv1alpha1.Subport{ - { - PortRef: "port1", - SegmentationType: "vlan", - SegmentationID: 100, - }, - }, - }, - }, - } - osResource := &trunks.Trunk{ID: "trunk-id"} - - mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return([]trunks.Subport{}, nil) - expectedTrunk := &trunks.Trunk{ID: "trunk-id"} - mockClient.EXPECT().AddSubports(ctx, "trunk-id", gomock.Any()).Return(expectedTrunk, nil) - - status := testActuator.reconcileSubports(ctx, obj, osResource) - if status == nil { - t.Fatal("Expected non-nil status after adding subport") - } - // Status should indicate refresh is needed (status will have a refresh message) - }) - - t.Run("remove subport", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Subports: []orcv1alpha1.Subport{}, - }, - }, - } - osResource := &trunks.Trunk{ID: "trunk-id"} - - currentSubports := []trunks.Subport{ - { - PortID: "port-id-1", - SegmentationType: "vlan", - SegmentationID: 100, - }, - } - - mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return(currentSubports, nil) - mockClient.EXPECT().RemoveSubports(ctx, "trunk-id", gomock.Any()).Return(nil) - - status := actuator.reconcileSubports(ctx, obj, osResource) - if status == nil { - t.Fatal("Expected non-nil status after removing subport") - } - // Status should indicate refresh is needed (status will have a refresh message) - }) - - t.Run("list subports error", func(t *testing.T) { - obj := &orcv1alpha1.Trunk{ - ObjectMeta: metav1.ObjectMeta{Name: "trunk", Namespace: "default"}, - Spec: orcv1alpha1.TrunkSpec{ - Resource: &orcv1alpha1.TrunkResourceSpec{ - Subports: []orcv1alpha1.Subport{}, - }, - }, - } - osResource := &trunks.Trunk{ID: "trunk-id"} - - expectedErr := errors.New("list error") - mockClient.EXPECT().ListTrunkSubports(ctx, "trunk-id").Return(nil, expectedErr) - - status := actuator.reconcileSubports(ctx, obj, osResource) - if status == nil { - t.Fatal("Expected non-nil status on error") - } - needsReschedule, err := status.NeedsReschedule() - if !needsReschedule { - t.Error("Expected needsReschedule to be true on error") - } - if err == nil { - t.Error("Expected error in status") - } - }) -} - - diff --git a/internal/controllers/trunk/controller.go b/internal/controllers/trunk/controller.go index 5fead1d3e..79efb943a 100644 --- a/internal/controllers/trunk/controller.go +++ b/internal/controllers/trunk/controller.go @@ -1,186 +1,156 @@ -/* -Copyright 2025 The ORC Authors. - -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 trunk - -import ( - "context" - "errors" - - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/controller" - - orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" - "github.com/k-orc/openstack-resource-controller/v2/pkg/predicates" - - "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" - "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/reconciler" - "github.com/k-orc/openstack-resource-controller/v2/internal/scope" - "github.com/k-orc/openstack-resource-controller/v2/internal/util/credentials" - "github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency" -) - -// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=trunks,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=trunks/status,verbs=get;update;patch - -const controllerName = "trunk" - -var ( - portDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( - "spec.resource.portRef", - func(trunk *orcv1alpha1.Trunk) []string { - resource := trunk.Spec.Resource - if resource == nil { - return nil - } - return []string{string(resource.PortRef)} - }, - finalizer, externalObjectFieldOwner, - ) - - portImportDependency = dependency.NewDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( - "spec.import.filter.portRef", - func(trunk *orcv1alpha1.Trunk) []string { - resource := trunk.Spec.Import - if resource == nil || resource.Filter == nil || resource.Filter.PortRef == nil { - return nil - } - return []string{string(*resource.Filter.PortRef)} - }, - ) - - subportDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( - "spec.resource.subports[].portRef", - func(trunk *orcv1alpha1.Trunk) []string { - if trunk.Spec.Resource == nil { - return nil - } - ports := make([]string, len(trunk.Spec.Resource.Subports)) - for i := range trunk.Spec.Resource.Subports { - ports[i] = string(trunk.Spec.Resource.Subports[i].PortRef) - } - return ports - }, - finalizer, externalObjectFieldOwner, - dependency.OverrideDependencyName("subport"), - ) - - projectDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Project]( - "spec.resource.projectRef", - func(trunk *orcv1alpha1.Trunk) []string { - resource := trunk.Spec.Resource - if resource == nil || resource.ProjectRef == nil { - return nil - } - return []string{string(*resource.ProjectRef)} - }, - finalizer, externalObjectFieldOwner, - ) - - projectImportDependency = dependency.NewDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Project]( - "spec.import.filter.projectRef", - func(trunk *orcv1alpha1.Trunk) []string { - resource := trunk.Spec.Import - if resource == nil || resource.Filter == nil || resource.Filter.ProjectRef == nil { - return nil - } - return []string{string(*resource.Filter.ProjectRef)} - }, - ) -) - -type trunkReconcilerConstructor struct { - scopeFactory scope.Factory -} - -func New(scopeFactory scope.Factory) interfaces.Controller { - return trunkReconcilerConstructor{ - scopeFactory: scopeFactory, - } -} - -func (trunkReconcilerConstructor) GetName() string { - return controllerName -} - -// SetupWithManager sets up the controller with the Manager. -func (c trunkReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { - log := ctrl.LoggerFrom(ctx) - k8sClient := mgr.GetClient() - - portWatchEventHandler, err := portDependency.WatchEventHandler(log, k8sClient) - if err != nil { - return err - } - - portImportWatchEventHandler, err := portImportDependency.WatchEventHandler(log, k8sClient) - if err != nil { - return err - } - - subportWatchEventHandler, err := subportDependency.WatchEventHandler(log, k8sClient) - if err != nil { - return err - } - - projectWatchEventHandler, err := projectDependency.WatchEventHandler(log, k8sClient) - if err != nil { - return err - } - - projectImportWatchEventHandler, err := projectImportDependency.WatchEventHandler(log, k8sClient) - if err != nil { - return err - } - - builder := ctrl.NewControllerManagedBy(mgr). - WithOptions(options). - For(&orcv1alpha1.Trunk{}). - Watches(&orcv1alpha1.Port{}, portWatchEventHandler, - builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), - ). - // A second watch is necessary because we need a different handler that omits deletion guards - Watches(&orcv1alpha1.Port{}, portImportWatchEventHandler, - builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), - ). - Watches(&orcv1alpha1.Port{}, subportWatchEventHandler, - builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), - ). - Watches(&orcv1alpha1.Project{}, projectWatchEventHandler, - builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Project{})), - ). - // A second watch is necessary because we need a different handler that omits deletion guards - Watches(&orcv1alpha1.Project{}, projectImportWatchEventHandler, - builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Project{})), - ) - - if err := errors.Join( - portDependency.AddToManager(ctx, mgr), - portImportDependency.AddToManager(ctx, mgr), - subportDependency.AddToManager(ctx, mgr), - projectDependency.AddToManager(ctx, mgr), - projectImportDependency.AddToManager(ctx, mgr), - credentialsDependency.AddToManager(ctx, mgr), - credentials.AddCredentialsWatch(log, mgr.GetClient(), builder, credentialsDependency), - ); err != nil { - return err - } - - r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, trunkHelperFactory{}, trunkStatusWriter{}) - return builder.Complete(&r) -} - +/* +Copyright 2025 The ORC Authors. + +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 trunk + +import ( + "context" + "errors" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/controller" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/reconciler" + "github.com/k-orc/openstack-resource-controller/v2/internal/scope" + "github.com/k-orc/openstack-resource-controller/v2/internal/util/credentials" + "github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency" + "github.com/k-orc/openstack-resource-controller/v2/pkg/predicates" +) + +const controllerName = "trunk" + +// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=trunks,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=trunks/status,verbs=get;update;patch + +type trunkReconcilerConstructor struct { + scopeFactory scope.Factory +} + +func New(scopeFactory scope.Factory) interfaces.Controller { + return trunkReconcilerConstructor{scopeFactory: scopeFactory} +} + +func (trunkReconcilerConstructor) GetName() string { + return controllerName +} + +var portDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( + "spec.resource.portRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Resource + if resource == nil { + return nil + } + return []string{string(resource.PortRef)} + }, + finalizer, externalObjectFieldOwner, +) + +var projectDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Project]( + "spec.resource.projectRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Resource + if resource == nil || resource.ProjectRef == nil { + return nil + } + return []string{string(*resource.ProjectRef)} + }, + finalizer, externalObjectFieldOwner, +) + +var portImportDependency = dependency.NewDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Port]( + "spec.import.filter.portRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Import + if resource == nil || resource.Filter == nil || resource.Filter.PortRef == nil { + return nil + } + return []string{string(*resource.Filter.PortRef)} + }, +) + +var projectImportDependency = dependency.NewDependency[*orcv1alpha1.TrunkList, *orcv1alpha1.Project]( + "spec.import.filter.projectRef", + func(trunk *orcv1alpha1.Trunk) []string { + resource := trunk.Spec.Import + if resource == nil || resource.Filter == nil || resource.Filter.ProjectRef == nil { + return nil + } + return []string{string(*resource.Filter.ProjectRef)} + }, +) + +// SetupWithManager sets up the controller with the Manager. +func (c trunkReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { + log := ctrl.LoggerFrom(ctx) + k8sClient := mgr.GetClient() + + portWatchEventHandler, err := portDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + projectWatchEventHandler, err := projectDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + portImportWatchEventHandler, err := portImportDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + projectImportWatchEventHandler, err := projectImportDependency.WatchEventHandler(log, k8sClient) + if err != nil { + return err + } + + builder := ctrl.NewControllerManagedBy(mgr). + WithOptions(options). + Watches(&orcv1alpha1.Port{}, portWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), + ). + Watches(&orcv1alpha1.Project{}, projectWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Project{})), + ). + // A second watch is necessary because we need a different handler that omits deletion guards + Watches(&orcv1alpha1.Port{}, portImportWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Port{})), + ). + // A second watch is necessary because we need a different handler that omits deletion guards + Watches(&orcv1alpha1.Project{}, projectImportWatchEventHandler, + builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Project{})), + ). + For(&orcv1alpha1.Trunk{}) + + if err := errors.Join( + portDependency.AddToManager(ctx, mgr), + projectDependency.AddToManager(ctx, mgr), + portImportDependency.AddToManager(ctx, mgr), + projectImportDependency.AddToManager(ctx, mgr), + credentialsDependency.AddToManager(ctx, mgr), + credentials.AddCredentialsWatch(log, mgr.GetClient(), builder, credentialsDependency), + ); err != nil { + return err + } + + r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, trunkHelperFactory{}, trunkStatusWriter{}) + return builder.Complete(&r) +} diff --git a/internal/controllers/trunk/status.go b/internal/controllers/trunk/status.go index 38e12f6ec..c43397bef 100644 --- a/internal/controllers/trunk/status.go +++ b/internal/controllers/trunk/status.go @@ -1,90 +1,65 @@ -/* -Copyright 2025 The ORC Authors. - -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 trunk - -import ( - "github.com/go-logr/logr" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" - "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" - orcapplyconfigv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" -) - -const ( - TrunkStatusActive = "ACTIVE" - TrunkStatusDown = "DOWN" -) - -type objectApplyPT = *orcapplyconfigv1alpha1.TrunkApplyConfiguration -type statusApplyPT = *orcapplyconfigv1alpha1.TrunkStatusApplyConfiguration - -type trunkStatusWriter struct{} - -var _ interfaces.ResourceStatusWriter[orcObjectPT, *osResourceT, objectApplyPT, statusApplyPT] = trunkStatusWriter{} - -func (trunkStatusWriter) GetApplyConfig(name, namespace string) objectApplyPT { - return orcapplyconfigv1alpha1.Trunk(name, namespace) -} - -func (trunkStatusWriter) ResourceAvailableStatus(orcObject orcObjectPT, osResource *osResourceT) (metav1.ConditionStatus, progress.ReconcileStatus) { - if osResource == nil { - if orcObject.Status.ID == nil { - return metav1.ConditionFalse, nil - } else { - return metav1.ConditionUnknown, nil - } - } - - // Both active and down trunks are Available - if osResource.Status == TrunkStatusActive || osResource.Status == TrunkStatusDown { - return metav1.ConditionTrue, nil - } - return metav1.ConditionFalse, nil -} - -func (trunkStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *osResourceT, statusApply statusApplyPT) { - resourceStatus := orcapplyconfigv1alpha1.TrunkResourceStatus(). - WithName(osResource.Name). - WithAdminStateUp(osResource.AdminStateUp). - WithStatus(osResource.Status). - WithProjectID(osResource.ProjectID). - WithPortID(osResource.PortID). - WithTags(osResource.Tags...). - WithRevisionNumber(int64(osResource.RevisionNumber)). - WithCreatedAt(metav1.NewTime(osResource.CreatedAt)). - WithUpdatedAt(metav1.NewTime(osResource.UpdatedAt)) - - if osResource.Description != "" { - resourceStatus = resourceStatus.WithDescription(osResource.Description) - } - - if len(osResource.Subports) > 0 { - subports := make([]*orcapplyconfigv1alpha1.SubportStatusApplyConfiguration, len(osResource.Subports)) - for i := range osResource.Subports { - subportStatus := orcapplyconfigv1alpha1.SubportStatus(). - WithPortID(osResource.Subports[i].PortID). - WithSegmentationType(osResource.Subports[i].SegmentationType). - WithSegmentationID(int32(osResource.Subports[i].SegmentationID)) - subports[i] = subportStatus - } - resourceStatus.WithSubports(subports...) - } - - statusApply.WithResource(resourceStatus) -} - +/* +Copyright 2025 The ORC Authors. + +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 trunk + +import ( + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" + orcapplyconfigv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/pkg/clients/applyconfiguration/api/v1alpha1" +) + +type trunkStatusWriter struct{} + +type objectApplyT = orcapplyconfigv1alpha1.TrunkApplyConfiguration +type statusApplyT = orcapplyconfigv1alpha1.TrunkStatusApplyConfiguration + +var _ interfaces.ResourceStatusWriter[*orcv1alpha1.Trunk, *osResourceT, *objectApplyT, *statusApplyT] = trunkStatusWriter{} + +func (trunkStatusWriter) GetApplyConfig(name, namespace string) *objectApplyT { + return orcapplyconfigv1alpha1.Trunk(name, namespace) +} + +func (trunkStatusWriter) ResourceAvailableStatus(orcObject *orcv1alpha1.Trunk, osResource *osResourceT) (metav1.ConditionStatus, progress.ReconcileStatus) { + if osResource == nil { + if orcObject.Status.ID == nil { + return metav1.ConditionFalse, nil + } else { + return metav1.ConditionUnknown, nil + } + } + return metav1.ConditionTrue, nil +} + +func (trunkStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *osResourceT, statusApply *statusApplyT) { + resourceStatus := orcapplyconfigv1alpha1.TrunkResourceStatus(). + WithPortID(osResource.PortID). + WithProjectID(osResource.ProjectID). + WithName(osResource.Name) + + // TODO(scaffolding): add all of the fields supported in the TrunkResourceStatus struct + // If a zero-value isn't expected in the response, place it behind a conditional + + if osResource.Description != "" { + resourceStatus.WithDescription(osResource.Description) + } + + statusApply.WithResource(resourceStatus) +} diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml index 6091e47f9..c3a7df9c9 100644 --- a/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml @@ -1,64 +1,38 @@ ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Trunk -metadata: - name: trunk-create-full -status: - resource: - name: trunk-create-full-override - description: Trunk from "create full" test - adminStateUp: true - status: ACTIVE - tags: - - tag1 - - tag2 - subports: - - portID: "" - segmentationType: vlan - segmentationID: 100 - - portID: "" - segmentationType: vlan - segmentationID: 200 ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: trunk - name: trunk-create-full - ref: trunk - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: port - name: trunk-create-full - ref: port - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: port - name: trunk-create-full-subport1 - ref: subport1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: port - name: trunk-create-full-subport2 - ref: subport2 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: project - name: trunk-create-full - ref: project -assertAll: - - celExpr: "trunk.status.id != ''" - - celExpr: "trunk.status.resource.createdAt != ''" - - celExpr: "trunk.status.resource.updatedAt != ''" - - celExpr: "trunk.status.resource.portID == port.status.id" - - celExpr: "trunk.status.resource.name == 'trunk-create-full-override'" - - celExpr: "trunk.status.resource.description == 'Trunk from \"create full\" test'" - - celExpr: "trunk.status.resource.adminStateUp == true" - - celExpr: "trunk.status.resource.projectID == project.status.id" - - celExpr: "size(trunk.status.resource.subports) == 2" - - celExpr: "trunk.status.resource.subports[0].portID == subport1.status.id" - - celExpr: "trunk.status.resource.subports[0].segmentationType == 'vlan'" - - celExpr: "trunk.status.resource.subports[0].segmentationID == 100" - - celExpr: "trunk.status.resource.subports[1].portID == subport2.status.id" - - celExpr: "trunk.status.resource.subports[1].segmentationType == 'vlan'" - - celExpr: "trunk.status.resource.subports[1].segmentationID == 200" - - celExpr: "'tag1' in trunk.status.resource.tags" - - celExpr: "'tag2' in trunk.status.resource.tags" - +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-full +status: + resource: + name: trunk-create-full-override + description: Trunk from "create full" test + # TODO(scaffolding): Add all fields the resource supports + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-create-full + ref: trunk + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: trunk-create-full + ref: port + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: trunk-create-full + ref: project +assertAll: + - celExpr: "trunk.status.id != ''" + - celExpr: "trunk.status.resource.portID == port.status.id" + - celExpr: "trunk.status.resource.projectID == project.status.id" + # TODO(scaffolding): Add more checks diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml index 118756c2f..bf1cf03da 100644 --- a/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml @@ -1,108 +1,43 @@ ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Project -metadata: - name: trunk-create-full -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: {} ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Network -metadata: - name: trunk-create-full -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - projectRef: trunk-create-full ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Subnet -metadata: - name: trunk-create-full -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - networkRef: trunk-create-full - ipVersion: 4 - cidr: 192.168.157.0/24 - projectRef: trunk-create-full ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port -metadata: - name: trunk-create-full -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - name: trunk-create-full-port - description: Parent port for trunk-create-full - networkRef: trunk-create-full - projectRef: trunk-create-full ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port -metadata: - name: trunk-create-full-subport1 -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - name: trunk-create-full-subport1 - networkRef: trunk-create-full - projectRef: trunk-create-full ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port -metadata: - name: trunk-create-full-subport2 -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - name: trunk-create-full-subport2 - networkRef: trunk-create-full - projectRef: trunk-create-full ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Trunk -metadata: - name: trunk-create-full -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - name: trunk-create-full-override - description: Trunk from "create full" test - portRef: trunk-create-full - adminStateUp: true - tags: - - tag1 - - tag2 - subports: - - portRef: trunk-create-full-subport1 - segmentationType: vlan - segmentationID: 100 - - portRef: trunk-create-full-subport2 - segmentationType: vlan - segmentationID: 200 - projectRef: trunk-create-full - +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Project +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-create-full-override + description: Trunk from "create full" test + portRef: trunk-create-full + projectRef: trunk-create-full + # TODO(scaffolding): Add all fields the resource supports diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-secret.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-full/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-create-full/README.md b/internal/controllers/trunk/tests/trunk-create-full/README.md index ad105a3a0..45eabc565 100644 --- a/internal/controllers/trunk/tests/trunk-create-full/README.md +++ b/internal/controllers/trunk/tests/trunk-create-full/README.md @@ -1,18 +1,11 @@ -# Create a trunk with all options - -## Step 00 - -Create a trunk with all fields set: -- Custom name -- Description -- Admin state -- Tags -- Subports with VLAN segmentation -- Project reference - -Verify that all fields are correctly reflected in the observed state. - -## Reference - -https://k-orc.cloud/development/writing-tests/#create-full - +# Create a Trunk with all the options + +## Step 00 + +Create a Trunk using all available fields, and verify that the observed state corresponds to the spec. + +Also validate that the OpenStack resource uses the name from the spec when it is specified. + +## Reference + +https://k-orc.cloud/development/writing-tests/#create-full diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml index 98f62359e..029262124 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml @@ -1,30 +1,32 @@ ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Trunk -metadata: - name: trunk-create-minimal -status: - resource: - name: trunk-create-minimal - adminStateUp: true - status: ACTIVE ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: trunk - name: trunk-create-minimal - ref: trunk - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: port - name: trunk-create-minimal - ref: port -assertAll: - - celExpr: "trunk.status.id != ''" - - celExpr: "trunk.status.resource.createdAt != ''" - - celExpr: "trunk.status.resource.updatedAt != ''" - - celExpr: "trunk.status.resource.portID == port.status.id" - - celExpr: "trunk.status.resource.name == 'trunk-create-minimal'" - - celExpr: "trunk.status.resource.adminStateUp == true" - +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-minimal +status: + resource: + name: trunk-create-minimal + # TODO(scaffolding): Add all fields the resource supports + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-create-minimal + ref: trunk + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: trunk-create-minimal + ref: port +assertAll: + - celExpr: "trunk.status.id != ''" + - celExpr: "trunk.status.resource.portID == port.status.id" + # TODO(scaffolding): Add more checks diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml index 33ef84722..0f61f5c8e 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml @@ -1,13 +1,28 @@ ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Trunk -metadata: - name: trunk-create-minimal -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - portRef: trunk-create-minimal - +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Only add the mandatory fields. It's possible the resource + # doesn't have mandatory fields, in that case, leave it empty. + resource: + portRef: trunk-create-full diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml index 251abc20d..35eab2add 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/01-assert.yaml @@ -1,12 +1,11 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: v1 - kind: Secret - name: openstack-clouds - ref: secret -assertAll: - - celExpr: "secret.metadata.deletionTimestamp != 0" - - celExpr: "'openstack.k-orc.cloud/trunk' in secret.metadata.finalizers" - +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: v1 + kind: Secret + name: openstack-clouds + ref: secret +assertAll: + - celExpr: "secret.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/trunk' in secret.metadata.finalizers" diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml index a4cb499df..1620791b9 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/01-delete-secret.yaml @@ -1,6 +1,7 @@ -apiVersion: kuttl.dev/v1beta1 -kind: TestStep -commands: - - command: kubectl delete secret openstack-clouds --wait=false - namespaced: true - +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # We expect the deletion to hang due to the finalizer, so use --wait=false + - command: kubectl delete secret openstack-clouds --wait=false + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/README.md b/internal/controllers/trunk/tests/trunk-create-minimal/README.md index f3fa33767..38bc702bb 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/README.md +++ b/internal/controllers/trunk/tests/trunk-create-minimal/README.md @@ -1,16 +1,15 @@ -# Create a trunk with the minimum options - -## Step 00 - -Create a minimal trunk, that sets only the required fields (portRef), and verify that the observed state corresponds to the spec. - -Also validate that the OpenStack resource uses the name of the ORC object when it is not specified. - -## Step 01 - -Try deleting the secret and ensure that it is not deleted thanks to the finalizer. - -## Reference - -https://k-orc.cloud/development/writing-tests/#create-minimal - +# Create a Trunk with the minimum options + +## Step 00 + +Create a minimal Trunk, that sets only the required fields, and verify that the observed state corresponds to the spec. + +Also validate that the OpenStack resource uses the name of the ORC object when it is not specified. + +## Step 01 + +Try deleting the secret and ensure that it is not deleted thanks to the finalizer. + +## Reference + +https://k-orc.cloud/development/writing-tests/#create-minimal diff --git a/internal/controllers/trunk/tests/trunk-dependency/00-assert.yaml b/internal/controllers/trunk/tests/trunk-dependency/00-assert.yaml new file mode 100644 index 000000000..b5b6be241 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/00-assert.yaml @@ -0,0 +1,45 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-secret +status: + conditions: + - type: Available + message: Waiting for Secret/trunk-dependency to be created + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for Secret/trunk-dependency to be created + status: "True" + reason: Progressing +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-port +status: + conditions: + - type: Available + message: Waiting for Port/trunk-dependency-pending to be created + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for Port/trunk-dependency-pending to be created + status: "True" + reason: Progressing +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-project +status: + conditions: + - type: Available + message: Waiting for Project/trunk-dependency to be created + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for Project/trunk-dependency to be created + status: "True" + reason: Progressing diff --git a/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml b/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml new file mode 100644 index 000000000..11a5ba69f --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-dependency +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-port +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + portRef: trunk-dependency-pending + # TODO(scaffolding): Add the necessary fields to create the resource +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-project +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + portRef: trunk-dependency + projectRef: trunk-dependency + # TODO(scaffolding): Add the necessary fields to create the resource +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-secret +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: trunk-dependency + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: + portRef: trunk-dependency diff --git a/internal/controllers/trunk/tests/trunk-dependency/00-secret.yaml b/internal/controllers/trunk/tests/trunk-dependency/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-dependency/01-assert.yaml b/internal/controllers/trunk/tests/trunk-dependency/01-assert.yaml new file mode 100644 index 000000000..c2a36dd02 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/01-assert.yaml @@ -0,0 +1,45 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-secret +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-port +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-dependency-no-project +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success diff --git a/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml b/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml new file mode 100644 index 000000000..c78751b67 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic trunk-dependency --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-dependency-pending +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Project +metadata: + name: trunk-dependency +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} diff --git a/internal/controllers/trunk/tests/trunk-dependency/02-assert.yaml b/internal/controllers/trunk/tests/trunk-dependency/02-assert.yaml new file mode 100644 index 000000000..7a14ca79c --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/02-assert.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: trunk-dependency + ref: port + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: trunk-dependency + ref: project + - apiVersion: v1 + kind: Secret + name: trunk-dependency + ref: secret +assertAll: + - celExpr: "port.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/trunk' in port.metadata.finalizers" + - celExpr: "project.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/trunk' in project.metadata.finalizers" + - celExpr: "secret.metadata.deletionTimestamp != 0" + - celExpr: "'openstack.k-orc.cloud/trunk' in secret.metadata.finalizers" diff --git a/internal/controllers/trunk/tests/trunk-dependency/02-delete-dependencies.yaml b/internal/controllers/trunk/tests/trunk-dependency/02-delete-dependencies.yaml new file mode 100644 index 000000000..caae7003d --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/02-delete-dependencies.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # We expect the deletion to hang due to the finalizer, so use --wait=false + - command: kubectl delete port trunk-dependency --wait=false + namespaced: true + - command: kubectl delete project trunk-dependency --wait=false + namespaced: true + - command: kubectl delete secret trunk-dependency --wait=false + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-dependency/03-assert.yaml b/internal/controllers/trunk/tests/trunk-dependency/03-assert.yaml new file mode 100644 index 000000000..066b783c7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/03-assert.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +# Dependencies that were prevented deletion before should now be gone +- script: "! kubectl get port trunk-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get project trunk-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get secret trunk-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/trunk/tests/trunk-dependency/03-delete-resources.yaml b/internal/controllers/trunk/tests/trunk-dependency/03-delete-resources.yaml new file mode 100644 index 000000000..25f2a80c5 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/03-delete-resources.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: +- apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-dependency-no-secret +- apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-dependency-no-port +- apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-dependency-no-project diff --git a/internal/controllers/trunk/tests/trunk-dependency/README.md b/internal/controllers/trunk/tests/trunk-dependency/README.md new file mode 100644 index 000000000..80a0dc0bf --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-dependency/README.md @@ -0,0 +1,21 @@ +# Creation and deletion dependencies + +## Step 00 + +Create Trunks referencing non-existing resources. Each Trunk is dependent on other non-existing resource. Verify that the Trunks are waiting for the needed resources to be created externally. + +## Step 01 + +Create the missing dependencies and make and verify all the Trunks are available. + +## Step 02 + +Delete all the dependencies and check that ORC prevents deletion since there is still a resource that depends on them. + +## Step 03 + +Delete the Trunks and validate that all resources are gone. + +## Reference + +https://k-orc.cloud/development/writing-tests/#dependency diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/00-assert.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/00-assert.yaml new file mode 100644 index 000000000..f6904cc7b --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/00-assert.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-dependency +status: + conditions: + - type: Available + message: |- + Waiting for Port/trunk-import-dependency to be ready + Waiting for Project/trunk-import-dependency to be ready + status: "False" + reason: Progressing + - type: Progressing + message: |- + Waiting for Port/trunk-import-dependency to be ready + Waiting for Project/trunk-import-dependency to be ready + status: "True" + reason: Progressing diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/00-import-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/00-import-resource.yaml new file mode 100644 index 000000000..2cf348d34 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/00-import-resource.yaml @@ -0,0 +1,40 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-dependency +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: unmanaged + import: + filter: + name: trunk-import-dependency-external +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Project +metadata: + name: trunk-import-dependency +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: unmanaged + import: + filter: + name: trunk-import-dependency-external +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-dependency +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: unmanaged + import: + filter: + portRef: trunk-import-dependency + projectRef: trunk-import-dependency diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/00-secret.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/01-assert.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/01-assert.yaml new file mode 100644 index 000000000..8190ecc75 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/01-assert.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-dependency-not-this-one +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-dependency +status: + conditions: + - type: Available + message: |- + Waiting for Port/trunk-import-dependency to be ready + Waiting for Project/trunk-import-dependency to be ready + status: "False" + reason: Progressing + - type: Progressing + message: |- + Waiting for Port/trunk-import-dependency to be ready + Waiting for Project/trunk-import-dependency to be ready + status: "True" + reason: Progressing diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml new file mode 100644 index 000000000..b5aeb8971 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-dependency-not-this-one +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Project +metadata: + name: trunk-import-dependency-not-this-one +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-dependency-not-this-one +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +# This `trunk-import-dependency-not-this-one` should not be picked by the import filter +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-dependency-not-this-one +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + portRef: trunk-import-dependency-not-this-one + portRef: trunk-import-dependency-not-this-one + projectRef: trunk-import-dependency-not-this-one + # TODO(scaffolding): Add the necessary fields to create the resource diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/02-assert.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/02-assert.yaml new file mode 100644 index 000000000..a57266e42 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/02-assert.yaml @@ -0,0 +1,39 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-import-dependency + ref: trunk1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-import-dependency-not-this-one + ref: trunk2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: trunk-import-dependency + ref: port + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: trunk-import-dependency + ref: project +assertAll: + - celExpr: "trunk1.status.id != trunk2.status.id" + - celExpr: "trunk1.status.resource.portID == port.status.id" + - celExpr: "trunk1.status.resource.projectID == project.status.id" +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-dependency +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml new file mode 100644 index 000000000..ca57bcc93 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml @@ -0,0 +1,55 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-dependency-external +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-dependency-external +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Project +metadata: + name: trunk-import-dependency-external +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-dependency-external +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + portRef: trunk-import-dependency-external + portRef: trunk-import-dependency-external + projectRef: trunk-import-dependency-external + # TODO(scaffolding): Add the necessary fields to create the resource diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/03-assert.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/03-assert.yaml new file mode 100644 index 000000000..88388e78f --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/03-assert.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get port trunk-import-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get project trunk-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/03-delete-import-dependencies.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/03-delete-import-dependencies.yaml new file mode 100644 index 000000000..c17489455 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/03-delete-import-dependencies.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # We should be able to delete the import dependencies + - command: kubectl delete port trunk-import-dependency + namespaced: true + - command: kubectl delete project trunk-import-dependency + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/04-assert.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/04-assert.yaml new file mode 100644 index 000000000..cf9454e52 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/04-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get trunk trunk-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/04-delete-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/04-delete-resource.yaml new file mode 100644 index 000000000..10b0d3c75 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/04-delete-resource.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-import-dependency diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/README.md b/internal/controllers/trunk/tests/trunk-import-dependency/README.md new file mode 100644 index 000000000..386f4830a --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-dependency/README.md @@ -0,0 +1,29 @@ +# Check dependency handling for imported Trunk + +## Step 00 + +Import a Trunk that references other imported resources. The referenced imported resources have no matching resources yet. +Verify the Trunk is waiting for the dependency to be ready. + +## Step 01 + +Create a Trunk matching the import filter, except for referenced resources, and verify that it's not being imported. + +## Step 02 + +Create the referenced resources and a Trunk matching the import filters. + +Verify that the observed status on the imported Trunk corresponds to the spec of the created Trunk. + +## Step 03 + +Delete the referenced resources and check that ORC does not prevent deletion. The OpenStack resources still exist because they +were imported resources and we only deleted the ORC representation of it. + +## Step 04 + +Delete the Trunk and validate that all resources are gone. + +## Reference + +https://k-orc.cloud/development/writing-tests/#import-dependency diff --git a/internal/controllers/trunk/tests/trunk-import-error/00-assert.yaml b/internal/controllers/trunk/tests/trunk-import-error/00-assert.yaml new file mode 100644 index 000000000..6e538ab91 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-error/00-assert.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-error-external-1 +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-error-external-2 +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success diff --git a/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml b/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml new file mode 100644 index 000000000..fa08cde79 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-error +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-error-external-1 +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + description: Trunk from "import error" test + portRef: trunk-import-error + # TODO(scaffolding): add any required field +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-error-external-2 +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + description: Trunk from "import error" test + portRef: trunk-import-error + # TODO(scaffolding): add any required field diff --git a/internal/controllers/trunk/tests/trunk-import-error/00-secret.yaml b/internal/controllers/trunk/tests/trunk-import-error/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-error/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-import-error/01-assert.yaml b/internal/controllers/trunk/tests/trunk-import-error/01-assert.yaml new file mode 100644 index 000000000..1f48b6bb9 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-error/01-assert.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-error +status: + conditions: + - type: Available + message: found more than one matching OpenStack resource during import + status: "False" + reason: InvalidConfiguration + - type: Progressing + message: found more than one matching OpenStack resource during import + status: "False" + reason: InvalidConfiguration diff --git a/internal/controllers/trunk/tests/trunk-import-error/01-import-resource.yaml b/internal/controllers/trunk/tests/trunk-import-error/01-import-resource.yaml new file mode 100644 index 000000000..d3d922853 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-error/01-import-resource.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-error +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: unmanaged + import: + filter: + description: Trunk from "import error" test diff --git a/internal/controllers/trunk/tests/trunk-import-error/README.md b/internal/controllers/trunk/tests/trunk-import-error/README.md new file mode 100644 index 000000000..ce3ec498f --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import-error/README.md @@ -0,0 +1,13 @@ +# Import Trunk with more than one matching resources + +## Step 00 + +Create two Trunks with identical specs. + +## Step 01 + +Ensure that an imported Trunk with a filter matching the resources returns an error. + +## Reference + +https://k-orc.cloud/development/writing-tests/#import-error diff --git a/internal/controllers/trunk/tests/trunk-import/00-assert.yaml b/internal/controllers/trunk/tests/trunk-import/00-assert.yaml new file mode 100644 index 000000000..4ee876cb5 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/00-assert.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import +status: + conditions: + - type: Available + message: Waiting for OpenStack resource to be created externally + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for OpenStack resource to be created externally + status: "True" + reason: Progressing diff --git a/internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml b/internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml new file mode 100644 index 000000000..787ad1312 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: unmanaged + import: + filter: + name: trunk-import-external + description: Trunk trunk-import-external from "trunk-import" test + # TODO(scaffolding): Add all fields supported by the filter diff --git a/internal/controllers/trunk/tests/trunk-import/00-secret.yaml b/internal/controllers/trunk/tests/trunk-import/00-secret.yaml new file mode 100644 index 000000000..045711ee7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/00-secret.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-import/01-assert.yaml b/internal/controllers/trunk/tests/trunk-import/01-assert.yaml new file mode 100644 index 000000000..ed077e4ba --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/01-assert.yaml @@ -0,0 +1,34 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-external-not-this-one +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success + resource: + name: trunk-import-external-not-this-one + description: Trunk trunk-import-external from "trunk-import" test + # TODO(scaffolding): Add fields necessary to match filter +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import +status: + conditions: + - type: Available + message: Waiting for OpenStack resource to be created externally + status: "False" + reason: Progressing + - type: Progressing + message: Waiting for OpenStack resource to be created externally + status: "True" + reason: Progressing diff --git a/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml b/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml new file mode 100644 index 000000000..05ea99726 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-external-not-this-one +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +# This `trunk-import-external-not-this-one` resource serves two purposes: +# - ensure that we can successfully create another resource which name is a substring of it (i.e. it's not being adopted) +# - ensure that importing a resource which name is a substring of it will not pick this one. +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-external-not-this-one +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + description: Trunk trunk-import-external from "trunk-import" test + portRef: trunk-import-external-not-this-one + # TODO(scaffolding): Add fields necessary to match filter diff --git a/internal/controllers/trunk/tests/trunk-import/02-assert.yaml b/internal/controllers/trunk/tests/trunk-import/02-assert.yaml new file mode 100644 index 000000000..607841b87 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/02-assert.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-import-external + ref: trunk1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-import-external-not-this-one + ref: trunk2 +assertAll: + - celExpr: "trunk1.status.id != trunk2.status.id" +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import +status: + conditions: + - type: Available + message: OpenStack resource is available + status: "True" + reason: Success + - type: Progressing + message: OpenStack resource is up to date + status: "False" + reason: Success + resource: + name: trunk-import-external + description: Trunk trunk-import-external from "trunk-import" test + # TODO(scaffolding): Add all fields the resource supports diff --git a/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml b/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml new file mode 100644 index 000000000..2596a4bf5 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-import-external +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + description: Trunk trunk-import-external from "trunk-import" test + portRef: trunk-import + # TODO(scaffolding): Add fields necessary to match filter diff --git a/internal/controllers/trunk/tests/trunk-import/README.md b/internal/controllers/trunk/tests/trunk-import/README.md new file mode 100644 index 000000000..e8a586cc7 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-import/README.md @@ -0,0 +1,18 @@ +# Import Trunk + +## Step 00 + +Import a trunk, matching all of the available filter's fields, and verify it is waiting for the external resource to be created. + +## Step 01 + +Create a trunk which name is a superstring of the one specified in the import filter, and otherwise matching the filter, and verify that it's not being imported. + +## Step 02 + +Create a trunk matching the filter and verify that the observed status on the imported trunk corresponds to the spec of the created trunk. +Also verify that the created trunk didn't adopt the one which name is a superstring of it. + +## Reference + +https://k-orc.cloud/development/writing-tests/#import diff --git a/internal/controllers/trunk/tests/trunk-update/00-assert.yaml b/internal/controllers/trunk/tests/trunk-update/00-assert.yaml index c3b738632..f241702a3 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-assert.yaml @@ -1,13 +1,26 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: trunk - name: trunk-update - ref: trunk -assertAll: - - celExpr: "trunk.status.id != ''" - - celExpr: "trunk.status.resource.name == 'trunk-update'" - - celExpr: "trunk.status.resource.adminStateUp == true" - +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-update + ref: trunk +assertAll: + - celExpr: "!has(trunk.status.resource.description)" +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +status: + resource: + name: trunk-update + # TODO(scaffolding): Add matches for more fields + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success diff --git a/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml index 990eab8f3..1226197a8 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml @@ -1,14 +1,28 @@ ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Trunk -metadata: - name: trunk-update -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - portRef: trunk-update - adminStateUp: true - +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: {} +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created or updated + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Only add the mandatory fields. It's possible the resource + # doesn't have mandatory fields, in that case, leave it empty. + resource: + portRef: trunk-update diff --git a/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml index de6f33262..045711ee7 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml @@ -1,44 +1,6 @@ -apiVersion: kuttl.dev/v1beta1 -kind: TestStep -commands: - - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} - namespaced: true ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Network -metadata: - name: trunk-update -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - name: trunk-update ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Subnet -metadata: - name: trunk-update -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - networkRef: trunk-update - ipVersion: 4 - cidr: 192.168.158.0/24 ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port -metadata: - name: trunk-update -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - networkRef: trunk-update - +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-update/01-assert.yaml b/internal/controllers/trunk/tests/trunk-update/01-assert.yaml index b0c7694a9..0750bc9ce 100644 --- a/internal/controllers/trunk/tests/trunk-update/01-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-update/01-assert.yaml @@ -1,15 +1,17 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: trunk - name: trunk-update - ref: trunk -assertAll: - - celExpr: "trunk.status.resource.name == 'trunk-update-updated'" - - celExpr: "trunk.status.resource.description == 'trunk-update-updated'" - - celExpr: "trunk.status.resource.adminStateUp == false" - - celExpr: "'tag1' in trunk.status.resource.tags" - - celExpr: "'tag2' in trunk.status.resource.tags" - +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +status: + resource: + name: trunk-update-updated + description: trunk-update-updated + # TODO(scaffolding): match all fields that were modified + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success diff --git a/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml b/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml index 0dbc6956a..4d9937eb0 100644 --- a/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml @@ -1,19 +1,10 @@ ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Trunk -metadata: - name: trunk-update -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - name: trunk-update-updated - description: trunk-update-updated - portRef: trunk-update - adminStateUp: false - tags: - - tag1 - - tag2 - +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +spec: + resource: + name: trunk-update-updated + description: trunk-update-updated + # TODO(scaffolding): update all mutable fields diff --git a/internal/controllers/trunk/tests/trunk-update/02-assert.yaml b/internal/controllers/trunk/tests/trunk-update/02-assert.yaml index bd433e88a..33096164f 100644 --- a/internal/controllers/trunk/tests/trunk-update/02-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-update/02-assert.yaml @@ -1,14 +1,26 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: trunk - name: trunk-update - ref: trunk -assertAll: - - celExpr: "trunk.status.resource.name == 'trunk-update'" - - celExpr: "trunk.status.resource.description == ''" - - celExpr: "trunk.status.resource.adminStateUp == true" - - celExpr: "size(trunk.status.resource.tags) == 0" - +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-update + ref: trunk +assertAll: + - celExpr: "!has(trunk.status.resource.description)" +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Trunk +metadata: + name: trunk-update +status: + resource: + name: trunk-update + # TODO(scaffolding): validate that updated fields were all reverted to their original value + conditions: + - type: Available + status: "True" + reason: Success + - type: Progressing + status: "False" + reason: Success diff --git a/internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml b/internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml index 990eab8f3..2c6c253ff 100644 --- a/internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-update/02-reverted-resource.yaml @@ -1,14 +1,7 @@ ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Trunk -metadata: - name: trunk-update -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - portRef: trunk-update - adminStateUp: true - +# NOTE: kuttl only does patch updates, which means we can't delete a field. +# We have to use a kubectl apply command instead. +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl replace -f 00-minimal-resource.yaml + namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-update/README.md b/internal/controllers/trunk/tests/trunk-update/README.md index f8056b138..90337704d 100644 --- a/internal/controllers/trunk/tests/trunk-update/README.md +++ b/internal/controllers/trunk/tests/trunk-update/README.md @@ -1,26 +1,17 @@ -# Update a trunk - -This test verifies that mutable fields of a trunk can be updated. - -## Step 00 - -Create a minimal trunk and verify its initial state. - -## Step 01 - -Update the trunk with: -- New name -- Description -- Admin state (false) -- Tags - -Verify that the changes are reflected in the observed status. - -## Step 02 - -Revert the changes back to the minimal configuration and verify that the resource status is similar to the one we had in step 00. - -## Reference - -https://k-orc.cloud/development/writing-tests/#update - +# Update Trunk + +## Step 00 + +Create a Trunk using only mandatory fields. + +## Step 01 + +Update all mutable fields. + +## Step 02 + +Revert the resource to its original value and verify the resulting object is similar to when if was first created. + +## Reference + +https://k-orc.cloud/development/writing-tests/#update diff --git a/internal/controllers/volume/tests/volume-create-full/00-assert.yaml b/internal/controllers/volume/tests/volume-create-full/00-assert.yaml index d45fe3795..e456f3d91 100644 --- a/internal/controllers/volume/tests/volume-create-full/00-assert.yaml +++ b/internal/controllers/volume/tests/volume-create-full/00-assert.yaml @@ -24,22 +24,3 @@ status: - type: Progressing status: "False" reason: Success ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Volume - name: volume-create-full - ref: volume - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: VolumeType - name: volume-create-full - ref: volumetype -assertAll: - - celExpr: "volume.status.id != ''" - - celExpr: "volume.status.resource.volumeType == volumetype.status.resource.name" - - celExpr: "volume.status.resource.tenantID != ''" - - celExpr: "volume.status.resource.userID != ''" - - celExpr: "volume.status.resource.createdAt != ''" - - celExpr: "volume.status.resource.updatedAt != ''" diff --git a/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml b/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml index dde63758c..644eacd37 100644 --- a/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml +++ b/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml @@ -27,11 +27,5 @@ resourceRefs: name: volume-create-minimal ref: volume assertAll: - - celExpr: "volume.status.id != ''" - - celExpr: "volume.status.resource.tenantID != ''" - - celExpr: "volume.status.resource.userID != ''" - - celExpr: "volume.status.resource.volumeType != ''" - - celExpr: "volume.status.resource.createdAt != ''" - - celExpr: "volume.status.resource.updatedAt != ''" - celExpr: "!has(volume.status.resource.description)" - celExpr: "!has(volume.status.resource.metadata)" diff --git a/internal/controllers/volume/tests/volume-dependency/03-assert.yaml b/internal/controllers/volume/tests/volume-dependency/03-assert.yaml index 05806c37a..e69de29bb 100644 --- a/internal/controllers/volume/tests/volume-dependency/03-assert.yaml +++ b/internal/controllers/volume/tests/volume-dependency/03-assert.yaml @@ -1,9 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: -# Dependencies that were prevented deletion before should now be gone -- script: "! kubectl get volumetype volume-dependency --namespace $NAMESPACE" - skipLogOutput: true -- script: "! kubectl get secret volume-dependency --namespace $NAMESPACE" - skipLogOutput: true diff --git a/internal/controllers/volume/tests/volume-import/02-assert.yaml b/internal/controllers/volume/tests/volume-import/02-assert.yaml index 428600cb2..dcd93813f 100644 --- a/internal/controllers/volume/tests/volume-import/02-assert.yaml +++ b/internal/controllers/volume/tests/volume-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Volume - name: volume-import-external - ref: volume1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Volume - name: volume-import-external-not-this-one - ref: volume2 -assertAll: - - celExpr: "volume1.status.id != volume2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Volume metadata: diff --git a/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml b/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml index 03e8d8761..d15e7270e 100644 --- a/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml +++ b/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml @@ -18,13 +18,3 @@ status: - type: Progressing status: "False" reason: Success ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: VolumeType - name: volumetype-create-full - ref: volumetype -assertAll: - - celExpr: "volumetype.status.id != ''" diff --git a/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml b/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml index 803045489..4ccf48b54 100644 --- a/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml +++ b/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml @@ -14,13 +14,3 @@ status: - type: Progressing status: "False" reason: Success ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: VolumeType - name: volumetype-create-minimal - ref: volumetype -assertAll: - - celExpr: "volumetype.status.id != ''" diff --git a/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml b/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml index dd3b98c46..3dbcc38b2 100644 --- a/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml +++ b/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml @@ -1,18 +1,4 @@ --- -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -resourceRefs: - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: VolumeType - name: volumetype-import-external - ref: volumetype1 - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: VolumeType - name: volumetype-import-external-not-this-one - ref: volumetype2 -assertAll: - - celExpr: "volumetype1.status.id != volumetype2.status.id" ---- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: VolumeType metadata: diff --git a/internal/osclients/trunk.go b/internal/osclients/trunk.go new file mode 100644 index 000000000..2dbed4edf --- /dev/null +++ b/internal/osclients/trunk.go @@ -0,0 +1,104 @@ +/* +Copyright 2025 The ORC Authors. + +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 osclients + +import ( + "context" + "fmt" + "iter" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/utils/v2/openstack/clientconfig" +) + +type TrunkClient interface { + ListTrunks(ctx context.Context, listOpts trunks.ListOptsBuilder) iter.Seq2[*trunks.Trunk, error] + CreateTrunk(ctx context.Context, opts trunks.CreateOptsBuilder) (*trunks.Trunk, error) + DeleteTrunk(ctx context.Context, resourceID string) error + GetTrunk(ctx context.Context, resourceID string) (*trunks.Trunk, error) + UpdateTrunk(ctx context.Context, id string, opts trunks.UpdateOptsBuilder) (*trunks.Trunk, error) +} + +type trunkClient struct{ client *gophercloud.ServiceClient } + +// NewTrunkClient returns a new OpenStack client. +func NewTrunkClient(providerClient *gophercloud.ProviderClient, providerClientOpts *clientconfig.ClientOpts) (TrunkClient, error) { + client, err := openstack.NewNetworkV2(providerClient, gophercloud.EndpointOpts{ + Region: providerClientOpts.RegionName, + Availability: clientconfig.GetEndpointType(providerClientOpts.EndpointType), + }) + + if err != nil { + return nil, fmt.Errorf("failed to create trunk service client: %v", err) + } + + return &trunkClient{client}, nil +} + +func (c trunkClient) ListTrunks(ctx context.Context, listOpts trunks.ListOptsBuilder) iter.Seq2[*trunks.Trunk, error] { + pager := trunks.List(c.client, listOpts) + return func(yield func(*trunks.Trunk, error) bool) { + _ = pager.EachPage(ctx, yieldPage(trunks.ExtractTrunks, yield)) + } +} + +func (c trunkClient) CreateTrunk(ctx context.Context, opts trunks.CreateOptsBuilder) (*trunks.Trunk, error) { + return trunks.Create(ctx, c.client, opts).Extract() +} + +func (c trunkClient) DeleteTrunk(ctx context.Context, resourceID string) error { + return trunks.Delete(ctx, c.client, resourceID).ExtractErr() +} + +func (c trunkClient) GetTrunk(ctx context.Context, resourceID string) (*trunks.Trunk, error) { + return trunks.Get(ctx, c.client, resourceID).Extract() +} + +func (c trunkClient) UpdateTrunk(ctx context.Context, id string, opts trunks.UpdateOptsBuilder) (*trunks.Trunk, error) { + return trunks.Update(ctx, c.client, id, opts).Extract() +} + +type trunkErrorClient struct{ error } + +// NewTrunkErrorClient returns a TrunkClient in which every method returns the given error. +func NewTrunkErrorClient(e error) TrunkClient { + return trunkErrorClient{e} +} + +func (e trunkErrorClient) ListTrunks(_ context.Context, _ trunks.ListOptsBuilder) iter.Seq2[*trunks.Trunk, error] { + return func(yield func(*trunks.Trunk, error) bool) { + yield(nil, e.error) + } +} + +func (e trunkErrorClient) CreateTrunk(_ context.Context, _ trunks.CreateOptsBuilder) (*trunks.Trunk, error) { + return nil, e.error +} + +func (e trunkErrorClient) DeleteTrunk(_ context.Context, _ string) error { + return e.error +} + +func (e trunkErrorClient) GetTrunk(_ context.Context, _ string) (*trunks.Trunk, error) { + return nil, e.error +} + +func (e trunkErrorClient) UpdateTrunk(_ context.Context, _ string, _ trunks.UpdateOptsBuilder) (*trunks.Trunk, error) { + return nil, e.error +} diff --git a/kuttl-test.yaml b/kuttl-test.yaml index 67828d420..d499782e6 100644 --- a/kuttl-test.yaml +++ b/kuttl-test.yaml @@ -19,7 +19,6 @@ testDirs: - ./internal/controllers/servergroup/tests/ - ./internal/controllers/service/tests/ - ./internal/controllers/subnet/tests/ -- ./internal/controllers/trunk/tests/ - ./internal/controllers/volume/tests/ - ./internal/controllers/volumetype/tests/ timeout: 240 diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/subport.go b/pkg/clients/applyconfiguration/api/v1alpha1/subport.go deleted file mode 100644 index 41d035e28..000000000 --- a/pkg/clients/applyconfiguration/api/v1alpha1/subport.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2025 The ORC Authors. - -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. -*/ - -// Code generated by applyconfiguration-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" -) - -// SubportApplyConfiguration represents a declarative configuration of the Subport type for use -// with apply. -type SubportApplyConfiguration struct { - PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` - SegmentationType *string `json:"segmentationType,omitempty"` - SegmentationID *int32 `json:"segmentationID,omitempty"` -} - -// SubportApplyConfiguration constructs a declarative configuration of the Subport type for use with -// apply. -func Subport() *SubportApplyConfiguration { - return &SubportApplyConfiguration{} -} - -// WithPortRef sets the PortRef field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the PortRef field is set to the value of the last call. -func (b *SubportApplyConfiguration) WithPortRef(value apiv1alpha1.KubernetesNameRef) *SubportApplyConfiguration { - b.PortRef = &value - return b -} - -// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the SegmentationType field is set to the value of the last call. -func (b *SubportApplyConfiguration) WithSegmentationType(value string) *SubportApplyConfiguration { - b.SegmentationType = &value - return b -} - -// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the SegmentationID field is set to the value of the last call. -func (b *SubportApplyConfiguration) WithSegmentationID(value int32) *SubportApplyConfiguration { - b.SegmentationID = &value - return b -} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go deleted file mode 100644 index 7f5982148..000000000 --- a/pkg/clients/applyconfiguration/api/v1alpha1/subportstatus.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2025 The ORC Authors. - -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. -*/ - -// Code generated by applyconfiguration-gen. DO NOT EDIT. - -package v1alpha1 - -// SubportStatusApplyConfiguration represents a declarative configuration of the SubportStatus type for use -// with apply. -type SubportStatusApplyConfiguration struct { - PortID *string `json:"portID,omitempty"` - SegmentationType *string `json:"segmentationType,omitempty"` - SegmentationID *int32 `json:"segmentationID,omitempty"` -} - -// SubportStatusApplyConfiguration constructs a declarative configuration of the SubportStatus type for use with -// apply. -func SubportStatus() *SubportStatusApplyConfiguration { - return &SubportStatusApplyConfiguration{} -} - -// WithPortID sets the PortID field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the PortID field is set to the value of the last call. -func (b *SubportStatusApplyConfiguration) WithPortID(value string) *SubportStatusApplyConfiguration { - b.PortID = &value - return b -} - -// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the SegmentationType field is set to the value of the last call. -func (b *SubportStatusApplyConfiguration) WithSegmentationType(value string) *SubportStatusApplyConfiguration { - b.SegmentationType = &value - return b -} - -// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the SegmentationID field is set to the value of the last call. -func (b *SubportStatusApplyConfiguration) WithSegmentationID(value int32) *SubportStatusApplyConfiguration { - b.SegmentationID = &value - return b -} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go index 3ab2b232a..c722a5734 100644 --- a/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go @@ -25,11 +25,10 @@ import ( // TrunkFilterApplyConfiguration represents a declarative configuration of the TrunkFilter type for use // with apply. type TrunkFilterApplyConfiguration struct { - Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` - Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` - PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` - ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` - FilterByNeutronTagsApplyConfiguration `json:",inline"` + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` } // TrunkFilterApplyConfiguration constructs a declarative configuration of the TrunkFilter type for use with @@ -49,7 +48,7 @@ func (b *TrunkFilterApplyConfiguration) WithName(value apiv1alpha1.OpenStackName // WithDescription sets the Description field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Description field is set to the value of the last call. -func (b *TrunkFilterApplyConfiguration) WithDescription(value apiv1alpha1.NeutronDescription) *TrunkFilterApplyConfiguration { +func (b *TrunkFilterApplyConfiguration) WithDescription(value string) *TrunkFilterApplyConfiguration { b.Description = &value return b } @@ -69,43 +68,3 @@ func (b *TrunkFilterApplyConfiguration) WithProjectRef(value apiv1alpha1.Kuberne b.ProjectRef = &value return b } - -// WithTags adds the given value to the Tags field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the Tags field. -func (b *TrunkFilterApplyConfiguration) WithTags(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { - for i := range values { - b.FilterByNeutronTagsApplyConfiguration.Tags = append(b.FilterByNeutronTagsApplyConfiguration.Tags, values[i]) - } - return b -} - -// WithTagsAny adds the given value to the TagsAny field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the TagsAny field. -func (b *TrunkFilterApplyConfiguration) WithTagsAny(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { - for i := range values { - b.FilterByNeutronTagsApplyConfiguration.TagsAny = append(b.FilterByNeutronTagsApplyConfiguration.TagsAny, values[i]) - } - return b -} - -// WithNotTags adds the given value to the NotTags field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the NotTags field. -func (b *TrunkFilterApplyConfiguration) WithNotTags(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { - for i := range values { - b.FilterByNeutronTagsApplyConfiguration.NotTags = append(b.FilterByNeutronTagsApplyConfiguration.NotTags, values[i]) - } - return b -} - -// WithNotTagsAny adds the given value to the NotTagsAny field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the NotTagsAny field. -func (b *TrunkFilterApplyConfiguration) WithNotTagsAny(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { - for i := range values { - b.FilterByNeutronTagsApplyConfiguration.NotTagsAny = append(b.FilterByNeutronTagsApplyConfiguration.NotTagsAny, values[i]) - } - return b -} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go index 1e0fc7711..5a67e41b8 100644 --- a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go @@ -25,13 +25,10 @@ import ( // TrunkResourceSpecApplyConfiguration represents a declarative configuration of the TrunkResourceSpec type for use // with apply. type TrunkResourceSpecApplyConfiguration struct { - Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` - Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` - PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` - Tags []apiv1alpha1.NeutronTag `json:"tags,omitempty"` - AdminStateUp *bool `json:"adminStateUp,omitempty"` - Subports []SubportApplyConfiguration `json:"subports,omitempty"` - ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` } // TrunkResourceSpecApplyConfiguration constructs a declarative configuration of the TrunkResourceSpec type for use with @@ -51,7 +48,7 @@ func (b *TrunkResourceSpecApplyConfiguration) WithName(value apiv1alpha1.OpenSta // WithDescription sets the Description field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Description field is set to the value of the last call. -func (b *TrunkResourceSpecApplyConfiguration) WithDescription(value apiv1alpha1.NeutronDescription) *TrunkResourceSpecApplyConfiguration { +func (b *TrunkResourceSpecApplyConfiguration) WithDescription(value string) *TrunkResourceSpecApplyConfiguration { b.Description = &value return b } @@ -64,37 +61,6 @@ func (b *TrunkResourceSpecApplyConfiguration) WithPortRef(value apiv1alpha1.Kube return b } -// WithTags adds the given value to the Tags field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the Tags field. -func (b *TrunkResourceSpecApplyConfiguration) WithTags(values ...apiv1alpha1.NeutronTag) *TrunkResourceSpecApplyConfiguration { - for i := range values { - b.Tags = append(b.Tags, values[i]) - } - return b -} - -// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the AdminStateUp field is set to the value of the last call. -func (b *TrunkResourceSpecApplyConfiguration) WithAdminStateUp(value bool) *TrunkResourceSpecApplyConfiguration { - b.AdminStateUp = &value - return b -} - -// WithSubports adds the given value to the Subports field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the Subports field. -func (b *TrunkResourceSpecApplyConfiguration) WithSubports(values ...*SubportApplyConfiguration) *TrunkResourceSpecApplyConfiguration { - for i := range values { - if values[i] == nil { - panic("nil value passed to WithSubports") - } - b.Subports = append(b.Subports, *values[i]) - } - return b -} - // WithProjectRef sets the ProjectRef field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the ProjectRef field is set to the value of the last call. diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go index 9f997f571..88a13c05c 100644 --- a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go @@ -18,22 +18,13 @@ limitations under the License. package v1alpha1 -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - // TrunkResourceStatusApplyConfiguration represents a declarative configuration of the TrunkResourceStatus type for use // with apply. type TrunkResourceStatusApplyConfiguration struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - PortID *string `json:"portID,omitempty"` - ProjectID *string `json:"projectID,omitempty"` - Status *string `json:"status,omitempty"` - Tags []string `json:"tags,omitempty"` - AdminStateUp *bool `json:"adminStateUp,omitempty"` - Subports []SubportStatusApplyConfiguration `json:"subports,omitempty"` - NeutronStatusMetadataApplyConfiguration `json:",inline"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + PortID *string `json:"portID,omitempty"` + ProjectID *string `json:"projectID,omitempty"` } // TrunkResourceStatusApplyConfiguration constructs a declarative configuration of the TrunkResourceStatus type for use with @@ -73,66 +64,3 @@ func (b *TrunkResourceStatusApplyConfiguration) WithProjectID(value string) *Tru b.ProjectID = &value return b } - -// WithStatus sets the Status field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the Status field is set to the value of the last call. -func (b *TrunkResourceStatusApplyConfiguration) WithStatus(value string) *TrunkResourceStatusApplyConfiguration { - b.Status = &value - return b -} - -// WithTags adds the given value to the Tags field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the Tags field. -func (b *TrunkResourceStatusApplyConfiguration) WithTags(values ...string) *TrunkResourceStatusApplyConfiguration { - for i := range values { - b.Tags = append(b.Tags, values[i]) - } - return b -} - -// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the AdminStateUp field is set to the value of the last call. -func (b *TrunkResourceStatusApplyConfiguration) WithAdminStateUp(value bool) *TrunkResourceStatusApplyConfiguration { - b.AdminStateUp = &value - return b -} - -// WithSubports adds the given value to the Subports field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the Subports field. -func (b *TrunkResourceStatusApplyConfiguration) WithSubports(values ...*SubportStatusApplyConfiguration) *TrunkResourceStatusApplyConfiguration { - for i := range values { - if values[i] == nil { - panic("nil value passed to WithSubports") - } - b.Subports = append(b.Subports, *values[i]) - } - return b -} - -// WithCreatedAt sets the CreatedAt field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the CreatedAt field is set to the value of the last call. -func (b *TrunkResourceStatusApplyConfiguration) WithCreatedAt(value v1.Time) *TrunkResourceStatusApplyConfiguration { - b.NeutronStatusMetadataApplyConfiguration.CreatedAt = &value - return b -} - -// WithUpdatedAt sets the UpdatedAt field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the UpdatedAt field is set to the value of the last call. -func (b *TrunkResourceStatusApplyConfiguration) WithUpdatedAt(value v1.Time) *TrunkResourceStatusApplyConfiguration { - b.NeutronStatusMetadataApplyConfiguration.UpdatedAt = &value - return b -} - -// WithRevisionNumber sets the RevisionNumber field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the RevisionNumber field is set to the value of the last call. -func (b *TrunkResourceStatusApplyConfiguration) WithRevisionNumber(value int64) *TrunkResourceStatusApplyConfiguration { - b.NeutronStatusMetadataApplyConfiguration.RevisionNumber = &value - return b -} diff --git a/pkg/clients/applyconfiguration/internal/internal.go b/pkg/clients/applyconfiguration/internal/internal.go index a06668cdc..385381882 100644 --- a/pkg/clients/applyconfiguration/internal/internal.go +++ b/pkg/clients/applyconfiguration/internal/internal.go @@ -2849,33 +2849,6 @@ var schemaYAML = typed.YAMLObject(`types: - name: resource type: namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.SubnetResourceStatus -- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Subport - map: - fields: - - name: portRef - type: - scalar: string - default: "" - - name: segmentationID - type: - scalar: numeric - default: 0 - - name: segmentationType - type: - scalar: string - default: "" -- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.SubportStatus - map: - fields: - - name: portID - type: - scalar: string - - name: segmentationID - type: - scalar: numeric - - name: segmentationType - type: - scalar: string - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Trunk map: fields: @@ -2906,36 +2879,12 @@ var schemaYAML = typed.YAMLObject(`types: - name: name type: scalar: string - - name: notTags - type: - list: - elementType: - scalar: string - elementRelationship: associative - - name: notTagsAny - type: - list: - elementType: - scalar: string - elementRelationship: associative - name: portRef type: scalar: string - name: projectRef type: scalar: string - - name: tags - type: - list: - elementType: - scalar: string - elementRelationship: associative - - name: tagsAny - type: - list: - elementType: - scalar: string - elementRelationship: associative - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkImport map: fields: @@ -2948,9 +2897,6 @@ var schemaYAML = typed.YAMLObject(`types: - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceSpec map: fields: - - name: adminStateUp - type: - scalar: boolean - name: description type: scalar: string @@ -2960,31 +2906,12 @@ var schemaYAML = typed.YAMLObject(`types: - name: portRef type: scalar: string - default: "" - name: projectRef type: scalar: string - - name: subports - type: - list: - elementType: - namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Subport - elementRelationship: atomic - - name: tags - type: - list: - elementType: - scalar: string - elementRelationship: associative - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceStatus map: fields: - - name: adminStateUp - type: - scalar: boolean - - name: createdAt - type: - namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time - name: description type: scalar: string @@ -2997,27 +2924,6 @@ var schemaYAML = typed.YAMLObject(`types: - name: projectID type: scalar: string - - name: revisionNumber - type: - scalar: numeric - - name: status - type: - scalar: string - - name: subports - type: - list: - elementType: - namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.SubportStatus - elementRelationship: atomic - - name: tags - type: - list: - elementType: - scalar: string - elementRelationship: atomic - - name: updatedAt - type: - namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSpec map: fields: diff --git a/pkg/clients/applyconfiguration/utils.go b/pkg/clients/applyconfiguration/utils.go index bbe791f6c..78a0e4142 100644 --- a/pkg/clients/applyconfiguration/utils.go +++ b/pkg/clients/applyconfiguration/utils.go @@ -336,10 +336,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.SubnetSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SubnetStatus"): return &apiv1alpha1.SubnetStatusApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("Subport"): - return &apiv1alpha1.SubportApplyConfiguration{} - case v1alpha1.SchemeGroupVersion.WithKind("SubportStatus"): - return &apiv1alpha1.SubportStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Trunk"): return &apiv1alpha1.TrunkApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("TrunkFilter"): diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md index 176622d41..bf77bbe5f 100644 --- a/website/docs/crd-reference.md +++ b/website/docs/crd-reference.md @@ -421,7 +421,6 @@ _Appears in:_ - [RouterFilter](#routerfilter) - [SecurityGroupFilter](#securitygroupfilter) - [SubnetFilter](#subnetfilter) -- [TrunkFilter](#trunkfilter) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -1635,7 +1634,6 @@ _Appears in:_ - [ServerVolumeSpec](#servervolumespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) -- [Subport](#subport) - [TrunkFilter](#trunkfilter) - [TrunkResourceSpec](#trunkresourcespec) - [UserDataSpec](#userdataspec) @@ -1926,8 +1924,6 @@ _Appears in:_ - [SecurityGroupRule](#securitygrouprule) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) -- [TrunkFilter](#trunkfilter) -- [TrunkResourceSpec](#trunkresourcespec) @@ -1945,7 +1941,6 @@ _Appears in:_ - [PortResourceStatus](#portresourcestatus) - [SecurityGroupResourceStatus](#securitygroupresourcestatus) - [SubnetResourceStatus](#subnetresourcestatus) -- [TrunkResourceStatus](#trunkresourcestatus) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -1979,8 +1974,6 @@ _Appears in:_ - [SecurityGroupResourceSpec](#securitygroupresourcespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) -- [TrunkFilter](#trunkfilter) -- [TrunkResourceSpec](#trunkresourcespec) @@ -3792,42 +3785,6 @@ _Appears in:_ | `resource` _[SubnetResourceStatus](#subnetresourcestatus)_ | resource contains the observed state of the OpenStack resource. | | | -#### Subport - - - -Subport represents a subport that will be attached to a trunk. - - - -_Appears in:_ -- [TrunkResourceSpec](#trunkresourcespec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which will be used as a subport. | | MaxLength: 253
MinLength: 1
| -| `segmentationType` _string_ | segmentationType is the type of segmentation to use (e.g., "vlan"). | | MaxLength: 64
| -| `segmentationID` _integer_ | segmentationID is the segmentation identifier (e.g., VLAN ID). | | | - - -#### SubportStatus - - - -SubportStatus represents the observed state of a subport. - - - -_Appears in:_ -- [TrunkResourceStatus](#trunkresourcestatus) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `portID` _string_ | portID is the ID of the port used as a subport. | | MaxLength: 1024
| -| `segmentationType` _string_ | segmentationType is the type of segmentation used. | | MaxLength: 1024
| -| `segmentationID` _integer_ | segmentationID is the segmentation identifier. | | | - - #### Trunk @@ -3851,7 +3808,7 @@ Trunk is the Schema for an ORC resource. -TrunkFilter specifies a filter to select a trunk. At least one parameter must be specified. +TrunkFilter defines an existing resource by its properties _Validation:_ - MinProperties: 1 @@ -3862,13 +3819,9 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `name` _[OpenStackName](#openstackname)_ | name of the existing resource | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
| -| `description` _[NeutronDescription](#neutrondescription)_ | description of the existing resource | | MaxLength: 255
MinLength: 1
| -| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which this trunk is associated with. | | MaxLength: 253
MinLength: 1
| -| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project this resource is associated with.
Typically, only used by admin. | | MaxLength: 253
MinLength: 1
| -| `tags` _[NeutronTag](#neutrontag) array_ | tags is a list of tags to filter by. If specified, the resource must
have all of the tags specified to be included in the result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| -| `tagsAny` _[NeutronTag](#neutrontag) array_ | tagsAny is a list of tags to filter by. If specified, the resource
must have at least one of the tags specified to be included in the
result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| -| `notTags` _[NeutronTag](#neutrontag) array_ | notTags is a list of tags to filter by. If specified, resources which
contain all of the given tags will be excluded from the result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| -| `notTagsAny` _[NeutronTag](#neutrontag) array_ | notTagsAny is a list of tags to filter by. If specified, resources
which contain any of the given tags will be excluded from the result. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| +| `description` _string_ | description of the existing resource | | MaxLength: 255
MinLength: 1
| +| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which this resource is associated with. | | MaxLength: 253
MinLength: 1
| +| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project which this resource is associated with. | | MaxLength: 253
MinLength: 1
| #### TrunkImport @@ -3895,7 +3848,7 @@ _Appears in:_ - +TrunkResourceSpec contains the desired state of the resource. @@ -3904,20 +3857,17 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `name` _[OpenStackName](#openstackname)_ | name is a human-readable name of the trunk. If not set, the object's name will be used. | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
| -| `description` _[NeutronDescription](#neutrondescription)_ | description is a human-readable description for the resource. | | MaxLength: 255
MinLength: 1
| -| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which will be used as the parent port for this trunk. | | MaxLength: 253
MinLength: 1
| -| `tags` _[NeutronTag](#neutrontag) array_ | tags is a list of tags which will be applied to the trunk. | | MaxItems: 64
MaxLength: 255
MinLength: 1
| -| `adminStateUp` _boolean_ | adminStateUp is the administrative state of the trunk, which is up (true) or down (false). | | | -| `subports` _[Subport](#subport) array_ | subports are the subports that will be attached to this trunk. | | MaxItems: 128
| -| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project this resource is associated with.
Typically, only used by admin. | | MaxLength: 253
MinLength: 1
| +| `name` _[OpenStackName](#openstackname)_ | name will be the name of the created resource. If not specified, the
name of the ORC object will be used. | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
| +| `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 255
MinLength: 1
| +| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which this resource is associated with. | | MaxLength: 253
MinLength: 1
| +| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project which this resource is associated with. | | MaxLength: 253
MinLength: 1
| #### TrunkResourceStatus - +TrunkResourceStatus represents the observed state of the resource. @@ -3926,17 +3876,10 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `name` _string_ | name is the human-readable name of the resource. Might not be unique. | | MaxLength: 1024
| +| `name` _string_ | name is a Human-readable name for the resource. Might not be unique. | | MaxLength: 1024
| | `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 1024
| -| `portID` _string_ | portID is the ID of the parent port. | | MaxLength: 1024
| -| `projectID` _string_ | projectID is the project owner of the resource. | | MaxLength: 1024
| -| `status` _string_ | status indicates the current status of the resource. | | MaxLength: 1024
| -| `tags` _string array_ | tags is the list of tags on the resource. | | MaxItems: 64
items:MaxLength: 1024
| -| `adminStateUp` _boolean_ | adminStateUp is the administrative state of the trunk,
which is up (true) or down (false). | | | -| `subports` _[SubportStatus](#subportstatus) array_ | subports is a list of subports attached to this trunk. | | MaxItems: 128
| -| `createdAt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#time-v1-meta)_ | createdAt shows the date and time when the resource was created. The date and time stamp format is ISO 8601 | | | -| `updatedAt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#time-v1-meta)_ | updatedAt shows the date and time when the resource was updated. The date and time stamp format is ISO 8601 | | | -| `revisionNumber` _integer_ | revisionNumber optionally set via extensions/standard-attr-revisions | | | +| `portID` _string_ | portID is the ID of the Port to which the resource is associated. | | MaxLength: 1024
| +| `projectID` _string_ | projectID is the ID of the Project to which the resource is associated. | | MaxLength: 1024
| #### TrunkSpec @@ -3952,8 +3895,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `import` _[TrunkImport]provider.go -(#trunkimport)_ | import refers to an existing OpenStack resource which will be imported instead of
creating a new one. | | MaxProperties: 1
MinProperties: 1
| +| `import` _[TrunkImport](#trunkimport)_ | import refers to an existing OpenStack resource which will be imported instead of
creating a new one. | | MaxProperties: 1
MinProperties: 1
| | `resource` _[TrunkResourceSpec](#trunkresourcespec)_ | resource specifies the desired state of the resource.
resource may not be specified if the management policy is `unmanaged`.
resource must be specified if the management policy is `managed`. | | | | `managementPolicy` _[ManagementPolicy](#managementpolicy)_ | managementPolicy defines how ORC will treat the object. Valid values are
`managed`: ORC will create, update, and delete the resource; `unmanaged`:
ORC will import an existing resource, and will not apply updates to it or
delete it. | managed | Enum: [managed unmanaged]
| | `managedOptions` _[ManagedOptions](#managedoptions)_ | managedOptions specifies options which may be applied to managed objects. | | | From 960bd5296d486a1260d4bc27a0076c7d3d35062f Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Thu, 18 Dec 2025 23:43:58 +0000 Subject: [PATCH 5/6] Trunk resource: fixed testing --- PROJECT | 8 ++ cmd/resource-generator/main.go | 3 + config/crd/kustomization.yaml | 1 + config/samples/kustomization.yaml | 1 + internal/controllers/trunk/actuator.go | 5 +- .../trunk-create-full/00-create-resource.yaml | 37 ++++- .../00-create-resource.yaml | 15 +- .../00-prerequisites.yaml | 88 ++++++------ .../tests/trunk-create-minimal/00-secret.yaml | 6 - .../00-create-resources-missing-deps.yaml | 45 +++++- .../01-create-dependencies.yaml | 3 +- .../01-create-trap-resource.yaml | 39 +++-- .../02-create-resource.yaml | 33 +++-- .../00-create-resources.yaml | 49 ++++++- .../trunk-import/01-create-trap-resource.yaml | 29 +++- .../trunk-import/02-create-resource.yaml | 3 +- .../trunk-update/00-minimal-resource.yaml | 13 -- .../tests/trunk-update/00-prerequisites.yaml | 39 +++++ internal/osclients/mock/doc.go | 3 + internal/osclients/mock/trunk.go | 133 ++++++++++++++++++ internal/scope/mock.go | 7 + internal/scope/provider.go | 4 + internal/scope/scope.go | 1 + kuttl-test.yaml | 1 + 24 files changed, 453 insertions(+), 113 deletions(-) delete mode 100644 internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml create mode 100644 internal/osclients/mock/trunk.go diff --git a/PROJECT b/PROJECT index 8d6e2c12d..3d09e3c30 100644 --- a/PROJECT +++ b/PROJECT @@ -144,6 +144,14 @@ resources: kind: Subnet path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: k-orc.cloud + group: openstack + kind: Trunk + path: github.com/k-orc/openstack-resource-controller/api/v1alpha1 + version: v1alpha1 - api: crdVersion: v1 namespaced: true diff --git a/cmd/resource-generator/main.go b/cmd/resource-generator/main.go index 6848b155f..6bbfa2cff 100644 --- a/cmd/resource-generator/main.go +++ b/cmd/resource-generator/main.go @@ -146,6 +146,9 @@ var resources []templateFields = []templateFields{ Name: "Subnet", ExistingOSClient: true, }, + { + Name: "Trunk", + }, { Name: "Volume", }, diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 33b8c85e2..b73dcac05 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -20,6 +20,7 @@ resources: - bases/openstack.k-orc.cloud_servergroups.yaml - bases/openstack.k-orc.cloud_services.yaml - bases/openstack.k-orc.cloud_subnets.yaml +- bases/openstack.k-orc.cloud_trunks.yaml - bases/openstack.k-orc.cloud_volumes.yaml - bases/openstack.k-orc.cloud_volumetypes.yaml # +kubebuilder:scaffold:crdkustomizeresource diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index dac467c69..aa2a75e43 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -18,6 +18,7 @@ resources: - openstack_v1alpha1_servergroup.yaml - openstack_v1alpha1_service.yaml - openstack_v1alpha1_subnet.yaml +- openstack_v1alpha1_trunk.yaml - openstack_v1alpha1_volume.yaml - openstack_v1alpha1_volumetype.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/internal/controllers/trunk/actuator.go b/internal/controllers/trunk/actuator.go index d8f1a3eca..fa504afd9 100644 --- a/internal/controllers/trunk/actuator.go +++ b/internal/controllers/trunk/actuator.go @@ -19,6 +19,7 @@ package trunk import ( "context" "iter" + "fmt" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" corev1 "k8s.io/api/core/v1" @@ -134,8 +135,8 @@ func (actuator trunkActuator) ListOSResourcesForImport(ctx context.Context, obj listOpts := trunks.ListOpts{ Name: string(ptr.Deref(filter.Name, "")), Description: string(ptr.Deref(filter.Description, "")), - Port: ptr.Deref(port.Status.ID, ""), - Project: ptr.Deref(project.Status.ID, ""), + PortID: ptr.Deref(port.Status.ID, ""), + ProjectID: ptr.Deref(project.Status.ID, ""), // TODO(scaffolding): Add more import filters } diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml index bf1cf03da..56a343f64 100644 --- a/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml @@ -1,16 +1,45 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + projectRef: trunk-create-full +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-create-full +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + cidr: 192.168.157.0/24 + ipVersion: 4 + networkRef: trunk-create-full + projectRef: trunk-create-full +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: name: trunk-create-full spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-create-full + projectRef: trunk-create-full --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project @@ -19,7 +48,7 @@ metadata: spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource @@ -32,7 +61,7 @@ metadata: spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed resource: diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml index 0f61f5c8e..bb2d39169 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml @@ -1,18 +1,5 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port -metadata: - name: trunk-create-minimal -spec: - cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk metadata: name: trunk-create-minimal @@ -25,4 +12,4 @@ spec: # TODO(scaffolding): Only add the mandatory fields. It's possible the resource # doesn't have mandatory fields, in that case, leave it empty. resource: - portRef: trunk-create-full + portRef: trunk-create-minimal diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml index 4b9042767..a7f8f0ac6 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml @@ -1,44 +1,44 @@ -apiVersion: kuttl.dev/v1beta1 -kind: TestStep -commands: - - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} - namespaced: true ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Network -metadata: - name: trunk-create-minimal -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - name: trunk-create-minimal ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Subnet -metadata: - name: trunk-create-minimal -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - networkRef: trunk-create-minimal - ipVersion: 4 - cidr: 192.168.156.0/24 ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port -metadata: - name: trunk-create-minimal -spec: - cloudCredentialsRef: - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - resource: - networkRef: trunk-create-minimal - +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} + namespaced: true +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-create-minimal +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-create-minimal + ipVersion: 4 + cidr: 192.168.156.0/24 +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-create-minimal +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-create-minimal + diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml deleted file mode 100644 index 045711ee7..000000000 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-secret.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -apiVersion: kuttl.dev/v1beta1 -kind: TestStep -commands: - - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} - namespaced: true diff --git a/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml b/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml index 11a5ba69f..b433114bc 100644 --- a/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml +++ b/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml @@ -1,5 +1,31 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-dependency +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-dependency +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-dependency +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + cidr: 192.168.160.0/24 + ipVersion: 4 + networkRef: trunk-dependency +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: name: trunk-dependency @@ -10,7 +36,22 @@ spec: secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-dependency +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-dependency-no-secret +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: + networkRef: trunk-dependency --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -53,4 +94,4 @@ spec: managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource resource: - portRef: trunk-dependency + portRef: trunk-dependency-no-secret diff --git a/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml b/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml index c78751b67..774d7765d 100644 --- a/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml +++ b/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml @@ -16,7 +16,8 @@ spec: secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-dependency --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml index b5aeb8971..66f67807a 100644 --- a/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml @@ -1,38 +1,54 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port +kind: Network metadata: name: trunk-import-dependency-not-this-one spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + projectRef: trunk-import-dependency-not-this-one --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Project +kind: Subnet +metadata: + name: trunk-import-dependency-not-this-one +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + cidr: 192.168.163.0/24 + ipVersion: 4 + networkRef: trunk-import-dependency-not-this-one + projectRef: trunk-import-dependency-not-this-one +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port metadata: name: trunk-import-dependency-not-this-one spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-import-dependency-not-this-one + projectRef: trunk-import-dependency-not-this-one --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port +kind: Project metadata: name: trunk-import-dependency-not-this-one spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource @@ -46,11 +62,10 @@ metadata: spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed resource: - portRef: trunk-import-dependency-not-this-one portRef: trunk-import-dependency-not-this-one projectRef: trunk-import-dependency-not-this-one # TODO(scaffolding): Add the necessary fields to create the resource diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml index ca57bcc93..85f3c7a54 100644 --- a/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml @@ -1,16 +1,30 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port +kind: Network metadata: name: trunk-import-dependency-external spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + projectRef: trunk-import-dependency-external +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-import-dependency-external +spec: + cloudCredentialsRef: + cloudName: openstack-admin + secretName: openstack-clouds + managementPolicy: managed + resource: + cidr: 192.168.164.0/24 + ipVersion: 4 + networkRef: trunk-import-dependency-external + projectRef: trunk-import-dependency-external --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port @@ -19,11 +33,13 @@ metadata: spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-import-dependency-external + projectRef: trunk-import-dependency-external --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project @@ -32,7 +48,7 @@ metadata: spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack + cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource @@ -49,7 +65,6 @@ spec: secretName: openstack-clouds managementPolicy: managed resource: - portRef: trunk-import-dependency-external portRef: trunk-import-dependency-external projectRef: trunk-import-dependency-external # TODO(scaffolding): Add the necessary fields to create the resource diff --git a/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml b/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml index fa08cde79..942809617 100644 --- a/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml +++ b/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml @@ -1,8 +1,34 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port +kind: Network metadata: name: trunk-import-error +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-import-error +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-import-error +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + cidr: 192.168.161.0/24 + ipVersion: 4 + networkRef: trunk-import-error +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-error-1 spec: cloudCredentialsRef: # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created @@ -10,7 +36,22 @@ spec: secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-import-error +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-import-error-2 +spec: + cloudCredentialsRef: + # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + # TODO(scaffolding): Add the necessary fields to create the resource + resource: + networkRef: trunk-import-error --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -24,7 +65,7 @@ spec: managementPolicy: managed resource: description: Trunk from "import error" test - portRef: trunk-import-error + portRef: trunk-import-error-1 # TODO(scaffolding): add any required field --- apiVersion: openstack.k-orc.cloud/v1alpha1 @@ -39,5 +80,5 @@ spec: managementPolicy: managed resource: description: Trunk from "import error" test - portRef: trunk-import-error + portRef: trunk-import-error-2 # TODO(scaffolding): add any required field diff --git a/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml b/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml index 05ea99726..f71879621 100644 --- a/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml @@ -1,5 +1,31 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-import +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-import +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-import +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + cidr: 192.168.162.0/24 + ipVersion: 4 + networkRef: trunk-import +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: name: trunk-import-external-not-this-one @@ -10,7 +36,8 @@ spec: secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-import --- # This `trunk-import-external-not-this-one` resource serves two purposes: # - ensure that we can successfully create another resource which name is a substring of it (i.e. it's not being adopted) diff --git a/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml b/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml index 2596a4bf5..f9404bac2 100644 --- a/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml @@ -10,7 +10,8 @@ spec: secretName: openstack-clouds managementPolicy: managed # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + networkRef: trunk-import --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk diff --git a/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml index 1226197a8..34494cd1e 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml @@ -1,18 +1,5 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Port -metadata: - name: trunk-update -spec: - cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack - secretName: openstack-clouds - managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk metadata: name: trunk-update diff --git a/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml index 045711ee7..868916bb3 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml @@ -4,3 +4,42 @@ kind: TestStep commands: - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT} namespaced: true +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Network +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + name: trunk-update +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Subnet +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-update + cidr: 192.168.158.0/24 + ipVersion: 4 +--- +apiVersion: openstack.k-orc.cloud/v1alpha1 +kind: Port +metadata: + name: trunk-update +spec: + cloudCredentialsRef: + cloudName: openstack + secretName: openstack-clouds + managementPolicy: managed + resource: + networkRef: trunk-update + \ No newline at end of file diff --git a/internal/osclients/mock/doc.go b/internal/osclients/mock/doc.go index 47292b65f..03bff6695 100644 --- a/internal/osclients/mock/doc.go +++ b/internal/osclients/mock/doc.go @@ -50,6 +50,9 @@ import ( //go:generate mockgen -package mock -destination=service.go -source=../service.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock ServiceClient //go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate.go.txt service.go > _service.go && mv _service.go service.go" +//go:generate mockgen -package mock -destination=trunk.go -source=../trunk.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock TrunkClient +//go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate.go.txt trunk.go > _trunk.go && mv _trunk.go trunk.go" + //go:generate mockgen -package mock -destination=volume.go -source=../volume.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock VolumeClient //go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate.go.txt volume.go > _volume.go && mv _volume.go volume.go" diff --git a/internal/osclients/mock/trunk.go b/internal/osclients/mock/trunk.go new file mode 100644 index 000000000..c70659adb --- /dev/null +++ b/internal/osclients/mock/trunk.go @@ -0,0 +1,133 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../trunk.go +// +// Generated by this command: +// +// mockgen -package mock -destination=trunk.go -source=../trunk.go github.com/k-orc/openstack-resource-controller/internal/osclients/mock TrunkClient +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + iter "iter" + reflect "reflect" + + trunks "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + gomock "go.uber.org/mock/gomock" +) + +// MockTrunkClient is a mock of TrunkClient interface. +type MockTrunkClient struct { + ctrl *gomock.Controller + recorder *MockTrunkClientMockRecorder + isgomock struct{} +} + +// MockTrunkClientMockRecorder is the mock recorder for MockTrunkClient. +type MockTrunkClientMockRecorder struct { + mock *MockTrunkClient +} + +// NewMockTrunkClient creates a new mock instance. +func NewMockTrunkClient(ctrl *gomock.Controller) *MockTrunkClient { + mock := &MockTrunkClient{ctrl: ctrl} + mock.recorder = &MockTrunkClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTrunkClient) EXPECT() *MockTrunkClientMockRecorder { + return m.recorder +} + +// CreateTrunk mocks base method. +func (m *MockTrunkClient) CreateTrunk(ctx context.Context, opts trunks.CreateOptsBuilder) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateTrunk", ctx, opts) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateTrunk indicates an expected call of CreateTrunk. +func (mr *MockTrunkClientMockRecorder) CreateTrunk(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTrunk", reflect.TypeOf((*MockTrunkClient)(nil).CreateTrunk), ctx, opts) +} + +// DeleteTrunk mocks base method. +func (m *MockTrunkClient) DeleteTrunk(ctx context.Context, resourceID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteTrunk", ctx, resourceID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteTrunk indicates an expected call of DeleteTrunk. +func (mr *MockTrunkClientMockRecorder) DeleteTrunk(ctx, resourceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTrunk", reflect.TypeOf((*MockTrunkClient)(nil).DeleteTrunk), ctx, resourceID) +} + +// GetTrunk mocks base method. +func (m *MockTrunkClient) GetTrunk(ctx context.Context, resourceID string) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTrunk", ctx, resourceID) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTrunk indicates an expected call of GetTrunk. +func (mr *MockTrunkClientMockRecorder) GetTrunk(ctx, resourceID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTrunk", reflect.TypeOf((*MockTrunkClient)(nil).GetTrunk), ctx, resourceID) +} + +// ListTrunks mocks base method. +func (m *MockTrunkClient) ListTrunks(ctx context.Context, listOpts trunks.ListOptsBuilder) iter.Seq2[*trunks.Trunk, error] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListTrunks", ctx, listOpts) + ret0, _ := ret[0].(iter.Seq2[*trunks.Trunk, error]) + return ret0 +} + +// ListTrunks indicates an expected call of ListTrunks. +func (mr *MockTrunkClientMockRecorder) ListTrunks(ctx, listOpts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTrunks", reflect.TypeOf((*MockTrunkClient)(nil).ListTrunks), ctx, listOpts) +} + +// UpdateTrunk mocks base method. +func (m *MockTrunkClient) UpdateTrunk(ctx context.Context, id string, opts trunks.UpdateOptsBuilder) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTrunk", ctx, id, opts) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateTrunk indicates an expected call of UpdateTrunk. +func (mr *MockTrunkClientMockRecorder) UpdateTrunk(ctx, id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTrunk", reflect.TypeOf((*MockTrunkClient)(nil).UpdateTrunk), ctx, id, opts) +} + + diff --git a/internal/scope/mock.go b/internal/scope/mock.go index ef959fae5..430baea35 100644 --- a/internal/scope/mock.go +++ b/internal/scope/mock.go @@ -43,6 +43,7 @@ type MockScopeFactory struct { NetworkClient *mock.MockNetworkClient RoleClient *mock.MockRoleClient ServiceClient *mock.MockServiceClient + TrunkClient *mock.MockTrunkClient VolumeClient *mock.MockVolumeClient VolumeTypeClient *mock.MockVolumeTypeClient @@ -59,6 +60,7 @@ func NewMockScopeFactory(mockCtrl *gomock.Controller) *MockScopeFactory { networkClient := mock.NewMockNetworkClient(mockCtrl) roleClient := mock.NewMockRoleClient(mockCtrl) serviceClient := mock.NewMockServiceClient(mockCtrl) + trunkClient := mock.NewMockTrunkClient(mockCtrl) volumeClient := mock.NewMockVolumeClient(mockCtrl) volumetypeClient := mock.NewMockVolumeTypeClient(mockCtrl) @@ -72,6 +74,7 @@ func NewMockScopeFactory(mockCtrl *gomock.Controller) *MockScopeFactory { NetworkClient: networkClient, RoleClient: roleClient, ServiceClient: serviceClient, + TrunkClient: trunkClient, VolumeClient: volumeClient, VolumeTypeClient: volumetypeClient, } @@ -132,6 +135,10 @@ func (f *MockScopeFactory) NewRoleClient() (osclients.RoleClient, error) { return f.RoleClient, nil } +func (f *MockScopeFactory) NewTrunkClient() (osclients.TrunkClient, error) { + return f.TrunkClient, nil +} + func (f *MockScopeFactory) ExtractToken() (*tokens.Token, error) { return &tokens.Token{ExpiresAt: time.Now().Add(24 * time.Hour)}, nil } diff --git a/internal/scope/provider.go b/internal/scope/provider.go index 65670ba60..797109df5 100644 --- a/internal/scope/provider.go +++ b/internal/scope/provider.go @@ -181,6 +181,10 @@ func (s *providerScope) NewRoleClient() (clients.RoleClient, error) { return clients.NewRoleClient(s.providerClient, s.providerClientOpts) } +func (s *providerScope) NewTrunkClient() (clients.TrunkClient, error) { + return clients.NewTrunkClient(s.providerClient, s.providerClientOpts) +} + func (s *providerScope) ExtractToken() (*tokens.Token, error) { client, err := openstack.NewIdentityV3(s.providerClient, gophercloud.EndpointOpts{}) if err != nil { diff --git a/internal/scope/scope.go b/internal/scope/scope.go index 7da50dc8f..8ce07a018 100644 --- a/internal/scope/scope.go +++ b/internal/scope/scope.go @@ -57,6 +57,7 @@ type Scope interface { NewNetworkClient() (osclients.NetworkClient, error) NewRoleClient() (osclients.RoleClient, error) NewServiceClient() (osclients.ServiceClient, error) + NewTrunkClient() (osclients.TrunkClient, error) NewVolumeClient() (osclients.VolumeClient, error) NewVolumeTypeClient() (osclients.VolumeTypeClient, error) ExtractToken() (*tokens.Token, error) diff --git a/kuttl-test.yaml b/kuttl-test.yaml index d499782e6..67828d420 100644 --- a/kuttl-test.yaml +++ b/kuttl-test.yaml @@ -19,6 +19,7 @@ testDirs: - ./internal/controllers/servergroup/tests/ - ./internal/controllers/service/tests/ - ./internal/controllers/subnet/tests/ +- ./internal/controllers/trunk/tests/ - ./internal/controllers/volume/tests/ - ./internal/controllers/volumetype/tests/ timeout: 240 From 545a5faabafeba5a4e0d4f57890e4f9f9e368d3d Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Sun, 28 Dec 2025 03:03:27 +0000 Subject: [PATCH 6/6] Trunk type: fix scaffolding issues + minor bugs --- api/v1alpha1/trunk_types.go | 118 +++++-- api/v1alpha1/zz_generated.deepcopy.go | 78 ++++- cmd/models-schema/zz_generated.openapi.go | 297 ++++++++++++++++++ .../bases/openstack.k-orc.cloud_trunks.yaml | 191 +++++++++++ config/samples/openstack_v1alpha1_trunk.yaml | 14 +- .../tests/domain-create-full/00-assert.yaml | 10 + .../domain-create-minimal/00-assert.yaml | 1 + .../domain/tests/domain-import/02-assert.yaml | 14 + .../domain/tests/domain-update/01-assert.yaml | 8 + .../domain/tests/domain-update/02-assert.yaml | 1 + .../flavor/tests/flavor-import/02-assert.yaml | 14 + .../floatingip-create-full/00-assert.yaml | 29 ++ .../floatingip-create-minimal/00-assert.yaml | 15 + .../00-assert.yaml | 14 + .../02-assert.yaml | 29 ++ .../03-assert.yaml | 10 + .../tests/floatingip-import/02-assert.yaml | 19 ++ .../tests/floatingip-update/00-assert.yaml | 2 + .../tests/floatingip-update/01-assert.yaml | 15 + .../tests/floatingip-update/02-assert.yaml | 2 + .../image/tests/image-import/02-assert.yaml | 14 + .../tests/network-create-full/00-assert.yaml | 4 + .../network-import-dependency/02-assert.yaml | 19 ++ .../network-import-dependency/03-assert.yaml | 6 + .../tests/network-import/02-assert.yaml | 14 + .../tests/network-update/01-assert.yaml | 8 + .../tests/port-create-full/00-assert.yaml | 7 + .../tests/port-create-minimal/00-assert.yaml | 4 + .../tests/port-create-sriov/00-assert.yaml | 5 + .../port-import-dependency/02-assert.yaml | 24 ++ .../port-import-dependency/03-assert.yaml | 8 + .../port/tests/port-import/02-assert.yaml | 14 + .../port/tests/port-update/00-assert.yaml | 4 + .../port/tests/port-update/01-assert.yaml | 25 ++ .../port/tests/port-update/02-assert.yaml | 4 + .../tests/project-import/02-assert.yaml | 14 + .../tests/project-update/01-assert.yaml | 8 + .../tests/router-create-full/00-assert.yaml | 23 ++ .../router-import-dependency/02-assert.yaml | 19 ++ .../router-import-dependency/03-assert.yaml | 6 + .../router/tests/router-import/02-assert.yaml | 14 + .../00-assert.yaml | 10 + .../securitygroup-create-full/00-assert.yaml | 18 ++ .../02-assert.yaml | 19 ++ .../03-assert.yaml | 6 + .../tests/securitygroup-import/02-assert.yaml | 14 + .../tests/securitygroup-update/01-assert.yaml | 8 + .../tests/server-create-full/00-assert.yaml | 15 + .../server-create-minimal/00-assert.yaml | 10 + .../server/tests/server-import/02-assert.yaml | 14 + .../server/tests/server-update/00-assert.yaml | 4 + .../server/tests/server-update/01-assert.yaml | 12 + .../server/tests/server-update/02-assert.yaml | 5 + .../tests/servergroup-import/02-assert.yaml | 14 + .../subnet-create-full-v4/00-assert.yaml | 3 + .../subnet-create-full-v6/00-assert.yaml | 23 ++ .../subnet-create-minimal-v4/00-assert.yaml | 1 + .../subnet-create-minimal-v6/00-assert.yaml | 1 + .../subnet-import-dependency/02-assert.yaml | 24 ++ .../subnet-import-dependency/03-assert.yaml | 8 + .../subnet/tests/subnet-import/02-assert.yaml | 19 ++ .../subnet/tests/subnet-update/00-assert.yaml | 1 + .../subnet/tests/subnet-update/01-assert.yaml | 14 + .../subnet/tests/subnet-update/02-assert.yaml | 1 + internal/controllers/trunk/status.go | 9 +- .../tests/trunk-create-full/00-assert.yaml | 7 - .../trunk-create-full/00-create-resource.yaml | 36 +-- .../tests/trunk-create-minimal/00-assert.yaml | 2 - .../00-create-resource.yaml | 3 - .../00-prerequisites.yaml | 2 + .../tests/trunk-create-minimal/README.md | 2 +- .../00-create-resources-missing-deps.yaml | 14 +- .../01-create-dependencies.yaml | 9 +- .../trunk/tests/trunk-dependency/README.md | 2 +- .../01-create-trap-resource.yaml | 43 +-- .../02-create-resource.yaml | 44 +-- .../00-create-resources.yaml | 16 +- .../trunk/tests/trunk-import/00-assert.yaml | 4 +- .../trunk-import/00-import-resource.yaml | 2 +- .../trunk/tests/trunk-import/01-assert.yaml | 7 +- .../trunk-import/01-create-trap-resource.yaml | 8 +- .../trunk/tests/trunk-import/02-assert.yaml | 1 - .../trunk-import/02-create-resource.yaml | 6 +- .../trunk/tests/trunk-import/README.md | 6 +- .../trunk/tests/trunk-update/00-assert.yaml | 1 - .../trunk-update/00-minimal-resource.yaml | 3 - .../tests/trunk-update/00-prerequisites.yaml | 4 +- .../trunk/tests/trunk-update/01-assert.yaml | 1 - .../trunk-update/01-updated-resource.yaml | 1 - .../trunk/tests/trunk-update/02-assert.yaml | 1 - .../trunk-update/03-delete-resources.yaml | 15 + .../trunk/tests/trunk-update/README.md | 2 +- .../tests/volume-create-full/00-assert.yaml | 19 ++ .../volume-create-minimal/00-assert.yaml | 6 + .../tests/volume-dependency/03-assert.yaml | 9 + .../volume/tests/volume-import/02-assert.yaml | 14 + .../volumetype-create-full/00-assert.yaml | 10 + .../volumetype-create-minimal/00-assert.yaml | 10 + .../tests/volumetype-import/02-assert.yaml | 14 + internal/osclients/mock/trunk.go | 2 - .../api/v1alpha1/trunkfilter.go | 69 +++- .../api/v1alpha1/trunkresourcespec.go | 44 ++- .../api/v1alpha1/trunkresourcestatus.go | 89 +++++- .../api/v1alpha1/trunksubportspec.go | 61 ++++ .../api/v1alpha1/trunksubportstatus.go | 57 ++++ .../applyconfiguration/internal/internal.go | 102 ++++++ pkg/clients/applyconfiguration/utils.go | 4 + website/docs/crd-reference.md | 74 ++++- website/docs/development/writing-tests.md | 2 +- 109 files changed, 1996 insertions(+), 210 deletions(-) create mode 100644 internal/controllers/trunk/tests/trunk-update/03-delete-resources.yaml create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunksubportspec.go create mode 100644 pkg/clients/applyconfiguration/api/v1alpha1/trunksubportstatus.go diff --git a/api/v1alpha1/trunk_types.go b/api/v1alpha1/trunk_types.go index 400373ce4..07c5f45c9 100644 --- a/api/v1alpha1/trunk_types.go +++ b/api/v1alpha1/trunk_types.go @@ -15,6 +15,43 @@ limitations under the License. */ package v1alpha1 +// TrunkSubportSpec represents a subport to attach to a trunk. +// It maps to gophercloud's trunks.Subport. +type TrunkSubportSpec struct { + // portRef is a reference to the ORC Port that will be attached as a subport. + // +required + PortRef KubernetesNameRef `json:"portRef"` + + // segmentationID is the segmentation ID for the subport (e.g. VLAN ID). + // +required + // +kubebuilder:validation:Minimum:=1 + // +kubebuilder:validation:Maximum:=4094 + SegmentationID int32 `json:"segmentationID"` + + // segmentationType is the segmentation type for the subport (e.g. vlan). + // +required + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=255 + SegmentationType string `json:"segmentationType"` +} + +// TrunkSubportStatus represents an attached subport on a trunk. +// It maps to gophercloud's trunks.Subport. +type TrunkSubportStatus struct { + // portID is the OpenStack ID of the Port attached as a subport. + // +kubebuilder:validation:MaxLength=1024 + // +optional + PortID string `json:"portID,omitempty"` + + // segmentationID is the segmentation ID for the subport (e.g. VLAN ID). + // +optional + SegmentationID int32 `json:"segmentationID,omitempty"` + + // segmentationType is the segmentation type for the subport (e.g. vlan). + // +kubebuilder:validation:MaxLength=1024 + // +optional + SegmentationType string `json:"segmentationType,omitempty"` +} // TrunkResourceSpec contains the desired state of the resource. type TrunkResourceSpec struct { @@ -24,10 +61,8 @@ type TrunkResourceSpec struct { Name *OpenStackName `json:"name,omitempty"` // description is a human-readable description for the resource. - // +kubebuilder:validation:MinLength:=1 - // +kubebuilder:validation:MaxLength:=255 // +optional - Description *string `json:"description,omitempty"` + Description *NeutronDescription `json:"description,omitempty"` // portRef is a reference to the ORC Port which this resource is associated with. // +required @@ -39,13 +74,30 @@ type TrunkResourceSpec struct { // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="projectRef is immutable" ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` - // TODO(scaffolding): Add more types. - // To see what is supported, you can take inspiration from the CreateOpts structure from - // github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks + // adminStateUp is the administrative state of the trunk. If false (down), + // the trunk does not forward packets. + // +optional + AdminStateUp *bool `json:"adminStateUp,omitempty"` + + // subports is the list of ports to attach to the trunk. // - // Until you have implemented mutability for the field, you must add a CEL validation - // preventing the field being modified: - // `// +kubebuilder:validation:XValidation:rule="self == oldSelf",message=" is immutable"` + // NOTE: ORC currently does not implement reconcile logic for subport updates + // (Neutron uses dedicated add/remove subport APIs). This field is immutable + // until that behavior is implemented in the controller. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="subports is immutable" + // +kubebuilder:validation:MaxItems:=1024 + // +listType=atomic + Subports []TrunkSubportSpec `json:"subports,omitempty"` + + // tags is a list of Neutron tags to apply to the trunk. + // + // NOTE: ORC does not currently reconcile tag updates for Trunk. + // +kubebuilder:validation:MaxItems:=64 + // +listType=set + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="tags is immutable" + Tags []NeutronTag `json:"tags,omitempty"` } // TrunkFilter defines an existing resource by its properties @@ -56,10 +108,8 @@ type TrunkFilter struct { Name *OpenStackName `json:"name,omitempty"` // description of the existing resource - // +kubebuilder:validation:MinLength:=1 - // +kubebuilder:validation:MaxLength:=255 // +optional - Description *string `json:"description,omitempty"` + Description *NeutronDescription `json:"description,omitempty"` // portRef is a reference to the ORC Port which this resource is associated with. // +optional @@ -69,9 +119,16 @@ type TrunkFilter struct { // +optional ProjectRef *KubernetesNameRef `json:"projectRef,omitempty"` - // TODO(scaffolding): Add more types. - // To see what is supported, you can take inspiration from the ListOpts structure from - // github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks + // status indicates whether the trunk is currently operational. Possible values include + // `ACTIVE', `DOWN', `BUILD', `DEGRADED' or `ERROR'. Plug-ins might define additional values. + // +optional + Status *string `json:"status,omitempty"` + + // adminStateUp is the administrative state of the trunk. + // +optional + AdminStateUp *bool `json:"adminStateUp,omitempty"` + + FilterByNeutronTags `json:",inline"` } // TrunkResourceStatus represents the observed state of the resource. @@ -96,7 +153,32 @@ type TrunkResourceStatus struct { // +optional ProjectID string `json:"projectID,omitempty"` - // TODO(scaffolding): Add more types. - // To see what is supported, you can take inspiration from the Trunk structure from - // github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks + // tenantID is the project owner of the trunk (alias of projectID in some deployments). + // +kubebuilder:validation:MaxLength=1024 + // +optional + TenantID string `json:"tenantID,omitempty"` + + // status indicates whether the trunk is currently operational. + // +kubebuilder:validation:MaxLength=1024 + // +optional + Status string `json:"status,omitempty"` + + // tags is the list of tags on the resource. + // +kubebuilder:validation:MaxItems=64 + // +kubebuilder:validation:items:MaxLength=1024 + // +listType=atomic + // +optional + Tags []string `json:"tags,omitempty"` + + NeutronStatusMetadata `json:",inline"` + + // adminStateUp is the administrative state of the trunk. + // +optional + AdminStateUp *bool `json:"adminStateUp,omitempty"` + + // subports is a list of ports associated with the trunk. + // +kubebuilder:validation:MaxItems=1024 + // +listType=atomic + // +optional + Subports []TrunkSubportStatus `json:"subports,omitempty"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 19f25803d..dac611de0 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -4996,7 +4996,7 @@ func (in *TrunkFilter) DeepCopyInto(out *TrunkFilter) { } if in.Description != nil { in, out := &in.Description, &out.Description - *out = new(string) + *out = new(NeutronDescription) **out = **in } if in.PortRef != nil { @@ -5009,6 +5009,17 @@ func (in *TrunkFilter) DeepCopyInto(out *TrunkFilter) { *out = new(KubernetesNameRef) **out = **in } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(string) + **out = **in + } + if in.AdminStateUp != nil { + in, out := &in.AdminStateUp, &out.AdminStateUp + *out = new(bool) + **out = **in + } + in.FilterByNeutronTags.DeepCopyInto(&out.FilterByNeutronTags) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkFilter. @@ -5088,7 +5099,7 @@ func (in *TrunkResourceSpec) DeepCopyInto(out *TrunkResourceSpec) { } if in.Description != nil { in, out := &in.Description, &out.Description - *out = new(string) + *out = new(NeutronDescription) **out = **in } if in.ProjectRef != nil { @@ -5096,6 +5107,21 @@ func (in *TrunkResourceSpec) DeepCopyInto(out *TrunkResourceSpec) { *out = new(KubernetesNameRef) **out = **in } + if in.AdminStateUp != nil { + in, out := &in.AdminStateUp, &out.AdminStateUp + *out = new(bool) + **out = **in + } + if in.Subports != nil { + in, out := &in.Subports, &out.Subports + *out = make([]TrunkSubportSpec, len(*in)) + copy(*out, *in) + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]NeutronTag, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkResourceSpec. @@ -5111,6 +5137,22 @@ func (in *TrunkResourceSpec) DeepCopy() *TrunkResourceSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrunkResourceStatus) DeepCopyInto(out *TrunkResourceStatus) { *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.NeutronStatusMetadata.DeepCopyInto(&out.NeutronStatusMetadata) + if in.AdminStateUp != nil { + in, out := &in.AdminStateUp, &out.AdminStateUp + *out = new(bool) + **out = **in + } + if in.Subports != nil { + in, out := &in.Subports, &out.Subports + *out = make([]TrunkSubportStatus, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkResourceStatus. @@ -5172,7 +5214,7 @@ func (in *TrunkStatus) DeepCopyInto(out *TrunkStatus) { if in.Resource != nil { in, out := &in.Resource, &out.Resource *out = new(TrunkResourceStatus) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -5186,6 +5228,36 @@ func (in *TrunkStatus) DeepCopy() *TrunkStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkSubportSpec) DeepCopyInto(out *TrunkSubportSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkSubportSpec. +func (in *TrunkSubportSpec) DeepCopy() *TrunkSubportSpec { + if in == nil { + return nil + } + out := new(TrunkSubportSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrunkSubportStatus) DeepCopyInto(out *TrunkSubportStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrunkSubportStatus. +func (in *TrunkSubportStatus) DeepCopy() *TrunkSubportStatus { + if in == nil { + return nil + } + out := new(TrunkSubportStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserDataSpec) DeepCopyInto(out *UserDataSpec) { *out = *in diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go index eb4943f25..e205e17f3 100644 --- a/cmd/models-schema/zz_generated.openapi.go +++ b/cmd/models-schema/zz_generated.openapi.go @@ -207,6 +207,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkResourceStatus": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSpec": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkSpec(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkStatus": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkStatus(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSubportSpec": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkSubportSpec(ref), + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSubportStatus": schema_openstack_resource_controller_v2_api_v1alpha1_TrunkSubportStatus(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.UserDataSpec": schema_openstack_resource_controller_v2_api_v1alpha1_UserDataSpec(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.Volume": schema_openstack_resource_controller_v2_api_v1alpha1_Volume(ref), "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeAttachmentStatus": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeAttachmentStatus(ref), @@ -9683,6 +9685,100 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkFilter(ref common Format: "", }, }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status indicates whether the trunk is currently operational. Possible values include `ACTIVE', `DOWN', `BUILD', `DEGRADED' or `ERROR'. Plug-ins might define additional values.", + Type: []string{"string"}, + Format: "", + }, + }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "adminStateUp is the administrative state of the trunk.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tags is a list of tags to filter by. If specified, the resource must have all of the tags specified to be included in the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "tagsAny": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tagsAny is a list of tags to filter by. If specified, the resource must have at least one of the tags specified to be included in the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "notTags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "notTags is a list of tags to filter by. If specified, resources which contain all of the given tags will be excluded from the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "notTagsAny": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "notTagsAny is a list of tags to filter by. If specified, resources which contain any of the given tags will be excluded from the result.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, }, }, @@ -9803,10 +9899,58 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceSpec(ref Format: "", }, }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "adminStateUp is the administrative state of the trunk. If false (down), the trunk does not forward packets.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "subports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "subports is the list of ports to attach to the trunk.\n\nNOTE: ORC currently does not implement reconcile logic for subport updates (Neutron uses dedicated add/remove subport APIs). This field is immutable until that behavior is implemented in the controller.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSubportSpec"), + }, + }, + }, + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tags is a list of Neutron tags to apply to the trunk.\n\nNOTE: ORC does not currently reconcile tag updates for Trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, Required: []string{"portRef"}, }, }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSubportSpec"}, } } @@ -9845,9 +9989,90 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkResourceStatus(re Format: "", }, }, + "tenantID": { + SchemaProps: spec.SchemaProps{ + Description: "tenantID is the project owner of the trunk (alias of projectID in some deployments).", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status indicates whether the trunk is currently operational.", + Type: []string{"string"}, + Format: "", + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "tags is the list of tags on the resource.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "createdAt": { + SchemaProps: spec.SchemaProps{ + Description: "createdAt shows the date and time when the resource was created. The date and time stamp format is ISO 8601", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "updatedAt": { + SchemaProps: spec.SchemaProps{ + Description: "updatedAt shows the date and time when the resource was updated. The date and time stamp format is ISO 8601", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "revisionNumber": { + SchemaProps: spec.SchemaProps{ + Description: "revisionNumber optionally set via extensions/standard-attr-revisions", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "adminStateUp is the administrative state of the trunk.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "subports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "subports is a list of ports associated with the trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSubportStatus"), + }, + }, + }, + }, + }, }, }, }, + Dependencies: []string{ + "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.TrunkSubportStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } @@ -9951,6 +10176,78 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkStatus(ref common } } +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkSubportSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TrunkSubportSpec represents a subport to attach to a trunk. It maps to gophercloud's trunks.Subport.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "portRef": { + SchemaProps: spec.SchemaProps{ + Description: "portRef is a reference to the ORC Port that will be attached as a subport.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "segmentationID": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationID is the segmentation ID for the subport (e.g. VLAN ID).", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "segmentationType": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationType is the segmentation type for the subport (e.g. vlan).", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"portRef", "segmentationID", "segmentationType"}, + }, + }, + } +} + +func schema_openstack_resource_controller_v2_api_v1alpha1_TrunkSubportStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TrunkSubportStatus represents an attached subport on a trunk. It maps to gophercloud's trunks.Subport.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "portID": { + SchemaProps: spec.SchemaProps{ + Description: "portID is the OpenStack ID of the Port attached as a subport.", + Type: []string{"string"}, + Format: "", + }, + }, + "segmentationID": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationID is the segmentation ID for the subport (e.g. VLAN ID).", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "segmentationType": { + SchemaProps: spec.SchemaProps{ + Description: "segmentationType is the segmentation type for the subport (e.g. vlan).", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_openstack_resource_controller_v2_api_v1alpha1_UserDataSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/config/crd/bases/openstack.k-orc.cloud_trunks.yaml b/config/crd/bases/openstack.k-orc.cloud_trunks.yaml index a5f3db488..28cc0dbfa 100644 --- a/config/crd/bases/openstack.k-orc.cloud_trunks.yaml +++ b/config/crd/bases/openstack.k-orc.cloud_trunks.yaml @@ -91,6 +91,10 @@ spec: error state and will not continue to retry. minProperties: 1 properties: + adminStateUp: + description: adminStateUp is the administrative state of the + trunk. + type: boolean description: description: description of the existing resource maxLength: 255 @@ -102,6 +106,34 @@ spec: minLength: 1 pattern: ^[^,]+$ type: string + notTags: + description: |- + notTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + notTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set portRef: description: portRef is a reference to the ORC Port which this resource is associated with. @@ -114,6 +146,40 @@ spec: maxLength: 253 minLength: 1 type: string + status: + description: |- + status indicates whether the trunk is currently operational. Possible values include + `ACTIVE', `DOWN', `BUILD', `DEGRADED' or `ERROR'. Plug-ins might define additional values. + type: string + tags: + description: |- + tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + tagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set type: object id: description: |- @@ -161,6 +227,11 @@ spec: resource must be specified if the management policy is `managed`. properties: + adminStateUp: + description: |- + adminStateUp is the administrative state of the trunk. If false (down), + the trunk does not forward packets. + type: boolean description: description: description is a human-readable description for the resource. @@ -193,6 +264,66 @@ spec: x-kubernetes-validations: - message: projectRef is immutable rule: self == oldSelf + subports: + description: |- + subports is the list of ports to attach to the trunk. + + NOTE: ORC currently does not implement reconcile logic for subport updates + (Neutron uses dedicated add/remove subport APIs). This field is immutable + until that behavior is implemented in the controller. + items: + description: |- + TrunkSubportSpec represents a subport to attach to a trunk. + It maps to gophercloud's trunks.Subport. + properties: + portRef: + description: portRef is a reference to the ORC Port that + will be attached as a subport. + maxLength: 253 + minLength: 1 + type: string + segmentationID: + description: segmentationID is the segmentation ID for the + subport (e.g. VLAN ID). + format: int32 + maximum: 4094 + minimum: 1 + type: integer + segmentationType: + description: segmentationType is the segmentation type for + the subport (e.g. vlan). + maxLength: 255 + minLength: 1 + type: string + required: + - portRef + - segmentationID + - segmentationType + type: object + maxItems: 1024 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: subports is immutable + rule: self == oldSelf + tags: + description: |- + tags is a list of Neutron tags to apply to the trunk. + + NOTE: ORC does not currently reconcile tag updates for Trunk. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + maxLength: 255 + minLength: 1 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: tags is immutable + rule: self == oldSelf required: - portRef type: object @@ -298,6 +429,14 @@ spec: description: resource contains the observed state of the OpenStack resource. properties: + adminStateUp: + description: adminStateUp is the administrative state of the trunk. + type: boolean + createdAt: + description: createdAt shows the date and time when the resource + was created. The date and time stamp format is ISO 8601 + format: date-time + type: string description: description: description is a human-readable description for the resource. @@ -318,6 +457,58 @@ spec: is associated. maxLength: 1024 type: string + revisionNumber: + description: revisionNumber optionally set via extensions/standard-attr-revisions + format: int64 + type: integer + status: + description: status indicates whether the trunk is currently operational. + maxLength: 1024 + type: string + subports: + description: subports is a list of ports associated with the trunk. + items: + description: |- + TrunkSubportStatus represents an attached subport on a trunk. + It maps to gophercloud's trunks.Subport. + properties: + portID: + description: portID is the OpenStack ID of the Port attached + as a subport. + maxLength: 1024 + type: string + segmentationID: + description: segmentationID is the segmentation ID for the + subport (e.g. VLAN ID). + format: int32 + type: integer + segmentationType: + description: segmentationType is the segmentation type for + the subport (e.g. vlan). + maxLength: 1024 + type: string + type: object + maxItems: 1024 + type: array + x-kubernetes-list-type: atomic + tags: + description: tags is the list of tags on the resource. + items: + maxLength: 1024 + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: atomic + tenantID: + description: tenantID is the project owner of the trunk (alias + of projectID in some deployments). + maxLength: 1024 + type: string + updatedAt: + description: updatedAt shows the date and time when the resource + was updated. The date and time stamp format is ISO 8601 + format: date-time + type: string type: object type: object type: object diff --git a/config/samples/openstack_v1alpha1_trunk.yaml b/config/samples/openstack_v1alpha1_trunk.yaml index f663b0346..7019315f1 100644 --- a/config/samples/openstack_v1alpha1_trunk.yaml +++ b/config/samples/openstack_v1alpha1_trunk.yaml @@ -5,10 +5,20 @@ metadata: name: trunk-sample spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: description: Sample Trunk - # TODO(scaffolding): Add all fields the resource supports + name: trunk-sample-name + portRef: my-port + subPorts: + - portRef: sub-port-1 + segmentationID: 101 + segmentationType: vlan + - portRef: sub-port-2 + segmentationID: 102 + segmentationType: vlan + tags: + - tag1 + - tag2 diff --git a/internal/controllers/domain/tests/domain-create-full/00-assert.yaml b/internal/controllers/domain/tests/domain-create-full/00-assert.yaml index f9a6b6c4a..31550542a 100644 --- a/internal/controllers/domain/tests/domain-create-full/00-assert.yaml +++ b/internal/controllers/domain/tests/domain-create-full/00-assert.yaml @@ -15,3 +15,13 @@ status: - type: Progressing status: "False" reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Domain + name: domain-create-full + ref: domain +assertAll: + - celExpr: "domain.status.id != ''" diff --git a/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml b/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml index 5dbc7e293..864c9bac6 100644 --- a/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml +++ b/internal/controllers/domain/tests/domain-create-minimal/00-assert.yaml @@ -22,4 +22,5 @@ resourceRefs: name: domain-create-minimal ref: domain assertAll: + - celExpr: "domain.status.id != ''" - celExpr: "!has(domain.status.resource.description)" \ No newline at end of file diff --git a/internal/controllers/domain/tests/domain-import/02-assert.yaml b/internal/controllers/domain/tests/domain-import/02-assert.yaml index 62a6ba634..69cfc4319 100644 --- a/internal/controllers/domain/tests/domain-import/02-assert.yaml +++ b/internal/controllers/domain/tests/domain-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Domain + name: domain-import-external + ref: domain1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Domain + name: domain-import-external-not-this-one + ref: domain2 +assertAll: + - celExpr: "domain1.status.id != domain2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Domain metadata: diff --git a/internal/controllers/domain/tests/domain-update/01-assert.yaml b/internal/controllers/domain/tests/domain-update/01-assert.yaml index 017267dee..b00b0270d 100644 --- a/internal/controllers/domain/tests/domain-update/01-assert.yaml +++ b/internal/controllers/domain/tests/domain-update/01-assert.yaml @@ -1,4 +1,12 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Domain + name: domain-update + ref: domain +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Domain metadata: diff --git a/internal/controllers/domain/tests/domain-update/02-assert.yaml b/internal/controllers/domain/tests/domain-update/02-assert.yaml index e109195f6..3e931f326 100644 --- a/internal/controllers/domain/tests/domain-update/02-assert.yaml +++ b/internal/controllers/domain/tests/domain-update/02-assert.yaml @@ -7,6 +7,7 @@ resourceRefs: name: domain-update ref: domain assertAll: + - celExpr: "domain.status.id != ''" - celExpr: "!has(domain.status.resource.description)" --- diff --git a/internal/controllers/flavor/tests/flavor-import/02-assert.yaml b/internal/controllers/flavor/tests/flavor-import/02-assert.yaml index a76b2d2e6..26745be04 100644 --- a/internal/controllers/flavor/tests/flavor-import/02-assert.yaml +++ b/internal/controllers/flavor/tests/flavor-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Flavor + name: flavor-import-external + ref: flavor1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Flavor + name: flavor-import-external-not-this-one + ref: flavor2 +assertAll: + - celExpr: "flavor1.status.id != flavor2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Flavor metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml index 7f7d44f47..cda640f2d 100644 --- a/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-create-full/00-assert.yaml @@ -1,4 +1,33 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-create-full + ref: floatingIP + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: floatingip-create-full-external + ref: network + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Router + name: floatingip-create-full + ref: router + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: floatingip-create-full + ref: port + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: project + name: floatingip-create-full + ref: project +assertAll: + - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" + - celExpr: "floatingIP.status.resource.portID == port.status.id" + - celExpr: "floatingIP.status.resource.routerID == router.status.id" + - celExpr: "floatingIP.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml index e69de29bb..fa451d399 100644 --- a/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-create-minimal/00-assert.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-create-minimal + ref: floatingIP + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: floatingip-create-minimal + ref: network +assertAll: + - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" + - celExpr: "floatingIP.status.resource.floatingIP != ''" diff --git a/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml index 37839a10b..9e997be07 100644 --- a/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-create-subnet-ref/00-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-create-subnet-ref + ref: floatingIP + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: floatingip-create-subnet-ref + ref: network +assertAll: + - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml b/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml index 48b2f3dba..3bd12c0a4 100644 --- a/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-import-dependency/02-assert.yaml @@ -1,4 +1,33 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-import-dependency + ref: fip1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-import-dependency-not-this-one + ref: fip2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: floatingip-import-dependency + ref: network + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: floatingip-import-dependency + ref: port + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: floatingip-import-dependency + ref: project +assertAll: + - celExpr: "fip1.status.id != fip2.status.id" + - celExpr: "fip1.status.resource.floatingNetworkID == network.status.id" + - celExpr: "fip1.status.resource.portID == port.status.id" + - celExpr: "fip1.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml b/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml index e69de29bb..6025dacfd 100644 --- a/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-import-dependency/03-assert.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get network floatingip-import-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get port floatingip-import-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get project floatingip-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml b/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml index 33cc17cc1..18e15169a 100644 --- a/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-import/02-assert.yaml @@ -1,4 +1,23 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-import + ref: fip1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-import-not-this-one + ref: fip2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-import-external + ref: fipExternal +assertAll: + - celExpr: "fip1.status.id != fip2.status.id" + - celExpr: "fip1.status.resource.floatingIP == fipExternal.status.resource.floatingIP" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml b/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml index 309a64678..ccaa21f97 100644 --- a/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-update/00-assert.yaml @@ -11,6 +11,8 @@ resourceRefs: name: floatingip-update ref: network assertAll: + - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" + - celExpr: "floatingIP.status.resource.floatingIP != ''" - celExpr: "!has(floatingIP.status.resource.tags)" - celExpr: "!has(floatingIP.status.resource.description)" --- diff --git a/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml b/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml index 12e07f10c..84c932ff6 100644 --- a/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-update/01-assert.yaml @@ -1,4 +1,19 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: FloatingIP + name: floatingip-update + ref: floatingIP + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: floatingip-update + ref: network +assertAll: + - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" + - celExpr: "floatingIP.status.resource.floatingIP != ''" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: FloatingIP metadata: diff --git a/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml b/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml index 309a64678..ccaa21f97 100644 --- a/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml +++ b/internal/controllers/floatingip/tests/floatingip-update/02-assert.yaml @@ -11,6 +11,8 @@ resourceRefs: name: floatingip-update ref: network assertAll: + - celExpr: "floatingIP.status.resource.floatingNetworkID == network.status.id" + - celExpr: "floatingIP.status.resource.floatingIP != ''" - celExpr: "!has(floatingIP.status.resource.tags)" - celExpr: "!has(floatingIP.status.resource.description)" --- diff --git a/internal/controllers/image/tests/image-import/02-assert.yaml b/internal/controllers/image/tests/image-import/02-assert.yaml index d6a29d5a0..2f6bf271c 100644 --- a/internal/controllers/image/tests/image-import/02-assert.yaml +++ b/internal/controllers/image/tests/image-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Image + name: image-import-external + ref: image1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Image + name: image-import-external-not-this-one + ref: image2 +assertAll: + - celExpr: "image1.status.id != image2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Image metadata: diff --git a/internal/controllers/network/tests/network-create-full/00-assert.yaml b/internal/controllers/network/tests/network-create-full/00-assert.yaml index ff2b57bb2..81d65cf81 100644 --- a/internal/controllers/network/tests/network-create-full/00-assert.yaml +++ b/internal/controllers/network/tests/network-create-full/00-assert.yaml @@ -31,4 +31,8 @@ resourceRefs: name: network-create-full ref: project assertAll: + - celExpr: "network.status.id != ''" + - celExpr: "network.status.resource.createdAt != ''" + - celExpr: "network.status.resource.updatedAt != ''" - celExpr: "network.status.resource.revisionNumber > 0" + - celExpr: "network.status.resource.projectID == project.status.id" diff --git a/internal/controllers/network/tests/network-import-dependency/02-assert.yaml b/internal/controllers/network/tests/network-import-dependency/02-assert.yaml index 4800215c7..d16f0596a 100644 --- a/internal/controllers/network/tests/network-import-dependency/02-assert.yaml +++ b/internal/controllers/network/tests/network-import-dependency/02-assert.yaml @@ -1,4 +1,23 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: network-import-dependency + ref: network1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: network-import-dependency-external-not-this-one + ref: network2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: network-import-dependency + ref: project +assertAll: + - celExpr: "network1.status.id != network2.status.id" + - celExpr: "network1.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Network metadata: diff --git a/internal/controllers/network/tests/network-import-dependency/03-assert.yaml b/internal/controllers/network/tests/network-import-dependency/03-assert.yaml index e69de29bb..0a5312ae1 100644 --- a/internal/controllers/network/tests/network-import-dependency/03-assert.yaml +++ b/internal/controllers/network/tests/network-import-dependency/03-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get project network-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/network/tests/network-import/02-assert.yaml b/internal/controllers/network/tests/network-import/02-assert.yaml index 5fd10f506..77bcbec7c 100644 --- a/internal/controllers/network/tests/network-import/02-assert.yaml +++ b/internal/controllers/network/tests/network-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: network-import-external + ref: network1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: network-import-external-not-this-one + ref: network2 +assertAll: + - celExpr: "network1.status.id != network2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Network metadata: diff --git a/internal/controllers/network/tests/network-update/01-assert.yaml b/internal/controllers/network/tests/network-update/01-assert.yaml index 94cfb4bc2..e7fbd943e 100644 --- a/internal/controllers/network/tests/network-update/01-assert.yaml +++ b/internal/controllers/network/tests/network-update/01-assert.yaml @@ -1,4 +1,12 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: network-update + ref: network +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Network metadata: diff --git a/internal/controllers/port/tests/port-create-full/00-assert.yaml b/internal/controllers/port/tests/port-create-full/00-assert.yaml index b28910518..b8340a6fe 100644 --- a/internal/controllers/port/tests/port-create-full/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-full/00-assert.yaml @@ -38,5 +38,12 @@ resourceRefs: name: port-create-full ref: project assertAll: + - celExpr: "port.status.id != ''" + - celExpr: "port.status.resource.createdAt != ''" + - celExpr: "port.status.resource.updatedAt != ''" + - celExpr: "port.status.resource.macAddress != ''" - celExpr: "port.status.resource.revisionNumber > 0" + - celExpr: "port.status.resource.fixedIPs[0].subnetID == subnet.status.id" - celExpr: "port.status.resource.fixedIPs[0].ip == '192.168.155.122'" + - celExpr: "port.status.resource.securityGroups[0] == sg.status.id" + - celExpr: "port.status.resource.projectID == project.status.id" diff --git a/internal/controllers/port/tests/port-create-minimal/00-assert.yaml b/internal/controllers/port/tests/port-create-minimal/00-assert.yaml index cfc645688..9c6d861fc 100644 --- a/internal/controllers/port/tests/port-create-minimal/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-minimal/00-assert.yaml @@ -21,4 +21,8 @@ resourceRefs: name: port-create-minimal ref: port assertAll: + - celExpr: "port.status.id != ''" + - celExpr: "port.status.resource.createdAt != ''" + - celExpr: "port.status.resource.updatedAt != ''" + - celExpr: "port.status.resource.macAddress != ''" - celExpr: "!has(port.status.resource.fixedIPs)" diff --git a/internal/controllers/port/tests/port-create-sriov/00-assert.yaml b/internal/controllers/port/tests/port-create-sriov/00-assert.yaml index 6af78c05c..3277dfaea 100644 --- a/internal/controllers/port/tests/port-create-sriov/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-sriov/00-assert.yaml @@ -27,7 +27,12 @@ resourceRefs: name: port-create-sriov ref: subnet assertAll: + - celExpr: "port.status.id != ''" + - celExpr: "port.status.resource.createdAt != ''" + - celExpr: "port.status.resource.updatedAt != ''" + - celExpr: "port.status.resource.macAddress != ''" - celExpr: "port.status.resource.revisionNumber > 0" + - celExpr: "port.status.resource.fixedIPs[0].subnetID == subnet.status.id" - celExpr: "port.status.resource.fixedIPs[0].ip == '192.168.155.122'" - celExpr: "!has(port.status.resource.allowedAddressPairs)" - celExpr: "!has(port.status.resource.securityGroups)" \ No newline at end of file diff --git a/internal/controllers/port/tests/port-import-dependency/02-assert.yaml b/internal/controllers/port/tests/port-import-dependency/02-assert.yaml index f266a9d75..f0524a0bb 100644 --- a/internal/controllers/port/tests/port-import-dependency/02-assert.yaml +++ b/internal/controllers/port/tests/port-import-dependency/02-assert.yaml @@ -1,4 +1,28 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: port-import-dependency + ref: port1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: port-import-dependency-external-not-this-one + ref: port2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: port-import-dependency + ref: network + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: port-import-dependency + ref: project +assertAll: + - celExpr: "port1.status.id != port2.status.id" + - celExpr: "port1.status.resource.networkID == network.status.id" + - celExpr: "port1.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: diff --git a/internal/controllers/port/tests/port-import-dependency/03-assert.yaml b/internal/controllers/port/tests/port-import-dependency/03-assert.yaml index e69de29bb..7fffe080d 100644 --- a/internal/controllers/port/tests/port-import-dependency/03-assert.yaml +++ b/internal/controllers/port/tests/port-import-dependency/03-assert.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get network port-import-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get project port-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/port/tests/port-import/02-assert.yaml b/internal/controllers/port/tests/port-import/02-assert.yaml index 56f3b3cb2..1d1486451 100644 --- a/internal/controllers/port/tests/port-import/02-assert.yaml +++ b/internal/controllers/port/tests/port-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: port-import-external + ref: port1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: port-import-external-not-this-one + ref: port2 +assertAll: + - celExpr: "port1.status.id != port2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: diff --git a/internal/controllers/port/tests/port-update/00-assert.yaml b/internal/controllers/port/tests/port-update/00-assert.yaml index d17ac94bb..fef380932 100644 --- a/internal/controllers/port/tests/port-update/00-assert.yaml +++ b/internal/controllers/port/tests/port-update/00-assert.yaml @@ -7,6 +7,10 @@ resourceRefs: name: port-update ref: port assertAll: + - celExpr: "port.status.id != ''" + - celExpr: "port.status.resource.createdAt != ''" + - celExpr: "port.status.resource.updatedAt != ''" + - celExpr: "port.status.resource.macAddress != ''" - celExpr: "!has(port.status.resource.fixedIPs)" - celExpr: "!has(port.status.resource.description)" --- diff --git a/internal/controllers/port/tests/port-update/01-assert.yaml b/internal/controllers/port/tests/port-update/01-assert.yaml index 83fd3c0b5..ef7850e61 100644 --- a/internal/controllers/port/tests/port-update/01-assert.yaml +++ b/internal/controllers/port/tests/port-update/01-assert.yaml @@ -1,3 +1,28 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: port-update + ref: port + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: port-update + ref: subnet + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: port-update + ref: network + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: SecurityGroup + name: port-update + ref: securitygroup +assertAll: + - celExpr: "port.status.resource.networkID == network.status.id" + - celExpr: "subnet.status.resource.networkID == network.status.id" + - celExpr: "port.status.resource.securityGroups[0] == securitygroup.status.id" + --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port diff --git a/internal/controllers/port/tests/port-update/02-assert.yaml b/internal/controllers/port/tests/port-update/02-assert.yaml index 838604d25..314caa5b0 100644 --- a/internal/controllers/port/tests/port-update/02-assert.yaml +++ b/internal/controllers/port/tests/port-update/02-assert.yaml @@ -7,6 +7,10 @@ resourceRefs: name: port-update ref: port assertAll: + - celExpr: "port.status.id != ''" + - celExpr: "port.status.resource.createdAt != ''" + - celExpr: "port.status.resource.updatedAt != ''" + - celExpr: "port.status.resource.macAddress != ''" - celExpr: "!has(port.status.resource.fixedIPs)" - celExpr: "!has(port.status.resource.description)" - celExpr: "!has(port.status.resource.securityGroups)" diff --git a/internal/controllers/project/tests/project-import/02-assert.yaml b/internal/controllers/project/tests/project-import/02-assert.yaml index 1774bdf96..270a3d060 100644 --- a/internal/controllers/project/tests/project-import/02-assert.yaml +++ b/internal/controllers/project/tests/project-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: project-import-external + ref: project1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: project-import-external-not-this-one + ref: project2 +assertAll: + - celExpr: "project1.status.id != project2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project metadata: diff --git a/internal/controllers/project/tests/project-update/01-assert.yaml b/internal/controllers/project/tests/project-update/01-assert.yaml index 0d180969f..c893540b0 100644 --- a/internal/controllers/project/tests/project-update/01-assert.yaml +++ b/internal/controllers/project/tests/project-update/01-assert.yaml @@ -1,4 +1,12 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: project-update + ref: project +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project metadata: diff --git a/internal/controllers/router/tests/router-create-full/00-assert.yaml b/internal/controllers/router/tests/router-create-full/00-assert.yaml index d2faadbbb..4213dfced 100644 --- a/internal/controllers/router/tests/router-create-full/00-assert.yaml +++ b/internal/controllers/router/tests/router-create-full/00-assert.yaml @@ -13,3 +13,26 @@ status: - tag1 - tag2 adminStateUp: false +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: router + name: router-create-full + ref: router + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: network + name: router-external-network + ref: network + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: project + name: router-create-full + ref: project +assertAll: + - celExpr: "router.status.id != ''" + # - celExpr: "router.status.resource.createdAt != ''" + # - celExpr: "router.status.resource.updatedAt != ''" + # - celExpr: "router.status.resource.revisionNumber > 0" + - celExpr: "router.status.resource.externalGateways[0].networkID == network.status.id" + - celExpr: "router.status.resource.projectID == project.status.id" diff --git a/internal/controllers/router/tests/router-import-dependency/02-assert.yaml b/internal/controllers/router/tests/router-import-dependency/02-assert.yaml index c708fad01..ddd39fbcb 100644 --- a/internal/controllers/router/tests/router-import-dependency/02-assert.yaml +++ b/internal/controllers/router/tests/router-import-dependency/02-assert.yaml @@ -1,4 +1,23 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Router + name: router-import-dependency + ref: router1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Router + name: router-import-dependency-external-not-this-one + ref: router2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: router-import-dependency + ref: project +assertAll: + - celExpr: "router1.status.id != router2.status.id" + - celExpr: "router1.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Router metadata: diff --git a/internal/controllers/router/tests/router-import-dependency/03-assert.yaml b/internal/controllers/router/tests/router-import-dependency/03-assert.yaml index e69de29bb..b143ec99c 100644 --- a/internal/controllers/router/tests/router-import-dependency/03-assert.yaml +++ b/internal/controllers/router/tests/router-import-dependency/03-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get project router-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/router/tests/router-import/02-assert.yaml b/internal/controllers/router/tests/router-import/02-assert.yaml index da639473b..91112be50 100644 --- a/internal/controllers/router/tests/router-import/02-assert.yaml +++ b/internal/controllers/router/tests/router-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Router + name: router-import-external + ref: router1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Router + name: router-import-external-not-this-one + ref: router2 +assertAll: + - celExpr: "router1.status.id != router2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Router metadata: diff --git a/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml b/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml index 0af8a590b..17a0aa42d 100644 --- a/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml +++ b/internal/controllers/routerinterface/tests/routerinterface-create-minimal/00-assert.yaml @@ -13,3 +13,13 @@ status: message: OpenStack resource is up to date status: "False" reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: routerinterface + name: routerinterface-create-minimal + ref: routerinterface +assertAll: + - celExpr: "routerinterface.status.id != ''" \ No newline at end of file diff --git a/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml index 633417899..6d6cbecbb 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-create-full/00-assert.yaml @@ -20,3 +20,21 @@ status: tags: - tag1 - tag2 +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: securitygroup + name: securitygroup-create-full + ref: sg + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: project + name: securitygroup-create-full + ref: project +assertAll: + - celExpr: "sg.status.id != ''" + - celExpr: "sg.status.resource.createdAt != ''" + - celExpr: "sg.status.resource.updatedAt != ''" + # - celExpr: "sg.status.resource.revisionNumber > 0" + - celExpr: "sg.status.resource.projectID == project.status.id" diff --git a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml index 419c878a8..5e427fdda 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/02-assert.yaml @@ -1,4 +1,23 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: SecurityGroup + name: securitygroup-import-dependency + ref: securitygroup1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: SecurityGroup + name: securitygroup-import-dependency-external-not-this-one + ref: securitygroup2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: securitygroup-import-dependency + ref: project +assertAll: + - celExpr: "securitygroup1.status.id != securitygroup2.status.id" + - celExpr: "securitygroup1.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: SecurityGroup metadata: diff --git a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml index e69de29bb..035006903 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-import-dependency/03-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get project securitygroup-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml index 945b1f77d..03c0a13af 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: SecurityGroup + name: securitygroup-import-external + ref: securitygroup1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: SecurityGroup + name: securitygroup-import-external-not-this-one + ref: securitygroup2 +assertAll: + - celExpr: "securitygroup1.status.id != securitygroup2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: SecurityGroup metadata: diff --git a/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml b/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml index aabc0f186..dd2e51ea3 100644 --- a/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml +++ b/internal/controllers/securitygroup/tests/securitygroup-update/01-assert.yaml @@ -1,4 +1,12 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: SecurityGroup + name: securitygroup-update + ref: secGroup +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: SecurityGroup metadata: diff --git a/internal/controllers/server/tests/server-create-full/00-assert.yaml b/internal/controllers/server/tests/server-create-full/00-assert.yaml index 841846187..5f351d6d1 100644 --- a/internal/controllers/server/tests/server-create-full/00-assert.yaml +++ b/internal/controllers/server/tests/server-create-full/00-assert.yaml @@ -35,10 +35,25 @@ resourceRefs: name: server-create-full ref: subnet assertAll: + - celExpr: "server.status.resource.hostID != ''" - celExpr: "server.status.resource.availabilityZone == 'nova'" + - celExpr: "server.status.resource.imageID == image.status.id" + - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" + - celExpr: "server.status.resource.volumes[0].id == volume.status.id" + - celExpr: "volume.status.resource.attachments[0].attachmentID != ''" + - celExpr: "volume.status.resource.attachments[0].serverID == server.status.id" + - celExpr: "volume.status.resource.attachments[0].device != ''" + - celExpr: "volume.status.resource.attachments[0].attachedAt != ''" + - celExpr: "port.status.resource.deviceID == server.status.id" - celExpr: "port.status.resource.status == 'ACTIVE'" - celExpr: "size(server.status.resource.interfaces) == 1" + - celExpr: "server.status.resource.interfaces[0].portID == port.status.id" + - celExpr: "server.status.resource.interfaces[0].netID == network.status.id" + - celExpr: "server.status.resource.interfaces[0].macAddr != ''" + - celExpr: "server.status.resource.interfaces[0].portState != ''" - celExpr: "size(server.status.resource.interfaces[0].fixedIPs) >= 1" + - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].ipAddress != ''" + - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].subnetID == subnet.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-create-minimal/00-assert.yaml b/internal/controllers/server/tests/server-create-minimal/00-assert.yaml index 14d9d90d4..e01b2f813 100644 --- a/internal/controllers/server/tests/server-create-minimal/00-assert.yaml +++ b/internal/controllers/server/tests/server-create-minimal/00-assert.yaml @@ -23,9 +23,19 @@ resourceRefs: name: server-create-minimal ref: subnet assertAll: + - celExpr: "server.status.resource.hostID != ''" + - celExpr: "server.status.resource.availabilityZone != ''" + - celExpr: "server.status.resource.imageID == image.status.id" + - celExpr: "port.status.resource.deviceID == server.status.id" - celExpr: "port.status.resource.status == 'ACTIVE'" - celExpr: "size(server.status.resource.interfaces) == 1" + - celExpr: "server.status.resource.interfaces[0].portID == port.status.id" + - celExpr: "server.status.resource.interfaces[0].netID == network.status.id" + - celExpr: "server.status.resource.interfaces[0].macAddr != ''" + - celExpr: "server.status.resource.interfaces[0].portState != ''" - celExpr: "size(server.status.resource.interfaces[0].fixedIPs) >= 1" + - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].ipAddress != ''" + - celExpr: "server.status.resource.interfaces[0].fixedIPs[0].subnetID == subnet.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-import/02-assert.yaml b/internal/controllers/server/tests/server-import/02-assert.yaml index 5a5b65a12..877c14b58 100644 --- a/internal/controllers/server/tests/server-import/02-assert.yaml +++ b/internal/controllers/server/tests/server-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Server + name: server-import-external + ref: server1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Server + name: server-import-external-not-this-one + ref: server2 +assertAll: + - celExpr: "server1.status.id != server2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server metadata: diff --git a/internal/controllers/server/tests/server-update/00-assert.yaml b/internal/controllers/server/tests/server-update/00-assert.yaml index 7256bcfc5..551244650 100644 --- a/internal/controllers/server/tests/server-update/00-assert.yaml +++ b/internal/controllers/server/tests/server-update/00-assert.yaml @@ -19,9 +19,13 @@ resourceRefs: name: server-update ref: sg assertAll: + - celExpr: "server.status.resource.hostID != ''" + - celExpr: "server.status.resource.imageID == image.status.id" + - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" - celExpr: "!has(server.status.resource.tags)" - celExpr: "!has(server.status.resource.volumes)" - celExpr: "size(server.status.resource.interfaces) == 1" + - celExpr: "server.status.resource.interfaces[0].portID == port.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-update/01-assert.yaml b/internal/controllers/server/tests/server-update/01-assert.yaml index b9a5ad237..473aecab0 100644 --- a/internal/controllers/server/tests/server-update/01-assert.yaml +++ b/internal/controllers/server/tests/server-update/01-assert.yaml @@ -27,9 +27,21 @@ resourceRefs: name: server-update ref: volume assertAll: + - celExpr: "server.status.resource.hostID != ''" + - celExpr: "server.status.resource.imageID == image.status.id" + - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" + - celExpr: "server.status.resource.volumes[0].id == volume.status.id" + - celExpr: "port1.status.resource.deviceID == server.status.id" - celExpr: "port1.status.resource.status == 'ACTIVE'" + - celExpr: "port2.status.resource.deviceID == server.status.id" - celExpr: "port2.status.resource.status == 'ACTIVE'" - celExpr: "size(server.status.resource.interfaces) == 2" + - celExpr: "server.status.resource.interfaces.exists(i, i.portID == port1.status.id)" + - celExpr: "server.status.resource.interfaces.exists(i, i.portID == port2.status.id)" + - celExpr: "volume.status.resource.attachments[0].attachmentID != ''" + - celExpr: "volume.status.resource.attachments[0].serverID == server.status.id" + - celExpr: "volume.status.resource.attachments[0].device != ''" + - celExpr: "volume.status.resource.attachments[0].attachedAt != ''" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/server/tests/server-update/02-assert.yaml b/internal/controllers/server/tests/server-update/02-assert.yaml index 88263e513..68beeb722 100644 --- a/internal/controllers/server/tests/server-update/02-assert.yaml +++ b/internal/controllers/server/tests/server-update/02-assert.yaml @@ -27,13 +27,18 @@ resourceRefs: name: server-update ref: volume assertAll: + - celExpr: "server.status.resource.hostID != ''" + - celExpr: "server.status.resource.imageID == image.status.id" + - celExpr: "server.status.resource.serverGroups[0] == sg.status.id" - celExpr: "!has(server.status.resource.tags)" - celExpr: "!has(server.status.resource.volumes)" - celExpr: "!has(volume.status.resource.attachments)" + - celExpr: "port1.status.resource.deviceID == server.status.id" - celExpr: "port1.status.resource.status == 'ACTIVE'" - celExpr: "!has(port2.status.resource.deviceID)" - celExpr: "port2.status.resource.status == 'DOWN'" - celExpr: "size(server.status.resource.interfaces) == 1" + - celExpr: "server.status.resource.interfaces[0].portID == port1.status.id" --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Server diff --git a/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml b/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml index ccbf63267..594f9fcae 100644 --- a/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml +++ b/internal/controllers/servergroup/tests/servergroup-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: ServerGroup + name: servergroup-import-external + ref: sg1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: ServerGroup + name: servergroup-import-external-not-this-one + ref: sg2 +assertAll: + - celExpr: "sg1.status.id != sg2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: ServerGroup metadata: diff --git a/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml index a1e8452a8..78eb9c1ab 100644 --- a/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-full-v4/00-assert.yaml @@ -15,9 +15,12 @@ resourceRefs: name: subnet-create-full-v4 ref: project assertAll: + - celExpr: "subnet.status.id != ''" # - celExpr: "subnet.status.resource.createdAt != ''" # - celExpr: "subnet.status.resource.updatedAt != ''" # - celExpr: "subnet.status.resource.revisionNumber > 0" + - celExpr: "subnet.status.resource.networkID == network.status.id" + - celExpr: "subnet.status.resource.projectID == project.status.id" - celExpr: "'ipv6AddressMode' in subnet.status.resource == false" - celExpr: "'ipv6RAMode' in subnet.status.resource == false" --- diff --git a/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml index 7907aad4c..8d812d6a1 100644 --- a/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-full-v6/00-assert.yaml @@ -1,4 +1,27 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: subnet-create-full-v6 + ref: subnet + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: subnet-create-full-v6 + ref: network + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: project + name: subnet-create-full-v6 + ref: project +assertAll: + - celExpr: "subnet.status.id != ''" + # - celExpr: "subnet.status.resource.createdAt != ''" + # - celExpr: "subnet.status.resource.updatedAt != ''" + # - celExpr: "subnet.status.resource.revisionNumber > 0" + - celExpr: "subnet.status.resource.networkID == network.status.id" + - celExpr: "subnet.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml index 5fa907a5a..a063a5836 100644 --- a/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-minimal-v4/00-assert.yaml @@ -11,6 +11,7 @@ resourceRefs: name: subnet-create-minimal-v4 ref: network assertAll: + - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "'description' in subnet.status.resource == false" - celExpr: "'ipv6AddressMode' in subnet.status.resource == false" - celExpr: "'ipv6RAMode' in subnet.status.resource == false" diff --git a/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml b/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml index dfe3431b3..d0460c9b9 100644 --- a/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-create-minimal-v6/00-assert.yaml @@ -11,6 +11,7 @@ resourceRefs: name: subnet-create-minimal-v6 ref: network assertAll: + - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "'description' in subnet.status.resource == false" - celExpr: "'ipv6AddressMode' in subnet.status.resource == false" - celExpr: "'ipv6RAMode' in subnet.status.resource == false" diff --git a/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml b/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml index cbd0d3cc0..fd416c9d3 100644 --- a/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-import-dependency/02-assert.yaml @@ -1,4 +1,28 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: subnet-import-dependency + ref: subnet1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: subnet-import-dependency-external-not-this-one + ref: subnet2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: subnet-import-dependency + ref: network + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Project + name: subnet-import-dependency + ref: project +assertAll: + - celExpr: "subnet1.status.id != subnet2.status.id" + - celExpr: "subnet1.status.resource.networkID == network.status.id" + - celExpr: "subnet1.status.resource.projectID == project.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml b/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml index e69de29bb..7c736b10f 100644 --- a/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-import-dependency/03-assert.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: "! kubectl get network subnet-import-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get project subnet-import-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/subnet/tests/subnet-import/02-assert.yaml b/internal/controllers/subnet/tests/subnet-import/02-assert.yaml index d9e366a08..7533a1c4f 100644 --- a/internal/controllers/subnet/tests/subnet-import/02-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-import/02-assert.yaml @@ -1,4 +1,23 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: subnet-import-external + ref: subnet1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: subnet-import-external-not-this-one + ref: subnet2 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: subnet-import + ref: network +assertAll: + - celExpr: "subnet1.status.id != subnet2.status.id" + - celExpr: "subnet1.status.resource.networkID == network.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-update/00-assert.yaml b/internal/controllers/subnet/tests/subnet-update/00-assert.yaml index 69f633bdc..d0f2c3de9 100644 --- a/internal/controllers/subnet/tests/subnet-update/00-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-update/00-assert.yaml @@ -11,6 +11,7 @@ resourceRefs: name: subnet-update ref: network assertAll: + - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "!has(subnet.status.resource.description)" - celExpr: "!has(subnet.status.resource.tags)" - celExpr: "!has(subnet.status.resource.hostRoutes)" diff --git a/internal/controllers/subnet/tests/subnet-update/01-assert.yaml b/internal/controllers/subnet/tests/subnet-update/01-assert.yaml index 2a62c1d82..65207d2f3 100644 --- a/internal/controllers/subnet/tests/subnet-update/01-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-update/01-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: subnet-update + ref: subnet + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: subnet-update + ref: network +assertAll: + - celExpr: "subnet.status.resource.networkID == network.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: diff --git a/internal/controllers/subnet/tests/subnet-update/02-assert.yaml b/internal/controllers/subnet/tests/subnet-update/02-assert.yaml index e51f0d639..3602f09c8 100644 --- a/internal/controllers/subnet/tests/subnet-update/02-assert.yaml +++ b/internal/controllers/subnet/tests/subnet-update/02-assert.yaml @@ -11,6 +11,7 @@ resourceRefs: name: subnet-update ref: network assertAll: + - celExpr: "subnet.status.resource.networkID == network.status.id" - celExpr: "!has(subnet.status.resource.description)" - celExpr: "!has(subnet.status.resource.tags)" - celExpr: "!has(subnet.status.resource.hostRoutes)" diff --git a/internal/controllers/trunk/status.go b/internal/controllers/trunk/status.go index c43397bef..3ca73cd53 100644 --- a/internal/controllers/trunk/status.go +++ b/internal/controllers/trunk/status.go @@ -54,8 +54,13 @@ func (trunkStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *osReso WithProjectID(osResource.ProjectID). WithName(osResource.Name) - // TODO(scaffolding): add all of the fields supported in the TrunkResourceStatus struct - // If a zero-value isn't expected in the response, place it behind a conditional + if len(osResource.Tags) > 0 { + resourceStatus.WithTags(osResource.Tags...) + } + + if len(osResource.Subports) > 0 { + resourceStatus.WithSubports(osResource.Subports...) + } if osResource.Description != "" { resourceStatus.WithDescription(osResource.Description) diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml index c3a7df9c9..8a61e25fb 100644 --- a/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-create-full/00-assert.yaml @@ -7,7 +7,6 @@ status: resource: name: trunk-create-full-override description: Trunk from "create full" test - # TODO(scaffolding): Add all fields the resource supports conditions: - type: Available status: "True" @@ -27,12 +26,6 @@ resourceRefs: kind: Port name: trunk-create-full ref: port - - apiVersion: openstack.k-orc.cloud/v1alpha1 - kind: Project - name: trunk-create-full - ref: project assertAll: - celExpr: "trunk.status.id != ''" - celExpr: "trunk.status.resource.portID == port.status.id" - - celExpr: "trunk.status.resource.projectID == project.status.id" - # TODO(scaffolding): Add more checks diff --git a/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml index 56a343f64..7019c1c43 100644 --- a/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-create-full/00-create-resource.yaml @@ -5,11 +5,11 @@ metadata: name: trunk-create-full spec: cloudCredentialsRef: - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: - projectRef: trunk-create-full + name: trunk-create-full --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet @@ -17,14 +17,13 @@ metadata: name: trunk-create-full spec: cloudCredentialsRef: - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: - cidr: 192.168.157.0/24 - ipVersion: 4 networkRef: trunk-create-full - projectRef: trunk-create-full + ipVersion: 4 + cidr: 192.168.158.0/24 --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port @@ -32,27 +31,13 @@ metadata: name: trunk-create-full spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-create-full - projectRef: trunk-create-full ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Project -metadata: - name: trunk-create-full -spec: - cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + addresses: + - subnetRef: trunk-create-full --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -60,13 +45,10 @@ metadata: name: trunk-create-full spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: name: trunk-create-full-override description: Trunk from "create full" test portRef: trunk-create-full - projectRef: trunk-create-full - # TODO(scaffolding): Add all fields the resource supports diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml index 029262124..8a0367973 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-assert.yaml @@ -6,7 +6,6 @@ metadata: status: resource: name: trunk-create-minimal - # TODO(scaffolding): Add all fields the resource supports conditions: - type: Available status: "True" @@ -29,4 +28,3 @@ resourceRefs: assertAll: - celExpr: "trunk.status.id != ''" - celExpr: "trunk.status.resource.portID == port.status.id" - # TODO(scaffolding): Add more checks diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml index bb2d39169..4e665ef51 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-create-resource.yaml @@ -5,11 +5,8 @@ metadata: name: trunk-create-minimal spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Only add the mandatory fields. It's possible the resource - # doesn't have mandatory fields, in that case, leave it empty. resource: portRef: trunk-create-minimal diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml b/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml index a7f8f0ac6..f8ada7749 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml +++ b/internal/controllers/trunk/tests/trunk-create-minimal/00-prerequisites.yaml @@ -41,4 +41,6 @@ spec: managementPolicy: managed resource: networkRef: trunk-create-minimal + addresses: + - subnetRef: trunk-create-minimal diff --git a/internal/controllers/trunk/tests/trunk-create-minimal/README.md b/internal/controllers/trunk/tests/trunk-create-minimal/README.md index 38bc702bb..49a8df75e 100644 --- a/internal/controllers/trunk/tests/trunk-create-minimal/README.md +++ b/internal/controllers/trunk/tests/trunk-create-minimal/README.md @@ -4,7 +4,7 @@ Create a minimal Trunk, that sets only the required fields, and verify that the observed state corresponds to the spec. -Also validate that the OpenStack resource uses the name of the ORC object when it is not specified. +Also validate that the OpenStack resource uses the name of the ORC object when no name is explicitly specified. ## Step 01 diff --git a/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml b/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml index b433114bc..9ecc8c4f0 100644 --- a/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml +++ b/internal/controllers/trunk/tests/trunk-dependency/00-create-resources-missing-deps.yaml @@ -31,13 +31,13 @@ metadata: name: trunk-dependency spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-dependency + addresses: + - subnetRef: trunk-dependency --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port @@ -45,13 +45,13 @@ metadata: name: trunk-dependency-no-secret spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-dependency + addresses: + - subnetRef: trunk-dependency --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -59,13 +59,11 @@ metadata: name: trunk-dependency-no-port spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: portRef: trunk-dependency-pending - # TODO(scaffolding): Add the necessary fields to create the resource --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -73,14 +71,12 @@ metadata: name: trunk-dependency-no-project spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: portRef: trunk-dependency projectRef: trunk-dependency - # TODO(scaffolding): Add the necessary fields to create the resource --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -88,10 +84,8 @@ metadata: name: trunk-dependency-no-secret spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: trunk-dependency managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: portRef: trunk-dependency-no-secret diff --git a/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml b/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml index 774d7765d..4df21bd81 100644 --- a/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml +++ b/internal/controllers/trunk/tests/trunk-dependency/01-create-dependencies.yaml @@ -11,13 +11,13 @@ metadata: name: trunk-dependency-pending spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-dependency + addresses: + - subnetRef: trunk-dependency --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project @@ -25,9 +25,8 @@ metadata: name: trunk-dependency spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + name: trunk-dependency diff --git a/internal/controllers/trunk/tests/trunk-dependency/README.md b/internal/controllers/trunk/tests/trunk-dependency/README.md index 80a0dc0bf..14d0a8ec4 100644 --- a/internal/controllers/trunk/tests/trunk-dependency/README.md +++ b/internal/controllers/trunk/tests/trunk-dependency/README.md @@ -6,7 +6,7 @@ Create Trunks referencing non-existing resources. Each Trunk is dependent on oth ## Step 01 -Create the missing dependencies and make and verify all the Trunks are available. +Create the missing dependencies and verify all the Trunks are available. ## Step 02 diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml index 66f67807a..074f801da 100644 --- a/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import-dependency/01-create-trap-resource.yaml @@ -2,29 +2,28 @@ apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Network metadata: - name: trunk-import-dependency-not-this-one + name: trunk-import-dependency spec: cloudCredentialsRef: - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: - projectRef: trunk-import-dependency-not-this-one + name: trunk-import-dependency --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Subnet metadata: - name: trunk-import-dependency-not-this-one + name: trunk-import-dependency spec: cloudCredentialsRef: - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: - cidr: 192.168.163.0/24 + networkRef: trunk-import-dependency ipVersion: 4 - networkRef: trunk-import-dependency-not-this-one - projectRef: trunk-import-dependency-not-this-one + cidr: 192.168.161.0/24 --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port @@ -32,27 +31,13 @@ metadata: name: trunk-import-dependency-not-this-one spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: - networkRef: trunk-import-dependency-not-this-one - projectRef: trunk-import-dependency-not-this-one ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Project -metadata: - name: trunk-import-dependency-not-this-one -spec: - cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + networkRef: trunk-import-dependency + addresses: + - subnetRef: trunk-import-dependency --- # This `trunk-import-dependency-not-this-one` should not be picked by the import filter apiVersion: openstack.k-orc.cloud/v1alpha1 @@ -61,11 +46,9 @@ metadata: name: trunk-import-dependency-not-this-one spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: portRef: trunk-import-dependency-not-this-one - projectRef: trunk-import-dependency-not-this-one - # TODO(scaffolding): Add the necessary fields to create the resource + description: Trunk trunk-import-dependency-not-this-one from "trunk-import-dependency" test diff --git a/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml b/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml index 85f3c7a54..7530c3f90 100644 --- a/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import-dependency/02-create-resource.yaml @@ -1,45 +1,17 @@ --- apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Network -metadata: - name: trunk-import-dependency-external -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - projectRef: trunk-import-dependency-external ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 -kind: Subnet -metadata: - name: trunk-import-dependency-external -spec: - cloudCredentialsRef: - cloudName: openstack-admin - secretName: openstack-clouds - managementPolicy: managed - resource: - cidr: 192.168.164.0/24 - ipVersion: 4 - networkRef: trunk-import-dependency-external - projectRef: trunk-import-dependency-external ---- -apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port metadata: name: trunk-import-dependency-external spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created - cloudName: openstack-admin + cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: - networkRef: trunk-import-dependency-external - projectRef: trunk-import-dependency-external + networkRef: trunk-import-dependency + addresses: + - subnetRef: trunk-import-dependency --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Project @@ -47,12 +19,11 @@ metadata: name: trunk-import-dependency-external spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource - resource: {} + resource: + name: trunk-import-dependency-external --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -60,11 +31,10 @@ metadata: name: trunk-import-dependency-external spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack-admin secretName: openstack-clouds managementPolicy: managed resource: portRef: trunk-import-dependency-external projectRef: trunk-import-dependency-external - # TODO(scaffolding): Add the necessary fields to create the resource + description: Trunk trunk-import-dependency-external from "trunk-import-dependency" test diff --git a/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml b/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml index 942809617..664ac9638 100644 --- a/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml +++ b/internal/controllers/trunk/tests/trunk-import-error/00-create-resources.yaml @@ -21,9 +21,9 @@ spec: secretName: openstack-clouds managementPolicy: managed resource: - cidr: 192.168.161.0/24 - ipVersion: 4 networkRef: trunk-import-error + ipVersion: 4 + cidr: 192.168.157.0/24 --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port @@ -31,13 +31,13 @@ metadata: name: trunk-import-error-1 spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-import-error + addresses: + - subnetRef: trunk-import-error --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port @@ -45,13 +45,13 @@ metadata: name: trunk-import-error-2 spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-import-error + addresses: + - subnetRef: trunk-import-error --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -59,14 +59,12 @@ metadata: name: trunk-import-error-external-1 spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: description: Trunk from "import error" test portRef: trunk-import-error-1 - # TODO(scaffolding): add any required field --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -74,11 +72,9 @@ metadata: name: trunk-import-error-external-2 spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: description: Trunk from "import error" test portRef: trunk-import-error-2 - # TODO(scaffolding): add any required field diff --git a/internal/controllers/trunk/tests/trunk-import/00-assert.yaml b/internal/controllers/trunk/tests/trunk-import/00-assert.yaml index 4ee876cb5..26ad45e58 100644 --- a/internal/controllers/trunk/tests/trunk-import/00-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-import/00-assert.yaml @@ -6,10 +6,10 @@ metadata: status: conditions: - type: Available - message: Waiting for OpenStack resource to be created externally + message: Waiting for Port/trunk-import to be created status: "False" reason: Progressing - type: Progressing - message: Waiting for OpenStack resource to be created externally + message: Waiting for Port/trunk-import to be created status: "True" reason: Progressing diff --git a/internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml b/internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml index 787ad1312..0787dae71 100644 --- a/internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import/00-import-resource.yaml @@ -12,4 +12,4 @@ spec: filter: name: trunk-import-external description: Trunk trunk-import-external from "trunk-import" test - # TODO(scaffolding): Add all fields supported by the filter + portRef: trunk-import diff --git a/internal/controllers/trunk/tests/trunk-import/01-assert.yaml b/internal/controllers/trunk/tests/trunk-import/01-assert.yaml index ed077e4ba..1dcdcf1fd 100644 --- a/internal/controllers/trunk/tests/trunk-import/01-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-import/01-assert.yaml @@ -15,8 +15,7 @@ status: reason: Success resource: name: trunk-import-external-not-this-one - description: Trunk trunk-import-external from "trunk-import" test - # TODO(scaffolding): Add fields necessary to match filter + description: Trunk trunk-import-external-not-this-one from "trunk-import" test --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -25,10 +24,10 @@ metadata: status: conditions: - type: Available - message: Waiting for OpenStack resource to be created externally + message: Waiting for Port/trunk-import to be created status: "False" reason: Progressing - type: Progressing - message: Waiting for OpenStack resource to be created externally + message: Waiting for Port/trunk-import to be created status: "True" reason: Progressing diff --git a/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml b/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml index f71879621..f900b8ba9 100644 --- a/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import/01-create-trap-resource.yaml @@ -31,13 +31,13 @@ metadata: name: trunk-import-external-not-this-one spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-import + addresses: + - subnetRef: trunk-import --- # This `trunk-import-external-not-this-one` resource serves two purposes: # - ensure that we can successfully create another resource which name is a substring of it (i.e. it's not being adopted) @@ -48,11 +48,9 @@ metadata: name: trunk-import-external-not-this-one spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: - description: Trunk trunk-import-external from "trunk-import" test + description: Trunk trunk-import-external-not-this-one from "trunk-import" test portRef: trunk-import-external-not-this-one - # TODO(scaffolding): Add fields necessary to match filter diff --git a/internal/controllers/trunk/tests/trunk-import/02-assert.yaml b/internal/controllers/trunk/tests/trunk-import/02-assert.yaml index 607841b87..9bd9c508c 100644 --- a/internal/controllers/trunk/tests/trunk-import/02-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-import/02-assert.yaml @@ -30,4 +30,3 @@ status: resource: name: trunk-import-external description: Trunk trunk-import-external from "trunk-import" test - # TODO(scaffolding): Add all fields the resource supports diff --git a/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml b/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml index f9404bac2..21ab9d8be 100644 --- a/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-import/02-create-resource.yaml @@ -5,13 +5,13 @@ metadata: name: trunk-import spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Add the necessary fields to create the resource resource: networkRef: trunk-import + addresses: + - subnetRef: trunk-import --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Trunk @@ -19,11 +19,9 @@ metadata: name: trunk-import-external spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created cloudName: openstack secretName: openstack-clouds managementPolicy: managed resource: description: Trunk trunk-import-external from "trunk-import" test portRef: trunk-import - # TODO(scaffolding): Add fields necessary to match filter diff --git a/internal/controllers/trunk/tests/trunk-import/README.md b/internal/controllers/trunk/tests/trunk-import/README.md index e8a586cc7..a0d99d2ce 100644 --- a/internal/controllers/trunk/tests/trunk-import/README.md +++ b/internal/controllers/trunk/tests/trunk-import/README.md @@ -2,16 +2,16 @@ ## Step 00 -Import a trunk, matching all of the available filter's fields, and verify it is waiting for the external resource to be created. +Import a trunk that matches all fields in the filter, and verify it is waiting for the external resource to be created. ## Step 01 -Create a trunk which name is a superstring of the one specified in the import filter, and otherwise matching the filter, and verify that it's not being imported. +Create a trunk whose name is a superstring of the one specified in the import filter, otherwise matching the filter, and verify that it's not being imported. ## Step 02 Create a trunk matching the filter and verify that the observed status on the imported trunk corresponds to the spec of the created trunk. -Also verify that the created trunk didn't adopt the one which name is a superstring of it. +Also, confirm that it does not adopt any trunk whose name is a superstring of its own. ## Reference diff --git a/internal/controllers/trunk/tests/trunk-update/00-assert.yaml b/internal/controllers/trunk/tests/trunk-update/00-assert.yaml index f241702a3..92a898175 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-assert.yaml @@ -16,7 +16,6 @@ metadata: status: resource: name: trunk-update - # TODO(scaffolding): Add matches for more fields conditions: - type: Available status: "True" diff --git a/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml index 34494cd1e..99de2965d 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-minimal-resource.yaml @@ -5,11 +5,8 @@ metadata: name: trunk-update spec: cloudCredentialsRef: - # TODO(scaffolding): Use openstack-admin if the resouce needs admin credentials to be created or updated cloudName: openstack secretName: openstack-clouds managementPolicy: managed - # TODO(scaffolding): Only add the mandatory fields. It's possible the resource - # doesn't have mandatory fields, in that case, leave it empty. resource: portRef: trunk-update diff --git a/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml index 868916bb3..db6b11404 100644 --- a/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml +++ b/internal/controllers/trunk/tests/trunk-update/00-prerequisites.yaml @@ -30,6 +30,7 @@ spec: networkRef: trunk-update cidr: 192.168.158.0/24 ipVersion: 4 + enableDHCP: false --- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Port @@ -42,4 +43,5 @@ spec: managementPolicy: managed resource: networkRef: trunk-update - \ No newline at end of file + addresses: + - subnetRef: trunk-update diff --git a/internal/controllers/trunk/tests/trunk-update/01-assert.yaml b/internal/controllers/trunk/tests/trunk-update/01-assert.yaml index 0750bc9ce..7b644691c 100644 --- a/internal/controllers/trunk/tests/trunk-update/01-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-update/01-assert.yaml @@ -7,7 +7,6 @@ status: resource: name: trunk-update-updated description: trunk-update-updated - # TODO(scaffolding): match all fields that were modified conditions: - type: Available status: "True" diff --git a/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml b/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml index 4d9937eb0..d15f198a6 100644 --- a/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml +++ b/internal/controllers/trunk/tests/trunk-update/01-updated-resource.yaml @@ -7,4 +7,3 @@ spec: resource: name: trunk-update-updated description: trunk-update-updated - # TODO(scaffolding): update all mutable fields diff --git a/internal/controllers/trunk/tests/trunk-update/02-assert.yaml b/internal/controllers/trunk/tests/trunk-update/02-assert.yaml index 33096164f..92a898175 100644 --- a/internal/controllers/trunk/tests/trunk-update/02-assert.yaml +++ b/internal/controllers/trunk/tests/trunk-update/02-assert.yaml @@ -16,7 +16,6 @@ metadata: status: resource: name: trunk-update - # TODO(scaffolding): validate that updated fields were all reverted to their original value conditions: - type: Available status: "True" diff --git a/internal/controllers/trunk/tests/trunk-update/03-delete-resources.yaml b/internal/controllers/trunk/tests/trunk-update/03-delete-resources.yaml new file mode 100644 index 000000000..4473d36d9 --- /dev/null +++ b/internal/controllers/trunk/tests/trunk-update/03-delete-resources.yaml @@ -0,0 +1,15 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Trunk + name: trunk-update + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Port + name: trunk-update + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Subnet + name: trunk-update + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Network + name: trunk-update \ No newline at end of file diff --git a/internal/controllers/trunk/tests/trunk-update/README.md b/internal/controllers/trunk/tests/trunk-update/README.md index 90337704d..a7040db70 100644 --- a/internal/controllers/trunk/tests/trunk-update/README.md +++ b/internal/controllers/trunk/tests/trunk-update/README.md @@ -10,7 +10,7 @@ Update all mutable fields. ## Step 02 -Revert the resource to its original value and verify the resulting object is similar to when if was first created. +Revert the resource to its original value and verify that the resulting object matches its state when first created. ## Reference diff --git a/internal/controllers/volume/tests/volume-create-full/00-assert.yaml b/internal/controllers/volume/tests/volume-create-full/00-assert.yaml index e456f3d91..d45fe3795 100644 --- a/internal/controllers/volume/tests/volume-create-full/00-assert.yaml +++ b/internal/controllers/volume/tests/volume-create-full/00-assert.yaml @@ -24,3 +24,22 @@ status: - type: Progressing status: "False" reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Volume + name: volume-create-full + ref: volume + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: VolumeType + name: volume-create-full + ref: volumetype +assertAll: + - celExpr: "volume.status.id != ''" + - celExpr: "volume.status.resource.volumeType == volumetype.status.resource.name" + - celExpr: "volume.status.resource.tenantID != ''" + - celExpr: "volume.status.resource.userID != ''" + - celExpr: "volume.status.resource.createdAt != ''" + - celExpr: "volume.status.resource.updatedAt != ''" diff --git a/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml b/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml index 644eacd37..dde63758c 100644 --- a/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml +++ b/internal/controllers/volume/tests/volume-create-minimal/00-assert.yaml @@ -27,5 +27,11 @@ resourceRefs: name: volume-create-minimal ref: volume assertAll: + - celExpr: "volume.status.id != ''" + - celExpr: "volume.status.resource.tenantID != ''" + - celExpr: "volume.status.resource.userID != ''" + - celExpr: "volume.status.resource.volumeType != ''" + - celExpr: "volume.status.resource.createdAt != ''" + - celExpr: "volume.status.resource.updatedAt != ''" - celExpr: "!has(volume.status.resource.description)" - celExpr: "!has(volume.status.resource.metadata)" diff --git a/internal/controllers/volume/tests/volume-dependency/03-assert.yaml b/internal/controllers/volume/tests/volume-dependency/03-assert.yaml index e69de29bb..05806c37a 100644 --- a/internal/controllers/volume/tests/volume-dependency/03-assert.yaml +++ b/internal/controllers/volume/tests/volume-dependency/03-assert.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +# Dependencies that were prevented deletion before should now be gone +- script: "! kubectl get volumetype volume-dependency --namespace $NAMESPACE" + skipLogOutput: true +- script: "! kubectl get secret volume-dependency --namespace $NAMESPACE" + skipLogOutput: true diff --git a/internal/controllers/volume/tests/volume-import/02-assert.yaml b/internal/controllers/volume/tests/volume-import/02-assert.yaml index dcd93813f..428600cb2 100644 --- a/internal/controllers/volume/tests/volume-import/02-assert.yaml +++ b/internal/controllers/volume/tests/volume-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Volume + name: volume-import-external + ref: volume1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: Volume + name: volume-import-external-not-this-one + ref: volume2 +assertAll: + - celExpr: "volume1.status.id != volume2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: Volume metadata: diff --git a/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml b/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml index d15e7270e..03e8d8761 100644 --- a/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml +++ b/internal/controllers/volumetype/tests/volumetype-create-full/00-assert.yaml @@ -18,3 +18,13 @@ status: - type: Progressing status: "False" reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: VolumeType + name: volumetype-create-full + ref: volumetype +assertAll: + - celExpr: "volumetype.status.id != ''" diff --git a/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml b/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml index 4ccf48b54..803045489 100644 --- a/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml +++ b/internal/controllers/volumetype/tests/volumetype-create-minimal/00-assert.yaml @@ -14,3 +14,13 @@ status: - type: Progressing status: "False" reason: Success +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: VolumeType + name: volumetype-create-minimal + ref: volumetype +assertAll: + - celExpr: "volumetype.status.id != ''" diff --git a/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml b/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml index 3dbcc38b2..dd3b98c46 100644 --- a/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml +++ b/internal/controllers/volumetype/tests/volumetype-import/02-assert.yaml @@ -1,4 +1,18 @@ --- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +resourceRefs: + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: VolumeType + name: volumetype-import-external + ref: volumetype1 + - apiVersion: openstack.k-orc.cloud/v1alpha1 + kind: VolumeType + name: volumetype-import-external-not-this-one + ref: volumetype2 +assertAll: + - celExpr: "volumetype1.status.id != volumetype2.status.id" +--- apiVersion: openstack.k-orc.cloud/v1alpha1 kind: VolumeType metadata: diff --git a/internal/osclients/mock/trunk.go b/internal/osclients/mock/trunk.go index c70659adb..23717a269 100644 --- a/internal/osclients/mock/trunk.go +++ b/internal/osclients/mock/trunk.go @@ -129,5 +129,3 @@ func (mr *MockTrunkClientMockRecorder) UpdateTrunk(ctx, id, opts any) *gomock.Ca mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTrunk", reflect.TypeOf((*MockTrunkClient)(nil).UpdateTrunk), ctx, id, opts) } - - diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go index c722a5734..f7ddd8cc5 100644 --- a/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkfilter.go @@ -25,10 +25,13 @@ import ( // TrunkFilterApplyConfiguration represents a declarative configuration of the TrunkFilter type for use // with apply. type TrunkFilterApplyConfiguration struct { - Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` - ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` + Status *string `json:"status,omitempty"` + AdminStateUp *bool `json:"adminStateUp,omitempty"` + FilterByNeutronTagsApplyConfiguration `json:",inline"` } // TrunkFilterApplyConfiguration constructs a declarative configuration of the TrunkFilter type for use with @@ -48,7 +51,7 @@ func (b *TrunkFilterApplyConfiguration) WithName(value apiv1alpha1.OpenStackName // WithDescription sets the Description field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Description field is set to the value of the last call. -func (b *TrunkFilterApplyConfiguration) WithDescription(value string) *TrunkFilterApplyConfiguration { +func (b *TrunkFilterApplyConfiguration) WithDescription(value apiv1alpha1.NeutronDescription) *TrunkFilterApplyConfiguration { b.Description = &value return b } @@ -68,3 +71,59 @@ func (b *TrunkFilterApplyConfiguration) WithProjectRef(value apiv1alpha1.Kuberne b.ProjectRef = &value return b } + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *TrunkFilterApplyConfiguration) WithStatus(value string) *TrunkFilterApplyConfiguration { + b.Status = &value + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *TrunkFilterApplyConfiguration) WithAdminStateUp(value bool) *TrunkFilterApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *TrunkFilterApplyConfiguration) WithTags(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.Tags = append(b.FilterByNeutronTagsApplyConfiguration.Tags, values[i]) + } + return b +} + +// WithTagsAny adds the given value to the TagsAny field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the TagsAny field. +func (b *TrunkFilterApplyConfiguration) WithTagsAny(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.TagsAny = append(b.FilterByNeutronTagsApplyConfiguration.TagsAny, values[i]) + } + return b +} + +// WithNotTags adds the given value to the NotTags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the NotTags field. +func (b *TrunkFilterApplyConfiguration) WithNotTags(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.NotTags = append(b.FilterByNeutronTagsApplyConfiguration.NotTags, values[i]) + } + return b +} + +// WithNotTagsAny adds the given value to the NotTagsAny field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the NotTagsAny field. +func (b *TrunkFilterApplyConfiguration) WithNotTagsAny(values ...apiv1alpha1.NeutronTag) *TrunkFilterApplyConfiguration { + for i := range values { + b.FilterByNeutronTagsApplyConfiguration.NotTagsAny = append(b.FilterByNeutronTagsApplyConfiguration.NotTagsAny, values[i]) + } + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go index 5a67e41b8..bc98141dd 100644 --- a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcespec.go @@ -25,10 +25,13 @@ import ( // TrunkResourceSpecApplyConfiguration represents a declarative configuration of the TrunkResourceSpec type for use // with apply. type TrunkResourceSpecApplyConfiguration struct { - Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` - ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` + AdminStateUp *bool `json:"adminStateUp,omitempty"` + Subports []TrunkSubportSpecApplyConfiguration `json:"subports,omitempty"` + Tags []apiv1alpha1.NeutronTag `json:"tags,omitempty"` } // TrunkResourceSpecApplyConfiguration constructs a declarative configuration of the TrunkResourceSpec type for use with @@ -48,7 +51,7 @@ func (b *TrunkResourceSpecApplyConfiguration) WithName(value apiv1alpha1.OpenSta // WithDescription sets the Description field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Description field is set to the value of the last call. -func (b *TrunkResourceSpecApplyConfiguration) WithDescription(value string) *TrunkResourceSpecApplyConfiguration { +func (b *TrunkResourceSpecApplyConfiguration) WithDescription(value apiv1alpha1.NeutronDescription) *TrunkResourceSpecApplyConfiguration { b.Description = &value return b } @@ -68,3 +71,34 @@ func (b *TrunkResourceSpecApplyConfiguration) WithProjectRef(value apiv1alpha1.K b.ProjectRef = &value return b } + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *TrunkResourceSpecApplyConfiguration) WithAdminStateUp(value bool) *TrunkResourceSpecApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithSubports adds the given value to the Subports field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Subports field. +func (b *TrunkResourceSpecApplyConfiguration) WithSubports(values ...*TrunkSubportSpecApplyConfiguration) *TrunkResourceSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSubports") + } + b.Subports = append(b.Subports, *values[i]) + } + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *TrunkResourceSpecApplyConfiguration) WithTags(values ...apiv1alpha1.NeutronTag) *TrunkResourceSpecApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go index 88a13c05c..18f50b6ac 100644 --- a/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunkresourcestatus.go @@ -18,13 +18,23 @@ limitations under the License. package v1alpha1 +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + // TrunkResourceStatusApplyConfiguration represents a declarative configuration of the TrunkResourceStatus type for use // with apply. type TrunkResourceStatusApplyConfiguration struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - PortID *string `json:"portID,omitempty"` - ProjectID *string `json:"projectID,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + PortID *string `json:"portID,omitempty"` + ProjectID *string `json:"projectID,omitempty"` + TenantID *string `json:"tenantID,omitempty"` + Status *string `json:"status,omitempty"` + Tags []string `json:"tags,omitempty"` + NeutronStatusMetadataApplyConfiguration `json:",inline"` + AdminStateUp *bool `json:"adminStateUp,omitempty"` + Subports []TrunkSubportStatusApplyConfiguration `json:"subports,omitempty"` } // TrunkResourceStatusApplyConfiguration constructs a declarative configuration of the TrunkResourceStatus type for use with @@ -64,3 +74,74 @@ func (b *TrunkResourceStatusApplyConfiguration) WithProjectID(value string) *Tru b.ProjectID = &value return b } + +// WithTenantID sets the TenantID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TenantID field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithTenantID(value string) *TrunkResourceStatusApplyConfiguration { + b.TenantID = &value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithStatus(value string) *TrunkResourceStatusApplyConfiguration { + b.Status = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *TrunkResourceStatusApplyConfiguration) WithTags(values ...string) *TrunkResourceStatusApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} + +// WithCreatedAt sets the CreatedAt field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreatedAt field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithCreatedAt(value v1.Time) *TrunkResourceStatusApplyConfiguration { + b.NeutronStatusMetadataApplyConfiguration.CreatedAt = &value + return b +} + +// WithUpdatedAt sets the UpdatedAt field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UpdatedAt field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithUpdatedAt(value v1.Time) *TrunkResourceStatusApplyConfiguration { + b.NeutronStatusMetadataApplyConfiguration.UpdatedAt = &value + return b +} + +// WithRevisionNumber sets the RevisionNumber field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RevisionNumber field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithRevisionNumber(value int64) *TrunkResourceStatusApplyConfiguration { + b.NeutronStatusMetadataApplyConfiguration.RevisionNumber = &value + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *TrunkResourceStatusApplyConfiguration) WithAdminStateUp(value bool) *TrunkResourceStatusApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithSubports adds the given value to the Subports field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Subports field. +func (b *TrunkResourceStatusApplyConfiguration) WithSubports(values ...*TrunkSubportStatusApplyConfiguration) *TrunkResourceStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSubports") + } + b.Subports = append(b.Subports, *values[i]) + } + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunksubportspec.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunksubportspec.go new file mode 100644 index 000000000..129b156d9 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunksubportspec.go @@ -0,0 +1,61 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" +) + +// TrunkSubportSpecApplyConfiguration represents a declarative configuration of the TrunkSubportSpec type for use +// with apply. +type TrunkSubportSpecApplyConfiguration struct { + PortRef *apiv1alpha1.KubernetesNameRef `json:"portRef,omitempty"` + SegmentationID *int32 `json:"segmentationID,omitempty"` + SegmentationType *string `json:"segmentationType,omitempty"` +} + +// TrunkSubportSpecApplyConfiguration constructs a declarative configuration of the TrunkSubportSpec type for use with +// apply. +func TrunkSubportSpec() *TrunkSubportSpecApplyConfiguration { + return &TrunkSubportSpecApplyConfiguration{} +} + +// WithPortRef sets the PortRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PortRef field is set to the value of the last call. +func (b *TrunkSubportSpecApplyConfiguration) WithPortRef(value apiv1alpha1.KubernetesNameRef) *TrunkSubportSpecApplyConfiguration { + b.PortRef = &value + return b +} + +// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationID field is set to the value of the last call. +func (b *TrunkSubportSpecApplyConfiguration) WithSegmentationID(value int32) *TrunkSubportSpecApplyConfiguration { + b.SegmentationID = &value + return b +} + +// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationType field is set to the value of the last call. +func (b *TrunkSubportSpecApplyConfiguration) WithSegmentationType(value string) *TrunkSubportSpecApplyConfiguration { + b.SegmentationType = &value + return b +} diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/trunksubportstatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/trunksubportstatus.go new file mode 100644 index 000000000..cabc50578 --- /dev/null +++ b/pkg/clients/applyconfiguration/api/v1alpha1/trunksubportstatus.go @@ -0,0 +1,57 @@ +/* +Copyright 2025 The ORC Authors. + +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TrunkSubportStatusApplyConfiguration represents a declarative configuration of the TrunkSubportStatus type for use +// with apply. +type TrunkSubportStatusApplyConfiguration struct { + PortID *string `json:"portID,omitempty"` + SegmentationID *int32 `json:"segmentationID,omitempty"` + SegmentationType *string `json:"segmentationType,omitempty"` +} + +// TrunkSubportStatusApplyConfiguration constructs a declarative configuration of the TrunkSubportStatus type for use with +// apply. +func TrunkSubportStatus() *TrunkSubportStatusApplyConfiguration { + return &TrunkSubportStatusApplyConfiguration{} +} + +// WithPortID sets the PortID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PortID field is set to the value of the last call. +func (b *TrunkSubportStatusApplyConfiguration) WithPortID(value string) *TrunkSubportStatusApplyConfiguration { + b.PortID = &value + return b +} + +// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationID field is set to the value of the last call. +func (b *TrunkSubportStatusApplyConfiguration) WithSegmentationID(value int32) *TrunkSubportStatusApplyConfiguration { + b.SegmentationID = &value + return b +} + +// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationType field is set to the value of the last call. +func (b *TrunkSubportStatusApplyConfiguration) WithSegmentationType(value string) *TrunkSubportStatusApplyConfiguration { + b.SegmentationType = &value + return b +} diff --git a/pkg/clients/applyconfiguration/internal/internal.go b/pkg/clients/applyconfiguration/internal/internal.go index 385381882..047f9030c 100644 --- a/pkg/clients/applyconfiguration/internal/internal.go +++ b/pkg/clients/applyconfiguration/internal/internal.go @@ -2873,18 +2873,48 @@ var schemaYAML = typed.YAMLObject(`types: - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkFilter map: fields: + - name: adminStateUp + type: + scalar: boolean - name: description type: scalar: string - name: name type: scalar: string + - name: notTags + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: notTagsAny + type: + list: + elementType: + scalar: string + elementRelationship: associative - name: portRef type: scalar: string - name: projectRef type: scalar: string + - name: status + type: + scalar: string + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: tagsAny + type: + list: + elementType: + scalar: string + elementRelationship: associative - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkImport map: fields: @@ -2897,6 +2927,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceSpec map: fields: + - name: adminStateUp + type: + scalar: boolean - name: description type: scalar: string @@ -2909,9 +2942,27 @@ var schemaYAML = typed.YAMLObject(`types: - name: projectRef type: scalar: string + - name: subports + type: + list: + elementType: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSubportSpec + elementRelationship: atomic + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: associative - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceStatus map: fields: + - name: adminStateUp + type: + scalar: boolean + - name: createdAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time - name: description type: scalar: string @@ -2924,6 +2975,30 @@ var schemaYAML = typed.YAMLObject(`types: - name: projectID type: scalar: string + - name: revisionNumber + type: + scalar: numeric + - name: status + type: + scalar: string + - name: subports + type: + list: + elementType: + namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSubportStatus + elementRelationship: atomic + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: tenantID + type: + scalar: string + - name: updatedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSpec map: fields: @@ -2960,6 +3035,33 @@ var schemaYAML = typed.YAMLObject(`types: - name: resource type: namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkResourceStatus +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSubportSpec + map: + fields: + - name: portRef + type: + scalar: string + default: "" + - name: segmentationID + type: + scalar: numeric + default: 0 + - name: segmentationType + type: + scalar: string + default: "" +- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.TrunkSubportStatus + map: + fields: + - name: portID + type: + scalar: string + - name: segmentationID + type: + scalar: numeric + - name: segmentationType + type: + scalar: string - name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.UserDataSpec map: fields: diff --git a/pkg/clients/applyconfiguration/utils.go b/pkg/clients/applyconfiguration/utils.go index 78a0e4142..94630c404 100644 --- a/pkg/clients/applyconfiguration/utils.go +++ b/pkg/clients/applyconfiguration/utils.go @@ -350,6 +350,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.TrunkSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("TrunkStatus"): return &apiv1alpha1.TrunkStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkSubportSpec"): + return &apiv1alpha1.TrunkSubportSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TrunkSubportStatus"): + return &apiv1alpha1.TrunkSubportStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("UserDataSpec"): return &apiv1alpha1.UserDataSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Volume"): diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md index bf77bbe5f..2826fb166 100644 --- a/website/docs/crd-reference.md +++ b/website/docs/crd-reference.md @@ -421,6 +421,7 @@ _Appears in:_ - [RouterFilter](#routerfilter) - [SecurityGroupFilter](#securitygroupfilter) - [SubnetFilter](#subnetfilter) +- [TrunkFilter](#trunkfilter) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -1636,6 +1637,7 @@ _Appears in:_ - [SubnetResourceSpec](#subnetresourcespec) - [TrunkFilter](#trunkfilter) - [TrunkResourceSpec](#trunkresourcespec) +- [TrunkSubportSpec](#trunksubportspec) - [UserDataSpec](#userdataspec) - [VolumeResourceSpec](#volumeresourcespec) @@ -1924,6 +1926,8 @@ _Appears in:_ - [SecurityGroupRule](#securitygrouprule) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) +- [TrunkFilter](#trunkfilter) +- [TrunkResourceSpec](#trunkresourcespec) @@ -1941,6 +1945,7 @@ _Appears in:_ - [PortResourceStatus](#portresourcestatus) - [SecurityGroupResourceStatus](#securitygroupresourcestatus) - [SubnetResourceStatus](#subnetresourcestatus) +- [TrunkResourceStatus](#trunkresourcestatus) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -1974,6 +1979,8 @@ _Appears in:_ - [SecurityGroupResourceSpec](#securitygroupresourcespec) - [SubnetFilter](#subnetfilter) - [SubnetResourceSpec](#subnetresourcespec) +- [TrunkFilter](#trunkfilter) +- [TrunkResourceSpec](#trunkresourcespec) @@ -3819,9 +3826,15 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `name` _[OpenStackName](#openstackname)_ | name of the existing resource | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
| -| `description` _string_ | description of the existing resource | | MaxLength: 255
MinLength: 1
| +| `description` _[NeutronDescription](#neutrondescription)_ | description of the existing resource | | MaxLength: 255
MinLength: 1
| | `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which this resource is associated with. | | MaxLength: 253
MinLength: 1
| | `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project which this resource is associated with. | | MaxLength: 253
MinLength: 1
| +| `status` _string_ | status indicates whether the trunk is currently operational. Possible values include
`ACTIVE', `DOWN', `BUILD', `DEGRADED' or `ERROR'. Plug-ins might define additional values. | | | +| `adminStateUp` _boolean_ | adminStateUp is the administrative state of the trunk. | | | +| `tags` _[NeutronTag](#neutrontag) array_ | tags is a list of tags to filter by. If specified, the resource must
have all of the tags specified to be included in the result. | | MaxItems: 64
ListType: set
MaxLength: 255
MinLength: 1
| +| `tagsAny` _[NeutronTag](#neutrontag) array_ | tagsAny is a list of tags to filter by. If specified, the resource
must have at least one of the tags specified to be included in the
result. | | MaxItems: 64
ListType: set
MaxLength: 255
MinLength: 1
| +| `notTags` _[NeutronTag](#neutrontag) array_ | notTags is a list of tags to filter by. If specified, resources which
contain all of the given tags will be excluded from the result. | | MaxItems: 64
ListType: set
MaxLength: 255
MinLength: 1
| +| `notTagsAny` _[NeutronTag](#neutrontag) array_ | notTagsAny is a list of tags to filter by. If specified, resources
which contain any of the given tags will be excluded from the result. | | MaxItems: 64
ListType: set
MaxLength: 255
MinLength: 1
| #### TrunkImport @@ -3858,9 +3871,12 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | | `name` _[OpenStackName](#openstackname)_ | name will be the name of the created resource. If not specified, the
name of the ORC object will be used. | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
| -| `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 255
MinLength: 1
| -| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which this resource is associated with. | | MaxLength: 253
MinLength: 1
| -| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project which this resource is associated with. | | MaxLength: 253
MinLength: 1
| +| `description` _[NeutronDescription](#neutrondescription)_ | description is a human-readable description for the resource. | | MaxLength: 255
MinLength: 1
| +| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port which this resource is associated with. | | MaxLength: 253
MinLength: 1
XValidation: rule="self == oldSelf",message="portRef is immutable"
| +| `projectRef` _[KubernetesNameRef](#kubernetesnameref)_ | projectRef is a reference to the ORC Project which this resource is associated with. | | MaxLength: 253
MinLength: 1
XValidation: rule="self == oldSelf",message="projectRef is immutable"
| +| `adminStateUp` _boolean_ | adminStateUp is the administrative state of the trunk. If false (down),
the trunk does not forward packets. | | | +| `subports` _[TrunkSubportSpec](#trunksubportspec) array_ | subports is the list of ports to attach to the trunk.
NOTE: ORC currently does not implement reconcile logic for subport updates
(Neutron uses dedicated add/remove subport APIs). This field is immutable
until that behavior is implemented in the controller. | | MaxItems: 1024
ListType: atomic
XValidation: rule="self == oldSelf",message="subports is immutable"
| +| `tags` _[NeutronTag](#neutrontag) array_ | tags is a list of Neutron tags to apply to the trunk.
NOTE: ORC does not currently reconcile tag updates for Trunk. | | MaxItems: 64
ListType: set
MaxLength: 255
MinLength: 1
XValidation: rule="self == oldSelf",message="tags is immutable"
| #### TrunkResourceStatus @@ -3880,6 +3896,14 @@ _Appears in:_ | `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 1024
| | `portID` _string_ | portID is the ID of the Port to which the resource is associated. | | MaxLength: 1024
| | `projectID` _string_ | projectID is the ID of the Project to which the resource is associated. | | MaxLength: 1024
| +| `tenantID` _string_ | tenantID is the project owner of the trunk (alias of projectID in some deployments). | | MaxLength: 1024
| +| `status` _string_ | status indicates whether the trunk is currently operational. | | MaxLength: 1024
| +| `tags` _string array_ | tags is the list of tags on the resource. | | MaxItems: 64
ListType: atomic
items:MaxLength: 1024
| +| `createdAt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#time-v1-meta)_ | createdAt shows the date and time when the resource was created. The date and time stamp format is ISO 8601 | | | +| `updatedAt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#time-v1-meta)_ | updatedAt shows the date and time when the resource was updated. The date and time stamp format is ISO 8601 | | | +| `revisionNumber` _integer_ | revisionNumber optionally set via extensions/standard-attr-revisions | | | +| `adminStateUp` _boolean_ | adminStateUp is the administrative state of the trunk. | | | +| `subports` _[TrunkSubportStatus](#trunksubportstatus) array_ | subports is a list of ports associated with the trunk. | | MaxItems: 1024
ListType: atomic
| #### TrunkSpec @@ -3897,7 +3921,7 @@ _Appears in:_ | --- | --- | --- | --- | | `import` _[TrunkImport](#trunkimport)_ | import refers to an existing OpenStack resource which will be imported instead of
creating a new one. | | MaxProperties: 1
MinProperties: 1
| | `resource` _[TrunkResourceSpec](#trunkresourcespec)_ | resource specifies the desired state of the resource.
resource may not be specified if the management policy is `unmanaged`.
resource must be specified if the management policy is `managed`. | | | -| `managementPolicy` _[ManagementPolicy](#managementpolicy)_ | managementPolicy defines how ORC will treat the object. Valid values are
`managed`: ORC will create, update, and delete the resource; `unmanaged`:
ORC will import an existing resource, and will not apply updates to it or
delete it. | managed | Enum: [managed unmanaged]
| +| `managementPolicy` _[ManagementPolicy](#managementpolicy)_ | managementPolicy defines how ORC will treat the object. Valid values are
`managed`: ORC will create, update, and delete the resource; `unmanaged`:
ORC will import an existing resource, and will not apply updates to it or
delete it. | managed | Enum: [managed unmanaged]
XValidation: rule="self == oldSelf",message="managementPolicy is immutable"
| | `managedOptions` _[ManagedOptions](#managedoptions)_ | managedOptions specifies options which may be applied to managed objects. | | | | `cloudCredentialsRef` _[CloudCredentialsReference](#cloudcredentialsreference)_ | cloudCredentialsRef points to a secret containing OpenStack credentials | | | @@ -3915,11 +3939,49 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#condition-v1-meta) array_ | conditions represents the observed status of the object.
Known .status.conditions.type are: "Available", "Progressing"
Available represents the availability of the OpenStack resource. If it is
true then the resource is ready for use.
Progressing indicates whether the controller is still attempting to
reconcile the current state of the OpenStack resource to the desired
state. Progressing will be False either because the desired state has
been achieved, or because some terminal error prevents it from ever being
achieved and the controller is no longer attempting to reconcile. If
Progressing is True, an observer waiting on the resource should continue
to wait. | | MaxItems: 32
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#condition-v1-meta) array_ | conditions represents the observed status of the object.
Known .status.conditions.type are: "Available", "Progressing"
Available represents the availability of the OpenStack resource. If it is
true then the resource is ready for use.
Progressing indicates whether the controller is still attempting to
reconcile the current state of the OpenStack resource to the desired
state. Progressing will be False either because the desired state has
been achieved, or because some terminal error prevents it from ever being
achieved and the controller is no longer attempting to reconcile. If
Progressing is True, an observer waiting on the resource should continue
to wait. | | MaxItems: 32
ListType: map
ListMapKey: type
PatchStrategy: merge
PatchMergeKey: type
| | `id` _string_ | id is the unique identifier of the OpenStack resource. | | | | `resource` _[TrunkResourceStatus](#trunkresourcestatus)_ | resource contains the observed state of the OpenStack resource. | | | +#### TrunkSubportSpec + + + +TrunkSubportSpec represents a subport to attach to a trunk. +It maps to gophercloud's trunks.Subport. + + + +_Appears in:_ +- [TrunkResourceSpec](#trunkresourcespec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `portRef` _[KubernetesNameRef](#kubernetesnameref)_ | portRef is a reference to the ORC Port that will be attached as a subport. | | MaxLength: 253
MinLength: 1
| +| `segmentationID` _integer_ | segmentationID is the segmentation ID for the subport (e.g. VLAN ID). | | Maximum: 4094
Minimum: 1
| +| `segmentationType` _string_ | segmentationType is the segmentation type for the subport (e.g. vlan). | | MaxLength: 255
MinLength: 1
| + + +#### TrunkSubportStatus + + + +TrunkSubportStatus represents an attached subport on a trunk. +It maps to gophercloud's trunks.Subport. + + + +_Appears in:_ +- [TrunkResourceStatus](#trunkresourcestatus) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `portID` _string_ | portID is the OpenStack ID of the Port attached as a subport. | | MaxLength: 1024
| +| `segmentationID` _integer_ | segmentationID is the segmentation ID for the subport (e.g. VLAN ID). | | | +| `segmentationType` _string_ | segmentationType is the segmentation type for the subport (e.g. vlan). | | MaxLength: 1024
| + + #### UserDataSpec diff --git a/website/docs/development/writing-tests.md b/website/docs/development/writing-tests.md index 0f8c0ccbd..0beeca6d8 100644 --- a/website/docs/development/writing-tests.md +++ b/website/docs/development/writing-tests.md @@ -36,7 +36,7 @@ can specify modules that you want to test by passing the package's path, separated by a blank space, for example: ```bash -TEST_PATHS="./internal/controller/server ./internal/controller/image" make test +TEST_PATHS="./internal/controllers/server ./internal/controllers/image" make test ``` ## E2E tests