sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/privateendpoints/spec_test.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package privateendpoints 18 19 import ( 20 "context" 21 "testing" 22 23 asonetworkv1 "github.com/Azure/azure-service-operator/v2/api/network/v1api20220701" 24 "github.com/Azure/azure-service-operator/v2/pkg/genruntime" 25 . "github.com/onsi/gomega" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/utils/ptr" 28 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 29 ) 30 31 var ( 32 fakePrivateEndpoint = PrivateEndpointSpec{ 33 Name: "test_private_endpoint_1", 34 ResourceGroup: "test_rg", 35 Location: "test_location", 36 CustomNetworkInterfaceName: "test_if_name", 37 PrivateIPAddresses: []string{"1.2.3.4", "5.6.7.8"}, 38 SubnetID: "test_subnet_id", 39 ApplicationSecurityGroups: []string{"test_asg1", "test_asg2"}, 40 ManualApproval: false, 41 PrivateLinkServiceConnections: []PrivateLinkServiceConnection{ 42 { 43 Name: "test_plsc_1", 44 PrivateLinkServiceID: "test_pl", 45 RequestMessage: "Please approve my connection.", 46 GroupIDs: []string{"aa", "bb"}}, 47 }, 48 AdditionalTags: infrav1.Tags{"test_tag1": "test_value1", "test_tag2": "test_value2"}, 49 ClusterName: "test_cluster", 50 } 51 52 fakeExtendedLocation = asonetworkv1.ExtendedLocation{ 53 Name: ptr.To("extended_location_name"), 54 Type: ptr.To(asonetworkv1.ExtendedLocationType_EdgeZone), 55 } 56 57 fakeASOPrivateEndpoint = asonetworkv1.PrivateEndpoint{ 58 ObjectMeta: metav1.ObjectMeta{ 59 Name: fakePrivateEndpoint.Name, 60 }, 61 Spec: asonetworkv1.PrivateEndpoint_Spec{ 62 ApplicationSecurityGroups: []asonetworkv1.ApplicationSecurityGroupSpec_PrivateEndpoint_SubResourceEmbedded{ 63 { 64 Reference: &genruntime.ResourceReference{ 65 ARMID: fakePrivateEndpoint.ApplicationSecurityGroups[0], 66 }, 67 }, 68 { 69 Reference: &genruntime.ResourceReference{ 70 ARMID: fakePrivateEndpoint.ApplicationSecurityGroups[1], 71 }, 72 }, 73 }, 74 AzureName: fakePrivateEndpoint.Name, 75 PrivateLinkServiceConnections: []asonetworkv1.PrivateLinkServiceConnection{{ 76 Name: ptr.To(fakePrivateEndpoint.PrivateLinkServiceConnections[0].Name), 77 PrivateLinkServiceReference: &genruntime.ResourceReference{ 78 ARMID: fakePrivateEndpoint.PrivateLinkServiceConnections[0].PrivateLinkServiceID, 79 }, 80 GroupIds: fakePrivateEndpoint.PrivateLinkServiceConnections[0].GroupIDs, 81 RequestMessage: ptr.To(fakePrivateEndpoint.PrivateLinkServiceConnections[0].RequestMessage), 82 }}, 83 Owner: &genruntime.KnownResourceReference{ 84 Name: fakePrivateEndpoint.ResourceGroup, 85 }, 86 Subnet: &asonetworkv1.Subnet_PrivateEndpoint_SubResourceEmbedded{ 87 Reference: &genruntime.ResourceReference{ 88 ARMID: fakePrivateEndpoint.SubnetID, 89 }, 90 }, 91 IpConfigurations: []asonetworkv1.PrivateEndpointIPConfiguration{ 92 { 93 PrivateIPAddress: ptr.To(fakePrivateEndpoint.PrivateIPAddresses[0]), 94 }, 95 { 96 PrivateIPAddress: ptr.To(fakePrivateEndpoint.PrivateIPAddresses[1]), 97 }, 98 }, 99 CustomNetworkInterfaceName: ptr.To(fakePrivateEndpoint.CustomNetworkInterfaceName), 100 Location: ptr.To(fakePrivateEndpoint.Location), 101 Tags: map[string]string{"sigs.k8s.io_cluster-api-provider-azure_cluster_test_cluster": "owned", "Name": "test_private_endpoint_1", "test_tag1": "test_value1", "test_tag2": "test_value2"}, 102 }, 103 } 104 105 fakeASOPrivateEndpointsStatus = asonetworkv1.PrivateEndpoint_STATUS_PrivateEndpoint_SubResourceEmbedded{ 106 ApplicationSecurityGroups: []asonetworkv1.ApplicationSecurityGroup_STATUS_PrivateEndpoint_SubResourceEmbedded{ 107 { 108 Id: ptr.To(fakePrivateEndpoint.ApplicationSecurityGroups[0]), 109 }, 110 { 111 Id: ptr.To(fakePrivateEndpoint.ApplicationSecurityGroups[1]), 112 }, 113 }, 114 Name: ptr.To(fakePrivateEndpoint.Name), 115 // ... other fields truncated for brevity 116 } 117 ) 118 119 func getASOPrivateEndpoint(changes ...func(*asonetworkv1.PrivateEndpoint)) *asonetworkv1.PrivateEndpoint { 120 privateEndpoint := fakeASOPrivateEndpoint.DeepCopy() 121 for _, change := range changes { 122 change(privateEndpoint) 123 } 124 return privateEndpoint 125 } 126 127 func TestParameters(t *testing.T) { 128 testcases := []struct { 129 name string 130 spec *PrivateEndpointSpec 131 existing *asonetworkv1.PrivateEndpoint 132 expect func(g *WithT, result asonetworkv1.PrivateEndpoint) 133 expectedError string 134 }{ 135 { 136 name: "Creating a new PrivateEndpoint", 137 spec: ptr.To(fakePrivateEndpoint), 138 existing: nil, 139 expect: func(g *WithT, result asonetworkv1.PrivateEndpoint) { 140 g.Expect(result).To(Not(BeNil())) 141 142 // ObjectMeta is populated later in the codeflow 143 g.Expect(result.ObjectMeta).To(Equal(metav1.ObjectMeta{})) 144 145 // Spec is populated from the spec passed in 146 g.Expect(result.Spec).To(Equal(getASOPrivateEndpoint().Spec)) 147 }, 148 }, 149 { 150 name: "user updates to private endpoints Extended Location should be accepted", 151 spec: ptr.To(fakePrivateEndpoint), 152 existing: getASOPrivateEndpoint( 153 // user added ExtendedLocation 154 func(endpoint *asonetworkv1.PrivateEndpoint) { 155 endpoint.Spec.ExtendedLocation = fakeExtendedLocation.DeepCopy() 156 }, 157 // user added Status 158 func(endpoint *asonetworkv1.PrivateEndpoint) { 159 endpoint.Status = fakeASOPrivateEndpointsStatus 160 }, 161 ), 162 expect: func(g *WithT, result asonetworkv1.PrivateEndpoint) { 163 g.Expect(result).To(Not(BeNil())) 164 resultantASOPrivateEndpoint := getASOPrivateEndpoint( 165 func(endpoint *asonetworkv1.PrivateEndpoint) { 166 endpoint.Spec.ExtendedLocation = fakeExtendedLocation.DeepCopy() 167 }, 168 ) 169 170 // ObjectMeta should be carried over from existing private endpoint. 171 g.Expect(result.ObjectMeta).To(Equal(resultantASOPrivateEndpoint.ObjectMeta)) 172 173 // Extended location addition is accepted. 174 g.Expect(result.Spec).To(Equal(resultantASOPrivateEndpoint.Spec)) 175 176 // Status should be carried over. 177 g.Expect(result.Status).To(Equal(fakeASOPrivateEndpointsStatus)) 178 }, 179 }, 180 { 181 name: "user updates ASO's private endpoint resource and capz should overwrite it", 182 spec: ptr.To(fakePrivateEndpoint), 183 existing: getASOPrivateEndpoint( 184 // add ExtendedLocation 185 func(endpoint *asonetworkv1.PrivateEndpoint) { 186 endpoint.Spec.ExtendedLocation = fakeExtendedLocation.DeepCopy() 187 }, 188 189 // User also updates private IP addresses and location. 190 // This change should be overwritten by CAPZ. 191 func(endpoint *asonetworkv1.PrivateEndpoint) { 192 endpoint.Spec.IpConfigurations = []asonetworkv1.PrivateEndpointIPConfiguration{ 193 { 194 PrivateIPAddress: ptr.To("9.9.9.9"), 195 }, 196 } 197 endpoint.Spec.Location = ptr.To("new_location") 198 }, 199 // add Status 200 func(endpoint *asonetworkv1.PrivateEndpoint) { 201 endpoint.Status = fakeASOPrivateEndpointsStatus 202 }, 203 ), 204 expect: func(g *WithT, result asonetworkv1.PrivateEndpoint) { 205 g.Expect(result).NotTo(BeNil()) 206 resultantASOPrivateEndpoint := getASOPrivateEndpoint( 207 func(endpoint *asonetworkv1.PrivateEndpoint) { 208 endpoint.Spec.ExtendedLocation = fakeExtendedLocation.DeepCopy() 209 }, 210 ) 211 212 // user changes except ExtendedLocation should be overwritten by CAPZ. 213 g.Expect(result.ObjectMeta).To(Equal(resultantASOPrivateEndpoint.ObjectMeta)) 214 215 // Extended location addition is accepted. 216 g.Expect(result.Spec).To(Equal(resultantASOPrivateEndpoint.Spec)) 217 218 // Status should be carried over. 219 g.Expect(result.Status).To(Equal(fakeASOPrivateEndpointsStatus)) 220 }, 221 }, 222 } 223 224 for _, tc := range testcases { 225 tc := tc 226 t.Run(tc.name, func(t *testing.T) { 227 g := NewWithT(t) 228 t.Parallel() 229 230 result, err := tc.spec.Parameters(context.TODO(), tc.existing) 231 if tc.expectedError != "" { 232 g.Expect(err).To(HaveOccurred()) 233 g.Expect(err).To(MatchError(tc.expectedError)) 234 } else { 235 g.Expect(err).NotTo(HaveOccurred()) 236 } 237 tc.expect(g, *result) 238 }) 239 } 240 }