From 6901189c4dc8e72bd14534d2c943e58bcd23c94e Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Mon, 12 Jan 2026 11:34:31 -0300 Subject: [PATCH 1/2] port: add propagationUplinkStatus field --- .github/workflows/e2e.yaml | 2 +- api/v1alpha1/port_types.go | 7 +++- api/v1alpha1/zz_generated.deepcopy.go | 10 +++--- cmd/models-schema/zz_generated.openapi.go | 7 ++++ .../bases/openstack.k-orc.cloud_ports.yaml | 5 +++ internal/controllers/port/actuator.go | 24 +++++++++---- internal/controllers/port/actuator_test.go | 28 +++++++++++++++ .../tests/port-create-full/00-assert.yaml | 1 + .../port-create-full/00-create-resource.yaml | 1 + .../tests/port-create-minimal/00-assert.yaml | 2 +- .../port/tests/port-update/00-assert.yaml | 6 ++-- .../port/tests/port-update/01-assert.yaml | 4 +-- .../port-update/01-updated-resource.yaml | 1 + .../port/tests/port-update/02-assert.yaml | 2 +- .../api/v1alpha1/portresourcespec.go | 35 ++++++++++++------- .../applyconfiguration/internal/internal.go | 3 ++ website/docs/crd-reference.md | 1 + 17 files changed, 107 insertions(+), 32 deletions(-) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index da07b7eee..3e8ef73d5 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -36,7 +36,7 @@ jobs: with: enable_workaround_docker_io: 'false' branch: ${{ matrix.openstack_version }} - enabled_services: "openstack-cli-server" + enabled_services: "openstack-cli-server,neutron-uplink-status-propagation" - name: Deploy a Kind Cluster uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab diff --git a/api/v1alpha1/port_types.go b/api/v1alpha1/port_types.go index 9e51d0153..70ac01628 100644 --- a/api/v1alpha1/port_types.go +++ b/api/v1alpha1/port_types.go @@ -185,6 +185,11 @@ type PortResourceSpec struct { // +kubebuilder:validation:MaxLength=36 // +optional HostID string `json:"hostID,omitempty"` + + // propagateUplinkStatus represents the uplink status propagation of + // the port. + // +optional + PropagateUplinkStatus *bool `json:"propagateUplinkStatus,omitempty"` } type PortResourceStatus struct { @@ -266,7 +271,7 @@ type PortResourceStatus struct { // propagateUplinkStatus represents the uplink status propagation of // the port. // +optional - PropagateUplinkStatus *bool `json:"propagateUplinkStatus,omitempty"` + PropagateUplinkStatus bool `json:"propagateUplinkStatus,omitempty"` // vnicType is the type of vNIC which this port is attached to. // +kubebuilder:validation:MaxLength:=64 diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 74fd9dcdf..3dc3ada57 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -2529,6 +2529,11 @@ func (in *PortResourceSpec) DeepCopyInto(out *PortResourceSpec) { *out = new(KubernetesNameRef) **out = **in } + if in.PropagateUplinkStatus != nil { + in, out := &in.PropagateUplinkStatus, &out.PropagateUplinkStatus + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortResourceSpec. @@ -2569,11 +2574,6 @@ func (in *PortResourceStatus) DeepCopyInto(out *PortResourceStatus) { *out = make([]string, len(*in)) copy(*out, *in) } - if in.PropagateUplinkStatus != nil { - in, out := &in.PropagateUplinkStatus, &out.PropagateUplinkStatus - *out = new(bool) - **out = **in - } if in.PortSecurityEnabled != nil { in, out := &in.PortSecurityEnabled, &out.PortSecurityEnabled *out = new(bool) diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go index 3b90d275e..1beb237d8 100644 --- a/cmd/models-schema/zz_generated.openapi.go +++ b/cmd/models-schema/zz_generated.openapi.go @@ -4918,6 +4918,13 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_PortResourceSpec(ref c Format: "", }, }, + "propagateUplinkStatus": { + SchemaProps: spec.SchemaProps{ + Description: "propagateUplinkStatus represents the uplink status propagation of the port.", + Type: []string{"boolean"}, + Format: "", + }, + }, }, Required: []string{"networkRef"}, }, diff --git a/config/crd/bases/openstack.k-orc.cloud_ports.yaml b/config/crd/bases/openstack.k-orc.cloud_ports.yaml index c14173d71..ea3135633 100644 --- a/config/crd/bases/openstack.k-orc.cloud_ports.yaml +++ b/config/crd/bases/openstack.k-orc.cloud_ports.yaml @@ -347,6 +347,11 @@ spec: x-kubernetes-validations: - message: projectRef is immutable rule: self == oldSelf + propagateUplinkStatus: + description: |- + propagateUplinkStatus represents the uplink status propagation of + the port. + type: boolean securityGroupRefs: description: |- securityGroupRefs are the names of the security groups associated diff --git a/internal/controllers/port/actuator.go b/internal/controllers/port/actuator.go index 822f6a5be..5b78e3041 100644 --- a/internal/controllers/port/actuator.go +++ b/internal/controllers/port/actuator.go @@ -171,12 +171,13 @@ func (actuator portActuator) CreateResource(ctx context.Context, obj *orcv1alpha } createOpts := ports.CreateOpts{ - NetworkID: *network.Status.ID, - Name: getResourceName(obj), - Description: string(ptr.Deref(resource.Description, "")), - ProjectID: projectID, - AdminStateUp: resource.AdminStateUp, - MACAddress: resource.MACAddress, + NetworkID: *network.Status.ID, + Name: getResourceName(obj), + Description: string(ptr.Deref(resource.Description, "")), + ProjectID: projectID, + AdminStateUp: resource.AdminStateUp, + MACAddress: resource.MACAddress, + PropagateUplinkStatus: resource.PropagateUplinkStatus, } if len(resource.AllowedAddressPairs) > 0 { @@ -345,6 +346,7 @@ func (actuator portActuator) updateResource(ctx context.Context, obj orcObjectPT handleAllowedAddressPairsUpdate(baseUpdateOpts, resource, osResource) handleSecurityGroupRefsUpdate(baseUpdateOpts, resource, osResource, secGroupMap) handleAdminStateUpUpdate(baseUpdateOpts, resource, osResource) + handlePropagateUplinkStatusUpdate(baseUpdateOpts, resource, osResource) updateOpts = baseUpdateOpts } @@ -530,6 +532,16 @@ func handleAdminStateUpUpdate(updateOpts *ports.UpdateOpts, resource *resourceSp } } +func handlePropagateUplinkStatusUpdate(updateOpts *ports.UpdateOpts, resource *resourceSpecT, osResource *osResourceT) { + // When this field is not defined, let's set this as `false` to + // avoid errors in environments where uplink-propagation-status + // extension isn't enabled. + propagateUplinkStatus := ptr.Deref(resource.PropagateUplinkStatus, false) + if propagateUplinkStatus != osResource.PropagateUplinkStatus { + updateOpts.PropagateUplinkStatus = &propagateUplinkStatus + } +} + type portHelperFactory struct{} var _ helperFactory = portHelperFactory{} diff --git a/internal/controllers/port/actuator_test.go b/internal/controllers/port/actuator_test.go index 81a2a7cc6..ee5d3e397 100644 --- a/internal/controllers/port/actuator_test.go +++ b/internal/controllers/port/actuator_test.go @@ -435,3 +435,31 @@ func TestHandleAdminStateUpUpdate(t *testing.T) { }) } } + +func TestHandlePropagateUplinkStatusUpdate(t *testing.T) { + testCases := []struct { + name string + newValue *bool + existingValue bool + expectChange bool + }{ + {name: "Set the same value as the existing one", newValue: ptr.To(true), existingValue: true, expectChange: false}, + {name: "Enabled when was disabled", newValue: ptr.To(true), existingValue: false, expectChange: true}, + {name: "Disable if it is not defined on spec", newValue: nil, existingValue: true, expectChange: true}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + resource := &orcv1alpha1.PortResourceSpec{PropagateUplinkStatus: tt.newValue} + osResource := &osclients.PortExt{Port: ports.Port{PropagateUplinkStatus: tt.existingValue}} + updateOpts := &ports.UpdateOpts{} + + handlePropagateUplinkStatusUpdate(updateOpts, resource, osResource) + + got, _ := needsUpdate(updateOpts) + if got != tt.expectChange { + t.Errorf("expected needsUpdate=%v, got %v", tt.expectChange, got) + } + }) + } +} 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 f026eea4a..6d854ac75 100644 --- a/internal/controllers/port/tests/port-create-full/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-full/00-assert.yaml @@ -17,6 +17,7 @@ status: vnicType: macvtap macAddress: fa:16:3e:23:fd:d7 hostID: devstack + propagateUplinkStatus: true tags: - tag1 --- diff --git a/internal/controllers/port/tests/port-create-full/00-create-resource.yaml b/internal/controllers/port/tests/port-create-full/00-create-resource.yaml index bbb52641d..3e8beeb75 100644 --- a/internal/controllers/port/tests/port-create-full/00-create-resource.yaml +++ b/internal/controllers/port/tests/port-create-full/00-create-resource.yaml @@ -86,3 +86,4 @@ spec: projectRef: port-create-full macAddress: fa:16:3e:23:fd:d7 hostID: devstack + propagateUplinkStatus: true 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..d1dc5833a 100644 --- a/internal/controllers/port/tests/port-create-minimal/00-assert.yaml +++ b/internal/controllers/port/tests/port-create-minimal/00-assert.yaml @@ -9,7 +9,7 @@ status: adminStateUp: true portSecurityEnabled: true propagateUplinkStatus: false - revisionNumber: 1 + revisionNumber: 2 status: DOWN vnicType: normal --- diff --git a/internal/controllers/port/tests/port-update/00-assert.yaml b/internal/controllers/port/tests/port-update/00-assert.yaml index 6ec7e451d..8ddd7baae 100644 --- a/internal/controllers/port/tests/port-update/00-assert.yaml +++ b/internal/controllers/port/tests/port-update/00-assert.yaml @@ -31,7 +31,9 @@ status: adminStateUp: true portSecurityEnabled: false propagateUplinkStatus: false - revisionNumber: 1 + # The revisionNumber has increased to enforce `propagateUplinkStatus` default + # value. + revisionNumber: 2 status: DOWN vnicType: normal conditions: @@ -54,7 +56,7 @@ status: adminStateUp: true portSecurityEnabled: true propagateUplinkStatus: false - revisionNumber: 1 + revisionNumber: 2 status: DOWN vnicType: normal conditions: diff --git a/internal/controllers/port/tests/port-update/01-assert.yaml b/internal/controllers/port/tests/port-update/01-assert.yaml index 1bcaf2d7f..0a67ad11f 100644 --- a/internal/controllers/port/tests/port-update/01-assert.yaml +++ b/internal/controllers/port/tests/port-update/01-assert.yaml @@ -34,7 +34,7 @@ status: description: port-update-updated adminStateUp: true portSecurityEnabled: true - propagateUplinkStatus: false + propagateUplinkStatus: true status: DOWN vnicType: direct allowedAddressPairs: @@ -63,4 +63,4 @@ status: reason: Success - type: Progressing status: "False" - reason: Success + reason: Success diff --git a/internal/controllers/port/tests/port-update/01-updated-resource.yaml b/internal/controllers/port/tests/port-update/01-updated-resource.yaml index 107b4ab5d..6dd73b18d 100644 --- a/internal/controllers/port/tests/port-update/01-updated-resource.yaml +++ b/internal/controllers/port/tests/port-update/01-updated-resource.yaml @@ -20,6 +20,7 @@ spec: - tag1 vnicType: direct portSecurity: Enabled + propagateUplinkStatus: true --- 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..bbea0df06 100644 --- a/internal/controllers/port/tests/port-update/02-assert.yaml +++ b/internal/controllers/port/tests/port-update/02-assert.yaml @@ -35,4 +35,4 @@ status: - type: Progressing message: OpenStack resource is up to date status: "False" - reason: Success \ No newline at end of file + reason: Success diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/portresourcespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/portresourcespec.go index aab07d8bc..a157e70ff 100644 --- a/pkg/clients/applyconfiguration/api/v1alpha1/portresourcespec.go +++ b/pkg/clients/applyconfiguration/api/v1alpha1/portresourcespec.go @@ -25,19 +25,20 @@ import ( // PortResourceSpecApplyConfiguration represents a declarative configuration of the PortResourceSpec type for use // with apply. type PortResourceSpecApplyConfiguration struct { - Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` - Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` - NetworkRef *apiv1alpha1.KubernetesNameRef `json:"networkRef,omitempty"` - Tags []apiv1alpha1.NeutronTag `json:"tags,omitempty"` - AllowedAddressPairs []AllowedAddressPairApplyConfiguration `json:"allowedAddressPairs,omitempty"` - Addresses []AddressApplyConfiguration `json:"addresses,omitempty"` - AdminStateUp *bool `json:"adminStateUp,omitempty"` - SecurityGroupRefs []apiv1alpha1.OpenStackName `json:"securityGroupRefs,omitempty"` - VNICType *string `json:"vnicType,omitempty"` - PortSecurity *apiv1alpha1.PortSecurityState `json:"portSecurity,omitempty"` - ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` - MACAddress *string `json:"macAddress,omitempty"` - HostID *string `json:"hostID,omitempty"` + Name *apiv1alpha1.OpenStackName `json:"name,omitempty"` + Description *apiv1alpha1.NeutronDescription `json:"description,omitempty"` + NetworkRef *apiv1alpha1.KubernetesNameRef `json:"networkRef,omitempty"` + Tags []apiv1alpha1.NeutronTag `json:"tags,omitempty"` + AllowedAddressPairs []AllowedAddressPairApplyConfiguration `json:"allowedAddressPairs,omitempty"` + Addresses []AddressApplyConfiguration `json:"addresses,omitempty"` + AdminStateUp *bool `json:"adminStateUp,omitempty"` + SecurityGroupRefs []apiv1alpha1.OpenStackName `json:"securityGroupRefs,omitempty"` + VNICType *string `json:"vnicType,omitempty"` + PortSecurity *apiv1alpha1.PortSecurityState `json:"portSecurity,omitempty"` + ProjectRef *apiv1alpha1.KubernetesNameRef `json:"projectRef,omitempty"` + MACAddress *string `json:"macAddress,omitempty"` + HostID *string `json:"hostID,omitempty"` + PropagateUplinkStatus *bool `json:"propagateUplinkStatus,omitempty"` } // PortResourceSpecApplyConfiguration constructs a declarative configuration of the PortResourceSpec type for use with @@ -163,3 +164,11 @@ func (b *PortResourceSpecApplyConfiguration) WithHostID(value string) *PortResou b.HostID = &value return b } + +// WithPropagateUplinkStatus sets the PropagateUplinkStatus 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 PropagateUplinkStatus field is set to the value of the last call. +func (b *PortResourceSpecApplyConfiguration) WithPropagateUplinkStatus(value bool) *PortResourceSpecApplyConfiguration { + b.PropagateUplinkStatus = &value + return b +} diff --git a/pkg/clients/applyconfiguration/internal/internal.go b/pkg/clients/applyconfiguration/internal/internal.go index abaeca27f..b6805275f 100644 --- a/pkg/clients/applyconfiguration/internal/internal.go +++ b/pkg/clients/applyconfiguration/internal/internal.go @@ -1351,6 +1351,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: projectRef type: scalar: string + - name: propagateUplinkStatus + type: + scalar: boolean - name: securityGroupRefs type: list: diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md index 32018f962..ae1df1118 100644 --- a/website/docs/crd-reference.md +++ b/website/docs/crd-reference.md @@ -2172,6 +2172,7 @@ _Appears in:_ | `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
| | `macAddress` _string_ | macAddress is the MAC address of the port. | | MaxLength: 32
| | `hostID` _string_ | hostID is the ID of host where the port resides. | | MaxLength: 36
| +| `propagateUplinkStatus` _boolean_ | propagateUplinkStatus represents the uplink status propagation of
the port. | | | #### PortResourceStatus From 4d215478c6b1822b8e1ddbc4ed5dd1fe5ca33984 Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Tue, 13 Jan 2026 13:22:41 -0300 Subject: [PATCH 2/2] add neutron plugin to the CI --- .github/workflows/e2e.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 3e8ef73d5..7a2b155ed 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -37,6 +37,8 @@ jobs: enable_workaround_docker_io: 'false' branch: ${{ matrix.openstack_version }} enabled_services: "openstack-cli-server,neutron-uplink-status-propagation" + conf_overrides: | + enable_plugin neutron https://opendev.org/openstack/neutron.git ${{ matrix.openstack_version }} - name: Deploy a Kind Cluster uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab