sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/cluster/inventory_test.go (about) 1 /* 2 Copyright 2019 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 cluster 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 . "github.com/onsi/gomega" 25 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/util/wait" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 30 clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" 31 "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test" 32 ) 33 34 func fakePollImmediateWaiter(_ context.Context, _, _ time.Duration, _ wait.ConditionWithContextFunc) error { 35 return nil 36 } 37 38 func Test_inventoryClient_CheckInventoryCRDs(t *testing.T) { 39 type fields struct { 40 alreadyHasCRD bool 41 } 42 tests := []struct { 43 name string 44 fields fields 45 want bool 46 wantErr bool 47 }{ 48 { 49 name: "Has not CRD", 50 fields: fields{ 51 alreadyHasCRD: false, 52 }, 53 want: false, 54 wantErr: false, 55 }, 56 { 57 name: "Already has CRD", 58 fields: fields{ 59 alreadyHasCRD: true, 60 }, 61 want: true, 62 wantErr: false, 63 }, 64 } 65 for _, tt := range tests { 66 t.Run(tt.name, func(t *testing.T) { 67 g := NewWithT(t) 68 69 ctx := context.Background() 70 71 proxy := test.NewFakeProxy() 72 p := newInventoryClient(proxy, fakePollImmediateWaiter) 73 if tt.fields.alreadyHasCRD { 74 // forcing creation of metadata before test 75 g.Expect(p.EnsureCustomResourceDefinitions(ctx)).To(Succeed()) 76 } 77 78 res, err := checkInventoryCRDs(ctx, proxy) 79 g.Expect(res).To(Equal(tt.want)) 80 if tt.wantErr { 81 g.Expect(err).To(HaveOccurred()) 82 } else { 83 g.Expect(err).ToNot(HaveOccurred()) 84 } 85 }) 86 } 87 } 88 89 var fooProvider = clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns1", ResourceVersion: "999"}} 90 var v1alpha4Contract = "v1alpha4" 91 92 func Test_inventoryClient_List(t *testing.T) { 93 type fields struct { 94 initObjs []client.Object 95 } 96 tests := []struct { 97 name string 98 fields fields 99 want []clusterctlv1.Provider 100 wantErr bool 101 }{ 102 { 103 name: "Get list", 104 fields: fields{ 105 initObjs: []client.Object{ 106 &fooProvider, 107 }, 108 }, 109 want: []clusterctlv1.Provider{ 110 fooProvider, 111 }, 112 wantErr: false, 113 }, 114 } 115 for _, tt := range tests { 116 t.Run(tt.name, func(t *testing.T) { 117 g := NewWithT(t) 118 119 p := newInventoryClient(test.NewFakeProxy().WithObjs(tt.fields.initObjs...), fakePollImmediateWaiter) 120 got, err := p.List(context.Background()) 121 if tt.wantErr { 122 g.Expect(err).To(HaveOccurred()) 123 return 124 } 125 126 g.Expect(err).ToNot(HaveOccurred()) 127 g.Expect(got.Items).To(ConsistOf(tt.want)) 128 }) 129 } 130 } 131 132 func Test_inventoryClient_Create(t *testing.T) { 133 type fields struct { 134 proxy Proxy 135 } 136 type args struct { 137 m clusterctlv1.Provider 138 } 139 providerV2 := fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v0.2.0", "") 140 // since this test object is used in a Create request, wherein setting ResourceVersion should no be set 141 providerV2.ResourceVersion = "" 142 providerV3 := fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v0.3.0", "") 143 144 tests := []struct { 145 name string 146 fields fields 147 args args 148 wantProviders []clusterctlv1.Provider 149 wantErr bool 150 }{ 151 { 152 name: "Creates a provider", 153 fields: fields{ 154 proxy: test.NewFakeProxy(), 155 }, 156 args: args{ 157 m: providerV2, 158 }, 159 wantProviders: []clusterctlv1.Provider{ 160 providerV2, 161 }, 162 wantErr: false, 163 }, 164 { 165 name: "Patches a provider", 166 fields: fields{ 167 proxy: test.NewFakeProxy().WithObjs(&providerV2), 168 }, 169 args: args{ 170 m: providerV3, 171 }, 172 wantProviders: []clusterctlv1.Provider{ 173 providerV3, 174 }, 175 wantErr: false, 176 }, 177 } 178 for _, tt := range tests { 179 t.Run(tt.name, func(t *testing.T) { 180 g := NewWithT(t) 181 182 ctx := context.Background() 183 184 p := &inventoryClient{ 185 proxy: tt.fields.proxy, 186 } 187 err := p.Create(ctx, tt.args.m) 188 if tt.wantErr { 189 g.Expect(err).To(HaveOccurred()) 190 return 191 } 192 193 g.Expect(err).ToNot(HaveOccurred()) 194 195 got, err := p.List(ctx) 196 if tt.wantErr { 197 g.Expect(err).To(HaveOccurred()) 198 return 199 } 200 201 g.Expect(err).ToNot(HaveOccurred()) 202 203 for i := range got.Items { 204 tt.wantProviders[i].ResourceVersion = got.Items[i].ResourceVersion 205 } 206 207 g.Expect(got.Items).To(ConsistOf(tt.wantProviders)) 208 }) 209 } 210 } 211 212 func Test_CheckCAPIContract(t *testing.T) { 213 type args struct { 214 options []CheckCAPIContractOption 215 } 216 type fields struct { 217 proxy Proxy 218 } 219 220 tests := []struct { 221 name string 222 fields fields 223 args args 224 wantErr bool 225 }{ 226 { 227 name: "Fails if Cluster API is not installed", 228 fields: fields{ 229 proxy: test.NewFakeProxy().WithObjs(), 230 }, 231 args: args{}, 232 wantErr: true, 233 }, 234 { 235 name: "Pass if Cluster API is not installed, but this is explicitly tolerated", 236 fields: fields{ 237 proxy: test.NewFakeProxy().WithObjs(), 238 }, 239 args: args{ 240 options: []CheckCAPIContractOption{AllowCAPINotInstalled{}}, 241 }, 242 wantErr: false, 243 }, 244 { 245 name: "Pass when Cluster API with current contract is installed", 246 fields: fields{ 247 proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{ 248 ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"}, 249 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 250 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 251 { 252 Name: test.PreviousCAPIContractNotSupported, 253 }, 254 { 255 Name: test.CurrentCAPIContract, 256 Storage: true, 257 }, 258 }, 259 }, 260 }), 261 }, 262 args: args{}, 263 wantErr: false, 264 }, 265 { 266 name: "Fails when Cluster API with previous contract is installed", 267 fields: fields{ 268 proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{ 269 ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"}, 270 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 271 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 272 { 273 Name: test.PreviousCAPIContractNotSupported, 274 Storage: true, 275 }, 276 { 277 Name: test.CurrentCAPIContract, 278 }, 279 }, 280 }, 281 }), 282 }, 283 args: args{}, 284 wantErr: true, 285 }, 286 { 287 name: "Pass when Cluster API with previous contract is installed, but this is explicitly tolerated", 288 fields: fields{ 289 proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{ 290 ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"}, 291 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 292 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 293 { 294 Name: test.PreviousCAPIContractNotSupported, 295 Storage: true, 296 }, 297 { 298 Name: test.CurrentCAPIContract, 299 }, 300 }, 301 }, 302 }), 303 }, 304 args: args{ 305 options: []CheckCAPIContractOption{AllowCAPIContract{Contract: v1alpha4Contract}, AllowCAPIContract{Contract: test.PreviousCAPIContractNotSupported}}, 306 }, 307 wantErr: false, 308 }, 309 { 310 name: "Fails when Cluster API with next contract is installed", 311 fields: fields{ 312 proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{ 313 ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"}, 314 Spec: apiextensionsv1.CustomResourceDefinitionSpec{ 315 Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ 316 { 317 Name: test.CurrentCAPIContract, 318 }, 319 { 320 Name: test.NextCAPIContractNotSupported, 321 Storage: true, 322 }, 323 }, 324 }, 325 }), 326 }, 327 args: args{}, 328 wantErr: true, 329 }, 330 } 331 for _, tt := range tests { 332 t.Run(tt.name, func(t *testing.T) { 333 g := NewWithT(t) 334 335 p := &inventoryClient{ 336 proxy: tt.fields.proxy, 337 } 338 err := p.CheckCAPIContract(context.Background(), tt.args.options...) 339 if tt.wantErr { 340 g.Expect(err).To(HaveOccurred()) 341 return 342 } 343 g.Expect(err).ToNot(HaveOccurred()) 344 }) 345 } 346 } 347 348 func Test_inventoryClient_CheckSingleProviderInstance(t *testing.T) { 349 type fields struct { 350 initObjs []client.Object 351 } 352 tests := []struct { 353 name string 354 fields fields 355 wantErr bool 356 }{ 357 { 358 name: "Returns error when there are multiple instances of the same provider", 359 fields: fields{ 360 initObjs: []client.Object{ 361 &clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns1"}}, 362 &clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns2"}}, 363 &clusterctlv1.Provider{Type: string(clusterctlv1.InfrastructureProviderType), ProviderName: "bar", ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "ns2"}}, 364 }, 365 }, 366 wantErr: true, 367 }, 368 { 369 name: "Does not return error when there is only single instance of all providers", 370 fields: fields{ 371 initObjs: []client.Object{ 372 &clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns1"}}, 373 &clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo-1", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns2"}}, 374 &clusterctlv1.Provider{Type: string(clusterctlv1.InfrastructureProviderType), ProviderName: "bar", ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "ns2"}}, 375 }, 376 }, 377 wantErr: false, 378 }, 379 } 380 for _, tt := range tests { 381 t.Run(tt.name, func(t *testing.T) { 382 g := NewWithT(t) 383 384 p := newInventoryClient(test.NewFakeProxy().WithObjs(tt.fields.initObjs...), fakePollImmediateWaiter) 385 err := p.CheckSingleProviderInstance(context.Background()) 386 if tt.wantErr { 387 g.Expect(err).To(HaveOccurred()) 388 return 389 } 390 391 g.Expect(err).ToNot(HaveOccurred()) 392 }) 393 } 394 }