github.com/ironcore-dev/gardener-extension-provider-ironcore@v0.3.2-0.20240314231816-8336447fb9a0/pkg/controller/controlplane/valuesprovider_test.go (about) 1 // SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and IronCore contributors 2 // SPDX-License-Identifier: Apache-2.0 3 4 package controlplane 5 6 import ( 7 "os" 8 "path/filepath" 9 10 "github.com/gardener/gardener/extensions/pkg/controller" 11 gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" 12 extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" 13 secretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager" 14 fakesecretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager/fake" 15 "github.com/ironcore-dev/ironcore/api/common/v1alpha1" 16 corev1alpha1 "github.com/ironcore-dev/ironcore/api/core/v1alpha1" 17 storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" 18 . "github.com/onsi/ginkgo/v2" 19 . "github.com/onsi/gomega" 20 corev1 "k8s.io/api/core/v1" 21 "k8s.io/apimachinery/pkg/api/resource" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/util/json" 26 "k8s.io/apimachinery/pkg/util/yaml" 27 "k8s.io/utils/ptr" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" 30 . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" 31 32 apisironcore "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/apis/ironcore" 33 "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/internal" 34 "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/ironcore" 35 ) 36 37 var _ = Describe("Valueprovider Reconcile", func() { 38 ns, vp, cluster := SetupTest() 39 40 var ( 41 fakeClient client.Client 42 fakeSecretsManager secretsmanager.Interface 43 ) 44 45 BeforeEach(func(ctx SpecContext) { 46 curDir, err := os.Getwd() 47 Expect(err).NotTo(HaveOccurred()) 48 Expect(os.Chdir(filepath.Join("..", "..", ".."))).To(Succeed()) 49 DeferCleanup(os.Chdir, curDir) 50 51 fakeClient = fakeclient.NewClientBuilder().Build() 52 fakeSecretsManager = fakesecretsmanager.New(fakeClient, ns.Name) 53 Expect(fakeClient.Create(ctx, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "cloud-controller-manager-server", Namespace: ns.Name}})).To(Succeed()) 54 }) 55 56 Describe("#GetConfigChartValues", func() { 57 It("should return correct config chart values", func(ctx SpecContext) { 58 cp := &extensionsv1alpha1.ControlPlane{ 59 ObjectMeta: metav1.ObjectMeta{ 60 Name: "control-plane", 61 Namespace: ns.Name, 62 }, 63 Spec: extensionsv1alpha1.ControlPlaneSpec{ 64 Region: "foo", 65 SecretRef: corev1.SecretReference{ 66 Name: "my-infra-creds", 67 Namespace: ns.Name, 68 }, 69 DefaultSpec: extensionsv1alpha1.DefaultSpec{ 70 Type: ironcore.Type, 71 ProviderConfig: &runtime.RawExtension{ 72 Raw: encode(&apisironcore.ControlPlaneConfig{ 73 CloudControllerManager: &apisironcore.CloudControllerManagerConfig{ 74 FeatureGates: map[string]bool{ 75 "CustomResourceValidation": true, 76 }, 77 }, 78 }), 79 }, 80 }, 81 InfrastructureProviderStatus: &runtime.RawExtension{ 82 Raw: encode(&apisironcore.InfrastructureStatus{ 83 NetworkRef: v1alpha1.LocalUIDReference{ 84 Name: "my-network", 85 UID: "1234", 86 }, 87 PrefixRef: v1alpha1.LocalUIDReference{ 88 Name: "my-prefix", 89 UID: "6789", 90 }, 91 }), 92 }, 93 }, 94 } 95 Expect(k8sClient.Create(ctx, cp)).To(Succeed()) 96 97 By("ensuring that the provider ConfigMap has been created") 98 config := &corev1.ConfigMap{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Namespace: ns.Name, 101 Name: internal.CloudProviderConfigMapName, 102 }, 103 } 104 Eventually(Get(config)).Should(Succeed()) 105 Expect(config.Data).To(HaveKey("cloudprovider.conf")) 106 cloudProviderConfig := map[string]interface{}{} 107 Expect(yaml.Unmarshal([]byte(config.Data["cloudprovider.conf"]), &cloudProviderConfig)).NotTo(HaveOccurred()) 108 Expect(cloudProviderConfig["networkName"]).To(Equal("my-network")) 109 Expect(cloudProviderConfig["prefixName"]).To(Equal("my-prefix")) 110 Expect(cloudProviderConfig["clusterName"]).To(Equal(cluster.Name)) 111 }) 112 }) 113 114 Describe("#GetStorageClassesChartValues", func() { 115 BeforeEach(func(ctx SpecContext) { 116 By("creating an expand only VolumeClass") 117 volumeClassExpandOnly := &storagev1alpha1.VolumeClass{ 118 ObjectMeta: metav1.ObjectMeta{ 119 Name: "volume-expandable", 120 }, 121 Capabilities: corev1alpha1.ResourceList{ 122 corev1alpha1.ResourceIOPS: resource.MustParse("100"), 123 corev1alpha1.ResourceTPS: resource.MustParse("100"), 124 }, 125 ResizePolicy: storagev1alpha1.ResizePolicyExpandOnly, 126 } 127 Expect(k8sClient.Create(ctx, volumeClassExpandOnly)).To(Succeed()) 128 DeferCleanup(k8sClient.Delete, volumeClassExpandOnly) 129 130 By("creating an static VolumeClass") 131 volumeClassStatic := &storagev1alpha1.VolumeClass{ 132 ObjectMeta: metav1.ObjectMeta{ 133 Name: "volume-static", 134 }, 135 Capabilities: corev1alpha1.ResourceList{ 136 corev1alpha1.ResourceIOPS: resource.MustParse("100"), 137 corev1alpha1.ResourceTPS: resource.MustParse("100"), 138 }, 139 ResizePolicy: storagev1alpha1.ResizePolicyStatic, 140 } 141 Expect(k8sClient.Create(ctx, volumeClassStatic)).To(Succeed()) 142 DeferCleanup(k8sClient.Delete, volumeClassStatic) 143 }) 144 It("should return an empty config chart value map if not storageclasses are present in the cloudprofile", func(ctx SpecContext) { 145 providerCloudProfile := &apisironcore.CloudProfileConfig{} 146 providerCloudProfileJson, err := json.Marshal(providerCloudProfile) 147 Expect(err).NotTo(HaveOccurred()) 148 149 cluster := &controller.Cluster{ 150 ObjectMeta: metav1.ObjectMeta{ 151 Name: ns.Name, 152 }, 153 CloudProfile: &gardencorev1beta1.CloudProfile{ 154 Spec: gardencorev1beta1.CloudProfileSpec{ 155 ProviderConfig: &runtime.RawExtension{ 156 Raw: providerCloudProfileJson, 157 }, 158 }, 159 }, 160 } 161 162 values, err := vp.GetStorageClassesChartValues(ctx, nil, cluster) 163 Expect(err).NotTo(HaveOccurred()) 164 Expect(values).To(Equal(map[string]interface{}{ 165 "storageClasses": []map[string]interface{}{}, 166 })) 167 }) 168 169 It("should return correct config chart values if default and additional storage classes are present in the cloudprofile", func(ctx SpecContext) { 170 providerCloudProfile := &apisironcore.CloudProfileConfig{ 171 StorageClasses: apisironcore.StorageClasses{ 172 Default: &apisironcore.StorageClass{ 173 Name: "foo", 174 Type: "volume-expandable", 175 }, 176 Additional: []apisironcore.StorageClass{ 177 { 178 Name: "foo1", 179 Type: "volume-expandable", 180 }, 181 { 182 Name: "foo2", 183 Type: "volume-static", 184 }, 185 }, 186 }, 187 } 188 providerCloudProfileJson, err := json.Marshal(providerCloudProfile) 189 Expect(err).NotTo(HaveOccurred()) 190 191 cluster := &controller.Cluster{ 192 ObjectMeta: metav1.ObjectMeta{ 193 Name: ns.Name, 194 }, 195 CloudProfile: &gardencorev1beta1.CloudProfile{ 196 Spec: gardencorev1beta1.CloudProfileSpec{ 197 ProviderConfig: &runtime.RawExtension{ 198 Raw: providerCloudProfileJson, 199 }, 200 }, 201 }, 202 } 203 204 values, err := vp.GetStorageClassesChartValues(ctx, nil, cluster) 205 Expect(err).NotTo(HaveOccurred()) 206 Expect(values).To(Equal(map[string]interface{}{ 207 "storageClasses": []map[string]interface{}{ 208 { 209 "name": "foo", 210 "type": "volume-expandable", 211 "default": true, 212 "expandable": true, 213 }, 214 { 215 "name": "foo1", 216 "type": "volume-expandable", 217 "expandable": true, 218 }, 219 { 220 "name": "foo2", 221 "type": "volume-static", 222 "expandable": false, 223 }, 224 }, 225 })) 226 }) 227 228 It("should return correct config chart values if only additional storage classes are present in the cloudprofile", func(ctx SpecContext) { 229 providerCloudProfile := &apisironcore.CloudProfileConfig{ 230 StorageClasses: apisironcore.StorageClasses{ 231 Additional: []apisironcore.StorageClass{ 232 { 233 Name: "foo1", 234 Type: "volume-expandable", 235 }, 236 { 237 Name: "foo2", 238 Type: "volume-static", 239 }, 240 }, 241 }, 242 } 243 providerCloudProfileJson, err := json.Marshal(providerCloudProfile) 244 Expect(err).NotTo(HaveOccurred()) 245 246 cluster := &controller.Cluster{ 247 ObjectMeta: metav1.ObjectMeta{ 248 Name: ns.Name, 249 }, 250 CloudProfile: &gardencorev1beta1.CloudProfile{ 251 Spec: gardencorev1beta1.CloudProfileSpec{ 252 ProviderConfig: &runtime.RawExtension{ 253 Raw: providerCloudProfileJson, 254 }, 255 }, 256 }, 257 } 258 259 values, err := vp.GetStorageClassesChartValues(ctx, nil, cluster) 260 Expect(err).NotTo(HaveOccurred()) 261 Expect(values).To(Equal(map[string]interface{}{ 262 "storageClasses": []map[string]interface{}{ 263 { 264 "name": "foo1", 265 "type": "volume-expandable", 266 "expandable": true, 267 }, 268 { 269 "name": "foo2", 270 "type": "volume-static", 271 "expandable": false, 272 }, 273 }, 274 })) 275 }) 276 277 It("should return error if volumeClass is not available", func(ctx SpecContext) { 278 providerCloudProfile := &apisironcore.CloudProfileConfig{ 279 StorageClasses: apisironcore.StorageClasses{ 280 Default: &apisironcore.StorageClass{ 281 Name: "foo", 282 Type: "volume-non-existing", 283 }, 284 }, 285 } 286 providerCloudProfileJson, err := json.Marshal(providerCloudProfile) 287 Expect(err).NotTo(HaveOccurred()) 288 289 cluster := &controller.Cluster{ 290 ObjectMeta: metav1.ObjectMeta{ 291 Name: ns.Name, 292 }, 293 CloudProfile: &gardencorev1beta1.CloudProfile{ 294 Spec: gardencorev1beta1.CloudProfileSpec{ 295 ProviderConfig: &runtime.RawExtension{ 296 Raw: providerCloudProfileJson, 297 }, 298 }, 299 }, 300 } 301 302 _, err = vp.GetStorageClassesChartValues(ctx, nil, cluster) 303 Expect(err).To(MatchError("could not get resize policy from volumeclass : VolumeClass not found")) 304 }) 305 306 }) 307 308 Describe("#GetControlPlaneShootCRDsChartValues", func() { 309 It("should return correct config chart values", func(ctx SpecContext) { 310 values, err := vp.GetControlPlaneShootCRDsChartValues(ctx, nil, nil) 311 Expect(err).NotTo(HaveOccurred()) 312 Expect(values).To(Equal(map[string]interface{}{})) 313 }) 314 }) 315 316 Describe("#GetControlPlaneShootChartValues", func() { 317 It("should return correct config chart values", func(ctx SpecContext) { 318 providerCloudProfile := &apisironcore.CloudProfileConfig{ 319 StorageClasses: apisironcore.StorageClasses{ 320 Default: &apisironcore.StorageClass{ 321 Name: "foo", 322 Type: "volume-expandable", 323 }, 324 Additional: []apisironcore.StorageClass{ 325 { 326 Name: "bar", 327 Type: "volume-static", 328 }, 329 }, 330 }, 331 } 332 providerCloudProfileJson, err := json.Marshal(providerCloudProfile) 333 Expect(err).NotTo(HaveOccurred()) 334 cluster := &controller.Cluster{ 335 CloudProfile: &gardencorev1beta1.CloudProfile{ 336 Spec: gardencorev1beta1.CloudProfileSpec{ 337 ProviderConfig: &runtime.RawExtension{ 338 Raw: providerCloudProfileJson, 339 }, 340 }, 341 }, 342 Shoot: &gardencorev1beta1.Shoot{ 343 ObjectMeta: metav1.ObjectMeta{ 344 Namespace: ns.Name, 345 Name: "my-shoot", 346 }, 347 Spec: gardencorev1beta1.ShootSpec{ 348 Kubernetes: gardencorev1beta1.Kubernetes{ 349 Version: "1.26.0", 350 VerticalPodAutoscaler: &gardencorev1beta1.VerticalPodAutoscaler{ 351 Enabled: true, 352 }, 353 }, 354 }, 355 }, 356 } 357 values, err := vp.GetControlPlaneShootChartValues(ctx, nil, cluster, nil, nil) 358 Expect(err).NotTo(HaveOccurred()) 359 Expect(values).To(Equal(map[string]interface{}{ 360 "cloud-controller-manager": map[string]interface{}{ 361 "enabled": true, 362 }, 363 "csi-driver-node": map[string]interface{}{ 364 "enabled": true, 365 "vpaEnabled": true, 366 "pspDisabled": true, 367 }, 368 })) 369 }) 370 }) 371 372 Describe("#GetControlPlaneChartValues", func() { 373 It("should return correct config chart values", func(ctx SpecContext) { 374 cp := &extensionsv1alpha1.ControlPlane{ 375 ObjectMeta: metav1.ObjectMeta{ 376 Name: "control-plane", 377 Namespace: ns.Name, 378 }, 379 Spec: extensionsv1alpha1.ControlPlaneSpec{ 380 Region: "foo", 381 SecretRef: corev1.SecretReference{ 382 Name: "my-infra-creds", 383 Namespace: ns.Name, 384 }, 385 DefaultSpec: extensionsv1alpha1.DefaultSpec{ 386 Type: ironcore.Type, 387 ProviderConfig: &runtime.RawExtension{ 388 Raw: encode(&apisironcore.ControlPlaneConfig{ 389 CloudControllerManager: &apisironcore.CloudControllerManagerConfig{ 390 FeatureGates: map[string]bool{ 391 "CustomResourceValidation": true, 392 }, 393 }, 394 }), 395 }, 396 }, 397 InfrastructureProviderStatus: &runtime.RawExtension{ 398 Raw: encode(&apisironcore.InfrastructureStatus{ 399 NetworkRef: v1alpha1.LocalUIDReference{ 400 Name: "my-network", 401 UID: "1234", 402 }, 403 }), 404 }, 405 }, 406 } 407 providerCloudProfile := &apisironcore.CloudProfileConfig{ 408 StorageClasses: apisironcore.StorageClasses{ 409 Default: &apisironcore.StorageClass{ 410 Name: "foo", 411 Type: "volumeTypeFoo", 412 }, 413 Additional: []apisironcore.StorageClass{ 414 { 415 Name: "bar", 416 Type: "volumeTypeBar", 417 }, 418 }, 419 }, 420 } 421 providerCloudProfileJson, err := json.Marshal(providerCloudProfile) 422 Expect(err).NotTo(HaveOccurred()) 423 networkProviderConfig := &unstructured.Unstructured{Object: map[string]any{ 424 "kind": "FooNetworkConfig", 425 "apiVersion": "v1alpha1", 426 "overlay": map[string]any{ 427 "enabled": false, 428 }, 429 }} 430 networkProviderConfigData, err := runtime.Encode(unstructured.UnstructuredJSONScheme, networkProviderConfig) 431 Expect(err).NotTo(HaveOccurred()) 432 cluster := &controller.Cluster{ 433 CloudProfile: &gardencorev1beta1.CloudProfile{ 434 Spec: gardencorev1beta1.CloudProfileSpec{ 435 ProviderConfig: &runtime.RawExtension{ 436 Raw: providerCloudProfileJson, 437 }, 438 }, 439 }, 440 Shoot: &gardencorev1beta1.Shoot{ 441 ObjectMeta: metav1.ObjectMeta{ 442 Namespace: ns.Name, 443 Name: "my-shoot", 444 }, 445 Spec: gardencorev1beta1.ShootSpec{ 446 Networking: &gardencorev1beta1.Networking{ 447 ProviderConfig: &runtime.RawExtension{Raw: networkProviderConfigData}, 448 Pods: ptr.To[string]("10.0.0.0/16"), 449 }, 450 Kubernetes: gardencorev1beta1.Kubernetes{ 451 Version: "1.26.0", 452 VerticalPodAutoscaler: &gardencorev1beta1.VerticalPodAutoscaler{ 453 Enabled: true, 454 }, 455 }, 456 }, 457 }, 458 } 459 460 checksums := map[string]string{ 461 ironcore.CloudProviderConfigName: "8bafb35ff1ac60275d62e1cbd495aceb511fb354f74a20f7d06ecb48b3a68432", 462 } 463 values, err := vp.GetControlPlaneChartValues(ctx, cp, cluster, fakeSecretsManager, checksums, false) 464 Expect(err).NotTo(HaveOccurred()) 465 Expect(values).To(Equal(map[string]interface{}{ 466 "global": map[string]interface{}{ 467 "genericTokenKubeconfigSecretName": "generic-token-kubeconfig", 468 }, 469 "cloud-controller-manager": map[string]interface{}{ 470 "enabled": true, 471 "replicas": 1, 472 "clusterName": ns.Name, 473 "podAnnotations": map[string]interface{}{ 474 "checksum/secret-cloud-provider-config": "8bafb35ff1ac60275d62e1cbd495aceb511fb354f74a20f7d06ecb48b3a68432", 475 }, 476 "podLabels": map[string]interface{}{ 477 "maintenance.gardener.cloud/restart": "true", 478 }, 479 "tlsCipherSuites": []string{ 480 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 481 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 482 "TLS_AES_128_GCM_SHA256", 483 "TLS_AES_256_GCM_SHA384", 484 "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 485 "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 486 "TLS_CHACHA20_POLY1305_SHA256", 487 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", 488 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", 489 }, 490 "secrets": map[string]interface{}{ 491 "server": "cloud-controller-manager-server", 492 }, 493 "featureGates": map[string]bool{ 494 "CustomResourceValidation": true, 495 }, 496 "podNetwork": "10.0.0.0/16", 497 "configureCloudRoutes": true, 498 }, 499 "csi-driver-controller": map[string]interface{}{ 500 "enabled": true, 501 "replicas": 1, 502 }, 503 })) 504 }) 505 }) 506 }) 507 508 func encode(obj runtime.Object) []byte { 509 data, _ := json.Marshal(obj) 510 return data 511 }