Skip to content

Commit 1f38d5a

Browse files
committed
subnetpool: make prefixes immutable
1 parent 8923c5f commit 1f38d5a

10 files changed

Lines changed: 16 additions & 114 deletions

File tree

api/v1alpha1/subnetpool_types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type SubnetPoolResourceSpec struct {
5555
// +kubebuilder:validation:MaxItems:=64
5656
// +listType=set
5757
// +required
58+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="prefixes is immutable"
5859
Prefixes []CIDR `json:"prefixes,omitempty"`
5960

6061
// minPrefixLength is the smallest prefix that can be allocated

config/crd/bases/openstack.k-orc.cloud_subnetpools.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/controllers/subnetpool/actuator.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package subnetpool
1919
import (
2020
"context"
2121
"iter"
22-
"slices"
2322

2423
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/subnetpools"
2524
corev1 "k8s.io/api/core/v1"
@@ -222,7 +221,6 @@ func (actuator subnetpoolActuator) updateResource(ctx context.Context, obj orcOb
222221

223222
handleNameUpdate(&updateOpts, obj, osResource)
224223
handleDescriptionUpdate(&updateOpts, resource, osResource)
225-
handlePrefixesUpdate(&updateOpts, resource, osResource)
226224
handleMinPrefixLengthUpdate(&updateOpts, resource, osResource)
227225
handleMaxPrefixLengthUpdate(&updateOpts, resource, osResource)
228226
handleIsDefaultUpdate(&updateOpts, resource, osResource)
@@ -291,22 +289,6 @@ func handleMaxPrefixLengthUpdate(updateOpts *subnetpools.UpdateOpts, resource *r
291289
}
292290
}
293291

294-
func handlePrefixesUpdate(updateOpts *subnetpools.UpdateOpts, resource *resourceSpecT, osResource *osResourceT) {
295-
desiredPrefixes := make([]string, len(resource.Prefixes))
296-
for i := range resource.Prefixes {
297-
desiredPrefixes[i] = string(resource.Prefixes[i])
298-
}
299-
slices.Sort(desiredPrefixes)
300-
301-
currentPrefixes := make([]string, len(osResource.Prefixes))
302-
copy(currentPrefixes, osResource.Prefixes)
303-
slices.Sort(currentPrefixes)
304-
305-
if !slices.Equal(desiredPrefixes, currentPrefixes) {
306-
updateOpts.Prefixes = desiredPrefixes
307-
}
308-
}
309-
310292
func handleIsDefaultUpdate(updateOpts *subnetpools.UpdateOpts, resource *resourceSpecT, osResource *osResourceT) {
311293
// fallback to the default value if unset.
312294
isDefault := ptr.Deref(resource.IsDefault, false)

internal/controllers/subnetpool/actuator_test.go

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -174,67 +174,6 @@ func TestHandleMaxPrefixLengthUpdate(t *testing.T) {
174174
}
175175
}
176176

177-
func TestHandlePrefixesUpdate(t *testing.T) {
178-
testCases := []struct {
179-
name string
180-
newPrefixes []orcv1alpha1.CIDR
181-
existingPrefixes []string
182-
expectChange bool
183-
}{
184-
{
185-
name: "Identical prefixes",
186-
newPrefixes: []orcv1alpha1.CIDR{"192.168.0.0/24", "10.0.0.0/16"},
187-
existingPrefixes: []string{"192.168.0.0/24", "10.0.0.0/16"},
188-
expectChange: false,
189-
},
190-
{
191-
name: "Different prefixes",
192-
newPrefixes: []orcv1alpha1.CIDR{"192.168.0.0/24"},
193-
existingPrefixes: []string{"10.0.0.0/16"},
194-
expectChange: true,
195-
},
196-
{
197-
name: "Prefixes out of order",
198-
newPrefixes: []orcv1alpha1.CIDR{"10.0.0.0/16", "192.168.0.0/24"},
199-
existingPrefixes: []string{"192.168.0.0/24", "10.0.0.0/16"},
200-
expectChange: false,
201-
},
202-
{
203-
name: "Extra prefix in existing",
204-
newPrefixes: []orcv1alpha1.CIDR{"192.168.0.0/24"},
205-
existingPrefixes: []string{"192.168.0.0/24", "10.0.0.0/16"},
206-
expectChange: true,
207-
},
208-
{
209-
name: "Extra prefix in new",
210-
newPrefixes: []orcv1alpha1.CIDR{"192.168.0.0/24", "10.0.0.0/16"},
211-
existingPrefixes: []string{"192.168.0.0/24"},
212-
expectChange: true,
213-
},
214-
{
215-
name: "Empty prefixes",
216-
newPrefixes: []orcv1alpha1.CIDR{},
217-
existingPrefixes: []string{},
218-
expectChange: false,
219-
},
220-
}
221-
222-
for _, tt := range testCases {
223-
t.Run(tt.name, func(t *testing.T) {
224-
resource := &orcv1alpha1.SubnetPoolResourceSpec{Prefixes: tt.newPrefixes}
225-
osResource := &osResourceT{Prefixes: tt.existingPrefixes}
226-
227-
updateOpts := subnetpools.UpdateOpts{}
228-
handlePrefixesUpdate(&updateOpts, resource, osResource)
229-
230-
got, _ := needsUpdate(updateOpts)
231-
if got != tt.expectChange {
232-
t.Errorf("Expected change: %v, got: %v", tt.expectChange, got)
233-
}
234-
})
235-
}
236-
}
237-
238177
func TestHandleIsDefaultUpdate(t *testing.T) {
239178
ptrToBool := ptr.To[bool]
240179
testCases := []struct {

internal/controllers/subnetpool/tests/subnetpool-update/01-assert.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ status:
88
name: subnetpool-update-updated
99
description: subnetpool-update-updated
1010
prefixes:
11-
- 10.0.0.0/16
1211
- 192.168.10.0/24
1312
minPrefixLength: 7
1413
maxPrefixLength: 30

internal/controllers/subnetpool/tests/subnetpool-update/01-updated-resource.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ spec:
77
resource:
88
name: subnetpool-update-updated
99
description: subnetpool-update-updated
10-
prefixes:
11-
- 10.0.0.0/16
12-
- 192.168.10.0/24
1310
minPrefixLength: 7
1411
maxPrefixLength: 30
1512

internal/controllers/subnetpool/tests/subnetpool-update/02-assert.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ status:
1818
resource:
1919
name: subnetpool-update
2020
prefixes:
21-
- 10.0.0.0/16
2221
- 192.168.10.0/24
2322
minPrefixLength: 8
2423
maxPrefixLength: 32
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# NOTE: kuttl only does patch updates, which means we can't delete a field.
22
# We have to use a kubectl apply command instead.
3-
# NOTE: prefixes must be a superset of the previous value because OpenStack
4-
# does not allow removing existing prefixes from a subnet pool.
53
apiVersion: kuttl.dev/v1beta1
64
kind: TestStep
75
commands:
8-
- command: kubectl replace -f 02-reverted-subnetpool.yaml
6+
- command: kubectl replace -f 00-minimal-resource.yaml
97
namespaced: true

internal/controllers/subnetpool/tests/subnetpool-update/02-reverted-subnetpool.yaml

Lines changed: 0 additions & 19 deletions
This file was deleted.

test/apivalidations/subnetpool_test.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,15 @@ var _ = Describe("ORC SubnetPool API validations", func() {
124124
Expect(applyObj(ctx, obj, patch)).To(MatchError(ContainSubstring("addressScopeRef is immutable")))
125125
})
126126

127-
// TODO(scaffolding): Add more resource-specific validation tests.
128-
// Some common things to test:
129-
// - Immutability of fields with `self == oldSelf` validation
130-
// - Enum validation (valid and invalid values)
131-
// - Numeric range validation (min/max bounds)
132-
// - Tag uniqueness (if the resource has tags with listType=set)
133-
// - Format validation (CIDR, UUID, etc.)
134-
// - Cross-field validation rules
127+
It("should have immutable prefixes", func(ctx context.Context) {
128+
obj := subnetpoolStub(namespace)
129+
patch := baseSubnetPoolPatch(obj)
130+
patch.Spec.WithResource(testSubnetPoolResource())
131+
Expect(applyObj(ctx, obj, patch)).To(Succeed())
132+
133+
patch.Spec.WithResource(testSubnetPoolResource().
134+
WithPrefixes(orcv1alpha1.CIDR("10.0.0.0/8")))
135+
136+
Expect(applyObj(ctx, obj, patch)).To(MatchError(ContainSubstring("prefixes is immutable")))
137+
})
135138
})

0 commit comments

Comments
 (0)