sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/bastionhosts/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 bastionhosts 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 24 asonetworkv1 "github.com/Azure/azure-service-operator/v2/api/network/v1api20220701" 25 "github.com/Azure/azure-service-operator/v2/pkg/genruntime" 26 . "github.com/onsi/gomega" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/utils/ptr" 29 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 30 ) 31 32 var ( 33 fakeSKU = asonetworkv1.Sku_Name("fake-SKU") 34 fakeBastionHost = asonetworkv1.BastionHost{ 35 Spec: asonetworkv1.BastionHost_Spec{ 36 Location: ptr.To(fakeAzureBastionSpec1.Location), 37 AzureName: fakeAzureBastionSpec1.Name, 38 DnsName: ptr.To(fakeAzureBastionSpec1.Name + "-bastion"), 39 Sku: &asonetworkv1.Sku{Name: &fakeSKU}, 40 Owner: &genruntime.KnownResourceReference{ 41 Name: fakeAzureBastionSpec1.ResourceGroup, 42 }, 43 Tags: fakeBastionHostTags, 44 EnableTunneling: ptr.To(false), 45 IpConfigurations: []asonetworkv1.BastionHostIPConfiguration{ 46 { 47 Name: ptr.To(fmt.Sprintf("%s-%s", fakeAzureBastionSpec1.Name, "bastionIP")), 48 Subnet: &asonetworkv1.BastionHostSubResource{ 49 Reference: &genruntime.ResourceReference{ 50 ARMID: fakeAzureBastionSpec1.SubnetID, 51 }, 52 }, 53 PublicIPAddress: &asonetworkv1.BastionHostSubResource{ 54 Reference: &genruntime.ResourceReference{ 55 ARMID: fakeAzureBastionSpec1.PublicIPID, 56 }, 57 }, 58 PrivateIPAllocationMethod: ptr.To(asonetworkv1.IPAllocationMethod_Dynamic), 59 }, 60 }, 61 }, 62 } 63 fakeAzureBastionSpec1 = AzureBastionSpec{ 64 Name: "my-bastion-host", 65 ClusterName: "cluster", 66 Location: "westus", 67 SubnetID: "my-subnet-id", 68 PublicIPID: "my-public-ip-id", 69 Sku: infrav1.BastionHostSkuName("fake-SKU"), 70 EnableTunneling: false, 71 } 72 73 fakeBastionHostStatus = asonetworkv1.BastionHost_STATUS{ 74 Name: ptr.To(fakeAzureBastionSpec1.Name), 75 ProvisioningState: ptr.To(asonetworkv1.BastionHostProvisioningState_STATUS_Succeeded), 76 } 77 78 fakeBastionHostTags = map[string]string{ 79 "sigs.k8s.io_cluster-api-provider-azure_cluster_cluster": "owned", 80 "sigs.k8s.io_cluster-api-provider-azure_role": "Bastion", 81 "Name": fakeAzureBastionSpec1.Name, 82 } 83 ) 84 85 func getASOBastionHost(changes ...func(*asonetworkv1.BastionHost)) *asonetworkv1.BastionHost { 86 BastionHost := fakeBastionHost.DeepCopy() 87 for _, change := range changes { 88 change(BastionHost) 89 } 90 return BastionHost 91 } 92 93 func TestAzureBastionSpec_Parameters(t *testing.T) { 94 testcases := []struct { 95 name string 96 spec *AzureBastionSpec 97 existing *asonetworkv1.BastionHost 98 expect func(g *WithT, result asonetworkv1.BastionHost) 99 expectedError string 100 }{ 101 { 102 name: "Creating a new BastionHost", 103 spec: &fakeAzureBastionSpec1, 104 existing: nil, 105 expect: func(g *WithT, result asonetworkv1.BastionHost) { 106 g.Expect(result).To(Not(BeNil())) 107 108 // ObjectMeta is populated later in the codeflow 109 g.Expect(result.ObjectMeta).To(Equal(metav1.ObjectMeta{})) 110 111 // Spec is populated from the spec passed in 112 g.Expect(result.Spec).To(Equal(getASOBastionHost().Spec)) 113 }, 114 }, 115 { 116 name: "user updates to bastion hosts DisableCopyPaste should be accepted", 117 spec: &fakeAzureBastionSpec1, 118 existing: getASOBastionHost( 119 // user added DisableCopyPaste 120 func(bastion *asonetworkv1.BastionHost) { 121 bastion.Spec.DisableCopyPaste = ptr.To(true) 122 }, 123 // user added Status 124 func(bastion *asonetworkv1.BastionHost) { 125 bastion.Status = fakeBastionHostStatus 126 }, 127 ), 128 expect: func(g *WithT, result asonetworkv1.BastionHost) { 129 g.Expect(result).To(Not(BeNil())) 130 resultantASOBastionHost := getASOBastionHost( 131 func(bastion *asonetworkv1.BastionHost) { 132 bastion.Spec.DisableCopyPaste = ptr.To(true) 133 }, 134 ) 135 136 // ObjectMeta should be carried over from existing bastion host. 137 g.Expect(result.ObjectMeta).To(Equal(resultantASOBastionHost.ObjectMeta)) 138 139 // DisableCopyPaste addition is accepted. 140 g.Expect(result.Spec).To(Equal(resultantASOBastionHost.Spec)) 141 142 // Status should be carried over. 143 g.Expect(result.Status).To(Equal(fakeBastionHostStatus)) 144 }, 145 }, 146 { 147 name: "user updates to ASO's bastion hosts resource and capz should overwrite it", 148 spec: &fakeAzureBastionSpec1, 149 existing: getASOBastionHost( 150 // user added DisableCopyPaste 151 func(bastion *asonetworkv1.BastionHost) { 152 bastion.Spec.DisableCopyPaste = ptr.To(true) 153 }, 154 155 // user also added EnableTunneling which should be overwritten by capz 156 func(bastion *asonetworkv1.BastionHost) { 157 bastion.Spec.EnableTunneling = ptr.To(true) 158 }, 159 // user added Status 160 func(bastion *asonetworkv1.BastionHost) { 161 bastion.Status = fakeBastionHostStatus 162 }, 163 ), 164 expect: func(g *WithT, result asonetworkv1.BastionHost) { 165 g.Expect(result).To(Not(BeNil())) 166 resultantASOBastionHost := getASOBastionHost( 167 func(endpoint *asonetworkv1.BastionHost) { 168 endpoint.Spec.DisableCopyPaste = ptr.To(true) 169 }, 170 ) 171 172 // user changes except DisableCopyPaste should be overwritten. 173 g.Expect(result.ObjectMeta).To(Equal(resultantASOBastionHost.ObjectMeta)) 174 175 // DisableCopyPaste addition is accepted. 176 g.Expect(result.Spec).To(Equal(resultantASOBastionHost.Spec)) 177 178 // Status should be carried over. 179 g.Expect(result.Status).To(Equal(fakeBastionHostStatus)) 180 }, 181 }, 182 } 183 184 for _, tc := range testcases { 185 tc := tc 186 t.Run(tc.name, func(t *testing.T) { 187 g := NewWithT(t) 188 t.Parallel() 189 190 result, err := tc.spec.Parameters(context.TODO(), tc.existing) 191 if tc.expectedError != "" { 192 g.Expect(err).To(HaveOccurred()) 193 g.Expect(err).To(MatchError(tc.expectedError)) 194 } else { 195 g.Expect(err).NotTo(HaveOccurred()) 196 } 197 tc.expect(g, *result) 198 }) 199 } 200 }