sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/cluster/installer_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 cluster 18 19 import ( 20 "context" 21 "testing" 22 23 . "github.com/onsi/gomega" 24 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 25 "k8s.io/apimachinery/pkg/runtime/schema" 26 27 clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" 28 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" 29 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository" 30 "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test" 31 ) 32 33 func Test_providerInstaller_Validate(t *testing.T) { 34 fakeReader := test.NewFakeReader(). 35 WithProvider("cluster-api", clusterctlv1.CoreProviderType, "https://somewhere.com"). 36 WithProvider("infra1", clusterctlv1.InfrastructureProviderType, "https://somewhere.com"). 37 WithProvider("infra2", clusterctlv1.InfrastructureProviderType, "https://somewhere.com") 38 39 repositoryMap := map[string]repository.Repository{ 40 "cluster-api": repository.NewMemoryRepository(). 41 WithVersions("v0.9.0", "v1.0.0", "v1.0.1", "v2.0.0"). 42 WithMetadata("v0.9.0", &clusterctlv1.Metadata{ 43 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 44 {Major: 0, Minor: 9, Contract: test.PreviousCAPIContractNotSupported}, 45 }, 46 }). 47 WithMetadata("v1.0.0", &clusterctlv1.Metadata{ 48 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 49 {Major: 0, Minor: 9, Contract: test.PreviousCAPIContractNotSupported}, 50 {Major: 1, Minor: 0, Contract: test.CurrentCAPIContract}, 51 }, 52 }). 53 WithMetadata("v2.0.0", &clusterctlv1.Metadata{ 54 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 55 {Major: 0, Minor: 9, Contract: test.PreviousCAPIContractNotSupported}, 56 {Major: 1, Minor: 0, Contract: test.CurrentCAPIContract}, 57 {Major: 2, Minor: 0, Contract: test.NextCAPIContractNotSupported}, 58 }, 59 }), 60 "infrastructure-infra1": repository.NewMemoryRepository(). 61 WithVersions("v0.9.0", "v1.0.0", "v1.0.1", "v2.0.0"). 62 WithMetadata("v0.9.0", &clusterctlv1.Metadata{ 63 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 64 {Major: 0, Minor: 9, Contract: test.PreviousCAPIContractNotSupported}, 65 }, 66 }). 67 WithMetadata("v1.0.0", &clusterctlv1.Metadata{ 68 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 69 {Major: 0, Minor: 9, Contract: test.PreviousCAPIContractNotSupported}, 70 {Major: 1, Minor: 0, Contract: test.CurrentCAPIContract}, 71 }, 72 }). 73 WithMetadata("v2.0.0", &clusterctlv1.Metadata{ 74 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 75 {Major: 0, Minor: 9, Contract: test.PreviousCAPIContractNotSupported}, 76 {Major: 1, Minor: 0, Contract: test.CurrentCAPIContract}, 77 {Major: 2, Minor: 0, Contract: test.NextCAPIContractNotSupported}, 78 }, 79 }), 80 "infrastructure-infra2": repository.NewMemoryRepository(). 81 WithVersions("v0.9.0", "v1.0.0", "v1.0.1", "v2.0.0"). 82 WithMetadata("v0.9.0", &clusterctlv1.Metadata{ 83 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 84 {Major: 0, Minor: 9, Contract: test.PreviousCAPIContractNotSupported}, 85 }, 86 }). 87 WithMetadata("v1.0.0", &clusterctlv1.Metadata{ 88 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 89 {Major: 1, Minor: 0, Contract: test.CurrentCAPIContract}, 90 }, 91 }). 92 WithMetadata("v2.0.0", &clusterctlv1.Metadata{ 93 ReleaseSeries: []clusterctlv1.ReleaseSeries{ 94 {Major: 1, Minor: 0, Contract: test.CurrentCAPIContract}, 95 {Major: 2, Minor: 0, Contract: test.NextCAPIContractNotSupported}, 96 }, 97 }), 98 } 99 100 type fields struct { 101 proxy Proxy 102 installQueue []repository.Components 103 } 104 tests := []struct { 105 name string 106 fields fields 107 wantErr bool 108 }{ 109 { 110 name: "install core/current contract + infra1/current contract on an empty cluster", 111 fields: fields{ 112 proxy: test.NewFakeProxy(), // empty cluster 113 installQueue: []repository.Components{ 114 newFakeComponents("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"), 115 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra1-system"), 116 }, 117 }, 118 wantErr: false, 119 }, 120 { 121 name: "install infra2/current contract on a cluster already initialized with core/current contract + infra1/current contract", 122 fields: fields{ 123 proxy: test.NewFakeProxy(). 124 WithProviderInventory("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"). 125 WithProviderInventory("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra1-system"), 126 installQueue: []repository.Components{ 127 newFakeComponents("infra2", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra2-system"), 128 }, 129 }, 130 wantErr: false, 131 }, 132 { 133 name: "install another instance of infra1/current contract on a cluster already initialized with core/current contract + infra1/current contract", 134 fields: fields{ 135 proxy: test.NewFakeProxy(). 136 WithProviderInventory("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"). 137 WithProviderInventory("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "ns1"), 138 installQueue: []repository.Components{ 139 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "ns2"), 140 }, 141 }, 142 wantErr: true, 143 }, 144 { 145 name: "install another instance of infra1/current contract on a cluster already initialized with core/current contract + infra1/current contract, same namespace of the existing infra1", 146 fields: fields{ 147 proxy: test.NewFakeProxy(). 148 WithProviderInventory("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"). 149 WithProviderInventory("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "n1"), 150 installQueue: []repository.Components{ 151 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "n1"), 152 }, 153 }, 154 wantErr: true, 155 }, 156 { 157 name: "install another instance of infra1/current contract on a cluster already initialized with core/current contract + infra1/current contract, different namespace of the existing infra1", 158 fields: fields{ 159 proxy: test.NewFakeProxy(). 160 WithProviderInventory("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"). 161 WithProviderInventory("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "n1"), 162 installQueue: []repository.Components{ 163 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "n2"), 164 }, 165 }, 166 wantErr: true, 167 }, 168 { 169 name: "install core/previous contract + infra1/previous contract on an empty cluster (not supported)", 170 fields: fields{ 171 proxy: test.NewFakeProxy(), // empty cluster 172 installQueue: []repository.Components{ 173 newFakeComponents("cluster-api", clusterctlv1.CoreProviderType, "v0.9.0", "cluster-api-system"), 174 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v0.9.0", "infra1-system"), 175 }, 176 }, 177 wantErr: true, 178 }, 179 { 180 name: "install core/previous contract + infra1/current contract on an empty cluster (not supported)", 181 fields: fields{ 182 proxy: test.NewFakeProxy(), // empty cluster 183 installQueue: []repository.Components{ 184 newFakeComponents("cluster-api", clusterctlv1.CoreProviderType, "v0.9.0", "cluster-api-system"), 185 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra1-system"), 186 }, 187 }, 188 wantErr: true, 189 }, 190 { 191 name: "install infra1/previous contract (not supported) on a cluster already initialized with core/current contract", 192 fields: fields{ 193 proxy: test.NewFakeProxy(). 194 WithProviderInventory("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "ns1"), 195 installQueue: []repository.Components{ 196 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v0.9.0", "infra1-system"), 197 }, 198 }, 199 wantErr: true, 200 }, 201 { 202 name: "install core/next contract + infra1/next contract on an empty cluster (not supported)", 203 fields: fields{ 204 proxy: test.NewFakeProxy(), // empty cluster 205 installQueue: []repository.Components{ 206 newFakeComponents("cluster-api", clusterctlv1.CoreProviderType, "v2.0.0", "cluster-api-system"), 207 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v2.0.0", "infra1-system"), 208 }, 209 }, 210 wantErr: true, 211 }, 212 { 213 name: "install core/current contract + infra1/next contract on an empty cluster (not supported)", 214 fields: fields{ 215 proxy: test.NewFakeProxy(), // empty cluster 216 installQueue: []repository.Components{ 217 newFakeComponents("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"), 218 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v2.0.0", "infra1-system"), 219 }, 220 }, 221 wantErr: true, 222 }, 223 { 224 name: "install infra1/next contract (not supported) on a cluster already initialized with core/current contract", 225 fields: fields{ 226 proxy: test.NewFakeProxy(). 227 WithProviderInventory("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "ns1"), 228 installQueue: []repository.Components{ 229 newFakeComponents("infra1", clusterctlv1.InfrastructureProviderType, "v2.0.0", "infra1-system"), 230 }, 231 }, 232 wantErr: true, 233 }, 234 } 235 236 for _, tt := range tests { 237 t.Run(tt.name, func(t *testing.T) { 238 g := NewWithT(t) 239 240 ctx := context.Background() 241 242 configClient, _ := config.New(ctx, "", config.InjectReader(fakeReader)) 243 244 i := &providerInstaller{ 245 configClient: configClient, 246 proxy: tt.fields.proxy, 247 providerInventory: newInventoryClient(tt.fields.proxy, nil), 248 repositoryClientFactory: func(ctx context.Context, provider config.Provider, configClient config.Client, _ ...repository.Option) (repository.Client, error) { 249 return repository.New(ctx, provider, configClient, repository.InjectRepository(repositoryMap[provider.ManifestLabel()])) 250 }, 251 installQueue: tt.fields.installQueue, 252 } 253 254 err := i.Validate(ctx) 255 if tt.wantErr { 256 g.Expect(err).To(HaveOccurred()) 257 } else { 258 g.Expect(err).ToNot(HaveOccurred()) 259 } 260 }) 261 } 262 } 263 264 func Test_providerInstaller_ValidateCRDName(t *testing.T) { 265 tests := []struct { 266 name string 267 crd unstructured.Unstructured 268 gk *schema.GroupKind 269 wantErr bool 270 }{ 271 { 272 name: "CRD with valid name", 273 crd: newFakeCRD("clusterclasses.cluster.x-k8s.io", nil), 274 gk: &schema.GroupKind{Group: "cluster.x-k8s.io", Kind: "ClusterClass"}, 275 wantErr: false, 276 }, 277 { 278 name: "CRD with invalid name", 279 crd: newFakeCRD("clusterclass.cluster.x-k8s.io", nil), 280 gk: &schema.GroupKind{Group: "cluster.x-k8s.io", Kind: "ClusterClass"}, 281 wantErr: true, 282 }, 283 { 284 name: "CRD with invalid name but has skip annotation", 285 crd: newFakeCRD("clusterclass.cluster.x-k8s.io", map[string]string{ 286 clusterctlv1.SkipCRDNamePreflightCheckAnnotation: "", 287 }), 288 gk: &schema.GroupKind{Group: "cluster.x-k8s.io", Kind: "ClusterClass"}, 289 wantErr: false, 290 }, 291 } 292 293 for _, tt := range tests { 294 t.Run(tt.name, func(t *testing.T) { 295 g := NewWithT(t) 296 297 err := validateCRDName(tt.crd, tt.gk) 298 if tt.wantErr { 299 g.Expect(err).To(HaveOccurred()) 300 } else { 301 g.Expect(err).ToNot(HaveOccurred()) 302 } 303 }) 304 } 305 } 306 307 type fakeComponents struct { 308 config.Provider 309 inventoryObject clusterctlv1.Provider 310 } 311 312 func (c *fakeComponents) Version() string { 313 panic("not implemented") 314 } 315 316 func (c *fakeComponents) Variables() []string { 317 panic("not implemented") 318 } 319 320 func (c *fakeComponents) Images() []string { 321 panic("not implemented") 322 } 323 324 func (c *fakeComponents) TargetNamespace() string { 325 panic("not implemented") 326 } 327 328 func (c *fakeComponents) InventoryObject() clusterctlv1.Provider { 329 return c.inventoryObject 330 } 331 332 func (c *fakeComponents) Objs() []unstructured.Unstructured { 333 return []unstructured.Unstructured{} 334 } 335 336 func (c *fakeComponents) Yaml() ([]byte, error) { 337 panic("not implemented") 338 } 339 340 func newFakeComponents(name string, providerType clusterctlv1.ProviderType, version, targetNamespace string) repository.Components { 341 inventoryObject := fakeProvider(name, providerType, version, targetNamespace) 342 return &fakeComponents{ 343 Provider: config.NewProvider(inventoryObject.ProviderName, "", clusterctlv1.ProviderType(inventoryObject.Type)), 344 inventoryObject: inventoryObject, 345 } 346 } 347 348 func newFakeCRD(name string, annotations map[string]string) unstructured.Unstructured { 349 u := unstructured.Unstructured{} 350 u.SetName(name) 351 u.SetAnnotations(annotations) 352 return u 353 }