diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml
index da07b7eee..7a2b155ed 100644
--- a/.github/workflows/e2e.yaml
+++ b/.github/workflows/e2e.yaml
@@ -36,7 +36,9 @@ 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"
+ conf_overrides: |
+ enable_plugin neutron https://opendev.org/openstack/neutron.git ${{ matrix.openstack_version }}
- 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