diff --git a/api/v1beta1/conversion_test.go b/api/v1beta1/conversion_test.go index a32052aa..70af192c 100644 --- a/api/v1beta1/conversion_test.go +++ b/api/v1beta1/conversion_test.go @@ -17,14 +17,19 @@ package v1beta1 import ( - "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" "testing" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + "k8s.io/apimachinery/pkg/conversion" + fuzz "github.com/google/gofuzz" . "github.com/onsi/gomega" "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "k8s.io/apimachinery/pkg/runtime" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" utilconversion "sigs.k8s.io/cluster-api/util/conversion" ) @@ -172,3 +177,710 @@ func TestFuzzyConversion(t *testing.T) { })) } + +func TestConvert_v1beta1_VCN_To_v1beta2_VCN(t *testing.T) { + type args struct { + in *VCN + out *v1beta2.VCN + s conversion.Scope + } + + // helper variables for pointers + id := "vcn-1" + + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "normal conversion", + args: args{ + in: &VCN{ + ID: &id, + CIDR: "10.0.0.0/16", + Name: "test-vcn", + }, + out: &v1beta2.VCN{}, + s: nil, // assuming Scope is not used in this test + }, + wantErr: false, + }, + { + name: "partial input", + args: args{ + in: &VCN{ + ID: &id, + }, + out: &v1beta2.VCN{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta1_VCN_To_v1beta2_VCN(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta1_VCN_To_v1beta2_VCN() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta2_VCN_To_v1beta1_VCN(t *testing.T) { + type args struct { + in *v1beta2.VCN + out *VCN + s conversion.Scope + } + + // helper variables for pointers + id := "vcn-1" + + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "normal conversion", + args: args{ + in: &v1beta2.VCN{ + ID: &id, // keep pointer for ID + CIDR: "10.0.0.0/16", // plain string + Name: "test-vcn", // plain string + }, + out: &VCN{}, + s: nil, + }, + wantErr: false, + }, + { + name: "partial input", + args: args{ + in: &v1beta2.VCN{ + ID: &id, // pointer + }, + out: &VCN{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta2_VCN_To_v1beta1_VCN(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta2_VCN_To_v1beta1_VCN() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta1_OCIClusterStatus_To_v1beta2_OCIClusterStatus(t *testing.T) { + type args struct { + in *OCIClusterStatus + out *v1beta2.OCIClusterStatus + s conversion.Scope + } + + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "normal conversion", + args: args{ + in: &OCIClusterStatus{ + Ready: true, + AvailabilityDomains: map[string]OCIAvailabilityDomain{ + "AD-1": { + Name: "Uocm:PHX-AD-1", + FaultDomains: []string{"FAULT-DOMAIN-1", "FAULT-DOMAIN-2", "FAULT-DOMAIN-3"}, + }, + }, + }, + out: &v1beta2.OCIClusterStatus{}, + s: nil, + }, + wantErr: false, + }, + { + name: "partial input", + args: args{ + in: &OCIClusterStatus{ + Ready: true, + }, + out: &v1beta2.OCIClusterStatus{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty input", + args: args{ + in: &OCIClusterStatus{}, + out: &v1beta2.OCIClusterStatus{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta1_OCIClusterStatus_To_v1beta2_OCIClusterStatus(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta1_OCIClusterStatus_To_v1beta2_OCIClusterStatus() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta2_OCIClusterSpec_To_v1beta1_OCIClusterSpec(t *testing.T) { + type args struct { + in *v1beta2.OCIClusterSpec + out *OCIClusterSpec + s conversion.Scope + } + + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "normal conversion", + args: args{ + in: &v1beta2.OCIClusterSpec{ + Region: "us-phoenix-1", + AvailabilityDomains: map[string]v1beta2.OCIAvailabilityDomain{ + "AD-1": { + Name: "Uocm:PHX-AD-1", + FaultDomains: []string{"FAULT-DOMAIN-1", "FAULT-DOMAIN-2", "FAULT-DOMAIN-3"}, + }, + }, + }, + out: &OCIClusterSpec{}, + s: nil, + }, + wantErr: false, + }, + { + name: "partial input", + args: args{ + in: &v1beta2.OCIClusterSpec{ + Region: "us-phoenix-1", + }, + out: &OCIClusterSpec{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty input", + args: args{ + in: &v1beta2.OCIClusterSpec{}, + out: &OCIClusterSpec{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta2_OCIClusterSpec_To_v1beta1_OCIClusterSpec(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta2_OCIClusterSpec_To_v1beta1_OCIClusterSpec() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta1_EgressSecurityRuleForNSG_To_v1beta2_EgressSecurityRuleForNSG(t *testing.T) { + type args struct { + in *EgressSecurityRuleForNSG + out *v1beta2.EgressSecurityRuleForNSG + s conversion.Scope + } + + // Example inline variables for pointer fields + destination := "10.0.0.0/16" + description := "test rule" + protocol := "6" // TCP + stateless := true + + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "normal conversion", + args: args{ + in: &EgressSecurityRuleForNSG{ + EgressSecurityRule: EgressSecurityRule{ + Destination: &destination, + Description: &description, + Protocol: &protocol, + DestinationType: "CIDR_BLOCK", + IsStateless: &stateless, + }, + }, + out: &v1beta2.EgressSecurityRuleForNSG{}, + s: nil, + }, + wantErr: false, + }, + { + name: "partial input", + args: args{ + in: &EgressSecurityRuleForNSG{ + EgressSecurityRule: EgressSecurityRule{ + Destination: &destination, + }, + }, + out: &v1beta2.EgressSecurityRuleForNSG{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty input", + args: args{ + in: &EgressSecurityRuleForNSG{}, + out: &v1beta2.EgressSecurityRuleForNSG{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta1_EgressSecurityRuleForNSG_To_v1beta2_EgressSecurityRuleForNSG(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta1_EgressSecurityRuleForNSG_To_v1beta2_EgressSecurityRuleForNSG() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta1_IngressSecurityRuleForNSG_To_v1beta2_IngressSecurityRuleForNSG(t *testing.T) { + type args struct { + in *IngressSecurityRuleForNSG + out *v1beta2.IngressSecurityRuleForNSG + s conversion.Scope + } + + source := "192.168.0.0/24" + protocol := "6" // TCP + description := "allow http" + stateless := false + + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "full rule", + args: args{ + in: &IngressSecurityRuleForNSG{ + IngressSecurityRule: IngressSecurityRule{ + Source: &source, + Description: &description, + Protocol: &protocol, + SourceType: "CIDR_BLOCK", + IsStateless: &stateless, + }, + }, + out: &v1beta2.IngressSecurityRuleForNSG{}, + s: nil, + }, + wantErr: false, + }, + { + name: "partial rule", + args: args{ + in: &IngressSecurityRuleForNSG{ + IngressSecurityRule: IngressSecurityRule{ + Source: &source, + }, + }, + out: &v1beta2.IngressSecurityRuleForNSG{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty rule", + args: args{ + in: &IngressSecurityRuleForNSG{}, + out: &v1beta2.IngressSecurityRuleForNSG{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta1_IngressSecurityRuleForNSG_To_v1beta2_IngressSecurityRuleForNSG(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta1_IngressSecurityRuleForNSG_To_v1beta2_IngressSecurityRuleForNSG() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta1_NetworkDetails_To_v1beta2_NetworkDetails(t *testing.T) { + type args struct { + in *NetworkDetails + out *v1beta2.NetworkDetails + s conversion.Scope + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "full network details", + args: args{ + in: &NetworkDetails{ + SubnetId: func() *string { s := "subnet-123"; return &s }(), + AssignIpv6Ip: true, + AssignPublicIp: false, + SubnetName: "my-subnet", + NSGIds: []string{"nsg-1", "nsg-2"}, + SkipSourceDestCheck: func() *bool { b := true; return &b }(), + NsgNames: []string{"nsg-a", "nsg-b"}, + HostnameLabel: func() *string { s := "host1"; return &s }(), + DisplayName: func() *string { s := "display"; return &s }(), + AssignPrivateDnsRecord: func() *bool { b := false; return &b }(), + }, + out: &v1beta2.NetworkDetails{}, + s: nil, + }, + wantErr: false, + }, + { + name: "partial network details", + args: args{ + in: &NetworkDetails{ + SubnetName: "default-subnet", + }, + out: &v1beta2.NetworkDetails{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty network details", + args: args{ + in: &NetworkDetails{}, + out: &v1beta2.NetworkDetails{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta1_NetworkDetails_To_v1beta2_NetworkDetails(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta1_NetworkDetails_To_v1beta2_NetworkDetails() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta1_OCIMachineSpec_To_v1beta2_OCIMachineSpec(t *testing.T) { + type args struct { + in *OCIMachineSpec + out *v1beta2.OCIMachineSpec + s conversion.Scope + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "all fields populated", + args: args{ + in: &OCIMachineSpec{ + Shape: "VM.Standard.E4.Flex", + ShapeConfig: ShapeConfig{ + Ocpus: "2", + MemoryInGBs: "16", + }, + BootVolumeSizeInGBs: "100", + NetworkDetails: NetworkDetails{ + SubnetName: "subnet-a", + AssignPublicIp: true, + }, + }, + out: &v1beta2.OCIMachineSpec{}, + s: nil, + }, + wantErr: false, + }, + { + name: "minimal spec", + args: args{ + in: &OCIMachineSpec{ + Shape: "VM.Standard2.1", + }, + out: &v1beta2.OCIMachineSpec{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty struct", + args: args{ + in: &OCIMachineSpec{}, + out: &v1beta2.OCIMachineSpec{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta1_OCIMachineSpec_To_v1beta2_OCIMachineSpec(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta1_OCIMachineSpec_To_v1beta2_OCIMachineSpec() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta2_LoadBalancer_To_v1beta1_LoadBalancer(t *testing.T) { + type args struct { + in *v1beta2.LoadBalancer + out *LoadBalancer + s conversion.Scope + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "all fields populated", + args: args{ + in: &v1beta2.LoadBalancer{ + Name: "lb-test", + LoadBalancerId: func() *string { s := "lb-ocid"; return &s }(), + LoadBalancerType: "NLB", + }, + out: &LoadBalancer{}, + s: nil, + }, + wantErr: false, + }, + { + name: "minimal load balancer", + args: args{ + in: &v1beta2.LoadBalancer{ + Name: "lb-test", + }, + out: &LoadBalancer{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty struct", + args: args{ + in: &v1beta2.LoadBalancer{}, + out: &LoadBalancer{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta2_LoadBalancer_To_v1beta1_LoadBalancer(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta2_LoadBalancer_To_v1beta1_LoadBalancer() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta2_OCIManagedControlPlaneStatus_To_v1beta1_OCIManagedControlPlaneStatus(t *testing.T) { + type args struct { + in *v1beta2.OCIManagedControlPlaneStatus + out *OCIManagedControlPlaneStatus + s conversion.Scope + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "all fields populated", + args: args{ + in: &v1beta2.OCIManagedControlPlaneStatus{ + Ready: true, + Version: func() *string { s := "v1.29.0"; return &s }(), + Initialized: true, + Conditions: v1beta1.Conditions{ + { + Type: "Available", + Status: v1.ConditionTrue, + }, + }, + }, + out: &OCIManagedControlPlaneStatus{}, + s: nil, + }, + wantErr: false, + }, + { + name: "minimal status", + args: args{ + in: &v1beta2.OCIManagedControlPlaneStatus{ + Ready: true, + }, + out: &OCIManagedControlPlaneStatus{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty status", + args: args{ + in: &v1beta2.OCIManagedControlPlaneStatus{}, + out: &OCIManagedControlPlaneStatus{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta2_OCIManagedControlPlaneStatus_To_v1beta1_OCIManagedControlPlaneStatus(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta2_OCIManagedControlPlaneStatus_To_v1beta1_OCIManagedControlPlaneStatus() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestConvert_v1beta2_OCIManagedControlPlaneSpec_To_v1beta1_OCIManagedControlPlaneSpec(t *testing.T) { + type args struct { + in *v1beta2.OCIManagedControlPlaneSpec + out *OCIManagedControlPlaneSpec + s conversion.Scope + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "almost all fields populated", + args: args{ + in: &v1beta2.OCIManagedControlPlaneSpec{ + Version: func() *string { s := "v1.29.0"; return &s }(), + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "1.2.3.4", + Port: 6443, + }, + }, + out: &OCIManagedControlPlaneSpec{}, + s: nil, + }, + wantErr: false, + }, + { + name: "minimal spec", + args: args{ + in: &v1beta2.OCIManagedControlPlaneSpec{ + Version: func() *string { s := "v1.29.0"; return &s }(), + }, + out: &OCIManagedControlPlaneSpec{}, + s: nil, + }, + wantErr: false, + }, + { + name: "empty spec", + args: args{ + in: &v1beta2.OCIManagedControlPlaneSpec{}, + out: &OCIManagedControlPlaneSpec{}, + s: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Convert_v1beta2_OCIManagedControlPlaneSpec_To_v1beta1_OCIManagedControlPlaneSpec(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("Convert_v1beta2_OCIManagedControlPlaneSpec_To_v1beta1_OCIManagedControlPlaneSpec() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// func TestConvert_v1beta1_OCIManagedClusterStatus_To_v1beta2_OCIManagedClusterStatus(t *testing.T) { +// type args struct { +// in *OCIManagedClusterStatus +// out *v1beta2.OCIManagedClusterStatus +// s conversion.Scope +// } +// tests := []struct { +// name string +// args args +// wantErr bool +// }{ +// // TODO: Add test cases. +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if err := Convert_v1beta1_OCIManagedClusterStatus_To_v1beta2_OCIManagedClusterStatus(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { +// t.Errorf("Convert_v1beta1_OCIManagedClusterStatus_To_v1beta2_OCIManagedClusterStatus() error = %v, wantErr %v", err, tt.wantErr) +// } +// }) +// } +// } + +// func TestConvert_v1beta2_OCIManagedClusterSpec_To_v1beta1_OCIManagedClusterSpec(t *testing.T) { +// type args struct { +// in *v1beta2.OCIManagedClusterSpec +// out *OCIManagedClusterSpec +// s conversion.Scope +// } +// tests := []struct { +// name string +// args args +// wantErr bool +// }{ +// // TODO: Add test cases. +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if err := Convert_v1beta2_OCIManagedClusterSpec_To_v1beta1_OCIManagedClusterSpec(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { +// t.Errorf("Convert_v1beta2_OCIManagedClusterSpec_To_v1beta1_OCIManagedClusterSpec() error = %v, wantErr %v", err, tt.wantErr) +// } +// }) +// } +// } + +// func TestConvert_v1beta2_ClusterOptions_To_v1beta1_ClusterOptions(t *testing.T) { +// type args struct { +// in *v1beta2.ClusterOptions +// out *ClusterOptions +// s conversion.Scope +// } +// tests := []struct { +// name string +// args args +// wantErr bool +// }{ +// // TODO: Add test cases. +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if err := Convert_v1beta2_ClusterOptions_To_v1beta1_ClusterOptions(tt.args.in, tt.args.out, tt.args.s); (err != nil) != tt.wantErr { +// t.Errorf("Convert_v1beta2_ClusterOptions_To_v1beta1_ClusterOptions() error = %v, wantErr %v", err, tt.wantErr) +// } +// }) +// } +// } diff --git a/api/v1beta1/ocicluster_conversion_test.go b/api/v1beta1/ocicluster_conversion_test.go new file mode 100644 index 00000000..787ccd8a --- /dev/null +++ b/api/v1beta1/ocicluster_conversion_test.go @@ -0,0 +1,206 @@ +/* + Copyright (c) 2023 Oracle and/or its affiliates. + + 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 + + https://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 v1beta1 + +import ( + "reflect" + "testing" + + "github.com/oracle/cluster-api-provider-oci/api/v1beta2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func TestOCICluster_ConvertTo(t *testing.T) { + type fields struct { + TypeMeta metav1.TypeMeta + ObjectMeta metav1.ObjectMeta + Spec OCIClusterSpec + Status OCIClusterStatus + } + type args struct { + dstRaw conversion.Hub + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "successful conversion", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + Namespace: "default", + }, + Spec: OCIClusterSpec{ + Region: "us-phoenix-1", + }, + Status: OCIClusterStatus{ + AvailabilityDomains: map[string]OCIAvailabilityDomain{ + "AD-1": { + Name: "Uocm:PHX-AD-1", + FaultDomains: []string{"FAULT-DOMAIN-1", "FAULT-DOMAIN-2", "FAULT-DOMAIN-3"}, + }, + }, + }, + }, + args: args{ + dstRaw: &v1beta2.OCICluster{}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + src := &OCICluster{ + TypeMeta: tt.fields.TypeMeta, + ObjectMeta: tt.fields.ObjectMeta, + Spec: tt.fields.Spec, + Status: tt.fields.Status, + } + if err := src.ConvertTo(tt.args.dstRaw); (err != nil) != tt.wantErr { + t.Errorf("OCICluster.ConvertTo() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_convertv1beta1NSGListTov1beta2NSGList(t *testing.T) { + type args struct { + in []*NSG + } + tests := []struct { + name string + args args + want []*v1beta2.NSG + wantErr bool + }{ + { + name: "normal conversion", + args: args{ + in: []*NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + {ID: func() *string { s := "nsg2"; return &s }(), Name: "NSG-2"}, + }, + }, + want: []*v1beta2.NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + {ID: func() *string { s := "nsg2"; return &s }(), Name: "NSG-2"}, + }, + wantErr: false, + }, + { + name: "slice contains nil", + args: args{ + in: []*NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + nil, + }, + }, + want: []*v1beta2.NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + nil, + }, + wantErr: false, + }, + { + name: "empty slice", + args: args{ + in: []*NSG{}, + }, + want: []*v1beta2.NSG{}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := convertv1beta1NSGListTov1beta2NSGList(tt.args.in) + if (err != nil) != tt.wantErr { + t.Errorf("convertv1beta1NSGListTov1beta2NSGList() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("convertv1beta1NSGListTov1beta2NSGList() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_convertv1beta2NSGListTov1beta1NSGList(t *testing.T) { + type args struct { + in []*v1beta2.NSG + } + tests := []struct { + name string + args args + want []*NSG + wantErr bool + }{ + { + name: "normal conversion", + args: args{ + in: []*v1beta2.NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + {ID: func() *string { s := "nsg2"; return &s }(), Name: "NSG-2"}, + }, + }, + want: []*NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + {ID: func() *string { s := "nsg2"; return &s }(), Name: "NSG-2"}, + }, + wantErr: false, + }, + { + name: "slice contains nil", + args: args{ + in: []*v1beta2.NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + nil, + }, + }, + want: []*NSG{ + {ID: func() *string { s := "nsg1"; return &s }(), Name: "NSG-1"}, + nil, + }, + wantErr: false, + }, + { + name: "empty slice", + args: args{ + in: []*v1beta2.NSG{}, + }, + want: []*NSG{}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := convertv1beta2NSGListTov1beta1NSGList(tt.args.in) + if (err != nil) != tt.wantErr { + t.Errorf("convertv1beta2NSGListTov1beta1NSGList() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("convertv1beta2NSGListTov1beta1NSGList() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/v1beta2/ocicluster_webhook_test.go b/api/v1beta2/ocicluster_webhook_test.go index cef2add5..034be37c 100644 --- a/api/v1beta2/ocicluster_webhook_test.go +++ b/api/v1beta2/ocicluster_webhook_test.go @@ -21,13 +21,17 @@ package v1beta2 import ( "context" + "reflect" "strings" "testing" "github.com/onsi/gomega" . "github.com/onsi/gomega" "github.com/oracle/oci-go-sdk/v65/common" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) func TestOCICluster_ValidateCreate(t *testing.T) { @@ -494,6 +498,55 @@ func TestOCICluster_ValidateCreate(t *testing.T) { } } +func TestOCIClusterWebhook_ValidateDelete(t *testing.T) { + tests := []struct { + name string + obj runtime.Object + wantErr bool + errMsg string + }{ + { + name: "valid OCICluster object", + obj: &OCICluster{ObjectMeta: metav1.ObjectMeta{Name: "test-cluster"}}, + wantErr: false, + }, + { + name: "invalid object type", + obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "not-a-cluster"}}, + wantErr: true, + errMsg: "expected an OCICluster object", + }, + { + name: "nil object", + obj: nil, + wantErr: true, + errMsg: "expected an OCICluster object", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &OCIClusterWebhook{} + warnings, err := w.ValidateDelete(context.Background(), tt.obj) + + if tt.wantErr { + if err == nil { + t.Errorf("expected error but got nil") + } else if !strings.Contains(err.Error(), tt.errMsg) { + t.Errorf("expected error containing %q but got %q", tt.errMsg, err.Error()) + } + } else if err != nil { + t.Errorf("unexpected error: %v", err) + } + + // In both cases warnings should be nil + if warnings != nil { + t.Errorf("expected no warnings but got: %v", warnings) + } + }) + } +} + func TestOCICluster_ValidateUpdate(t *testing.T) { goodSubnets := []*Subnet{ &Subnet{ @@ -789,3 +842,114 @@ func TestOCICluster_CreateDefault(t *testing.T) { }) } } + +func TestOCICluster_GetConditions(t *testing.T) { + tests := []struct { + name string + cluster *OCICluster + want clusterv1.Conditions + }{ + { + name: "no conditions set, returns empty", + cluster: &OCICluster{ + Status: OCIClusterStatus{ + Conditions: nil, + }, + }, + want: nil, + }, + { + name: "returns existing conditions", + cluster: &OCICluster{ + Status: OCIClusterStatus{ + Conditions: clusterv1.Conditions{ + { + Type: clusterv1.ReadyCondition, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + want: clusterv1.Conditions{ + { + Type: clusterv1.ReadyCondition, + Status: corev1.ConditionTrue, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.cluster.GetConditions(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetConditions() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOCICluster_SetConditions(t *testing.T) { + tests := []struct { + name string + conditions clusterv1.Conditions + }{ + { + name: "set single condition", + conditions: clusterv1.Conditions{ + { + Type: "Ready", + Status: corev1.ConditionTrue, + }, + }, + }, + { + name: "set empty conditions", + conditions: clusterv1.Conditions{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &OCICluster{} + c.SetConditions(tt.conditions) + + if !reflect.DeepEqual(c.Status.Conditions, tt.conditions) { + t.Errorf("SetConditions() got = %v, want %v", c.Status.Conditions, tt.conditions) + } + }) + } +} + +func TestOCICluster_GetOCIResourceIdentifier(t *testing.T) { + tests := []struct { + name string + spec OCIClusterSpec + want string + }{ + { + name: "with resource identifier", + spec: OCIClusterSpec{ + OCIResourceIdentifier: "resource-123", + }, + want: "resource-123", + }, + { + name: "empty resource identifier", + spec: OCIClusterSpec{ + OCIResourceIdentifier: "", + }, + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &OCICluster{ + Spec: tt.spec, + } + if got := c.GetOCIResourceIdentifier(); got != tt.want { + t.Errorf("GetOCIResourceIdentifier() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/v1beta2/ocimachinetemplate_webhook_test.go b/api/v1beta2/ocimachinetemplate_webhook_test.go index e0c50b22..5449b729 100644 --- a/api/v1beta2/ocimachinetemplate_webhook_test.go +++ b/api/v1beta2/ocimachinetemplate_webhook_test.go @@ -18,11 +18,17 @@ package v1beta2 import ( "context" + "reflect" "strings" "testing" + apierrors "k8s.io/apimachinery/pkg/api/errors" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) var tests = []struct { @@ -111,6 +117,61 @@ func TestOCIMachineTemplate_ValidateCreate(t *testing.T) { } } +func TestOCIMachineTemplateWebhook_ValidateDelete(t *testing.T) { + tests := []struct { + name string + obj runtime.Object + wantErr bool + errMsg string + }{ + { + name: "valid OCIMachineTemplate object", + obj: &OCIMachineTemplate{ObjectMeta: metav1.ObjectMeta{Name: "test-machine-template"}}, + wantErr: false, + }, + { + name: "invalid object type (Pod)", + obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "not-a-machine-template"}}, + wantErr: true, + errMsg: "expected a OCIMachineTemplate", + }, + { + name: "nil object", + obj: nil, + wantErr: true, + errMsg: "expected a OCIMachineTemplate", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &OCIMachineTemplateWebhook{} + warnings, err := w.ValidateDelete(context.Background(), tt.obj) + + if tt.wantErr { + if err == nil { + t.Errorf("expected error but got nil") + } else { + // check it's an API error and contains the expected message + if !apierrors.IsBadRequest(err) { + t.Errorf("expected BadRequest error but got %T", err) + } + if !strings.Contains(err.Error(), tt.errMsg) { + t.Errorf("expected error containing %q but got %q", tt.errMsg, err.Error()) + } + } + } else if err != nil { + t.Errorf("unexpected error: %v", err) + } + + // warnings should always be nil + if warnings != nil { + t.Errorf("expected no warnings but got: %v", warnings) + } + }) + } +} + func TestOCIMachineTemplate_ValidateUpdate(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -127,3 +188,78 @@ func TestOCIMachineTemplate_ValidateUpdate(t *testing.T) { }) } } + +func TestOCIMachine_GetConditions(t *testing.T) { + tests := []struct { + name string + conditions clusterv1.Conditions + }{ + { + name: "non-empty conditions", + conditions: clusterv1.Conditions{ + { + Type: "Ready", + Status: corev1.ConditionTrue, + }, + { + Type: "Provisioned", + Status: corev1.ConditionFalse, + }, + }, + }, + { + name: "empty conditions", + conditions: clusterv1.Conditions{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &OCIMachine{ + Status: OCIMachineStatus{ + Conditions: tt.conditions, + }, + } + + if got := m.GetConditions(); !reflect.DeepEqual(got, tt.conditions) { + t.Errorf("GetConditions() = %v, want %v", tt.conditions, tt.conditions) + } + }) + } +} + +func TestOCIMachine_SetConditions(t *testing.T) { + tests := []struct { + name string + conditions clusterv1.Conditions + }{ + { + name: "set non-empty conditions", + conditions: clusterv1.Conditions{ + { + Type: "Ready", + Status: corev1.ConditionTrue, + }, + { + Type: "Provisioned", + Status: corev1.ConditionFalse, + }, + }, + }, + { + name: "set empty conditions", + conditions: clusterv1.Conditions{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &OCIMachine{} + m.SetConditions(tt.conditions) + + if !reflect.DeepEqual(m.Status.Conditions, tt.conditions) { + t.Errorf("SetConditions() = %v, want %v", m.Status.Conditions, tt.conditions) + } + }) + } +} diff --git a/api/v1beta2/ocimanagedcluster_webhook_test.go b/api/v1beta2/ocimanagedcluster_webhook_test.go index 93a25662..d6470324 100644 --- a/api/v1beta2/ocimanagedcluster_webhook_test.go +++ b/api/v1beta2/ocimanagedcluster_webhook_test.go @@ -23,11 +23,15 @@ import ( "context" "strings" "testing" + "time" "github.com/onsi/gomega" . "github.com/onsi/gomega" "github.com/oracle/oci-go-sdk/v65/common" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) func TestOCIManagedCluster_ValidateCreate(t *testing.T) { @@ -522,6 +526,58 @@ func TestOCIManagedCluster_ValidateCreate(t *testing.T) { } } +func TestOCIManagedCluster_ValidateDelete(t *testing.T) { + tests := []struct { + name string + obj runtime.Object + wantErr bool + errMsg string + }{ + { + name: "valid OCIManagedCluster object", + obj: &OCIManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: "test-managed-cluster"}}, + wantErr: false, + }, + { + name: "invalid object type (Pod)", + obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "not-a-managed-cluster"}}, + wantErr: true, + errMsg: "expected an OCIManagedCluster", + }, + { + name: "nil object", + obj: nil, + wantErr: true, + errMsg: "expected an OCIManagedCluster", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &OCIManagedClusterWebhook{} + warnings, err := w.ValidateDelete(context.Background(), tt.obj) + + if tt.wantErr { + if err == nil { + t.Errorf("expected error but got nil") + } else { + // check error contains the expected message + if !strings.Contains(err.Error(), tt.errMsg) { + t.Errorf("expected error containing %q but got %q", tt.errMsg, err.Error()) + } + } + } else if err != nil { + t.Errorf("unexpected error: %v", err) + } + + // warnings should always be nil + if warnings != nil { + t.Errorf("expected no warnings but got: %v", warnings) + } + }) + } +} + func TestOCIManagedCluster_ValidateUpdate(t *testing.T) { goodSubnets := []*Subnet{ &Subnet{ @@ -768,3 +824,48 @@ func TestOCIManagedCluster_CreateDefault(t *testing.T) { }) } } + +func TestOCIManagedCluster_GetConditions(t *testing.T) { + cond := clusterv1.Condition{ + Type: "Ready", + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.NewTime(time.Now()), + Reason: "TestReason", + Message: "TestMessage", + } + + cluster := &OCIManagedCluster{ + Status: OCIManagedClusterStatus{ + Conditions: clusterv1.Conditions{cond}, + }, + } + + got := cluster.GetConditions() + if len(got) != 1 { + t.Errorf("GetConditions returned wrong length, got %d, want 1", len(got)) + } + if got[0].Type != cond.Type || got[0].Status != cond.Status || got[0].Reason != cond.Reason || got[0].Message != cond.Message { + t.Errorf("GetConditions returned %+v, want %+v", got[0], cond) + } +} + +func TestOCIManagedCluster_SetConditions(t *testing.T) { + cond := clusterv1.Condition{ + Type: "Ready", + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.NewTime(time.Now()), + Reason: "TestReason", + Message: "TestMessage", + } + + cluster := &OCIManagedCluster{} + + cluster.SetConditions(clusterv1.Conditions{cond}) + + if len(cluster.Status.Conditions) != 1 { + t.Errorf("SetConditions failed, expected 1 condition, got %d", len(cluster.Status.Conditions)) + } + if cluster.Status.Conditions[0].Type != cond.Type || cluster.Status.Conditions[0].Status != cond.Status { + t.Errorf("SetConditions stored wrong condition %+v, want %+v", cluster.Status.Conditions[0], cond) + } +} diff --git a/api/v1beta2/ocimanagedcontrolplane_webhook_test.go b/api/v1beta2/ocimanagedcontrolplane_webhook_test.go index fb907d4e..3a2b7ed0 100644 --- a/api/v1beta2/ocimanagedcontrolplane_webhook_test.go +++ b/api/v1beta2/ocimanagedcontrolplane_webhook_test.go @@ -18,6 +18,7 @@ package v1beta2 import ( "context" + "reflect" "strings" "testing" @@ -25,7 +26,10 @@ import ( "github.com/onsi/gomega" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) func TestOCIManagedControlPlane_CreateDefault(t *testing.T) { @@ -177,3 +181,114 @@ func TestOCIManagedControlPlane_ValidateCreate(t *testing.T) { }) } } + +func TestOCIManagedControlPlaneWebhook_ValidateDelete(t *testing.T) { + h := &OCIManagedControlPlaneWebhook{} + + tests := []struct { + name string + obj runtime.Object + wantErr bool + }{ + { + name: "nil object", + obj: nil, + wantErr: false, + }, + { + name: "some OCIManagedControlPlane object", + obj: &OCIManagedControlPlane{}, // assuming you have this type + wantErr: false, + }, + { + name: "other type", + obj: &OCICluster{}, // passing a different type should still return nil + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotWarnings, err := h.ValidateDelete(context.TODO(), tt.obj) + if (err != nil) != tt.wantErr { + t.Errorf("ValidateDelete() error = %v, wantErr %v", err, tt.wantErr) + } + if gotWarnings != nil && len(gotWarnings) != 0 { + t.Errorf("ValidateDelete() warnings = %v, want nil", gotWarnings) + } + }) + } +} + +func TestOCIManagedControlPlane_GetConditions(t *testing.T) { + want := clusterv1.Conditions{ + {Type: "Ready", Status: corev1.ConditionTrue}, + } + c := &OCIManagedControlPlane{ + Status: OCIManagedControlPlaneStatus{ + Conditions: want, + }, + } + + got := c.GetConditions() + if !reflect.DeepEqual(got, want) { + t.Errorf("GetConditions() = %v, want %v", got, want) + } +} + +func TestOCIManagedControlPlane_SetConditions(t *testing.T) { + cond := clusterv1.Conditions{ + {Type: "Updated", Status: corev1.ConditionFalse}, + } + c := &OCIManagedControlPlane{} + + c.SetConditions(cond) + if !reflect.DeepEqual(c.Status.Conditions, cond) { + t.Errorf("SetConditions() failed, got %v, want %v", c.Status.Conditions, cond) + } +} + +func TestOCIManagedControlPlane_SetAddonStatus(t *testing.T) { + version := "v1.0.0" + state := "Installed" + message := "Addon installed successfully" + + status := AddonStatus{ + CurrentlyInstalledVersion: &version, + LifecycleState: &state, + AddonError: &AddonError{ + Message: &message, + }, + } + + c := &OCIManagedControlPlane{} + + // Add first addon + c.SetAddonStatus("metrics-server", status) + got := c.Status.AddonStatus["metrics-server"] + + if got.CurrentlyInstalledVersion == nil || *got.CurrentlyInstalledVersion != version { + t.Errorf("SetAddonStatus() CurrentlyInstalledVersion = %v, want %v", got.CurrentlyInstalledVersion, version) + } + if got.LifecycleState == nil || *got.LifecycleState != state { + t.Errorf("SetAddonStatus() LifecycleState = %v, want %v", got.LifecycleState, state) + } + if got.AddonError == nil || got.AddonError.Message == nil || *got.AddonError.Message != message { + t.Errorf("SetAddonStatus() AddonError.Message = %v, want %v", got.AddonError.Message, message) + } + + // Add second addon to test map initialization and multiple entries + secondVersion := "v2.0.0" + secondStatus := AddonStatus{ + CurrentlyInstalledVersion: &secondVersion, + } + c.SetAddonStatus("logging", secondStatus) + + if len(c.Status.AddonStatus) != 2 { + t.Errorf("Expected 2 addon statuses, got %d", len(c.Status.AddonStatus)) + } + got2 := c.Status.AddonStatus["logging"] + if got2.CurrentlyInstalledVersion == nil || *got2.CurrentlyInstalledVersion != secondVersion { + t.Errorf("SetAddonStatus() second addon CurrentlyInstalledVersion = %v, want %v", got2.CurrentlyInstalledVersion, secondVersion) + } +} diff --git a/cloud/config/config_test.go b/cloud/config/config_test.go new file mode 100644 index 00000000..ad494b20 --- /dev/null +++ b/cloud/config/config_test.go @@ -0,0 +1,414 @@ +/* +Copyright (c) 2021, 2022 Oracle and/or its affiliates. + +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 config + +import ( + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/oracle/oci-go-sdk/v65/common" +) + +func TestFromDir(t *testing.T) { + type args struct { + path string + } + + // Setup: valid config file + validContent := []byte(` +region: us-phoenix-1 +tenancy: ocid1.tenancy.oc1..aaaaaaa +user: ocid1.user.oc1..bbbbbbb +key: some-private-key +fingerprint: 11:22:33:44 +passphrase: secret-pass +useInstancePrincipals: true +`) + tmpFile, err := os.CreateTemp("", "auth-*.yaml") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + defer os.Remove(tmpFile.Name()) + if _, err := tmpFile.Write(validContent); err != nil { + t.Fatalf("failed to write temp file: %v", err) + } + tmpFile.Close() + + // Setup: directory path (empty, so expect error from getConfigFromDir) + tmpDir, err := os.MkdirTemp("", "auth-dir-*") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + tests := []struct { + name string + args args + want *AuthConfig + wantErr bool + }{ + { + name: "non-existent path", + args: args{path: "no-such-path"}, + want: nil, + wantErr: true, + }, + { + name: "valid file path", + args: args{path: tmpFile.Name()}, + want: &AuthConfig{ + Region: "us-phoenix-1", + TenancyID: "ocid1.tenancy.oc1..aaaaaaa", + UserID: "ocid1.user.oc1..bbbbbbb", + PrivateKey: "some-private-key", + Fingerprint: "11:22:33:44", + Passphrase: "secret-pass", + UseInstancePrincipals: true, + }, + wantErr: false, + }, + { + name: "directory path", + args: args{path: tmpDir}, + want: nil, // since empty dir will make getConfigFromDir fail + wantErr: true, // adjust depending on getConfigFromDir impl + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := FromDir(tt.args.path) + if (err != nil) != tt.wantErr { + t.Errorf("FromDir() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FromDir() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getConfigFromFile(t *testing.T) { + type args struct { + path string + } + + // Create a temporary valid config file + validContent := []byte(` +region: us-phoenix-1 +tenancy: ocid1.tenancy.oc1..aaaaaaa +user: ocid1.user.oc1..bbbbbbb +key: some-private-key +fingerprint: 11:22:33:44 +passphrase: secret-pass +useInstancePrincipals: true +`) + tmpValid, err := os.CreateTemp("", "valid-auth-*.yaml") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + defer os.Remove(tmpValid.Name()) + if _, err := tmpValid.Write(validContent); err != nil { + t.Fatalf("failed to write to temp file: %v", err) + } + tmpValid.Close() + + tests := []struct { + name string + args args + wantAuthConfig *AuthConfig + wantErr bool + }{ + { + name: "file does not exist", + args: args{path: "non-existent-file.yaml"}, + wantErr: true, + }, + { + name: "empty file", + args: args{path: func() string { + f, _ := os.CreateTemp("", "empty-auth-*.yaml") + defer f.Close() + return f.Name() + }()}, + wantErr: true, + }, + { + name: "valid yaml", + args: args{path: tmpValid.Name()}, + wantAuthConfig: &AuthConfig{ + Region: "us-phoenix-1", + TenancyID: "ocid1.tenancy.oc1..aaaaaaa", + UserID: "ocid1.user.oc1..bbbbbbb", + PrivateKey: "some-private-key", + Fingerprint: "11:22:33:44", + Passphrase: "secret-pass", + UseInstancePrincipals: true, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotAuthConfig, err := getConfigFromFile(tt.args.path) + if (err != nil) != tt.wantErr { + t.Errorf("getConfigFromFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotAuthConfig, tt.wantAuthConfig) { + t.Errorf("getConfigFromFile() = %v, want %v", gotAuthConfig, tt.wantAuthConfig) + } + }) + } +} + +func Test_getConfigFromDir(t *testing.T) { + type args struct { + path string + } + tests := []struct { + name string + args args + want *AuthConfig + wantErr bool + }{ + { + name: "missing useInstancePrincipals file", + args: args{path: t.TempDir()}, + want: nil, + wantErr: true, + }, + { + name: "useInstancePrincipals true", + args: args{path: func() string { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, UseInstancePrincipal), []byte("true"), 0644) + return dir + }()}, + want: &AuthConfig{ + UseInstancePrincipals: true, + }, + wantErr: false, + }, + { + name: "useInstancePrincipals false but missing other files", + args: args{path: func() string { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, UseInstancePrincipal), []byte("false"), 0644) + return dir + }()}, + want: nil, + wantErr: true, + }, + { + name: "all files present with valid content", + args: args{path: func() string { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, UseInstancePrincipal), []byte("false"), 0644) + os.WriteFile(filepath.Join(dir, Region), []byte("us-phoenix-1"), 0644) + os.WriteFile(filepath.Join(dir, Tenancy), []byte("ocid1.tenancy.oc1..aaaaaaa"), 0644) + os.WriteFile(filepath.Join(dir, User), []byte("ocid1.user.oc1..bbbbbbb"), 0644) + os.WriteFile(filepath.Join(dir, Fingerprint), []byte("11:22:33:44"), 0644) + os.WriteFile(filepath.Join(dir, Passphrase), []byte("secret-pass"), 0644) + os.WriteFile(filepath.Join(dir, Key), []byte("some-private-key"), 0644) + return dir + }()}, + want: &AuthConfig{ + Region: "us-phoenix-1", + TenancyID: "ocid1.tenancy.oc1..aaaaaaa", + UserID: "ocid1.user.oc1..bbbbbbb", + PrivateKey: "some-private-key", + Fingerprint: "11:22:33:44", + Passphrase: "secret-pass", + UseInstancePrincipals: false, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := getConfigFromDir(tt.args.path) + if (err != nil) != tt.wantErr { + t.Errorf("getConfigFromDir() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("getConfigFromDir() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNewConfigurationProvider(t *testing.T) { + type args struct { + cfg *AuthConfig + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "nil config", + args: args{cfg: nil}, + wantErr: true, + }, + { + name: "use instance principals", + args: args{cfg: &AuthConfig{ + UseInstancePrincipals: true, + }}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewConfigurationProvider(tt.args.cfg) + if (err != nil) != tt.wantErr { + t.Errorf("NewConfigurationProvider() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr && got == nil { + t.Errorf("NewConfigurationProvider() returned nil, want non-nil") + } + }) + } +} + +func TestNewConfigurationProviderWithUserPrincipal(t *testing.T) { + type args struct { + cfg *AuthConfig + } + tests := []struct { + name string + args args + want common.ConfigurationProvider + wantErr bool + }{ + { + name: "nil config", + args: args{cfg: nil}, + want: nil, + wantErr: true, + }, + { + name: "use user principals", + args: args{cfg: &AuthConfig{ + Region: "us-phoenix-1", + TenancyID: "ocid1.tenancy.oc1..aaaaaaa", + UserID: "ocid1.user.oc1..bbbbbbb", + PrivateKey: "some-private-key", + Fingerprint: "11:22:33:44", + Passphrase: "secret-pass", + }}, + want: common.NewRawConfigurationProvider( + "ocid1.tenancy.oc1..aaaaaaa", + "ocid1.user.oc1..bbbbbbb", + "us-phoenix-1", + "11:22:33:44", + "some-private-key", + common.String("secret-pass")), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewConfigurationProviderWithUserPrincipal(tt.args.cfg) + if (err != nil) != tt.wantErr { + t.Errorf("NewConfigurationProviderWithUserPrincipal() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewConfigurationProviderWithUserPrincipal() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestReadFile(t *testing.T) { + type args struct { + path string + key string + } + + // Setup: temporary directory and files + tmpDir := t.TempDir() + + // File with content + contentFile := "test-key" + contentValue := "hello world" + err := os.WriteFile(filepath.Join(tmpDir, contentFile), []byte(contentValue), 0644) + if err != nil { + t.Fatalf("failed to write test file: %v", err) + } + + // Empty file + emptyFile := "empty-key" + err = os.WriteFile(filepath.Join(tmpDir, emptyFile), []byte(""), 0644) + if err != nil { + t.Fatalf("failed to write empty file: %v", err) + } + + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "file exists with content", + args: args{ + path: tmpDir, + key: contentFile, + }, + want: contentValue, + wantErr: false, + }, + { + name: "file does not exist", + args: args{ + path: tmpDir, + key: "non-existent-key", + }, + want: "", + wantErr: true, + }, + { + name: "empty file", + args: args{ + path: tmpDir, + key: emptyFile, + }, + want: "", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ReadFile(tt.args.path, tt.args.key) + if (err != nil) != tt.wantErr { + t.Errorf("ReadFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ReadFile() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cloud/ociutil/ptr/from_ptr.go b/cloud/ociutil/ptr/from_ptr.go index 035fed3f..e8d89a24 100644 --- a/cloud/ociutil/ptr/from_ptr.go +++ b/cloud/ociutil/ptr/from_ptr.go @@ -38,3 +38,17 @@ func ToNSGSlice(vs []*infrastructurev1beta2.NSG) []infrastructurev1beta2.NSG { return ps } + +// ToSubnetSlice returns a slice of Subnet values, that are +// dereferenced if the passed in pointer was not nil. Returns an empty slice +// zero value if the pointer was nil. +func ToSubnetSlice(vs []*infrastructurev1beta2.Subnet) []infrastructurev1beta2.Subnet { + ps := make([]infrastructurev1beta2.Subnet, len(vs)) + for i, v := range vs { + if v != nil { + ps[i] = *v + } + } + + return ps +} diff --git a/cloud/ociutil/ptr/from_ptr_test.go b/cloud/ociutil/ptr/from_ptr_test.go index 0088b454..8aa70895 100644 --- a/cloud/ociutil/ptr/from_ptr_test.go +++ b/cloud/ociutil/ptr/from_ptr_test.go @@ -116,3 +116,45 @@ func TestToNSGSlice(t *testing.T) { }) } } + +func TestToSubnetSlice(t *testing.T) { + tests := []struct { + name string + input []*infrastructurev1beta2.Subnet + want []infrastructurev1beta2.Subnet + }{ + { + name: "nil slice", + input: nil, + want: []infrastructurev1beta2.Subnet{}, + }, + { + name: "empty slice", + input: []*infrastructurev1beta2.Subnet{}, + want: []infrastructurev1beta2.Subnet{}, + }, + { + name: "slice with nil elements", + input: []*infrastructurev1beta2.Subnet{nil, nil}, + want: []infrastructurev1beta2.Subnet{{}, {}}, + }, + { + name: "slice with non-nil elements", + input: []*infrastructurev1beta2.Subnet{&infrastructurev1beta2.Subnet{}, &infrastructurev1beta2.Subnet{}}, + want: []infrastructurev1beta2.Subnet{{}, {}}, + }, + { + name: "slice with mixed nil and non-nil elements", + input: []*infrastructurev1beta2.Subnet{nil, &infrastructurev1beta2.Subnet{}, nil}, + want: []infrastructurev1beta2.Subnet{{}, {}, {}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToSubnetSlice(tt.input); len(got) != len(tt.want) { + t.Errorf("ToSubnetlice() length = %v, want %v", len(got), len(tt.want)) + } + }) + } +} diff --git a/cloud/scope/load_balancer_reconciler.go b/cloud/scope/load_balancer_reconciler.go index 2c5cecdf..b26f850e 100644 --- a/cloud/scope/load_balancer_reconciler.go +++ b/cloud/scope/load_balancer_reconciler.go @@ -161,8 +161,8 @@ func (s *ClusterScope) CreateLB(ctx context.Context, lb infrastructurev1beta2.Lo Backends: []loadbalancer.BackendDetails{}, } var controlPlaneEndpointSubnets []string - for _, subnet := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.ID != nil && subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { + for _, subnet := range ptr.ToSubnetSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.ID != nil && subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { controlPlaneEndpointSubnets = append(controlPlaneEndpointSubnets, *subnet.ID) } } diff --git a/cloud/scope/machine.go b/cloud/scope/machine.go index 6708d47e..64d58163 100644 --- a/cloud/scope/machine.go +++ b/cloud/scope/machine.go @@ -28,6 +28,7 @@ import ( "github.com/go-logr/logr" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" "github.com/oracle/cluster-api-provider-oci/cloud/services/compute" lb "github.com/oracle/cluster-api-provider-oci/cloud/services/loadbalancer" nlb "github.com/oracle/cluster-api-provider-oci/cloud/services/networkloadbalancer" @@ -743,8 +744,8 @@ func (m *MachineScope) getCompartmentId() string { } func (m *MachineScope) getGetControlPlaneMachineSubnet() *string { - for _, subnet := range m.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.ControlPlaneRole { + for _, subnet := range ptr.ToSubnetSlice(m.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.ControlPlaneRole { return subnet.ID } } @@ -753,8 +754,8 @@ func (m *MachineScope) getGetControlPlaneMachineSubnet() *string { func (m *MachineScope) getGetControlPlaneMachineNSGs() []string { nsgs := make([]string, 0) - for _, nsg := range m.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Role == infrastructurev1beta2.ControlPlaneRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.Role == infrastructurev1beta2.ControlPlaneRole { if nsg.ID != nil { nsgs = append(nsgs, *nsg.ID) } @@ -766,8 +767,8 @@ func (m *MachineScope) getGetControlPlaneMachineNSGs() []string { // getMachineSubnet iterates through the OCICluster Vcn subnets // and returns the subnet ID if the name matches func (m *MachineScope) getMachineSubnet(name string) (*string, error) { - for _, subnet := range m.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Name == name { + for _, subnet := range ptr.ToSubnetSlice(m.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Name == name { return subnet.ID, nil } } @@ -775,8 +776,8 @@ func (m *MachineScope) getMachineSubnet(name string) (*string, error) { } func (m *MachineScope) getWorkerMachineSubnet() *string { - for _, subnet := range m.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.WorkerRole { + for _, subnet := range ptr.ToSubnetSlice(m.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.WorkerRole { // if a subnet name is defined, use the correct subnet if m.OCIMachine.Spec.SubnetName != "" { if m.OCIMachine.Spec.SubnetName == subnet.Name { @@ -794,8 +795,8 @@ func (m *MachineScope) getWorkerMachineNSGs() []string { if len(m.OCIMachine.Spec.NetworkDetails.NsgNames) > 0 { nsgs := make([]string, 0) for _, nsgName := range m.OCIMachine.Spec.NetworkDetails.NsgNames { - for _, nsg := range m.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Name == nsgName { + for _, nsg := range ptr.ToNSGSlice(m.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.Name == nsgName { if nsg.ID != nil { nsgs = append(nsgs, *nsg.ID) } @@ -805,8 +806,8 @@ func (m *MachineScope) getWorkerMachineNSGs() []string { return nsgs } else { nsgs := make([]string, 0) - for _, nsg := range m.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Role == infrastructurev1beta2.WorkerRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.Role == infrastructurev1beta2.WorkerRole { if nsg.ID != nil { nsgs = append(nsgs, *nsg.ID) } diff --git a/cloud/scope/machine_pool.go b/cloud/scope/machine_pool.go index 371ddda6..15b1a466 100644 --- a/cloud/scope/machine_pool.go +++ b/cloud/scope/machine_pool.go @@ -29,6 +29,7 @@ import ( "github.com/go-logr/logr" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" "github.com/oracle/cluster-api-provider-oci/cloud/services/computemanagement" expinfra1 "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" infrav2exp "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" @@ -146,8 +147,8 @@ func (m *MachinePoolScope) SetReplicaCount(count int32) { // GetWorkerMachineSubnet returns the WorkerRole core.Subnet id for the cluster func (m *MachinePoolScope) GetWorkerMachineSubnet() *string { - for _, subnet := range m.OCIClusterAccesor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.WorkerRole { + for _, subnet := range ptr.ToSubnetSlice(m.OCIClusterAccesor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.WorkerRole { return subnet.ID } } @@ -243,8 +244,8 @@ func (m *MachinePoolScope) GetBootstrapData() (string, error) { // GetWorkerMachineNSG returns the worker role core.NetworkSecurityGroup id for the cluster func (m *MachinePoolScope) GetWorkerMachineNSG() *string { - for _, nsg := range m.OCIClusterAccesor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Role == infrastructurev1beta2.WorkerRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIClusterAccesor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.Role == infrastructurev1beta2.WorkerRole { return nsg.ID } } @@ -859,8 +860,8 @@ func (m *MachinePoolScope) getWorkerMachineNSGs() []string { if instanceVnicConfiguration != nil && len(instanceVnicConfiguration.NsgNames) > 0 { nsgs := make([]string, 0) for _, nsgName := range instanceVnicConfiguration.NsgNames { - for _, nsg := range m.OCIClusterAccesor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Name == nsgName { + for _, nsg := range ptr.ToNSGSlice(m.OCIClusterAccesor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Name == nsgName { nsgs = append(nsgs, *nsg.ID) } } @@ -868,8 +869,8 @@ func (m *MachinePoolScope) getWorkerMachineNSGs() []string { return nsgs } else { nsgs := make([]string, 0) - for _, nsg := range m.OCIClusterAccesor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Role == infrastructurev1beta2.WorkerRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIClusterAccesor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Role == infrastructurev1beta2.WorkerRole { nsgs = append(nsgs, *nsg.ID) } } diff --git a/cloud/scope/managed_control_plane.go b/cloud/scope/managed_control_plane.go index 4ad8489e..c51e9482 100644 --- a/cloud/scope/managed_control_plane.go +++ b/cloud/scope/managed_control_plane.go @@ -29,6 +29,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" baseclient "github.com/oracle/cluster-api-provider-oci/cloud/services/base" "github.com/oracle/cluster-api-provider-oci/cloud/services/containerengine" "github.com/oracle/oci-go-sdk/v65/common" @@ -386,8 +387,8 @@ func (s *ManagedControlPlaneScope) getFreeFormTags() map[string]string { func (s *ManagedControlPlaneScope) getServiceLbSubnets() []string { subnets := make([]string, 0) - for _, subnet := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.ServiceLoadBalancerRole { + for _, subnet := range ptr.ToSubnetSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.ServiceLoadBalancerRole { subnets = append(subnets, *subnet.ID) } } @@ -395,8 +396,8 @@ func (s *ManagedControlPlaneScope) getServiceLbSubnets() []string { } func (s *ManagedControlPlaneScope) getControlPlaneEndpointSubnet() *string { - for _, subnet := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { + for _, subnet := range ptr.ToSubnetSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { return subnet.ID } } @@ -405,8 +406,8 @@ func (s *ManagedControlPlaneScope) getControlPlaneEndpointSubnet() *string { func (s *ManagedControlPlaneScope) getControlPlaneEndpointNSGList() []string { nsgs := make([]string, 0) - for _, nsg := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Role == infrastructurev1beta2.ControlPlaneEndpointRole { + for _, nsg := range ptr.ToNSGSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.Role == infrastructurev1beta2.ControlPlaneEndpointRole { nsgs = append(nsgs, *nsg.ID) } } @@ -415,8 +416,8 @@ func (s *ManagedControlPlaneScope) getControlPlaneEndpointNSGList() []string { // IsControlPlaneEndpointSubnetPublic returns true if the control plane endpoint subnet is public func (s *ManagedControlPlaneScope) IsControlPlaneEndpointSubnetPublic() bool { - for _, subnet := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole && subnet.Type == infrastructurev1beta2.Public { + for _, subnet := range ptr.ToSubnetSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole && subnet.Type == infrastructurev1beta2.Public { return true } } diff --git a/cloud/scope/managed_machine_pool.go b/cloud/scope/managed_machine_pool.go index 5521c911..fec1c557 100644 --- a/cloud/scope/managed_machine_pool.go +++ b/cloud/scope/managed_machine_pool.go @@ -128,8 +128,8 @@ func (m *ManagedMachinePoolScope) SetReplicaCount(count int32) { // GetWorkerMachineSubnet returns the WorkerRole core.Subnet id for the cluster func (m *ManagedMachinePoolScope) GetWorkerMachineSubnet() *string { - for _, subnet := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.WorkerRole { + for _, subnet := range ptr.ToSubnetSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.WorkerRole { return subnet.ID } } @@ -484,8 +484,8 @@ func (m *ManagedMachinePoolScope) getFreeFormTags() map[string]string { func (m *ManagedMachinePoolScope) getWorkerMachineSubnets() []string { subnetList := make([]string, 0) - for _, subnet := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.WorkerRole { + for _, subnet := range ptr.ToSubnetSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.WorkerRole { subnetList = append(subnetList, subnet.Name) } } @@ -497,15 +497,15 @@ func (m *ManagedMachinePoolScope) getWorkerMachineNSGs() []string { specNsgNames := m.OCIManagedMachinePool.Spec.NodePoolNodeConfig.NsgNames if len(specNsgNames) > 0 { for _, nsgName := range specNsgNames { - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Name == nsgName { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Name == nsgName { nsgList = append(nsgList, *nsg.ID) } } } } else { - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Role == infrastructurev1beta2.WorkerRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Role == infrastructurev1beta2.WorkerRole { nsgList = append(nsgList, *nsg.ID) } } @@ -515,8 +515,8 @@ func (m *ManagedMachinePoolScope) getWorkerMachineNSGs() []string { func (m *ManagedMachinePoolScope) getWorkerMachineNSGList() []string { nsgList := make([]string, 0) - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Role == infrastructurev1beta2.WorkerRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.Role == infrastructurev1beta2.WorkerRole { nsgList = append(nsgList, nsg.Name) } } @@ -527,8 +527,8 @@ func (m *ManagedMachinePoolScope) getPodSubnets(subnets []string) []string { subnetList := make([]string, 0) if len(subnets) > 0 { for _, subnetName := range subnets { - for _, subnet := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets { - if subnet != nil && subnet.ID != nil && subnet.Name == subnetName { + for _, subnet := range ptr.ToSubnetSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets) { + if subnet.ID != nil && subnet.Name == subnetName { subnetList = append(subnetList, *subnet.ID) } } @@ -541,8 +541,8 @@ func (m *ManagedMachinePoolScope) getPodNSGs(nsgs []string) []string { nsgList := make([]string, 0) if len(nsgs) > 0 { for _, nsgName := range nsgs { - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Name == nsgName { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Name == nsgName { nsgList = append(nsgList, *nsg.ID) } } @@ -581,8 +581,8 @@ func (m *ManagedMachinePoolScope) getInitialNodeKeyValuePairs() []oke.KeyValue { } func (m *ManagedMachinePoolScope) getWorkerMachineSubnet(name *string) *string { - for _, subnet := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets { - if subnet != nil && subnet.ID != nil && subnet.Name == ptr.ToString(name) { + for _, subnet := range ptr.ToSubnetSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets) { + if subnet.ID != nil && subnet.Name == ptr.ToString(name) { return subnet.ID } } diff --git a/cloud/scope/network_load_balancer_reconciler.go b/cloud/scope/network_load_balancer_reconciler.go index 7b4e560d..babed06e 100644 --- a/cloud/scope/network_load_balancer_reconciler.go +++ b/cloud/scope/network_load_balancer_reconciler.go @@ -22,6 +22,7 @@ import ( infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/networkloadbalancer" "github.com/pkg/errors" @@ -175,8 +176,8 @@ func (s *ClusterScope) CreateNLB(ctx context.Context, lb infrastructurev1beta2.L } var controlPlaneEndpointSubnets []string - for _, subnet := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { + for _, subnet := range ptr.ToSubnetSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { if subnet.ID != nil { controlPlaneEndpointSubnets = append(controlPlaneEndpointSubnets, *subnet.ID) } @@ -200,8 +201,8 @@ func (s *ClusterScope) CreateNLB(ctx context.Context, lb infrastructurev1beta2.L DefinedTags: s.GetDefinedTags(), } nsgs := make([]string, 0) - for _, nsg := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Role == infrastructurev1beta2.ControlPlaneEndpointRole { + for _, nsg := range ptr.ToNSGSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup.List) { + if nsg.Role == infrastructurev1beta2.ControlPlaneEndpointRole { if nsg.ID != nil { nsgs = append(nsgs, *nsg.ID) } diff --git a/cloud/scope/nsg_reconciler.go b/cloud/scope/nsg_reconciler.go index 68628428..40ef326f 100644 --- a/cloud/scope/nsg_reconciler.go +++ b/cloud/scope/nsg_reconciler.go @@ -24,6 +24,7 @@ import ( infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/core" "github.com/pkg/errors" @@ -35,8 +36,8 @@ func (s *ClusterScope) ReconcileNSG(ctx context.Context) error { return nil } desiredNSGs := s.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup - for _, desiredNSG := range desiredNSGs.List { - nsg, err := s.GetNSG(ctx, *desiredNSG) + for _, desiredNSG := range ptr.ToNSGSlice(desiredNSGs.List) { + nsg, err := s.GetNSG(ctx, desiredNSG) if err != nil { s.Logger.Error(err, "error to get nsg") return err @@ -44,8 +45,8 @@ func (s *ClusterScope) ReconcileNSG(ctx context.Context) error { if nsg != nil { nsgOCID := nsg.Id desiredNSG.ID = nsgOCID - if !s.IsNSGEqual(nsg, *desiredNSG) { - err = s.UpdateNSG(ctx, *desiredNSG) + if !s.IsNSGEqual(nsg, desiredNSG) { + err = s.UpdateNSG(ctx, desiredNSG) if err != nil { return err } @@ -54,7 +55,7 @@ func (s *ClusterScope) ReconcileNSG(ctx context.Context) error { continue } s.Logger.Info("Creating the network security list") - nsgID, err := s.CreateNSG(ctx, *desiredNSG) + nsgID, err := s.CreateNSG(ctx, desiredNSG) if err != nil { return err } @@ -134,8 +135,8 @@ func (s *ClusterScope) DeleteNSGs(ctx context.Context) error { return nil } desiredNSGs := s.OCIClusterAccessor.GetNetworkSpec().Vcn.NetworkSecurityGroup - for _, desiredNSG := range desiredNSGs.List { - nsg, err := s.GetNSG(ctx, *desiredNSG) + for _, desiredNSG := range ptr.ToNSGSlice(desiredNSGs.List) { + nsg, err := s.GetNSG(ctx, desiredNSG) if err != nil && !ociutil.IsNotFound(err) { return err } @@ -483,8 +484,8 @@ func getProtocolOptionsForSpec(icmp *core.IcmpOptions, tcp *core.TcpOptions, udp } func getNsgIdFromName(nsgName *string, list []*infrastructurev1beta2.NSG) *string { - for _, nsg := range list { - if nsg != nil && nsg.Name == *nsgName { + for _, nsg := range ptr.ToNSGSlice(list) { + if nsg.Name == *nsgName { return nsg.ID } } @@ -495,8 +496,8 @@ func getNsgNameFromId(nsgId *string, list []*infrastructurev1beta2.NSG) *string if nsgId == nil { return nil } - for _, nsg := range list { - if nsg != nil && nsg.ID != nil && reflect.DeepEqual(nsg.ID, nsgId) { + for _, nsg := range ptr.ToNSGSlice(list) { + if nsg.ID != nil && reflect.DeepEqual(nsg.ID, nsgId) { return &nsg.Name } } diff --git a/cloud/scope/security_list_reconciler.go b/cloud/scope/security_list_reconciler.go index 462989ca..369eab52 100644 --- a/cloud/scope/security_list_reconciler.go +++ b/cloud/scope/security_list_reconciler.go @@ -21,6 +21,7 @@ import ( "reflect" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/oci-go-sdk/v65/common" @@ -29,7 +30,7 @@ import ( ) func (s *ClusterScope) DeleteSecurityLists(ctx context.Context) error { - desiredSubnets := s.GetSubnetsSpec() + desiredSubnets := ptr.ToSubnetSlice(s.GetSubnetsSpec()) for _, desiredSubnet := range desiredSubnets { if desiredSubnet.SecurityList != nil { securityList, err := s.GetSecurityList(ctx, *desiredSubnet.SecurityList) @@ -245,8 +246,8 @@ func getProtocolOptions(icmp *infrastructurev1beta2.IcmpOptions, tcp *infrastruc } func (s *ClusterScope) IsSecurityListExitsByRole(role infrastructurev1beta2.Role) bool { - for _, subnet := range s.GetSubnetsSpec() { - if subnet != nil && role == subnet.Role { + for _, subnet := range ptr.ToSubnetSlice(s.GetSubnetsSpec()) { + if role == subnet.Role { if subnet.SecurityList != nil { return true } diff --git a/cloud/scope/subnet_reconciler.go b/cloud/scope/subnet_reconciler.go index 64025162..5e340629 100644 --- a/cloud/scope/subnet_reconciler.go +++ b/cloud/scope/subnet_reconciler.go @@ -32,13 +32,13 @@ import ( ) func (s *ClusterScope) ReconcileSubnet(ctx context.Context) error { - desiredSubnets := s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets + desiredSubnets := ptr.ToSubnetSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) for _, desiredSubnet := range desiredSubnets { if desiredSubnet.Skip { s.Logger.Info("Skipping Subnet reconciliation as per spec") continue } - subnet, err := s.GetSubnet(ctx, *desiredSubnet) + subnet, err := s.GetSubnet(ctx, desiredSubnet) if err != nil { return err } @@ -69,10 +69,10 @@ func (s *ClusterScope) ReconcileSubnet(ctx context.Context) error { } } } - if s.IsSubnetsEqual(subnet, *desiredSubnet) { + if s.IsSubnetsEqual(subnet, desiredSubnet) { s.Logger.Info("No Reconciliation Required for Subnet", "subnet", subnetOCID) } else { - err = s.UpdateSubnet(ctx, *desiredSubnet) + err = s.UpdateSubnet(ctx, desiredSubnet) if err != nil { return err } @@ -90,7 +90,7 @@ func (s *ClusterScope) ReconcileSubnet(ctx context.Context) error { s.Logger.Info("Created the security list", "ocid", seclistId) desiredSubnet.SecurityList.ID = seclistId } - subnetId, err := s.CreateSubnet(ctx, *desiredSubnet) + subnetId, err := s.CreateSubnet(ctx, desiredSubnet) if err != nil { return err } @@ -197,13 +197,13 @@ func (s *ClusterScope) UpdateSubnet(ctx context.Context, spec infrastructurev1be } func (s *ClusterScope) DeleteSubnets(ctx context.Context) error { - desiredSubnets := s.GetSubnetsSpec() + desiredSubnets := ptr.ToSubnetSlice(s.GetSubnetsSpec()) for _, desiredSubnet := range desiredSubnets { if desiredSubnet.Skip { s.Logger.Info("Skipping Subnet reconciliation as per spec") continue } - subnet, err := s.GetSubnet(ctx, *desiredSubnet) + subnet, err := s.GetSubnet(ctx, desiredSubnet) if err != nil && !ociutil.IsNotFound(err) { return err } @@ -316,8 +316,8 @@ func (s *ClusterScope) IsSubnetsEqual(actual *core.Subnet, desired infrastructur } func (s *ClusterScope) isControlPlaneEndpointSubnetPrivate() bool { - for _, subnet := range s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole && subnet.Type == infrastructurev1beta2.Private { + for _, subnet := range ptr.ToSubnetSlice(s.OCIClusterAccessor.GetNetworkSpec().Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole && subnet.Type == infrastructurev1beta2.Private { return true } } @@ -325,8 +325,8 @@ func (s *ClusterScope) isControlPlaneEndpointSubnetPrivate() bool { } func (s *ClusterScope) GetControlPlaneEndpointSubnetCidr() string { - for _, subnet := range s.GetSubnetsSpec() { - if subnet != nil && subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { + for _, subnet := range ptr.ToSubnetSlice(s.GetSubnetsSpec()) { + if subnet.Role == infrastructurev1beta2.ControlPlaneEndpointRole { if subnet.CIDR != "" { return subnet.CIDR } @@ -336,8 +336,8 @@ func (s *ClusterScope) GetControlPlaneEndpointSubnetCidr() string { } func (s *ClusterScope) GetServiceLoadBalancerSubnetCidr() string { - for _, subnet := range s.GetSubnetsSpec() { - if subnet != nil && subnet.Role == infrastructurev1beta2.ServiceLoadBalancerRole { + for _, subnet := range ptr.ToSubnetSlice(s.GetSubnetsSpec()) { + if subnet.Role == infrastructurev1beta2.ServiceLoadBalancerRole { if subnet.CIDR != "" { return subnet.CIDR } @@ -349,8 +349,8 @@ func (s *ClusterScope) GetServiceLoadBalancerSubnetCidr() string { func (s *ClusterScope) NodeSubnetCidr() []string { subnets := s.GetNodeSubnet() var nodeCIDR []string - for _, subnet := range subnets { - if subnet != nil && subnet.CIDR != "" { + for _, subnet := range ptr.ToSubnetSlice(subnets) { + if subnet.CIDR != "" { nodeCIDR = append(nodeCIDR, subnet.CIDR) } nodeCIDR = append(nodeCIDR, WorkerSubnetDefaultCIDR) @@ -372,7 +372,7 @@ func (s *ClusterScope) GetControlPlaneMachineSubnetCidr() string { // IsAllSubnetsPrivate determines if all the ClusterScope's subnets are private func (s *ClusterScope) IsAllSubnetsPrivate() bool { - for _, subnet := range s.GetSubnetsSpec() { + for _, subnet := range ptr.ToSubnetSlice(s.GetSubnetsSpec()) { if subnet.Type == infrastructurev1beta2.Public { return false } @@ -385,8 +385,8 @@ func (s *ClusterScope) IsAllSubnetsPrivate() bool { // IsAllSubnetsPublic determines if all the ClusterScope's subnets are public func (s *ClusterScope) IsAllSubnetsPublic() bool { - for _, subnet := range s.GetSubnetsSpec() { - if subnet != nil && subnet.Type == infrastructurev1beta2.Private { + for _, subnet := range ptr.ToSubnetSlice(s.GetSubnetsSpec()) { + if subnet.Type == infrastructurev1beta2.Private { return false } } diff --git a/cloud/scope/util.go b/cloud/scope/util.go index 714725e0..4221f017 100644 --- a/cloud/scope/util.go +++ b/cloud/scope/util.go @@ -18,6 +18,7 @@ package scope import ( infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" ) const ( @@ -31,8 +32,8 @@ func GetNsgNamesFromId(ids []string, nsgs []*infrastructurev1beta2.NSG) []string } names := make([]string, 0) for _, id := range ids { - for _, nsg := range nsgs { - if nsg != nil && nsg.ID != nil && id == *nsg.ID { + for _, nsg := range ptr.ToNSGSlice(nsgs) { + if nsg.ID != nil && id == *nsg.ID { names = append(names, nsg.Name) } } @@ -42,8 +43,8 @@ func GetNsgNamesFromId(ids []string, nsgs []*infrastructurev1beta2.NSG) []string // GetSubnetNameFromId returns the name of the Subnet with the provided ID func GetSubnetNameFromId(id *string, subnets []*infrastructurev1beta2.Subnet) string { - for _, subnet := range subnets { - if subnet != nil && subnet.ID != nil && *id == *subnet.ID { + for _, subnet := range ptr.ToSubnetSlice(subnets) { + if subnet.ID != nil && *id == *subnet.ID { return subnet.Name } } @@ -54,8 +55,8 @@ func GetSubnetNameFromId(id *string, subnets []*infrastructurev1beta2.Subnet) st func GetSubnetNamesFromId(ids []string, subnets []*infrastructurev1beta2.Subnet) []string { names := make([]string, 0) for _, id := range ids { - for _, subnet := range subnets { - if subnet != nil && subnet.ID != nil && id == *subnet.ID { + for _, subnet := range ptr.ToSubnetSlice(subnets) { + if subnet.ID != nil && id == *subnet.ID { names = append(names, subnet.Name) } } diff --git a/cloud/scope/virtual_machine_pool.go b/cloud/scope/virtual_machine_pool.go index 5a732215..37063bca 100644 --- a/cloud/scope/virtual_machine_pool.go +++ b/cloud/scope/virtual_machine_pool.go @@ -28,6 +28,7 @@ import ( "github.com/go-logr/logr" infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2" "github.com/oracle/cluster-api-provider-oci/cloud/ociutil" + "github.com/oracle/cluster-api-provider-oci/cloud/ociutil/ptr" "github.com/oracle/cluster-api-provider-oci/cloud/services/computemanagement" "github.com/oracle/cluster-api-provider-oci/cloud/services/containerengine" expinfra1 "github.com/oracle/cluster-api-provider-oci/exp/api/v1beta2" @@ -127,8 +128,8 @@ func (m *VirtualMachinePoolScope) SetReplicaCount(count int32) { // GetWorkerMachineSubnet returns the WorkerRole core.Subnet id for the cluster func (m *VirtualMachinePoolScope) GetWorkerMachineSubnet() *string { - for _, subnet := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.WorkerRole { + for _, subnet := range ptr.ToSubnetSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.WorkerRole { return subnet.ID } } @@ -378,8 +379,8 @@ func (m *VirtualMachinePoolScope) getFreeFormTags() map[string]string { func (m *VirtualMachinePoolScope) getWorkerMachineSubnets() []string { subnetList := make([]string, 0) - for _, subnet := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets { - if subnet != nil && subnet.Role == infrastructurev1beta2.WorkerRole { + for _, subnet := range ptr.ToSubnetSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets) { + if subnet.Role == infrastructurev1beta2.WorkerRole { subnetList = append(subnetList, subnet.Name) } } @@ -391,15 +392,15 @@ func (m *VirtualMachinePoolScope) getWorkerMachineNSGs() []string { specNsgNames := m.OCIVirtualMachinePool.Spec.NsgNames if len(specNsgNames) > 0 { for _, nsgName := range specNsgNames { - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Name == nsgName { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Name == nsgName { nsgList = append(nsgList, *nsg.ID) } } } } else { - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Role == infrastructurev1beta2.WorkerRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Role == infrastructurev1beta2.WorkerRole { nsgList = append(nsgList, *nsg.ID) } } @@ -409,8 +410,8 @@ func (m *VirtualMachinePoolScope) getWorkerMachineNSGs() []string { func (m *VirtualMachinePoolScope) getWorkerMachineNSGList() []string { nsgList := make([]string, 0) - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.Role == infrastructurev1beta2.WorkerRole { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.Role == infrastructurev1beta2.WorkerRole { nsgList = append(nsgList, nsg.Name) } } @@ -421,8 +422,8 @@ func (m *VirtualMachinePoolScope) getPodNSGs(nsgs []string) []string { nsgList := make([]string, 0) if len(nsgs) > 0 { for _, nsgName := range nsgs { - for _, nsg := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List { - if nsg != nil && nsg.ID != nil && nsg.Name == nsgName { + for _, nsg := range ptr.ToNSGSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.NetworkSecurityGroup.List) { + if nsg.ID != nil && nsg.Name == nsgName { nsgList = append(nsgList, *nsg.ID) } } @@ -472,8 +473,8 @@ func (m *VirtualMachinePoolScope) getTaints() []oke.Taint { } func (m *VirtualMachinePoolScope) getSubnet(name *string) *string { - for _, subnet := range m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets { - if subnet != nil && subnet.Name == *name { + for _, subnet := range ptr.ToSubnetSlice(m.OCIManagedCluster.Spec.NetworkSpec.Vcn.Subnets) { + if subnet.Name == *name { return subnet.ID } }