Skip to content

Commit f3b5e58

Browse files
authored
[fix] Make ID the source of truth for vpc and subnet names (#807)
* Don't filter VPCs by label * Handle and adopt changing subnet labels
1 parent ae140f5 commit f3b5e58

File tree

2 files changed

+99
-13
lines changed

2 files changed

+99
-13
lines changed

internal/controller/linodevpc_controller_helpers.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ func reconcileVPC(ctx context.Context, vpcScope *scope.VPCScope, logger logr.Log
4444

4545
createConfig.Label = vpcScope.LinodeVPC.Name
4646
listFilter := util.Filter{
47-
ID: vpcScope.LinodeVPC.Spec.VPCID,
48-
Label: createConfig.Label,
49-
Tags: nil,
47+
ID: vpcScope.LinodeVPC.Spec.VPCID,
48+
Tags: nil,
5049
}
5150
filter, err := listFilter.String()
5251
if err != nil {
@@ -78,23 +77,35 @@ func reconcileVPC(ctx context.Context, vpcScope *scope.VPCScope, logger logr.Log
7877
func reconcileExistingVPC(ctx context.Context, vpcScope *scope.VPCScope, vpc *linodego.VPC) error {
7978
setVPCFields(&vpcScope.LinodeVPC.Spec, vpc)
8079

81-
// build a map of existing subnets to easily check for existence
82-
existingSubnets := make(map[string]int, len(vpc.Subnets))
83-
existingSubnetsIPv6 := make(map[string][]linodego.VPCIPv6Range, len(vpc.Subnets))
80+
// Build a map of VPC subnets by both label and ID. We check for
81+
// the subnet ID but fallback to the label because the ID is not guaranteed
82+
// to be set until we've processed the subnet at least once.
83+
type SubnetConfig struct {
84+
ID int
85+
Label string
86+
IPv6 []linodego.VPCIPv6Range
87+
}
88+
subnetsByLabel := make(map[string]SubnetConfig, len(vpc.Subnets))
89+
subnetsById := make(map[int]SubnetConfig, len(vpc.Subnets))
8490
for _, subnet := range vpc.Subnets {
85-
existingSubnets[subnet.Label] = subnet.ID
86-
existingSubnetsIPv6[subnet.Label] = subnet.IPv6
91+
config := SubnetConfig{subnet.ID, subnet.Label, subnet.IPv6}
92+
subnetsByLabel[subnet.Label], subnetsById[subnet.ID] = config, config
8793
}
8894

8995
// adopt or create subnets
9096
for idx, subnet := range vpcScope.LinodeVPC.Spec.Subnets {
9197
if subnet.SubnetID != 0 {
92-
continue
93-
}
94-
if id, ok := existingSubnets[subnet.Label]; ok {
95-
vpcScope.LinodeVPC.Spec.Subnets[idx].SubnetID = id
96-
vpcScope.LinodeVPC.Spec.Subnets[idx].IPv6 = existingSubnetsIPv6[subnet.Label]
98+
if config, ok := subnetsById[subnet.SubnetID]; ok {
99+
vpcScope.LinodeVPC.Spec.Subnets[idx].Label = config.Label
100+
vpcScope.LinodeVPC.Spec.Subnets[idx].IPv6 = config.IPv6
101+
}
102+
} else if config, ok := subnetsByLabel[subnet.Label]; ok {
103+
// Handle subnets that exist in the Linode API but have not had their
104+
// ID set on the LinodeVPC yet.
105+
vpcScope.LinodeVPC.Spec.Subnets[idx].SubnetID = config.ID
106+
vpcScope.LinodeVPC.Spec.Subnets[idx].IPv6 = config.IPv6
97107
} else {
108+
// Handle subnets that we need to create in the Linode API.
98109
ipv6 := []linodego.VPCSubnetCreateOptionsIPv6{}
99110
for _, ipv6Range := range subnet.IPv6Range {
100111
ipv6 = append(ipv6, linodego.VPCSubnetCreateOptionsIPv6{

internal/controller/linodevpc_controller_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,78 @@ var _ = Describe("adopt existing VPC", Label("vpc", "lifecycle"), func() {
510510
),
511511
)
512512
})
513+
514+
var _ = Describe("name changing VPC", Label("vpc", "lifecycle"), func() {
515+
suite := NewControllerSuite(GinkgoT(), mock.MockLinodeClient{})
516+
517+
var reconciler LinodeVPCReconciler
518+
var vpcScope scope.VPCScope
519+
var linodeVPC infrav1alpha2.LinodeVPC
520+
521+
suite.BeforeEach(func(ctx context.Context, mck Mock) {
522+
vpcScope.Client = k8sClient
523+
linodeVPC = infrav1alpha2.LinodeVPC{
524+
ObjectMeta: metav1.ObjectMeta{
525+
GenerateName: "changing-vpc-",
526+
Namespace: "default",
527+
},
528+
Spec: infrav1alpha2.LinodeVPCSpec{
529+
Region: "us-east",
530+
Subnets: []infrav1alpha2.VPCSubnetCreateOptions{
531+
{Label: "changing-subnet", SubnetID: 1, IPv4: "10.0.0.0/8"},
532+
},
533+
},
534+
}
535+
Expect(k8sClient.Create(ctx, &linodeVPC)).To(Succeed())
536+
537+
vpcScope.LinodeClient = mck.LinodeClient
538+
539+
reconciler = LinodeVPCReconciler{
540+
Recorder: mck.Recorder(),
541+
}
542+
543+
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&linodeVPC), &linodeVPC)).To(Succeed())
544+
vpcScope.LinodeVPC = &linodeVPC
545+
546+
patchHelper, err := patch.NewHelper(&linodeVPC, k8sClient)
547+
Expect(err).NotTo(HaveOccurred())
548+
vpcScope.PatchHelper = patchHelper
549+
})
550+
551+
AfterEach(func(ctx SpecContext) {
552+
err := k8sClient.Delete(ctx, &linodeVPC)
553+
if err != nil {
554+
Expect(apierrors.IsNotFound(err)).To(BeTrue())
555+
}
556+
})
557+
558+
suite.Run(
559+
Path(
560+
Call("get existing VPC and adapt to name changes", func(ctx context.Context, mck Mock) {
561+
mck.LinodeClient.EXPECT().ListVPCs(ctx, gomock.Any()).Return([]linodego.VPC{
562+
{
563+
ID: 1,
564+
Label: "changed-vpc-",
565+
Region: "us-east",
566+
Subnets: []linodego.VPCSubnet{
567+
{
568+
ID: 1,
569+
Label: "changed-subnet-",
570+
IPv4: "10.0.0.0/8",
571+
},
572+
},
573+
},
574+
}, nil)
575+
}),
576+
Result("reconcile VPC with changed name and create success", func(ctx context.Context, mck Mock) {
577+
_, err := reconciler.reconcile(ctx, mck.Logger(), &vpcScope)
578+
Expect(err).NotTo(HaveOccurred())
579+
580+
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&linodeVPC), &linodeVPC)).To(Succeed())
581+
Expect(len(linodeVPC.Spec.Subnets)).To(Equal(1))
582+
Expect(linodeVPC.Spec.Subnets[0].SubnetID).To(Equal(1))
583+
Expect(linodeVPC.Spec.Subnets[0].Label).To(Equal("changed-subnet-"))
584+
}),
585+
),
586+
)
587+
})

0 commit comments

Comments
 (0)