sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/upgrade_test.go (about) 1 /* 2 Copyright 2020 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 client 18 19 import ( 20 "context" 21 "sort" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 . "github.com/onsi/gomega" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 28 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 29 clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" 30 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster" 31 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" 32 "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test" 33 ) 34 35 func Test_clusterctlClient_PlanCertUpgrade(t *testing.T) { 36 // create a fake config with a provider named P1 and a variable named var 37 repository1Config := config.NewProvider("p1", "url", clusterctlv1.CoreProviderType) 38 39 ctx := context.Background() 40 41 config1 := newFakeConfig(ctx). 42 WithVar("var", "value"). 43 WithProvider(repository1Config) 44 45 // create a fake repository with some YAML files in it (usually matching 46 // the list of providers defined in the config) 47 repository1 := newFakeRepository(ctx, repository1Config, config1). 48 WithPaths("root", "components"). 49 WithDefaultVersion("v1.0"). 50 WithFile("v1.0", "components.yaml", []byte("content")) 51 52 certManagerPlan := CertManagerUpgradePlan{ 53 From: "v0.16.1", 54 To: "v1.1.0", 55 ShouldUpgrade: true, 56 } 57 // create a fake cluster, with a cert manager client that has an upgrade 58 // plan 59 cluster1 := newFakeCluster(cluster.Kubeconfig{Path: "cluster1"}, config1). 60 WithCertManagerClient(newFakeCertManagerClient(nil, nil).WithCertManagerPlan(certManagerPlan)) 61 62 client := newFakeClient(ctx, config1). 63 WithRepository(repository1). 64 WithCluster(cluster1) 65 66 tests := []struct { 67 name string 68 client *fakeClient 69 expectErr bool 70 }{ 71 { 72 name: "returns plan for upgrading cert-manager", 73 client: client, 74 expectErr: false, 75 }, 76 } 77 78 for _, tt := range tests { 79 t.Run(tt.name, func(t *testing.T) { 80 g := NewWithT(t) 81 82 ctx := context.Background() 83 84 options := PlanUpgradeOptions{ 85 Kubeconfig: Kubeconfig{Path: "cluster1"}, 86 } 87 actualPlan, err := tt.client.PlanCertManagerUpgrade(ctx, options) 88 if tt.expectErr { 89 g.Expect(err).To(HaveOccurred()) 90 g.Expect(actualPlan).To(BeComparableTo(CertManagerUpgradePlan{})) 91 return 92 } 93 g.Expect(err).ToNot(HaveOccurred()) 94 g.Expect(actualPlan).To(BeComparableTo(certManagerPlan)) 95 }) 96 } 97 } 98 99 func Test_clusterctlClient_PlanUpgrade(t *testing.T) { 100 type fields struct { 101 client *fakeClient 102 } 103 type args struct { 104 options PlanUpgradeOptions 105 } 106 tests := []struct { 107 name string 108 fields fields 109 args args 110 wantErr bool 111 }{ 112 { 113 name: "does not return error if cluster client is found", 114 fields: fields{ 115 client: fakeClientForUpgrade(), // core v1.0.0 (v1.0.1 available), infra v2.0.0 (v2.0.1 available) 116 }, 117 args: args{ 118 options: PlanUpgradeOptions{ 119 Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, 120 }, 121 }, 122 wantErr: false, 123 }, 124 { 125 name: "returns an error if cluster client is not found", 126 fields: fields{ 127 client: fakeClientForUpgrade(), // core v1.0.0 (v1.0.1 available), infra v2.0.0 (v2.0.1 available) 128 }, 129 args: args{ 130 options: PlanUpgradeOptions{ 131 Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "some-other-context"}, 132 }, 133 }, 134 wantErr: true, 135 }, 136 } 137 138 for _, tt := range tests { 139 t.Run(tt.name, func(t *testing.T) { 140 g := NewWithT(t) 141 142 ctx := context.Background() 143 144 _, err := tt.fields.client.PlanUpgrade(ctx, tt.args.options) 145 if tt.wantErr { 146 g.Expect(err).To(HaveOccurred()) 147 return 148 } 149 g.Expect(err).ToNot(HaveOccurred()) 150 }) 151 } 152 } 153 154 func Test_clusterctlClient_ApplyUpgrade(t *testing.T) { 155 type fields struct { 156 client *fakeClient 157 } 158 type args struct { 159 options ApplyUpgradeOptions 160 } 161 tests := []struct { 162 name string 163 fields fields 164 args args 165 wantProviders *clusterctlv1.ProviderList 166 wantErr bool 167 }{ 168 { 169 name: "apply a plan", 170 fields: fields{ 171 client: fakeClientForUpgrade(), // core v1.0.0 (v1.0.1 available), infra v2.0.0 (v2.0.1 available) 172 }, 173 args: args{ 174 options: ApplyUpgradeOptions{ 175 Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, 176 Contract: test.CurrentCAPIContract, 177 CoreProvider: "", 178 BootstrapProviders: nil, 179 ControlPlaneProviders: nil, 180 InfrastructureProviders: nil, 181 }, 182 }, 183 wantProviders: &clusterctlv1.ProviderList{ 184 ListMeta: metav1.ListMeta{}, 185 Items: []clusterctlv1.Provider{ // both providers should be upgraded 186 fakeProvider("cluster-api", clusterctlv1.CoreProviderType, "v1.0.1", "cluster-api-system"), 187 fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v2.0.1", "infra-system"), 188 }, 189 }, 190 wantErr: false, 191 }, 192 { 193 name: "apply a custom plan - core provider only", 194 fields: fields{ 195 client: fakeClientForUpgrade(), // core v1.0.0 (v1.0.1 available), infra v2.0.0 (v2.0.1 available) 196 }, 197 args: args{ 198 options: ApplyUpgradeOptions{ 199 Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, 200 Contract: "", 201 CoreProvider: "cluster-api-system/cluster-api:v1.0.1", 202 BootstrapProviders: nil, 203 ControlPlaneProviders: nil, 204 InfrastructureProviders: nil, 205 }, 206 }, 207 wantProviders: &clusterctlv1.ProviderList{ 208 ListMeta: metav1.ListMeta{}, 209 Items: []clusterctlv1.Provider{ // only one provider should be upgraded 210 fakeProvider("cluster-api", clusterctlv1.CoreProviderType, "v1.0.1", "cluster-api-system"), 211 fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v2.0.0", "infra-system"), 212 }, 213 }, 214 wantErr: false, 215 }, 216 { 217 name: "apply a custom plan - infra provider only", 218 fields: fields{ 219 client: fakeClientForUpgrade(), // core v1.0.0 (v1.0.1 available), infra v2.0.0 (v2.0.1 available) 220 }, 221 args: args{ 222 options: ApplyUpgradeOptions{ 223 Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, 224 Contract: "", 225 CoreProvider: "", 226 BootstrapProviders: nil, 227 ControlPlaneProviders: nil, 228 InfrastructureProviders: []string{"infra-system/infra:v2.0.1"}, 229 }, 230 }, 231 wantProviders: &clusterctlv1.ProviderList{ 232 ListMeta: metav1.ListMeta{}, 233 Items: []clusterctlv1.Provider{ // only one provider should be upgraded 234 fakeProvider("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"), 235 fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v2.0.1", "infra-system"), 236 }, 237 }, 238 wantErr: false, 239 }, 240 { 241 name: "apply a custom plan - both providers", 242 fields: fields{ 243 client: fakeClientForUpgrade(), // core v1.0.0 (v1.0.1 available), infra v2.0.0 (v2.0.1 available) 244 }, 245 args: args{ 246 options: ApplyUpgradeOptions{ 247 Kubeconfig: Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, 248 Contract: "", 249 CoreProvider: "cluster-api-system/cluster-api:v1.0.1", 250 BootstrapProviders: nil, 251 ControlPlaneProviders: nil, 252 InfrastructureProviders: []string{"infra-system/infra:v2.0.1"}, 253 }, 254 }, 255 wantProviders: &clusterctlv1.ProviderList{ 256 ListMeta: metav1.ListMeta{}, 257 Items: []clusterctlv1.Provider{ 258 fakeProvider("cluster-api", clusterctlv1.CoreProviderType, "v1.0.1", "cluster-api-system"), 259 fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v2.0.1", "infra-system"), 260 }, 261 }, 262 wantErr: false, 263 }, 264 } 265 for _, tt := range tests { 266 t.Run(tt.name, func(t *testing.T) { 267 g := NewWithT(t) 268 269 ctx := context.Background() 270 271 err := tt.fields.client.ApplyUpgrade(ctx, tt.args.options) 272 if tt.wantErr { 273 g.Expect(err).To(HaveOccurred()) 274 return 275 } 276 g.Expect(err).ToNot(HaveOccurred()) 277 278 // converting between client and cluster alias for Kubeconfig 279 input := cluster.Kubeconfig(tt.args.options.Kubeconfig) 280 proxy := tt.fields.client.clusters[input].Proxy() 281 gotProviders := &clusterctlv1.ProviderList{} 282 283 c, err := proxy.NewClient(ctx) 284 g.Expect(err).ToNot(HaveOccurred()) 285 286 g.Expect(c.List(ctx, gotProviders)).To(Succeed()) 287 288 sort.Slice(gotProviders.Items, func(i, j int) bool { 289 return gotProviders.Items[i].Name < gotProviders.Items[j].Name 290 }) 291 sort.Slice(tt.wantProviders.Items, func(i, j int) bool { 292 return tt.wantProviders.Items[i].Name < tt.wantProviders.Items[j].Name 293 }) 294 for i := range gotProviders.Items { 295 tt.wantProviders.Items[i].ResourceVersion = gotProviders.Items[i].ResourceVersion 296 } 297 g.Expect(gotProviders).To(BeComparableTo(tt.wantProviders), cmp.Diff(gotProviders, tt.wantProviders)) 298 }) 299 } 300 } 301 302 func fakeClientForUpgrade() *fakeClient { 303 core := config.NewProvider("cluster-api", "https://somewhere.com", clusterctlv1.CoreProviderType) 304 infra := config.NewProvider("infra", "https://somewhere.com", clusterctlv1.InfrastructureProviderType) 305 306 ctx := context.Background() 307 308 config1 := newFakeConfig(ctx). 309 WithProvider(core). 310 WithProvider(infra) 311 312 repository1 := newFakeRepository(ctx, core, config1). 313 WithPaths("root", "components.yaml"). 314 WithDefaultVersion("v1.0.1"). 315 WithFile("v1.0.1", "components.yaml", componentsYAML("ns2")). 316 WithVersions("v1.0.0", "v1.0.1"). 317 WithMetadata("v1.0.1", &clusterctlv1.Metadata{ 318 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 319 {Major: 1, Minor: 0, Contract: test.CurrentCAPIContract}, 320 }, 321 }) 322 repository2 := newFakeRepository(ctx, infra, config1). 323 WithPaths("root", "components.yaml"). 324 WithDefaultVersion("v2.0.0"). 325 WithFile("v2.0.1", "components.yaml", componentsYAML("ns2")). 326 WithVersions("v2.0.0", "v2.0.1"). 327 WithMetadata("v2.0.1", &clusterctlv1.Metadata{ 328 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 329 {Major: 2, Minor: 0, Contract: test.CurrentCAPIContract}, 330 }, 331 }) 332 333 cluster1 := newFakeCluster(cluster.Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, config1). 334 WithRepository(repository1). 335 WithRepository(repository2). 336 WithProviderInventory(core.Name(), core.Type(), "v1.0.0", "cluster-api-system"). 337 WithProviderInventory(infra.Name(), infra.Type(), "v2.0.0", "infra-system"). 338 WithObjs(test.FakeCAPISetupObjects()...) 339 340 client := newFakeClient(ctx, config1). 341 WithRepository(repository1). 342 WithRepository(repository2). 343 WithCluster(cluster1) 344 345 return client 346 } 347 348 func fakeProvider(name string, providerType clusterctlv1.ProviderType, version, targetNamespace string) clusterctlv1.Provider { 349 return clusterctlv1.Provider{ 350 TypeMeta: metav1.TypeMeta{ 351 APIVersion: clusterctlv1.GroupVersion.String(), 352 Kind: "Provider", 353 }, 354 ObjectMeta: metav1.ObjectMeta{ 355 Namespace: targetNamespace, 356 Name: clusterctlv1.ManifestLabel(name, providerType), 357 Labels: map[string]string{ 358 clusterctlv1.ClusterctlLabel: "", 359 clusterv1.ProviderNameLabel: clusterctlv1.ManifestLabel(name, providerType), 360 clusterctlv1.ClusterctlCoreLabel: clusterctlv1.ClusterctlCoreLabelInventoryValue, 361 }, 362 }, 363 ProviderName: name, 364 Type: string(providerType), 365 Version: version, 366 WatchedNamespace: "", 367 } 368 } 369 370 func Test_parseUpgradeItem(t *testing.T) { 371 type args struct { 372 provider string 373 } 374 375 ctx := context.Background() 376 377 configClient := newFakeConfig(ctx) 378 clusterClient := newFakeCluster(cluster.Kubeconfig{Path: "cluster1"}, configClient) 379 clusterClient.WithProviderInventory("best-provider", clusterctlv1.CoreProviderType, "v1.0.0", "best-provider-system") 380 381 tests := []struct { 382 name string 383 args args 384 want *cluster.UpgradeItem 385 wantErr bool 386 }{ 387 { 388 name: "namespace/provider", 389 args: args{ 390 provider: "namespace/provider", 391 }, 392 want: &cluster.UpgradeItem{ 393 Provider: clusterctlv1.Provider{ 394 ObjectMeta: metav1.ObjectMeta{ 395 Namespace: "namespace", 396 Name: clusterctlv1.ManifestLabel("provider", clusterctlv1.CoreProviderType), 397 }, 398 ProviderName: "provider", 399 Type: string(clusterctlv1.CoreProviderType), 400 }, 401 NextVersion: "", 402 }, 403 wantErr: false, 404 }, 405 { 406 name: "namespace/provider:version", 407 args: args{ 408 provider: "namespace/provider:version", 409 }, 410 want: &cluster.UpgradeItem{ 411 Provider: clusterctlv1.Provider{ 412 ObjectMeta: metav1.ObjectMeta{ 413 Namespace: "namespace", 414 Name: clusterctlv1.ManifestLabel("provider", clusterctlv1.CoreProviderType), 415 }, 416 ProviderName: "provider", 417 Type: string(clusterctlv1.CoreProviderType), 418 }, 419 NextVersion: "version", 420 }, 421 wantErr: false, 422 }, 423 { 424 name: "provider:version", 425 args: args{ 426 provider: "best-provider:v1.0.0", 427 }, 428 want: &cluster.UpgradeItem{ 429 Provider: clusterctlv1.Provider{ 430 ObjectMeta: metav1.ObjectMeta{ 431 Namespace: "best-provider-system", 432 Name: clusterctlv1.ManifestLabel("best-provider", clusterctlv1.CoreProviderType), 433 }, 434 ProviderName: "best-provider", 435 Type: string(clusterctlv1.CoreProviderType), 436 }, 437 NextVersion: "v1.0.0", 438 }, 439 wantErr: false, 440 }, 441 { 442 name: "provider: with no version", 443 args: args{ 444 provider: "provider:", 445 }, 446 want: nil, 447 wantErr: true, 448 }, 449 { 450 name: "provider with no version", 451 args: args{ 452 provider: "provider", 453 }, 454 want: nil, 455 wantErr: true, 456 }, 457 { 458 name: "namespace empty", 459 args: args{ 460 provider: "/provider:version", 461 }, 462 want: nil, 463 wantErr: true, 464 }, 465 } 466 for _, tt := range tests { 467 t.Run(tt.name, func(t *testing.T) { 468 g := NewWithT(t) 469 470 ctx := context.Background() 471 472 got, err := parseUpgradeItem(ctx, clusterClient, tt.args.provider, clusterctlv1.CoreProviderType) 473 if tt.wantErr { 474 g.Expect(err).To(HaveOccurred()) 475 return 476 } 477 g.Expect(err).ToNot(HaveOccurred()) 478 479 g.Expect(got).To(BeComparableTo(tt.want)) 480 }) 481 } 482 }