Best Mock code snippet using core.EXPECT
admission_test.go
Source:admission_test.go
1// Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14package validator_test15import (16 "context"17 "fmt"18 "time"19 "github.com/gardener/gardener/pkg/apis/core"20 gardencorev1alpha1 "github.com/gardener/gardener/pkg/apis/core/v1alpha1"21 v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"22 extcoreinformers "github.com/gardener/gardener/pkg/client/core/informers/externalversions"23 coreinformers "github.com/gardener/gardener/pkg/client/core/informers/internalversion"24 "github.com/gardener/gardener/pkg/client/kubernetes"25 "github.com/gardener/gardener/pkg/controllerutils"26 "github.com/gardener/gardener/pkg/features"27 gutil "github.com/gardener/gardener/pkg/utils/gardener"28 "github.com/gardener/gardener/pkg/utils/test"29 . "github.com/gardener/gardener/pkg/utils/test/matchers"30 . "github.com/gardener/gardener/plugin/pkg/shoot/validator"31 . "github.com/onsi/ginkgo/v2"32 . "github.com/onsi/gomega"33 "github.com/onsi/gomega/types"34 corev1 "k8s.io/api/core/v1"35 "k8s.io/apimachinery/pkg/api/resource"36 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"37 "k8s.io/apimachinery/pkg/runtime"38 "k8s.io/apimachinery/pkg/util/sets"39 "k8s.io/apiserver/pkg/admission"40 utilfeature "k8s.io/apiserver/pkg/util/feature"41 "k8s.io/client-go/tools/cache"42 "k8s.io/utils/pointer"43)44var _ = Describe("validator", func() {45 Describe("#Admit", func() {46 var (47 admissionHandler *ValidateShoot48 coreInformerFactory coreinformers.SharedInformerFactory49 extCoreInformerFactory extcoreinformers.SharedInformerFactory50 cloudProfile core.CloudProfile51 seed core.Seed52 secretBinding core.SecretBinding53 project core.Project54 shoot core.Shoot55 shootState gardencorev1alpha1.ShootState56 podsCIDR = "100.96.0.0/11"57 servicesCIDR = "100.64.0.0/13"58 nodesCIDR = "10.250.0.0/16"59 falseVar = false60 seedName = "seed"61 namespaceName = "garden-my-project"62 projectName = "my-project"63 unmanagedDNSProvider = core.DNSUnmanaged64 baseDomain = "example.com"65 validMachineImageName = "some-machineimage"66 validMachineImageVersions = []core.MachineImageVersion{67 {68 ExpirableVersion: core.ExpirableVersion{69 Version: "0.0.1",70 },71 CRI: []core.CRI{72 {73 Name: core.CRINameContainerD,74 ContainerRuntimes: []core.ContainerRuntime{75 {76 Type: "test-cr",77 },78 },79 },80 },81 },82 }83 volumeType = "volume-type-1"84 volumeType2 = "volume-type-2"85 minVolSize = resource.MustParse("100Gi")86 minVolSizeMachine = resource.MustParse("50Gi")87 seedPodsCIDR = "10.241.128.0/17"88 seedServicesCIDR = "10.241.0.0/17"89 seedNodesCIDR = "10.240.0.0/16"90 seedSecret = corev1.Secret{91 TypeMeta: metav1.TypeMeta{},92 ObjectMeta: metav1.ObjectMeta{93 Name: seedName,94 Namespace: "garden",95 },96 Data: map[string][]byte{97 kubernetes.KubeConfig: []byte(""),98 },99 Type: corev1.SecretTypeOpaque,100 }101 projectBase = core.Project{102 ObjectMeta: metav1.ObjectMeta{103 Name: projectName,104 },105 Spec: core.ProjectSpec{106 Namespace: &namespaceName,107 },108 }109 cloudProfileBase = core.CloudProfile{110 ObjectMeta: metav1.ObjectMeta{111 Name: "profile",112 },113 Spec: core.CloudProfileSpec{114 Type: "unknown",115 Kubernetes: core.KubernetesSettings{116 Versions: []core.ExpirableVersion{{Version: "1.6.4"}},117 },118 MachineImages: []core.MachineImage{119 {120 Name: validMachineImageName,121 Versions: validMachineImageVersions,122 },123 },124 MachineTypes: []core.MachineType{125 {126 Name: "machine-type-1",127 CPU: resource.MustParse("2"),128 GPU: resource.MustParse("0"),129 Memory: resource.MustParse("100Gi"),130 },131 {132 Name: "machine-type-old",133 CPU: resource.MustParse("2"),134 GPU: resource.MustParse("0"),135 Memory: resource.MustParse("100Gi"),136 Usable: &falseVar,137 },138 {139 Name: "machine-type-2",140 CPU: resource.MustParse("2"),141 GPU: resource.MustParse("0"),142 Memory: resource.MustParse("100Gi"),143 Storage: &core.MachineTypeStorage{144 Type: volumeType,145 MinSize: &minVolSizeMachine,146 },147 },148 },149 VolumeTypes: []core.VolumeType{150 {151 Name: volumeType,152 Class: "super-premium",153 },154 {155 Name: volumeType2,156 Class: "super-premium",157 MinSize: &minVolSize,158 },159 },160 Regions: []core.Region{161 {162 Name: "europe",163 Zones: []core.AvailabilityZone{{Name: "europe-a"}},164 },165 {166 Name: "asia",167 Zones: []core.AvailabilityZone{{Name: "asia-a"}},168 },169 },170 },171 }172 seedBase = core.Seed{173 ObjectMeta: metav1.ObjectMeta{174 Name: seedName,175 },176 Spec: core.SeedSpec{177 Backup: &core.SeedBackup{},178 Networks: core.SeedNetworks{179 Pods: seedPodsCIDR,180 Services: seedServicesCIDR,181 Nodes: &seedNodesCIDR,182 },183 SecretRef: &corev1.SecretReference{184 Name: seedSecret.Name,185 Namespace: seedSecret.Namespace,186 },187 },188 }189 secretBindingBase = core.SecretBinding{190 ObjectMeta: metav1.ObjectMeta{191 Name: "my-secret",192 Namespace: namespaceName,193 },194 }195 shootBase = core.Shoot{196 ObjectMeta: metav1.ObjectMeta{197 Name: "shoot",198 Namespace: namespaceName,199 },200 Spec: core.ShootSpec{201 CloudProfileName: "profile",202 Region: "europe",203 SecretBindingName: "my-secret",204 SeedName: &seedName,205 DNS: &core.DNS{206 Domain: pointer.String(fmt.Sprintf("shoot.%s", baseDomain)),207 Providers: []core.DNSProvider{208 {209 Type: &unmanagedDNSProvider,210 },211 },212 },213 Kubernetes: core.Kubernetes{214 Version: "1.6.4",215 },216 Networking: core.Networking{217 Nodes: &nodesCIDR,218 Pods: &podsCIDR,219 Services: &servicesCIDR,220 },221 Provider: core.Provider{222 Type: "unknown",223 Workers: []core.Worker{224 {225 Name: "worker-name",226 Machine: core.Machine{227 Type: "machine-type-1",228 Image: &core.ShootMachineImage{229 Name: validMachineImageName,230 },231 },232 Minimum: 1,233 Maximum: 1,234 Volume: &core.Volume{235 VolumeSize: "40Gi",236 Type: &volumeType,237 },238 Zones: []string{"europe-a"},239 },240 },241 InfrastructureConfig: &runtime.RawExtension{Raw: []byte(`{242"kind": "InfrastructureConfig",243"apiVersion": "some.random.config/v1beta1"}`)},244 },245 },246 }247 shootStateBase = gardencorev1alpha1.ShootState{248 ObjectMeta: metav1.ObjectMeta{249 Name: shootBase.Name,250 Namespace: shootBase.Namespace,251 },252 }253 cleanup func()254 )255 BeforeEach(func() {256 project = projectBase257 cloudProfile = *cloudProfileBase.DeepCopy()258 seed = seedBase259 secretBinding = secretBindingBase260 shoot = *shootBase.DeepCopy()261 shootState = *shootStateBase.DeepCopy()262 admissionHandler, _ = New()263 admissionHandler.AssignReadyFunc(func() bool { return true })264 coreInformerFactory = coreinformers.NewSharedInformerFactory(nil, 0)265 admissionHandler.SetInternalCoreInformerFactory(coreInformerFactory)266 extCoreInformerFactory = extcoreinformers.NewSharedInformerFactory(nil, 0)267 admissionHandler.SetExternalCoreInformerFactory(extCoreInformerFactory)268 cleanup = test.WithFeatureGate(utilfeature.DefaultFeatureGate, features.SecretBindingProviderValidation, false)269 })270 AfterEach(func() {271 cleanup()272 })273 Context("name/project length checks", func() {274 It("should reject create operations on Shoot resources in projects which shall be deleted", func() {275 deletionTimestamp := metav1.NewTime(time.Now())276 project.ObjectMeta.DeletionTimestamp = &deletionTimestamp277 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())278 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())279 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())280 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)281 err := admissionHandler.Admit(context.TODO(), attrs, nil)282 Expect(err).To(BeForbiddenError())283 Expect(err.Error()).To(ContainSubstring("already marked for deletion"))284 })285 It("should reject Shoot resources with not fulfilling the length constraints", func() {286 tooLongName := "too-long-namespace"287 project.ObjectMeta = metav1.ObjectMeta{288 Name: tooLongName,289 }290 shoot.ObjectMeta = metav1.ObjectMeta{291 Name: "too-long-name",292 Namespace: namespaceName,293 }294 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())295 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())296 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())297 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)298 err := admissionHandler.Admit(context.TODO(), attrs, nil)299 Expect(err).To(BeInvalidError())300 Expect(err.Error()).To(ContainSubstring("name must not exceed"))301 })302 It("should not test length constraints for operations other than CREATE", func() {303 shortName := "short"304 projectName := "too-long-long-long-label"305 project.ObjectMeta = metav1.ObjectMeta{306 Name: projectName,307 }308 shoot.ObjectMeta = metav1.ObjectMeta{309 Name: shortName,310 Namespace: shortName,311 }312 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())313 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())314 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())315 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)316 err := admissionHandler.Admit(context.TODO(), attrs, nil)317 Expect(err).To(HaveOccurred())318 Expect(err.Error()).NotTo(ContainSubstring("name must not exceed"))319 attrs = admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Delete, &metav1.DeleteOptions{}, false, nil)320 err = admissionHandler.Admit(context.TODO(), attrs, nil)321 Expect(err).To(HaveOccurred())322 Expect(err.Error()).NotTo(ContainSubstring("name must not exceed"))323 })324 })325 Context("shoot with generate name", func() {326 BeforeEach(func() {327 shoot.ObjectMeta = metav1.ObjectMeta{328 GenerateName: "demo-",329 Namespace: namespaceName,330 }331 })332 It("should admit Shoot resources", func() {333 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())334 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())335 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())336 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)337 err := admissionHandler.Admit(context.TODO(), attrs, nil)338 Expect(err).NotTo(HaveOccurred())339 })340 It("should reject Shoot resources with not fulfilling the length constraints", func() {341 tooLongName := "too-long-namespace"342 project.ObjectMeta = metav1.ObjectMeta{343 Name: tooLongName,344 }345 shoot.ObjectMeta = metav1.ObjectMeta{346 GenerateName: "too-long-name",347 Namespace: namespaceName,348 }349 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())350 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())351 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())352 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)353 err := admissionHandler.Admit(context.TODO(), attrs, nil)354 Expect(err).To(BeInvalidError())355 Expect(err.Error()).To(ContainSubstring("name must not exceed"))356 })357 })358 Context("finalizer removal checks", func() {359 var (360 oldShoot *core.Shoot361 )362 BeforeEach(func() {363 shoot = *shootBase.DeepCopy()364 shoot.Status.TechnicalID = "some-id"365 shoot.Status.LastOperation = &core.LastOperation{366 Type: core.LastOperationTypeReconcile,367 State: core.LastOperationStateSucceeded,368 Progress: 100,369 }370 // set old shoot for update and add gardener finalizer to it371 oldShoot = shoot.DeepCopy()372 finalizers := sets.NewString(oldShoot.GetFinalizers()...)373 finalizers.Insert(core.GardenerName)374 oldShoot.SetFinalizers(finalizers.UnsortedList())375 })376 It("should reject removing the gardener finalizer if the shoot has not yet been deleted successfully", func() {377 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())378 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())379 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())380 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)381 err := admissionHandler.Admit(context.TODO(), attrs, nil)382 Expect(err).To(HaveOccurred())383 Expect(err.Error()).To(ContainSubstring("shoot deletion has not completed successfully yet"))384 })385 It("should admit removing the gardener finalizer if the shoot deletion succeeded ", func() {386 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())387 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())388 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())389 shoot.Status.LastOperation = &core.LastOperation{390 Type: core.LastOperationTypeDelete,391 State: core.LastOperationStateSucceeded,392 Progress: 100,393 }394 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)395 err := admissionHandler.Admit(context.TODO(), attrs, nil)396 Expect(err).ToNot(HaveOccurred())397 })398 })399 Context("hibernation checks", func() {400 var (401 oldShoot *core.Shoot402 )403 BeforeEach(func() {404 shoot = *shootBase.DeepCopy()405 oldShoot = shoot.DeepCopy()406 oldShoot.Spec.Hibernation = &core.Hibernation{Enabled: pointer.Bool(false)}407 shoot.Spec.Hibernation = &core.Hibernation{Enabled: pointer.Bool(true)}408 })409 DescribeTable("should allow/deny hibernating the Shoot according to HibernationPossible constraint",410 func(constraints []core.Condition, match types.GomegaMatcher) {411 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())412 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())413 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())414 shoot.Status.Constraints = constraints415 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)416 err := admissionHandler.Admit(context.TODO(), attrs, nil)417 Expect(err).To(match)418 },419 Entry("should allow if set to True", []core.Condition{420 {421 Type: core.ShootHibernationPossible,422 Status: core.ConditionTrue,423 },424 }, Not(HaveOccurred())),425 Entry("should deny if set to False", []core.Condition{426 {427 Type: core.ShootHibernationPossible,428 Status: core.ConditionFalse,429 Message: "foo",430 },431 }, And(HaveOccurred(), MatchError(ContainSubstring("foo")))),432 Entry("should deny if set to Unknown", []core.Condition{433 {434 Type: core.ShootHibernationPossible,435 Status: core.ConditionUnknown,436 Message: "foo",437 },438 }, And(HaveOccurred(), MatchError(ContainSubstring("foo")))),439 Entry("should allow if unset", []core.Condition{}, Not(HaveOccurred())),440 )441 })442 Context("shoot maintenance checks", func() {443 var (444 oldShoot *core.Shoot445 confineEnabled = true446 specUpdate = true447 operationFaild = &core.LastOperation{State: core.LastOperationStateFailed}448 operationSucceeded = &core.LastOperation{State: core.LastOperationStateSucceeded}449 )450 BeforeEach(func() {451 shoot = *shootBase.DeepCopy()452 shoot.Spec.Maintenance = &core.Maintenance{}453 oldShoot = shoot.DeepCopy()454 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())455 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())456 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())457 })458 DescribeTable("confine spec roll-out checks",459 func(specChange, oldConfine, confine bool, oldOperation, operation *core.LastOperation, matcher types.GomegaMatcher) {460 oldShoot.Spec.Maintenance.ConfineSpecUpdateRollout = pointer.Bool(oldConfine)461 oldShoot.Status.LastOperation = oldOperation462 shoot.Spec.Maintenance.ConfineSpecUpdateRollout = pointer.Bool(confine)463 shoot.Status.LastOperation = operation464 if specChange {465 shoot.Spec.Kubernetes.AllowPrivilegedContainers = pointer.Bool(466 oldShoot.Spec.Kubernetes.AllowPrivilegedContainers == nil ||467 !(*oldShoot.Spec.Kubernetes.AllowPrivilegedContainers))468 }469 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)470 Expect(admissionHandler.Admit(context.TODO(), attrs, nil)).To(Succeed())471 Expect(shoot.Annotations).To(matcher)472 },473 Entry(474 "should add annotation for failed shoot",475 specUpdate, confineEnabled, confineEnabled, operationFaild, operationFaild,476 HaveKeyWithValue(v1beta1constants.FailedShootNeedsRetryOperation, "true"),477 ),478 Entry(479 "should not add annotation for failed shoot because of missing spec change",480 !specUpdate, confineEnabled, confineEnabled, operationFaild, operationFaild,481 Not(HaveKey(v1beta1constants.FailedShootNeedsRetryOperation)),482 ),483 Entry(484 "should not add annotation for succeeded shoot",485 specUpdate, confineEnabled, confineEnabled, operationFaild, operationSucceeded,486 Not(HaveKey(v1beta1constants.FailedShootNeedsRetryOperation)),487 ),488 Entry(489 "should not add annotation for shoot w/o confine spec roll-out enabled",490 specUpdate, confineEnabled, !confineEnabled, operationFaild, operationFaild,491 Not(HaveKey(v1beta1constants.FailedShootNeedsRetryOperation)),492 ),493 Entry(494 "should not add annotation for shoot w/o last operation",495 specUpdate, confineEnabled, confineEnabled, nil, nil,496 Not(HaveKey(v1beta1constants.FailedShootNeedsRetryOperation)),497 ),498 )499 })500 Context("checks for shoots referencing a deleted seed", func() {501 var oldShoot *core.Shoot502 BeforeEach(func() {503 oldShoot = shootBase.DeepCopy()504 seed = *seedBase.DeepCopy()505 now := metav1.Now()506 seed.DeletionTimestamp = &now507 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())508 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())509 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())510 })511 It("should reject creating a shoot on a seed which is marked for deletion", func() {512 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)513 err := admissionHandler.Admit(context.TODO(), attrs, nil)514 Expect(err).To(HaveOccurred())515 Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("cannot schedule shoot '%s' on seed '%s' that is already marked for deletion", shoot.Name, seed.Name)))516 })517 It("should allow no-op updates", func() {518 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)519 err := admissionHandler.Admit(context.TODO(), attrs, nil)520 Expect(err).ToNot(HaveOccurred())521 })522 It("should allow modifying the finalizers array", func() {523 oldShoot.Finalizers = []string{core.GardenerName}524 shoot.Finalizers = []string{}525 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)526 err := admissionHandler.Admit(context.TODO(), attrs, nil)527 Expect(err).ToNot(HaveOccurred())528 })529 It("should allow adding the deletion confirmation", func() {530 shoot.Annotations = make(map[string]string)531 shoot.Annotations[gutil.ConfirmationDeletion] = "true"532 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)533 err := admissionHandler.Admit(context.TODO(), attrs, nil)534 Expect(err).ToNot(HaveOccurred())535 })536 It("should reject modifying the shoot spec when seed is marked for deletion", func() {537 shoot.Spec.Region = "other-region"538 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)539 err := admissionHandler.Admit(context.TODO(), attrs, nil)540 Expect(err).To(HaveOccurred())541 Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("cannot update spec of shoot '%s' on seed '%s' already marked for deletion", shoot.Name, seed.Name)))542 })543 It("should reject modifying other annotations than the deletion confirmation when seed is marked for deletion", func() {544 shoot.Annotations = make(map[string]string)545 shoot.Annotations["foo"] = "bar"546 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)547 err := admissionHandler.Admit(context.TODO(), attrs, nil)548 Expect(err).To(HaveOccurred())549 Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("cannot update annotations of shoot '%s' on seed '%s' already marked for deletion", shoot.Name, seed.Name)))550 })551 })552 Context("reference checks", func() {553 It("should reject because the referenced cloud profile was not found", func() {554 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)555 err := admissionHandler.Admit(context.TODO(), attrs, nil)556 Expect(err).To(BeInternalServerError())557 })558 It("should reject because the referenced seed was not found", func() {559 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())560 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())561 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)562 err := admissionHandler.Admit(context.TODO(), attrs, nil)563 Expect(err).To(BeInternalServerError())564 })565 It("should reject because the referenced project was not found", func() {566 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())567 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())568 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)569 err := admissionHandler.Admit(context.TODO(), attrs, nil)570 Expect(err).To(BeInternalServerError())571 })572 It("should reject because the cloud provider in shoot and profile differ", func() {573 cloudProfile.Spec.Type = "gcp"574 shoot.Spec.Provider.Type = "aws"575 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())576 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())577 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())578 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)579 err := admissionHandler.Admit(context.TODO(), attrs, nil)580 Expect(err).To(BeForbiddenError())581 })582 Context("when SecretBindingProviderValidation=true", func() {583 It("should reject because the cloud provider in shoot and secret binding differ", func() {584 defer test.WithFeatureGate(utilfeature.DefaultFeatureGate, features.SecretBindingProviderValidation, true)()585 secretBinding.Provider = &core.SecretBindingProvider{586 Type: "gcp",587 }588 shoot.Spec.Provider.Type = "aws"589 cloudProfile.Spec.Type = "aws"590 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())591 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())592 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())593 Expect(coreInformerFactory.Core().InternalVersion().SecretBindings().Informer().GetStore().Add(&secretBinding)).To(Succeed())594 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)595 err := admissionHandler.Admit(context.TODO(), attrs, nil)596 Expect(err).To(BeForbiddenError())597 })598 })599 It("should pass because no seed has to be specified (however can be). The scheduler sets the seed instead.", func() {600 shoot.Spec.SeedName = nil601 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())602 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())603 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)604 err := admissionHandler.Admit(context.TODO(), attrs, nil)605 Expect(err).NotTo(HaveOccurred())606 })607 })608 Context("tests deploy task", func() {609 var (610 oldShoot *core.Shoot611 )612 BeforeEach(func() {613 oldShoot = shootBase.DeepCopy()614 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())615 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())616 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())617 })618 It("should add deploy tasks because shoot is being created", func() {619 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)620 err := admissionHandler.Admit(context.TODO(), attrs, nil)621 Expect(err).To(Not(HaveOccurred()))622 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployInfrastructure")).To(BeTrue())623 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordInternal")).To(BeTrue())624 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordExternal")).To(BeTrue())625 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordIngress")).To(BeTrue())626 })627 It("should add deploy tasks because shoot is waking up from hibernation", func() {628 oldShoot.Spec.Hibernation = &core.Hibernation{629 Enabled: pointer.Bool(true),630 }631 shoot.Spec.Hibernation = &core.Hibernation{632 Enabled: pointer.Bool(false),633 }634 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)635 err := admissionHandler.Admit(context.TODO(), attrs, nil)636 Expect(err).To(Not(HaveOccurred()))637 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployInfrastructure")).To(BeTrue())638 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordInternal")).To(BeTrue())639 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordExternal")).To(BeTrue())640 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordIngress")).To(BeTrue())641 })642 It("should add deploy infrastructure task because infrastructure config has changed", func() {643 shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{644 Raw: []byte("infrastructure"),645 }646 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)647 err := admissionHandler.Admit(context.TODO(), attrs, nil)648 Expect(err).To(Not(HaveOccurred()))649 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployInfrastructure")).To(BeTrue())650 })651 It("should add deploy dnsrecord tasks because dns config has changed", func() {652 shoot.Spec.DNS = &core.DNS{}653 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)654 err := admissionHandler.Admit(context.TODO(), attrs, nil)655 Expect(err).To(Not(HaveOccurred()))656 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordInternal")).To(BeTrue())657 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordExternal")).To(BeTrue())658 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordIngress")).To(BeTrue())659 })660 It("should add deploy infrastructure task because shoot operation annotation to rotate ssh keypair was set", func() {661 shoot.Annotations = make(map[string]string)662 shoot.Annotations[v1beta1constants.GardenerOperation] = v1beta1constants.ShootOperationRotateSSHKeypair663 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)664 err := admissionHandler.Admit(context.TODO(), attrs, nil)665 Expect(err).To(Not(HaveOccurred()))666 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployInfrastructure")).To(BeTrue())667 })668 It("should add deploy infrastructure task because shoot operation annotation to rotate all credentials was set", func() {669 shoot.Annotations = make(map[string]string)670 shoot.Annotations[v1beta1constants.GardenerOperation] = v1beta1constants.ShootOperationRotateCredentialsStart671 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)672 err := admissionHandler.Admit(context.TODO(), attrs, nil)673 Expect(err).To(Not(HaveOccurred()))674 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployInfrastructure")).To(BeTrue())675 })676 It("should not add deploy tasks because spec has not changed", func() {677 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)678 err := admissionHandler.Admit(context.TODO(), attrs, nil)679 Expect(err).To(Not(HaveOccurred()))680 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployInfrastructure")).To(BeFalse())681 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordInternal")).To(BeFalse())682 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordExternal")).To(BeFalse())683 Expect(controllerutils.HasTask(shoot.ObjectMeta.Annotations, "deployDNSRecordIngress")).To(BeFalse())684 })685 })686 Context("tests for region/zone updates", func() {687 var (688 oldShoot *core.Shoot689 )690 BeforeEach(func() {691 oldShoot = shootBase.DeepCopy()692 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())693 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())694 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())695 })696 It("should pass update for non existing region in cloud profile because shoot region is unchanged", func() {697 cloudProfile.Spec.Regions = nil698 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)699 err := admissionHandler.Admit(context.TODO(), attrs, nil)700 Expect(err).To(Not(HaveOccurred()))701 })702 It("should reject update because shoot changed to unknown region", func() {703 shoot.Spec.Region = "does-not-exist"704 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)705 err := admissionHandler.Admit(context.TODO(), attrs, nil)706 Expect(err).To(BeForbiddenError())707 Expect(err.Error()).To(ContainSubstring("Unsupported value: \"does-not-exist\": supported values: \"europe\", \"asia\""))708 })709 It("should pass update for non existing zone in cloud profile because shoot worker zone is unchanged", func() {710 cloudProfile.Spec.Regions[0].Zones = []core.AvailabilityZone{{Name: "not-available"}}711 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)712 err := admissionHandler.Admit(context.TODO(), attrs, nil)713 Expect(err).To(Not(HaveOccurred()))714 })715 It("should reject update because shoot changed to region with unavailable zone", func() {716 shoot.Spec.Region = "asia"717 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)718 err := admissionHandler.Admit(context.TODO(), attrs, nil)719 Expect(err).To(BeForbiddenError())720 Expect(err.Error()).To(ContainSubstring("Unsupported value: \"europe-a\": supported values: \"asia-a\""))721 })722 It("should reject update because shoot and cloud profile changed zones", func() {723 cloudProfile.Spec.Regions[0].Zones = []core.AvailabilityZone{{Name: "zone-1"}, {Name: "zone-2"}}724 shoot.Spec.Provider.Workers[0].Zones = append(shoot.Spec.Provider.Workers[0].Zones, "zone-1")725 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)726 err := admissionHandler.Admit(context.TODO(), attrs, nil)727 Expect(err).To(BeForbiddenError())728 Expect(err.Error()).To(ContainSubstring("Unsupported value: \"europe-a\": supported values: \"zone-1\", \"zone-2\""))729 })730 It("should reject due to an invalid zone", func() {731 shoot.Spec.Provider.Workers[0].Zones = []string{"invalid-zone"}732 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())733 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())734 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())735 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)736 err := admissionHandler.Admit(context.TODO(), attrs, nil)737 Expect(err).To(BeForbiddenError())738 })739 It("should reject due to an invalid zone update", func() {740 oldShoot := shoot.DeepCopy()741 shoot.Spec.Provider.Workers[0].Zones = append(shoot.Spec.Provider.Workers[0].Zones, oldShoot.Spec.Provider.Workers[0].Zones...)742 shoot.Spec.Provider.Workers[0].Zones = append(shoot.Spec.Provider.Workers[0].Zones, "invalid-zone")743 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())744 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())745 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())746 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)747 err := admissionHandler.Admit(context.TODO(), attrs, nil)748 Expect(err).To(BeForbiddenError())749 })750 It("should allow update when zone has removed from CloudProfile", func() {751 oldShoot := shoot.DeepCopy()752 shoot.Spec.Provider.Workers[0].Zones = []string{}753 cloudProfile.Spec.Regions = cloudProfile.Spec.Regions[1:]754 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())755 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())756 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())757 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)758 err := admissionHandler.Admit(context.TODO(), attrs, nil)759 Expect(err).ToNot(HaveOccurred())760 })761 })762 Context("tests for unknown provider", func() {763 Context("scheduling checks", func() {764 var (765 oldShoot *core.Shoot766 )767 BeforeEach(func() {768 oldShoot = shoot.DeepCopy()769 oldShoot.Spec.SeedName = nil770 })771 Context("taints and tolerations", func() {772 It("create should pass because the Seed specified in shoot manifest does not have any taints", func() {773 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())774 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())775 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())776 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)777 err := admissionHandler.Admit(context.TODO(), attrs, nil)778 Expect(err).ToNot(HaveOccurred())779 })780 It("update should pass because the Seed specified in shoot manifest does not have any taints", func() {781 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())782 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())783 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())784 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)785 err := admissionHandler.Admit(context.TODO(), attrs, nil)786 Expect(err).ToNot(HaveOccurred())787 })788 It("update should pass because the Seed has new non-tolerated taints that were added after the shoot was scheduled to it", func() {789 seed.Spec.Taints = []core.SeedTaint{{Key: core.SeedTaintProtected}}790 oldShoot.Spec.SeedName = shoot.Spec.SeedName791 shoot.Spec.Provider.Workers[0].Maximum++792 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())793 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())794 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())795 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)796 err := admissionHandler.Admit(context.TODO(), attrs, nil)797 Expect(err).ToNot(HaveOccurred())798 })799 It("create should fail because the Seed specified in shoot manifest has non-tolerated taints", func() {800 seed.Spec.Taints = []core.SeedTaint{{Key: core.SeedTaintProtected}}801 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())802 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())803 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())804 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)805 err := admissionHandler.Admit(context.TODO(), attrs, nil)806 Expect(err).To(BeForbiddenError())807 })808 It("update should fail because the new Seed specified in shoot manifest has non-tolerated taints", func() {809 oldShoot.Spec.SeedName = pointer.String("old-seed")810 seed.Spec.Taints = []core.SeedTaint{{Key: core.SeedTaintProtected}}811 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())812 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())813 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())814 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)815 err := admissionHandler.Admit(context.TODO(), attrs, nil)816 Expect(err).To(HaveOccurred())817 })818 It("update should pass because the Seed stays the same, even if it has non-tolerated taints", func() {819 oldShoot.Spec.SeedName = pointer.String("seed")820 seed.Spec.Taints = []core.SeedTaint{{Key: core.SeedTaintProtected}}821 // Modify the Shoot spec to avoid an early exit optimization during the admission822 shoot.Spec.Provider.Workers[0].Maximum = 10823 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())824 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())825 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())826 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)827 err := admissionHandler.Admit(context.TODO(), attrs, nil)828 Expect(err).ToNot(HaveOccurred())829 })830 It("create should pass because shoot tolerates all taints of the seed", func() {831 seed.Spec.Taints = []core.SeedTaint{{Key: core.SeedTaintProtected}}832 shoot.Spec.Tolerations = []core.Toleration{{Key: core.SeedTaintProtected}}833 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())834 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())835 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())836 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)837 err := admissionHandler.Admit(context.TODO(), attrs, nil)838 Expect(err).ToNot(HaveOccurred())839 })840 It("update should pass because shoot tolerates all taints of the seed", func() {841 seed.Spec.Taints = []core.SeedTaint{{Key: "foo"}}842 shoot.Spec.Tolerations = []core.Toleration{{Key: "foo", Value: pointer.String("bar")}}843 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())844 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())845 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())846 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)847 err := admissionHandler.Admit(context.TODO(), attrs, nil)848 Expect(err).ToNot(HaveOccurred())849 })850 It("delete should pass even if the Seed specified in shoot manifest has non-tolerated taints", func() {851 seed.Spec.Taints = []core.SeedTaint{{Key: core.SeedTaintProtected}}852 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())853 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())854 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())855 attrs := admission.NewAttributesRecord(nil, &shoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Delete, &metav1.UpdateOptions{}, false, nil)856 err := admissionHandler.Admit(context.TODO(), attrs, nil)857 Expect(err).ToNot(HaveOccurred())858 })859 })860 Context("seed capacity", func() {861 var (862 allocatableShoots resource.Quantity863 )864 BeforeEach(func() {865 allocatableShoots = *resource.NewQuantity(1, resource.DecimalSI)866 shoot.Spec.DNS = nil867 })868 It("should pass because seed allocatable capacity is not set", func() {869 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())870 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())871 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())872 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)873 err := admissionHandler.Admit(context.TODO(), attrs, nil)874 Expect(err).NotTo(HaveOccurred())875 })876 It("should pass because seed allocatable capacity is not exhausted", func() {877 seed.Status.Allocatable = corev1.ResourceList{"shoots": allocatableShoots}878 otherShoot := shoot.DeepCopy()879 otherShoot.Name = "other-shoot-1"880 otherShoot.Spec.SeedName = pointer.String("other-seed")881 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())882 otherShoot = shoot.DeepCopy()883 otherShoot.Name = "other-shoot-2"884 otherShoot.Spec.SeedName = nil885 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())886 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())887 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())888 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())889 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)890 err := admissionHandler.Admit(context.TODO(), attrs, nil)891 Expect(err).NotTo(HaveOccurred())892 })893 It("should reject because seed allocatable capacity is exhausted", func() {894 seed.Status.Allocatable = corev1.ResourceList{"shoots": allocatableShoots}895 otherShoot := shoot.DeepCopy()896 otherShoot.Name = "other-shoot-1"897 otherShoot.Spec.SeedName = &seedName898 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())899 otherShoot = shoot.DeepCopy()900 otherShoot.Name = "other-shoot-2"901 otherShoot.Spec.SeedName = nil902 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())903 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())904 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())905 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())906 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)907 err := admissionHandler.Admit(context.TODO(), attrs, nil)908 Expect(err).To(MatchError(ContainSubstring("already has the maximum number of shoots scheduled on it")))909 })910 It("should reject because seed allocatable capacity is over-exhausted", func() {911 seed.Status.Allocatable = corev1.ResourceList{"shoots": allocatableShoots}912 otherShoot := shoot.DeepCopy()913 otherShoot.Name = "other-shoot-1"914 otherShoot.Spec.SeedName = &seedName915 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())916 otherShoot = shoot.DeepCopy()917 otherShoot.Name = "other-shoot-2"918 otherShoot.Spec.SeedName = &seedName919 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())920 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())921 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())922 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())923 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)924 err := admissionHandler.Admit(context.TODO(), attrs, nil)925 Expect(err).To(MatchError(ContainSubstring("already has the maximum number of shoots scheduled on it")))926 })927 It("should allow Shoot deletion even though seed's allocatable capacity is exhausted / over exhausted", func() {928 seed.Status.Allocatable = corev1.ResourceList{"shoots": allocatableShoots}929 otherShoot := shoot.DeepCopy()930 otherShoot.Name = "other-shoot-1"931 otherShoot.Spec.SeedName = &seedName932 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())933 otherShoot = shoot.DeepCopy()934 otherShoot.Name = "other-shoot-2"935 otherShoot.Spec.SeedName = &seedName936 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(otherShoot)).To(Succeed())937 // admission for DELETION uses the old Shoot object938 oldShoot.Spec.SeedName = &seedName939 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())940 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())941 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())942 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Delete, &metav1.DeleteOptions{}, false, nil)943 err := admissionHandler.Admit(context.TODO(), attrs, nil)944 Expect(err).ToNot(HaveOccurred())945 })946 })947 })948 Context("networking settings checks", func() {949 var (950 oldShoot *core.Shoot951 )952 BeforeEach(func() {953 oldShoot = shoot.DeepCopy()954 oldShoot.Spec.SeedName = nil955 })956 It("update should pass because validation of network disjointedness should not be executed", func() {957 // set shoot pod cidr to overlap with vpn pod cidr958 shoot.Spec.Networking.Pods = pointer.String(v1beta1constants.DefaultVpnRange)959 oldShoot.Spec.SeedName = shoot.Spec.SeedName960 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())961 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())962 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())963 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)964 err := admissionHandler.Admit(context.TODO(), attrs, nil)965 Expect(err).ToNot(HaveOccurred())966 })967 It("update should fail because validation of network disjointedness is executed", func() {968 // set shoot pod cidr to overlap with vpn pod cidr969 shoot.Spec.Networking.Pods = pointer.String(v1beta1constants.DefaultVpnRange)970 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())971 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())972 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())973 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)974 err := admissionHandler.Admit(context.TODO(), attrs, nil)975 Expect(err).To(BeForbiddenError())976 })977 It("should reject because shoot pods network is missing", func() {978 shoot.Spec.Networking.Pods = nil979 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())980 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())981 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())982 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)983 err := admissionHandler.Admit(context.TODO(), attrs, nil)984 Expect(err).To(BeForbiddenError())985 })986 It("should reject because shoot services network is missing", func() {987 shoot.Spec.Networking.Services = nil988 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())989 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())990 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())991 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)992 err := admissionHandler.Admit(context.TODO(), attrs, nil)993 Expect(err).To(BeForbiddenError())994 })995 It("should default shoot networks if seed provides ShootDefaults", func() {996 seed.Spec.Networks.ShootDefaults = &core.ShootNetworks{997 Pods: &podsCIDR,998 Services: &servicesCIDR,999 }1000 shoot.Spec.Networking.Pods = nil1001 shoot.Spec.Networking.Services = nil1002 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1003 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1004 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1005 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1006 err := admissionHandler.Admit(context.TODO(), attrs, nil)1007 Expect(err).NotTo(HaveOccurred())1008 Expect(shoot.Spec.Networking.Pods).To(Equal(&podsCIDR))1009 Expect(shoot.Spec.Networking.Services).To(Equal(&servicesCIDR))1010 })1011 It("should reject because the shoot node and the seed node networks intersect", func() {1012 shoot.Spec.Networking.Nodes = &seedNodesCIDR1013 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1014 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1015 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1016 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1017 err := admissionHandler.Admit(context.TODO(), attrs, nil)1018 Expect(err).To(BeForbiddenError())1019 })1020 It("should reject because the shoot pod and the seed pod networks intersect", func() {1021 shoot.Spec.Networking.Pods = &seedPodsCIDR1022 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1023 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1024 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1025 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1026 err := admissionHandler.Admit(context.TODO(), attrs, nil)1027 Expect(err).To(BeForbiddenError())1028 })1029 It("should reject because the shoot service and the seed service networks intersect", func() {1030 shoot.Spec.Networking.Services = &seedServicesCIDR1031 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1032 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1033 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1034 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1035 err := admissionHandler.Admit(context.TODO(), attrs, nil)1036 Expect(err).To(BeForbiddenError())1037 })1038 It("should reject because the shoot pod and the seed node networks intersect", func() {1039 shoot.Spec.Networking.Pods = &seedNodesCIDR1040 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1041 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1042 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1043 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1044 err := admissionHandler.Admit(context.TODO(), attrs, nil)1045 Expect(err).To(BeForbiddenError())1046 })1047 It("should reject because the shoot service and the seed node networks intersect", func() {1048 shoot.Spec.Networking.Services = &seedNodesCIDR1049 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1050 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1051 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1052 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1053 err := admissionHandler.Admit(context.TODO(), attrs, nil)1054 Expect(err).To(BeForbiddenError())1055 })1056 It("should reject because the shoot service and the shoot node networks intersect", func() {1057 shoot.Spec.Networking.Services = shoot.Spec.Networking.Nodes1058 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1059 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1060 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1061 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1062 err := admissionHandler.Admit(context.TODO(), attrs, nil)1063 Expect(err).To(BeForbiddenError())1064 })1065 It("should reject because the shoot pod and the shoot node networks intersect", func() {1066 shoot.Spec.Networking.Pods = shoot.Spec.Networking.Nodes1067 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1068 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1069 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1070 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1071 err := admissionHandler.Admit(context.TODO(), attrs, nil)1072 Expect(err).To(BeForbiddenError())1073 })1074 It("should reject because the shoot pod and the shoot service networks intersect", func() {1075 shoot.Spec.Networking.Pods = shoot.Spec.Networking.Services1076 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1077 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1078 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1079 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1080 err := admissionHandler.Admit(context.TODO(), attrs, nil)1081 Expect(err).To(BeForbiddenError())1082 })1083 })1084 Context("dns settings checks", func() {1085 It("should reject because the specified domain is already used by another shoot", func() {1086 anotherShoot := shoot.DeepCopy()1087 anotherShoot.Name = "another-shoot"1088 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1089 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1090 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1091 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(anotherShoot)).To(Succeed())1092 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1093 err := admissionHandler.Admit(context.TODO(), attrs, nil)1094 Expect(err).To(BeForbiddenError())1095 })1096 It("should reject because the specified domain is a subdomain of a domain already used by another shoot", func() {1097 anotherShoot := shoot.DeepCopy()1098 anotherShoot.Name = "another-shoot"1099 subdomain := fmt.Sprintf("subdomain.%s", *anotherShoot.Spec.DNS.Domain)1100 shoot.Spec.DNS.Domain = &subdomain1101 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1102 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1103 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1104 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(anotherShoot)).To(Succeed())1105 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1106 err := admissionHandler.Admit(context.TODO(), attrs, nil)1107 Expect(err).To(BeForbiddenError())1108 })1109 It("should reject because the specified domain is a subdomain of a domain already used by another shoot (case one)", func() {1110 anotherShoot := shoot.DeepCopy()1111 anotherShoot.Name = "another-shoot"1112 subdomain := fmt.Sprintf("subdomain.%s", *anotherShoot.Spec.DNS.Domain)1113 shoot.Spec.DNS.Domain = &subdomain1114 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1115 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1116 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1117 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(anotherShoot)).To(Succeed())1118 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1119 err := admissionHandler.Admit(context.TODO(), attrs, nil)1120 Expect(err).To(BeForbiddenError())1121 })1122 It("should reject because the specified domain is a subdomain of a domain already used by another shoot (case two)", func() {1123 anotherShoot := shoot.DeepCopy()1124 anotherShoot.Name = "another-shoot"1125 shoot.Spec.DNS.Domain = &baseDomain1126 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1127 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1128 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1129 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(anotherShoot)).To(Succeed())1130 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1131 err := admissionHandler.Admit(context.TODO(), attrs, nil)1132 Expect(err).To(BeForbiddenError())1133 })1134 It("should allow because the specified domain is not a subdomain of a domain already used by another shoot", func() {1135 anotherShoot := shoot.DeepCopy()1136 anotherShoot.Name = "another-shoot"1137 anotherDomain := fmt.Sprintf("someprefix%s", *anotherShoot.Spec.DNS.Domain)1138 shoot.Spec.DNS.Domain = &anotherDomain1139 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1140 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1141 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1142 Expect(coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore().Add(anotherShoot)).To(Succeed())1143 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1144 err := admissionHandler.Admit(context.TODO(), attrs, nil)1145 Expect(err).To(BeNil())1146 })1147 })1148 Context("kubernetes version checks", func() {1149 It("should reject due to an invalid kubernetes version", func() {1150 shoot.Spec.Kubernetes.Version = "1.2.3"1151 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1152 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1153 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1154 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1155 err := admissionHandler.Admit(context.TODO(), attrs, nil)1156 Expect(err).To(BeForbiddenError())1157 })1158 It("should default a major.minor kubernetes version to latest patch version", func() {1159 shoot.Spec.Kubernetes.Version = "1.6"1160 highestPatchVersion := core.ExpirableVersion{Version: "1.6.6"}1161 cloudProfile.Spec.Kubernetes.Versions = append(cloudProfile.Spec.Kubernetes.Versions, highestPatchVersion, core.ExpirableVersion{Version: "1.7.1"}, core.ExpirableVersion{Version: "1.7.2"})1162 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1163 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1164 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1165 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1166 err := admissionHandler.Admit(context.TODO(), attrs, nil)1167 Expect(err).To(Not(HaveOccurred()))1168 Expect(shoot.Spec.Kubernetes.Version).To(Equal(highestPatchVersion.Version))1169 })1170 It("should default a major.minor kubernetes version only to non-preview versions", func() {1171 shoot.Spec.Kubernetes.Version = "1.6"1172 preview := core.ClassificationPreview1173 previewVersion := core.ExpirableVersion{Version: "1.6.6", Classification: &preview}1174 highestNonPreviewPatchVersion := core.ExpirableVersion{Version: "1.6.5"}1175 cloudProfile.Spec.Kubernetes.Versions = append(cloudProfile.Spec.Kubernetes.Versions, previewVersion, highestNonPreviewPatchVersion, core.ExpirableVersion{Version: "1.7.1"}, core.ExpirableVersion{Version: "1.7.2"})1176 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1177 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1178 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1179 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1180 err := admissionHandler.Admit(context.TODO(), attrs, nil)1181 Expect(err).To(Not(HaveOccurred()))1182 Expect(shoot.Spec.Kubernetes.Version).To(Equal(highestNonPreviewPatchVersion.Version))1183 })1184 It("should reject defaulting a major.minor kubernetes version if there is no higher non-preview version available for defaulting", func() {1185 shoot.Spec.Kubernetes.Version = "1.6"1186 preview := core.ClassificationPreview1187 previewVersion := core.ExpirableVersion{Version: "1.6.6", Classification: &preview}1188 highestNonPreviewPatchVersion := core.ExpirableVersion{Version: "1.6.5", Classification: &preview}1189 cloudProfile.Spec.Kubernetes.Versions = []core.ExpirableVersion{previewVersion, highestNonPreviewPatchVersion, {Version: "1.7.1"}, {Version: "1.7.2"}}1190 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1191 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1192 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1193 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1194 err := admissionHandler.Admit(context.TODO(), attrs, nil)1195 Expect(err).To(BeForbiddenError())1196 })1197 It("should be able to explicitly pick preview versions", func() {1198 shoot.Spec.Kubernetes.Version = "1.6.6"1199 preview := core.ClassificationPreview1200 previewVersion := core.ExpirableVersion{Version: "1.6.6", Classification: &preview}1201 cloudProfile.Spec.Kubernetes.Versions = []core.ExpirableVersion{previewVersion}1202 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1203 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1204 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1205 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1206 err := admissionHandler.Admit(context.TODO(), attrs, nil)1207 Expect(err).To(Not(HaveOccurred()))1208 })1209 It("should reject: default only exactly matching minor kubernetes version", func() {1210 shoot.Spec.Kubernetes.Version = "1.8"1211 highestPatchVersion := core.ExpirableVersion{Version: "1.81.5"}1212 cloudProfile.Spec.Kubernetes.Versions = append(cloudProfile.Spec.Kubernetes.Versions, core.ExpirableVersion{Version: "1.81.0"}, highestPatchVersion)1213 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1214 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1215 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1216 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1217 err := admissionHandler.Admit(context.TODO(), attrs, nil)1218 Expect(err).To(BeForbiddenError())1219 })1220 It("should choose the default kubernetes version if only major.minor is given in a worker group", func() {1221 shoot.Spec.Provider.Workers[0].Kubernetes = &core.WorkerKubernetes{Version: pointer.String("1.18")}1222 highestPatchVersion := core.ExpirableVersion{Version: "1.18.5"}1223 cloudProfile.Spec.Kubernetes.Versions = append(cloudProfile.Spec.Kubernetes.Versions, core.ExpirableVersion{Version: "1.18.0"}, highestPatchVersion)1224 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1225 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1226 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1227 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1228 Expect(admissionHandler.Admit(context.TODO(), attrs, nil)).To(Succeed())1229 Expect(*shoot.Spec.Provider.Workers[0].Kubernetes.Version).To(Equal(highestPatchVersion.Version))1230 })1231 It("should work to create a cluster without a worker group kubernetes version set", func() {1232 shoot.Spec.Kubernetes.Version = "1.18.5"1233 highestPatchVersion := core.ExpirableVersion{Version: "1.18.5"}1234 cloudProfile.Spec.Kubernetes.Versions = append(cloudProfile.Spec.Kubernetes.Versions, core.ExpirableVersion{Version: "1.18.0"}, highestPatchVersion)1235 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1236 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1237 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1238 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1239 Expect(admissionHandler.Admit(context.TODO(), attrs, nil)).To(Succeed())1240 Expect(shoot.Spec.Provider.Workers[0].Kubernetes).To(BeNil())1241 })1242 It("should work to create a cluster with a worker group kubernetes version set smaller than control plane version", func() {1243 shoot.Spec.Kubernetes.Version = "1.18.5"1244 shoot.Spec.Provider.Workers[0].Kubernetes = &core.WorkerKubernetes{Version: pointer.String("1.17.0")}1245 cloudProfile.Spec.Kubernetes.Versions = append(cloudProfile.Spec.Kubernetes.Versions, core.ExpirableVersion{Version: "1.17.0"}, core.ExpirableVersion{Version: "1.18.5"})1246 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1247 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1248 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1249 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1250 Expect(admissionHandler.Admit(context.TODO(), attrs, nil)).To(Succeed())1251 Expect(shoot.Spec.Provider.Workers[0].Kubernetes.Version).To(Equal(pointer.String("1.17.0")))1252 })1253 It("should work to create a cluster with a worker group kubernetes version set equal to control plane version", func() {1254 shoot.Spec.Kubernetes.Version = "1.18.5"1255 shoot.Spec.Provider.Workers[0].Kubernetes = &core.WorkerKubernetes{Version: pointer.String("1.18.5")}1256 cloudProfile.Spec.Kubernetes.Versions = append(cloudProfile.Spec.Kubernetes.Versions, core.ExpirableVersion{Version: "1.17.0"}, core.ExpirableVersion{Version: "1.18.5"})1257 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1258 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1259 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1260 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1261 Expect(admissionHandler.Admit(context.TODO(), attrs, nil)).To(Succeed())1262 Expect(shoot.Spec.Provider.Workers[0].Kubernetes.Version).To(Equal(pointer.String("1.18.5")))1263 })1264 })1265 Context("machine image checks", func() {1266 var (1267 classificationPreview = core.ClassificationPreview1268 imageName1 = validMachineImageName1269 imageName2 = "other-image"1270 expiredVersion = "1.1.1"1271 expiringVersion = "1.2.1"1272 nonExpiredVersion1 = "2.0.0"1273 nonExpiredVersion2 = "2.0.1"1274 latestNonExpiredVersion = "2.1.0"1275 previewVersion = "3.0.0"1276 cloudProfileMachineImages = []core.MachineImage{1277 {1278 Name: imageName1,1279 Versions: []core.MachineImageVersion{1280 {1281 ExpirableVersion: core.ExpirableVersion{1282 Version: previewVersion,1283 Classification: &classificationPreview,1284 },1285 },1286 {1287 ExpirableVersion: core.ExpirableVersion{1288 Version: latestNonExpiredVersion,1289 },1290 },1291 {1292 ExpirableVersion: core.ExpirableVersion{1293 Version: nonExpiredVersion1,1294 },1295 },1296 {1297 ExpirableVersion: core.ExpirableVersion{1298 Version: nonExpiredVersion2,1299 },1300 },1301 {1302 ExpirableVersion: core.ExpirableVersion{1303 Version: expiringVersion,1304 ExpirationDate: &metav1.Time{Time: metav1.Now().Add(time.Second * 1000)},1305 },1306 },1307 {1308 ExpirableVersion: core.ExpirableVersion{1309 Version: expiredVersion,1310 ExpirationDate: &metav1.Time{Time: metav1.Now().Add(time.Second * -1000)},1311 },1312 },1313 },1314 }, {1315 Name: imageName2,1316 Versions: []core.MachineImageVersion{1317 {1318 ExpirableVersion: core.ExpirableVersion{1319 Version: previewVersion,1320 Classification: &classificationPreview,1321 },1322 },1323 {1324 ExpirableVersion: core.ExpirableVersion{1325 Version: latestNonExpiredVersion,1326 },1327 },1328 {1329 ExpirableVersion: core.ExpirableVersion{1330 Version: nonExpiredVersion1,1331 },1332 },1333 {1334 ExpirableVersion: core.ExpirableVersion{1335 Version: nonExpiredVersion2,1336 },1337 },1338 {1339 ExpirableVersion: core.ExpirableVersion{1340 Version: expiringVersion,1341 ExpirationDate: &metav1.Time{Time: metav1.Now().Add(time.Second * 1000)},1342 },1343 },1344 {1345 ExpirableVersion: core.ExpirableVersion{1346 Version: expiredVersion,1347 ExpirationDate: &metav1.Time{Time: metav1.Now().Add(time.Second * -1000)},1348 },1349 },1350 },1351 },1352 }1353 )1354 BeforeEach(func() {1355 cloudProfile.Spec.MachineImages = cloudProfileMachineImages1356 })1357 Context("create Shoot", func() {1358 It("should reject due to an invalid machine image", func() {1359 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1360 Name: "not-supported",1361 Version: "not-supported",1362 }1363 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1364 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1365 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1366 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1367 err := admissionHandler.Admit(context.TODO(), attrs, nil)1368 Expect(err).To(BeForbiddenError())1369 })1370 It("should reject due to an invalid machine image (version unset)", func() {1371 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1372 Name: "not-supported",1373 }1374 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1375 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1376 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1377 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1378 err := admissionHandler.Admit(context.TODO(), attrs, nil)1379 Expect(err).To(HaveOccurred())1380 Expect(err.Error()).To(ContainSubstring("image name \"not-supported\" is not supported"))1381 })1382 It("should reject due to a machine image with expiration date in the past", func() {1383 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1384 Name: imageName1,1385 Version: expiredVersion,1386 }1387 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1388 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1389 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1390 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1391 err := admissionHandler.Admit(context.TODO(), attrs, nil)1392 Expect(err).To(BeForbiddenError())1393 })1394 It("should default version to latest non-preview version as shoot does not specify one", func() {1395 shoot.Spec.Provider.Workers[0].Machine.Image = nil1396 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1397 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1398 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1399 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1400 err := admissionHandler.Admit(context.TODO(), attrs, nil)1401 Expect(err).NotTo(HaveOccurred())1402 Expect(shoot.Spec.Provider.Workers[0].Machine.Image).To(Equal(&core.ShootMachineImage{1403 Name: imageName1,1404 Version: latestNonExpiredVersion,1405 }))1406 })1407 It("should default version to latest non-preview version as shoot only specifies name", func() {1408 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1409 Name: imageName1,1410 }1411 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1412 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1413 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1414 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1415 err := admissionHandler.Admit(context.TODO(), attrs, nil)1416 Expect(err).NotTo(HaveOccurred())1417 Expect(shoot.Spec.Provider.Workers[0].Machine.Image).To(Equal(&core.ShootMachineImage{1418 Name: imageName1,1419 Version: latestNonExpiredVersion,1420 }))1421 })1422 It("should allow supported CRI and CRs", func() {1423 shoot.Spec.Provider.Workers = []core.Worker{1424 {1425 CRI: &core.CRI{1426 Name: core.CRINameContainerD,1427 ContainerRuntimes: []core.ContainerRuntime{1428 {Type: "supported-cr-1"},1429 {Type: "supported-cr-2"},1430 },1431 },1432 Machine: core.Machine{1433 Image: &core.ShootMachineImage{1434 Name: "cr-image-name",1435 Version: "1.2.3",1436 },1437 },1438 },1439 }1440 cloudProfile.Spec.MachineImages = append(1441 cloudProfile.Spec.MachineImages,1442 core.MachineImage{1443 Name: "cr-image-name",1444 Versions: []core.MachineImageVersion{1445 {1446 ExpirableVersion: core.ExpirableVersion{1447 Version: "1.2.3",1448 },1449 CRI: []core.CRI{1450 {1451 Name: core.CRINameContainerD,1452 ContainerRuntimes: []core.ContainerRuntime{1453 {1454 Type: "supported-cr-1",1455 },1456 {1457 Type: "supported-cr-2",1458 },1459 },1460 },1461 },1462 },1463 },1464 })1465 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1466 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1467 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1468 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1469 err := admissionHandler.Admit(context.TODO(), attrs, nil)1470 Expect(err).NotTo(HaveOccurred())1471 })1472 It("should reject unsupported CRI", func() {1473 shoot.Spec.Provider.Workers = append(1474 shoot.Spec.Provider.Workers,1475 core.Worker{1476 CRI: &core.CRI{1477 Name: "unsupported-cri",1478 ContainerRuntimes: []core.ContainerRuntime{1479 {Type: "supported-cr-1"},1480 {Type: "supported-cr-2"},1481 },1482 },1483 Machine: core.Machine{1484 Image: &core.ShootMachineImage{1485 Name: "cr-image-name",1486 Version: "1.2.3",1487 },1488 },1489 })1490 cloudProfile.Spec.MachineImages = append(1491 cloudProfile.Spec.MachineImages,1492 core.MachineImage{1493 Name: "cr-image-name",1494 Versions: []core.MachineImageVersion{1495 {1496 ExpirableVersion: core.ExpirableVersion{1497 Version: "1.2.3",1498 },1499 CRI: []core.CRI{1500 {1501 Name: core.CRINameContainerD,1502 ContainerRuntimes: []core.ContainerRuntime{1503 {1504 Type: "supported-cr-1",1505 },1506 {1507 Type: "supported-cr-2",1508 },1509 },1510 },1511 },1512 },1513 },1514 })1515 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1516 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1517 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1518 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1519 err := admissionHandler.Admit(context.TODO(), attrs, nil)1520 Expect(err).To(BeForbiddenError())1521 Expect(err.Error()).To(ContainSubstring("machine image 'cr-image-name@1.2.3' does not support CRI 'unsupported-cri', supported values: [containerd]"))1522 })1523 It("should reject unsupported CR", func() {1524 shoot.Spec.Provider.Workers = append(1525 shoot.Spec.Provider.Workers,1526 core.Worker{1527 CRI: &core.CRI{1528 Name: core.CRINameContainerD,1529 ContainerRuntimes: []core.ContainerRuntime{1530 {Type: "supported-cr-1"},1531 {Type: "supported-cr-2"},1532 {Type: "unsupported-cr-1"},1533 },1534 },1535 Machine: core.Machine{1536 Image: &core.ShootMachineImage{1537 Name: "cr-image-name",1538 Version: "1.2.3",1539 },1540 },1541 })1542 cloudProfile.Spec.MachineImages = append(1543 cloudProfile.Spec.MachineImages,1544 core.MachineImage{1545 Name: "cr-image-name",1546 Versions: []core.MachineImageVersion{1547 {1548 ExpirableVersion: core.ExpirableVersion{1549 Version: "1.2.3",1550 },1551 CRI: []core.CRI{1552 {1553 Name: core.CRINameContainerD,1554 ContainerRuntimes: []core.ContainerRuntime{1555 {Type: "supported-cr-1"},1556 {Type: "supported-cr-2"},1557 },1558 },1559 },1560 },1561 },1562 })1563 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1564 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1565 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1566 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1567 err := admissionHandler.Admit(context.TODO(), attrs, nil)1568 Expect(err).To(BeForbiddenError())1569 Expect(err.Error()).To(ContainSubstring("machine image 'cr-image-name@1.2.3' does not support container runtime 'unsupported-cr-1', supported values: [supported-cr-1 supported-cr-2"))1570 })1571 })1572 Context("update Shoot", func() {1573 BeforeEach(func() {1574 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1575 Name: imageName1,1576 Version: nonExpiredVersion1,1577 }1578 })1579 It("should deny updating to an MachineImage which does not support the selected container runtime", func() {1580 cloudProfile.Spec.MachineImages = append(1581 cloudProfile.Spec.MachineImages,1582 core.MachineImage{1583 Name: "cr-image-name",1584 Versions: []core.MachineImageVersion{1585 {1586 ExpirableVersion: core.ExpirableVersion{1587 Version: "1.2.3",1588 },1589 CRI: []core.CRI{1590 {1591 Name: core.CRINameContainerD,1592 },1593 },1594 },1595 },1596 })1597 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1598 Name: "cr-image-name",1599 Version: "1.2.3",1600 }1601 shoot.Spec.Provider.Workers[0].CRI = &core.CRI{Name: core.CRINameContainerD}1602 newShoot := shoot.DeepCopy()1603 newShoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1604 Name: imageName1,1605 Version: latestNonExpiredVersion,1606 }1607 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1608 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1609 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1610 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)1611 err := admissionHandler.Admit(context.TODO(), attrs, nil)1612 Expect(err).To(HaveOccurred())1613 })1614 It("should deny updating to an MachineImageVersion which does not support the selected container runtime", func() {1615 cloudProfile.Spec.MachineImages = append(1616 cloudProfile.Spec.MachineImages,1617 core.MachineImage{1618 Name: "cr-image-name",1619 Versions: []core.MachineImageVersion{1620 {1621 ExpirableVersion: core.ExpirableVersion{1622 Version: "1.2.3",1623 },1624 CRI: []core.CRI{1625 {1626 Name: core.CRINameContainerD,1627 },1628 },1629 },1630 {1631 ExpirableVersion: core.ExpirableVersion{1632 Version: "2.3.4",1633 },1634 },1635 },1636 })1637 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1638 Name: "cr-image-name",1639 Version: "1.2.3",1640 }1641 shoot.Spec.Provider.Workers[0].CRI = &core.CRI{Name: core.CRINameContainerD}1642 newShoot := shoot.DeepCopy()1643 newShoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1644 Name: "cr-image-name",1645 Version: "2.3.4",1646 }1647 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1648 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1649 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1650 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)1651 err := admissionHandler.Admit(context.TODO(), attrs, nil)1652 Expect(err).To(HaveOccurred())1653 })1654 It("should keep machine image of the old shoot (unset in new shoot)", func() {1655 newShoot := shoot.DeepCopy()1656 newShoot.Spec.Provider.Workers[0].Machine.Image = nil1657 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1658 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1659 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1660 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)1661 err := admissionHandler.Admit(context.TODO(), attrs, nil)1662 Expect(err).NotTo(HaveOccurred())1663 Expect(*newShoot).To(Equal(shoot))1664 })1665 It("should keep machine image of the old shoot (version unset in new shoot)", func() {1666 newShoot := shoot.DeepCopy()1667 newShoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1668 Name: imageName1,1669 }1670 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1671 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1672 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1673 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)1674 err := admissionHandler.Admit(context.TODO(), attrs, nil)1675 Expect(err).NotTo(HaveOccurred())1676 Expect(*newShoot).To(Equal(shoot))1677 })1678 It("should use updated machine image version as specified", func() {1679 newShoot := shoot.DeepCopy()1680 newShoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1681 Name: imageName1,1682 Version: nonExpiredVersion2,1683 }1684 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1685 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1686 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1687 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)1688 err := admissionHandler.Admit(context.TODO(), attrs, nil)1689 Expect(err).NotTo(HaveOccurred())1690 Expect(newShoot.Spec.Provider.Workers[0].Machine.Image).To(Equal(&core.ShootMachineImage{1691 Name: imageName1,1692 Version: nonExpiredVersion2,1693 }))1694 })1695 It("should default version of new worker pool to latest non-preview version", func() {1696 newShoot := shoot.DeepCopy()1697 newWorker := newShoot.Spec.Provider.Workers[0].DeepCopy()1698 newWorker.Name = "second-worker"1699 newWorker.Machine.Image = nil1700 newShoot.Spec.Provider.Workers = append(newShoot.Spec.Provider.Workers, *newWorker)1701 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1702 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1703 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1704 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), newShoot.Namespace, newShoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil)1705 err := admissionHandler.Admit(context.TODO(), attrs, nil)1706 Expect(err).NotTo(HaveOccurred())1707 Expect(newShoot.Spec.Provider.Workers[0]).To(Equal(shoot.Spec.Provider.Workers[0]))1708 Expect(newShoot.Spec.Provider.Workers[1].Machine.Image).To(Equal(&core.ShootMachineImage{1709 Name: imageName1,1710 Version: latestNonExpiredVersion,1711 }))1712 })1713 It("should default version of new worker pool to latest non-preview version (version unset)", func() {1714 newShoot := shoot.DeepCopy()1715 newWorker := newShoot.Spec.Provider.Workers[0].DeepCopy()1716 newWorker.Name = "second-worker"1717 newWorker.Machine.Image = &core.ShootMachineImage{1718 Name: imageName2,1719 }1720 newShoot.Spec.Provider.Workers = append(newShoot.Spec.Provider.Workers, *newWorker)1721 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1722 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1723 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1724 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), newShoot.Namespace, newShoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil)1725 err := admissionHandler.Admit(context.TODO(), attrs, nil)1726 Expect(err).NotTo(HaveOccurred())1727 Expect(newShoot.Spec.Provider.Workers[0]).To(Equal(shoot.Spec.Provider.Workers[0]))1728 Expect(newShoot.Spec.Provider.Workers[1].Machine.Image).To(Equal(&core.ShootMachineImage{1729 Name: imageName2,1730 Version: latestNonExpiredVersion,1731 }))1732 })1733 It("should use version of new worker pool as specified", func() {1734 newShoot := shoot.DeepCopy()1735 newWorker := newShoot.Spec.Provider.Workers[0].DeepCopy()1736 newWorker.Name = "second-worker"1737 newWorker.Machine.Image = &core.ShootMachineImage{1738 Name: imageName2,1739 Version: nonExpiredVersion1,1740 }1741 newShoot.Spec.Provider.Workers = append(newShoot.Spec.Provider.Workers, *newWorker)1742 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1743 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1744 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1745 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), newShoot.Namespace, newShoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil)1746 err := admissionHandler.Admit(context.TODO(), attrs, nil)1747 Expect(err).NotTo(HaveOccurred())1748 Expect(newShoot.Spec.Provider.Workers[0]).To(Equal(shoot.Spec.Provider.Workers[0]))1749 Expect(newShoot.Spec.Provider.Workers[1].Machine.Image).To(Equal(&core.ShootMachineImage{1750 Name: imageName2,1751 Version: nonExpiredVersion1,1752 }))1753 })1754 It("should default version of new image to latest non-preview version (version unset)", func() {1755 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1756 Name: imageName1,1757 Version: nonExpiredVersion2,1758 }1759 newShoot := shoot.DeepCopy()1760 newShoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1761 Name: imageName2,1762 }1763 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1764 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1765 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1766 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), newShoot.Namespace, newShoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil)1767 err := admissionHandler.Admit(context.TODO(), attrs, nil)1768 Expect(err).NotTo(HaveOccurred())1769 Expect(newShoot.Spec.Provider.Workers[0].Machine.Image).To(Equal(&core.ShootMachineImage{1770 Name: imageName2,1771 Version: latestNonExpiredVersion,1772 }))1773 })1774 It("should use version of new image as specified", func() {1775 shoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1776 Name: imageName1,1777 Version: nonExpiredVersion2,1778 }1779 newShoot := shoot.DeepCopy()1780 newShoot.Spec.Provider.Workers[0].Machine.Image = &core.ShootMachineImage{1781 Name: imageName2,1782 Version: nonExpiredVersion2,1783 }1784 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1785 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1786 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1787 attrs := admission.NewAttributesRecord(newShoot, &shoot, core.Kind("Shoot").WithVersion("version"), newShoot.Namespace, newShoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil)1788 err := admissionHandler.Admit(context.TODO(), attrs, nil)1789 Expect(err).NotTo(HaveOccurred())1790 Expect(newShoot.Spec.Provider.Workers[0].Machine.Image).To(Equal(&core.ShootMachineImage{1791 Name: imageName2,1792 Version: nonExpiredVersion2,1793 }))1794 })1795 })1796 })1797 Context("machine type checks", func() {1798 It("should not reject due to an usable machine type", func() {1799 shoot.Spec.Provider.Workers = []core.Worker{1800 {1801 Machine: core.Machine{1802 Type: "machine-type-1",1803 },1804 },1805 }1806 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1807 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1808 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1809 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1810 err := admissionHandler.Admit(context.TODO(), attrs, nil)1811 Expect(err).NotTo(HaveOccurred())1812 })1813 It("should reject due to a not usable machine type", func() {1814 shoot.Spec.Provider.Workers = []core.Worker{1815 {1816 Machine: core.Machine{1817 Type: "machine-type-old",1818 },1819 },1820 }1821 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1822 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1823 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1824 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1825 err := admissionHandler.Admit(context.TODO(), attrs, nil)1826 Expect(err).To(BeForbiddenError())1827 })1828 It("should reject due to an invalid machine type", func() {1829 shoot.Spec.Provider.Workers = []core.Worker{1830 {1831 Machine: core.Machine{1832 Type: "not-allowed",1833 },1834 },1835 }1836 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1837 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1838 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1839 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1840 err := admissionHandler.Admit(context.TODO(), attrs, nil)1841 Expect(err).To(BeForbiddenError())1842 })1843 })1844 Context("volume checks", func() {1845 It("should reject due to an invalid volume type", func() {1846 notAllowed := "not-allowed"1847 shoot.Spec.Provider.Workers = []core.Worker{1848 {1849 Machine: core.Machine{1850 Type: "machine-type-1",1851 },1852 Volume: &core.Volume{1853 Type: ¬Allowed,1854 },1855 },1856 }1857 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1858 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1859 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1860 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1861 err := admissionHandler.Admit(context.TODO(), attrs, nil)1862 Expect(err).To(BeForbiddenError())1863 })1864 It("should allow volume removal", func() {1865 oldShoot := shoot.DeepCopy()1866 shoot.Spec.Provider.Workers[0].Volume = nil1867 oldShoot.Spec.Provider.Workers[0].Volume.VolumeSize = "20Gi"1868 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1869 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1870 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1871 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)1872 err := admissionHandler.Admit(context.TODO(), attrs, nil)1873 Expect(err).ToNot(HaveOccurred())1874 })1875 It("should reject due to wrong volume size (volume type constraint)", func() {1876 boundaryVolSize := minVolSize1877 boundaryVolSize.Add(resource.MustParse("-1"))1878 boundaryVolSizeMachine := minVolSizeMachine1879 boundaryVolSizeMachine.Add(resource.MustParse("-1"))1880 shoot.Spec.Provider.Workers = []core.Worker{1881 {1882 Machine: core.Machine{1883 Type: "machine-type-1",1884 },1885 Volume: &core.Volume{1886 Type: &volumeType2,1887 VolumeSize: boundaryVolSize.String(),1888 },1889 },1890 {1891 Machine: core.Machine{1892 Type: "machine-type-2",1893 },1894 Volume: &core.Volume{1895 Type: &volumeType,1896 VolumeSize: boundaryVolSize.String(),1897 },1898 },1899 {1900 Machine: core.Machine{1901 Type: "machine-type-2",1902 },1903 Volume: &core.Volume{1904 Type: &volumeType,1905 VolumeSize: boundaryVolSizeMachine.String(),1906 },1907 },1908 }1909 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1910 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1911 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1912 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1913 err := admissionHandler.Admit(context.TODO(), attrs, nil)1914 Expect(err).To(BeForbiddenError())1915 Expect(err.Error()).To(ContainSubstring("spec.provider.workers[0].volume.size"))1916 Expect(err.Error()).To(ContainSubstring("spec.provider.workers[2].volume.size"))1917 })1918 })1919 Context("RawExtension internal API usage checks", func() {1920 BeforeEach(func() {1921 shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{1922 Raw: []byte(`{1923 "kind": "InfrastructureConfig",1924 "apiVersion": "azure.provider.extensions.gardener.cloud/__internal",1925 "key": "value"1926 }`),1927 }1928 shoot.Spec.Provider.ControlPlaneConfig = &runtime.RawExtension{1929 Raw: []byte(`{1930 "apiVersion": "aws.provider.extensions.gardener.cloud/__internal",1931 "kind": "ControlPlaneConfig",1932 "key": "value"1933 }`),1934 }1935 shoot.Spec.Networking.ProviderConfig = &runtime.RawExtension{1936 Raw: []byte(`{1937 "apiVersion": "calico.networking.extensions.gardener.cloud/__internal",1938 "kind": "NetworkConfig",1939 "key": "value"1940 }`)}1941 shoot.Spec.Provider.Workers = append(shoot.Spec.Provider.Workers, core.Worker{1942 Name: "worker-with-invalid-providerConfig",1943 Machine: core.Machine{1944 Type: "machine-type-1",1945 Image: &core.ShootMachineImage{1946 Name: validMachineImageName,1947 Version: "0.0.1",1948 ProviderConfig: &runtime.RawExtension{1949 Raw: []byte(`{1950 "apiVersion": "memoryone-chost.os.extensions.gardener.cloud/__internal",1951 "kind": "OperatingSystemConfiguration",1952 "key": "value"1953 }`)},1954 },1955 },1956 CRI: &core.CRI{1957 Name: core.CRINameContainerD,1958 ContainerRuntimes: []core.ContainerRuntime{1959 {1960 Type: "test-cr",1961 ProviderConfig: &runtime.RawExtension{1962 Raw: []byte(`{1963 "apiVersion": "some.api/__internal",1964 "kind": "ContainerRuntimeConfig",1965 "some-key": "some-value"1966 }`)},1967 },1968 },1969 },1970 Minimum: 1,1971 Maximum: 1,1972 Volume: &core.Volume{1973 VolumeSize: "40Gi",1974 Type: &volumeType,1975 },1976 Zones: []string{"europe-a"},1977 ProviderConfig: &runtime.RawExtension{1978 Raw: []byte(`{1979 "apiVersion": "aws.provider.extensions.gardener.cloud/__internal",1980 "kind": "WorkerConfig",1981 "key": "value"1982 }`)},1983 })1984 })1985 It("ensures new clusters cannot use the apiVersion 'internal'", func() {1986 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())1987 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())1988 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())1989 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)1990 err := admissionHandler.Admit(context.TODO(), attrs, nil)1991 Expect(err).To(BeForbiddenError())1992 Expect(err.Error()).To(ContainSubstring("spec.provider.infrastructureConfig: Invalid value: \"azure.provider.extensions.gardener.cloud/__internal, Kind=InfrastructureConfig\": must not use apiVersion 'internal'"))1993 Expect(err.Error()).To(ContainSubstring("spec.provider.controlPlaneConfig: Invalid value: \"aws.provider.extensions.gardener.cloud/__internal, Kind=ControlPlaneConfig\": must not use apiVersion 'internal'"))1994 Expect(err.Error()).To(ContainSubstring("spec.networking.providerConfig: Invalid value: \"calico.networking.extensions.gardener.cloud/__internal, Kind=NetworkConfig\": must not use apiVersion 'internal'"))1995 Expect(err.Error()).To(ContainSubstring("spec.provider.workers[1].providerConfig: Invalid value: \"aws.provider.extensions.gardener.cloud/__internal, Kind=WorkerConfig\": must not use apiVersion 'internal'"))1996 Expect(err.Error()).To(ContainSubstring("spec.provider.workers[1].machine.image.providerConfig: Invalid value: \"memoryone-chost.os.extensions.gardener.cloud/__internal, Kind=OperatingSystemConfiguration\": must not use apiVersion 'internal'"))1997 Expect(err.Error()).To(ContainSubstring("spec.provider.workers[1].cri.containerRuntimes[0].providerConfig: Invalid value: \"some.api/__internal, Kind=ContainerRuntimeConfig\": must not use apiVersion 'internal'"))1998 })1999 // TODO (voelzmo): remove this test and the associated production code once we gave owners of existing Shoots a nice grace period to move away from 'internal' apiVersion2000 It("ensures existing clusters can still use the apiVersion 'internal' for compatibility reasons", func() {2001 oldShoot := shoot.DeepCopy()2002 // update the Shoot spec to avoid early exit in the admission process2003 shoot.Spec.Provider.Workers[0].Maximum = 13372004 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())2005 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())2006 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())2007 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil)2008 err := admissionHandler.Admit(context.TODO(), attrs, nil)2009 Expect(err).ToNot(HaveOccurred())2010 })2011 It("admits new clusters using other apiVersion than 'internal'", func() {2012 shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{2013 Raw: []byte(`{2014 "kind": "InfrastructureConfig",2015 "apiVersion": "azure.provider.extensions.gardener.cloud/v1",2016 "key": "value"2017 }`),2018 }2019 shoot.Spec.Provider.ControlPlaneConfig = &runtime.RawExtension{2020 Raw: []byte(`{2021 "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",2022 "kind": "ControlPlaneConfig",2023 "key": "value"2024 }`),2025 }2026 shoot.Spec.Networking.ProviderConfig = &runtime.RawExtension{2027 Raw: []byte(`{2028 "apiVersion": "calico.networking.extensions.gardener.cloud/v1alpha1",2029 "kind": "NetworkConfig",2030 "key": "value"2031 }`)}2032 shoot.Spec.Provider.Workers[1].Machine.Image = &core.ShootMachineImage{2033 Name: validMachineImageName,2034 Version: "0.0.1",2035 ProviderConfig: &runtime.RawExtension{Raw: []byte(`{2036 "apiVersion": "memoryone-chost.os.extensions.gardener.cloud/v1alpha1",2037 "kind": "OperatingSystemConfiguration",2038 "key": "value"2039 }`)},2040 }2041 shoot.Spec.Provider.Workers[1].ProviderConfig = &runtime.RawExtension{2042 Raw: []byte(`{2043 "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",2044 "kind": "WorkerConfig",2045 "key": "value"2046 }`)}2047 shoot.Spec.Provider.Workers[1].CRI = &core.CRI{2048 Name: core.CRINameContainerD,2049 ContainerRuntimes: []core.ContainerRuntime{2050 {2051 Type: "test-cr",2052 ProviderConfig: &runtime.RawExtension{2053 Raw: []byte(`{2054 "apiVersion": "some.api/v1alpha1",2055 "kind": "ContainerRuntimeConfig",2056 "key": "value"2057 }`)},2058 },2059 },2060 }2061 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())2062 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())2063 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())2064 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)2065 err := admissionHandler.Admit(context.TODO(), attrs, nil)2066 Expect(err).ToNot(HaveOccurred())2067 })2068 })2069 It("allows RawExtensions to contain arbitrary json blobs", func() {2070 shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{2071 Raw: []byte(`{2072 "key": "value"2073 }`),2074 }2075 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())2076 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())2077 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())2078 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)2079 err := admissionHandler.Admit(context.TODO(), attrs, nil)2080 Expect(err).ToNot(HaveOccurred())2081 })2082 It("doesn't throw an error when the passed json is invalid", func() {2083 shoot.Spec.Provider.InfrastructureConfig = &runtime.RawExtension{2084 Raw: []byte(`{2085 "key": invalid-value2086 }`),2087 }2088 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())2089 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())2090 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())2091 attrs := admission.NewAttributesRecord(&shoot, nil, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)2092 err := admissionHandler.Admit(context.TODO(), attrs, nil)2093 Expect(err).ToNot(HaveOccurred())2094 })2095 })2096 Context("backup configuration on seed", func() {2097 It("should allow new Shoot creation when Seed doesn't have configuration for backup", func() {2098 oldShoot := shoot.DeepCopy()2099 oldShoot.Spec.SeedName = nil2100 seed.Spec.Backup = nil2101 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())2102 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())2103 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())2104 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2105 err := admissionHandler.Admit(context.TODO(), attrs, nil)2106 Expect(err).ToNot(HaveOccurred())2107 })2108 })2109 Context("control plane migration", func() {2110 var (2111 oldSeedName string2112 oldSeed *core.Seed2113 oldShoot *core.Shoot2114 )2115 BeforeEach(func() {2116 oldSeedName = fmt.Sprintf("old-%s", seedName)2117 oldSeed = seed.DeepCopy()2118 oldSeed.Name = oldSeedName2119 oldShoot = shoot.DeepCopy()2120 oldShoot.Spec.SeedName = &oldSeedName2121 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())2122 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())2123 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())2124 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(oldSeed)).To(Succeed())2125 Expect(extCoreInformerFactory.Core().V1alpha1().ShootStates().Informer().GetStore().Add(&shootState)).To(Succeed())2126 })2127 It("should fail to change Seed name, because Seed doesn't have configuration for backup", func() {2128 seed.Spec.Backup = nil2129 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2130 err := admissionHandler.Admit(context.TODO(), attrs, nil)2131 Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("backup is not configured for seed %q", seedName))))2132 })2133 It("should fail to change Seed name, because old Seed doesn't have configuration for backup", func() {2134 oldSeed.Spec.Backup = nil2135 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2136 err := admissionHandler.Admit(context.TODO(), attrs, nil)2137 Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("backup is not configured for old seed %q", oldSeedName))))2138 })2139 It("should fail to change Seed name, because cloud provider for new Seed is not equal to cloud provider for old Seed", func() {2140 oldSeed.Spec.Provider.Type = "gcp"2141 seed.Spec.Provider.Type = "aws"2142 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2143 err := admissionHandler.Admit(context.TODO(), attrs, nil)2144 Expect(err).To(BeForbiddenError())2145 })2146 It("should fail to change Seed name when other values in the Shoot's spec are changed as well", func() {2147 shoot.Spec.Kubernetes.Version = "1.6.4"2148 oldShoot.Spec.Kubernetes.Version = "1.6.3"2149 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2150 err := admissionHandler.Admit(context.TODO(), attrs, nil)2151 Expect(err).To(BeForbiddenError())2152 })2153 DescribeTable("Changing shoot spec during migration",2154 func(newSeedName *string, lastOperationType core.LastOperationType, lastOperationState core.LastOperationState, matcher types.GomegaMatcher) {2155 shoot.Status.LastOperation = &core.LastOperation{2156 Type: lastOperationType,2157 State: lastOperationState,2158 }2159 oldShoot.Spec.SeedName = &seedName2160 oldShoot.Spec.Kubernetes.Version = "1.6.3"2161 shoot.Spec.Kubernetes.Version = "1.6.4"2162 shoot.Status.SeedName = newSeedName2163 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2164 err := admissionHandler.Admit(context.TODO(), attrs, nil)2165 Expect(err).To(matcher)2166 },2167 Entry("should reject if migration has not started, but seed was previously changed", &oldSeedName, core.LastOperationTypeReconcile, core.LastOperationStateSucceeded, BeForbiddenError()),2168 Entry("should reject if the shoot has lastOperation=Migrate:Processing", &oldSeedName, core.LastOperationTypeMigrate, core.LastOperationStateProcessing, BeForbiddenError()),2169 Entry("should reject if the shoot has lastOperation=Migrate:Error", &oldSeedName, core.LastOperationTypeMigrate, core.LastOperationStateError, BeForbiddenError()),2170 Entry("should reject if the shoot has lastOperation=Migrate:Succeeded", nil, core.LastOperationTypeMigrate, core.LastOperationStateSucceeded, BeForbiddenError()),2171 Entry("should reject if the shoot has lastOperation=Restore:Pending", nil, core.LastOperationTypeRestore, core.LastOperationStatePending, BeForbiddenError()),2172 Entry("should reject if the shoot has lastOperation=Restore:Processing", &seedName, core.LastOperationTypeRestore, core.LastOperationStateProcessing, BeForbiddenError()),2173 Entry("should reject if the shoot has lastOperation=Restore:Error", &seedName, core.LastOperationTypeRestore, core.LastOperationStateError, BeForbiddenError()),2174 Entry("should allow if the shoot has lastOperation=Restore:Succeeded", &seedName, core.LastOperationTypeRestore, core.LastOperationStateSucceeded, Not(HaveOccurred())),2175 )2176 It("should allow changes to shoot spec if nothing else has changed", func() {2177 oldShoot.Spec.SeedName = &seedName2178 shoot.Spec.Kubernetes.Version = "1.6.4"2179 oldShoot.Spec.Kubernetes.Version = "1.6.3"2180 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2181 err := admissionHandler.Admit(context.TODO(), attrs, nil)2182 Expect(err).NotTo(HaveOccurred())2183 })2184 It("should forbid changes to Seed name when etcd encryption key is missing", func() {2185 oldShoot.Spec.SeedName = &oldSeedName2186 shoot.Spec.SeedName = &seedName2187 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2188 err := admissionHandler.Admit(context.TODO(), attrs, nil)2189 Expect(err).To(MatchError(ContainSubstring("because etcd encryption key not found in shoot state")))2190 })2191 It("should allow changes to Seed name when etcd encryption key is present and nothing else has changed", func() {2192 shootState.Spec.Gardener = append(shootState.Spec.Gardener, gardencorev1alpha1.GardenerResourceData{2193 Labels: map[string]string{2194 "name": "kube-apiserver-etcd-encryption-key",2195 "managed-by": "secrets-manager",2196 },2197 })2198 Expect(extCoreInformerFactory.Core().V1alpha1().ShootStates().Informer().GetStore().Update(&shootState)).To(Succeed())2199 oldShoot.Spec.SeedName = &oldSeedName2200 shoot.Spec.SeedName = &seedName2201 attrs := admission.NewAttributesRecord(&shoot, oldShoot, core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)2202 err := admissionHandler.Admit(context.TODO(), attrs, nil)2203 Expect(err).NotTo(HaveOccurred())2204 })2205 })2206 Context("shoot deletion", func() {2207 var shootStore cache.Store2208 BeforeEach(func() {2209 shootStore = coreInformerFactory.Core().InternalVersion().Shoots().Informer().GetStore()2210 Expect(coreInformerFactory.Core().InternalVersion().CloudProfiles().Informer().GetStore().Add(&cloudProfile)).To(Succeed())2211 Expect(coreInformerFactory.Core().InternalVersion().Seeds().Informer().GetStore().Add(&seed)).To(Succeed())2212 Expect(coreInformerFactory.Core().InternalVersion().Projects().Informer().GetStore().Add(&project)).To(Succeed())2213 })2214 DescribeTable("DeleteShootInMigration",2215 func(lastOperationType core.LastOperationType, lastOperationState core.LastOperationState, matcher types.GomegaMatcher) {2216 shootBase.Status = core.ShootStatus{2217 LastOperation: &core.LastOperation{2218 Type: lastOperationType,2219 State: lastOperationState,2220 },2221 }2222 shoot.Annotations = map[string]string{2223 gutil.ConfirmationDeletion: "true",2224 }2225 attrs := admission.NewAttributesRecord(nil, shootBase.DeepCopyObject(), core.Kind("Shoot").WithVersion("version"), shoot.Namespace, shoot.Name, core.Resource("shoots").WithVersion("version"), "", admission.Delete, &metav1.DeleteOptions{}, false, nil)2226 Expect(shootStore.Add(&shoot)).NotTo(HaveOccurred())2227 err := admissionHandler.Admit(context.TODO(), attrs, nil)2228 Expect(err).To(matcher)2229 },2230 Entry("should reject if the shoot has lastOperation=Migrate:Processing", core.LastOperationTypeMigrate, core.LastOperationStateProcessing, BeForbiddenError()),2231 Entry("should reject if the shoot has lastOperation=Migrate:Succeeded", core.LastOperationTypeMigrate, core.LastOperationStateSucceeded, BeForbiddenError()),2232 Entry("should reject if the shoot has lastOperation=Migrate:Error", core.LastOperationTypeMigrate, core.LastOperationStateError, BeForbiddenError()),2233 Entry("should reject if the shoot has lastOperation=Restore:Processing", core.LastOperationTypeRestore, core.LastOperationStateProcessing, BeForbiddenError()),2234 Entry("should reject if the shoot has lastOperation=Restore:Error", core.LastOperationTypeRestore, core.LastOperationStateError, BeForbiddenError()),2235 Entry("should not reject if the shoot has lastOperation=Restore:Succeeded ", core.LastOperationTypeRestore, core.LastOperationStateSucceeded, BeNil()),2236 Entry("should not reject the delete operation", core.LastOperationTypeReconcile, core.LastOperationStateSucceeded, BeNil()))2237 })2238 })2239})...
grok_test.go
Source:grok_test.go
1// Copyright 2015-2018 trivago N.V.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14package format15import (16 "testing"17 "github.com/trivago/gollum/core"18 "github.com/trivago/tgo/ttesting"19)20func TestMultilineGrok(t *testing.T) {21 expect := ttesting.NewExpect(t)22 config := core.NewPluginConfig("", "format.Grok")23 config.Override("Patterns", []string{`(?sm)%{GREEDYDATA:data}`})24 plugin, err := core.NewPluginWithConfig(config)25 expect.NoError(err)26 formatter, casted := plugin.(*Grok)27 expect.True(casted)28 msg := core.NewMessage(nil, []byte("us-west.servicename.webserver0.this.\nis.\nthe.\nmeasurement 12.0 1497003802"), nil, core.InvalidStreamID)29 err = formatter.ApplyFormatter(msg)30 expect.NoError(err)31 value, err := msg.GetMetadata().String("data")32 expect.NoError(err)33 expect.Equal(value, "us-west.servicename.webserver0.this.\nis.\nthe.\nmeasurement 12.0 1497003802")34}35func TestPatternStoreAtKey(t *testing.T) {36 expect := ttesting.NewExpect(t)37 config := core.NewPluginConfig("", "format.Grok")38 config.Override("Target", "root")39 config.Override("Patterns", []string{`(?P<datacenter>[^\.]+?)\.(?P<service>[^\.]+?)\.(?P<host>[^\.]+?)\.(?P<measurement>[^\s]+?)\s%{NUMBER:value:float}\s%{INT:time}`})40 plugin, err := core.NewPluginWithConfig(config)41 expect.NoError(err)42 formatter, casted := plugin.(*Grok)43 expect.True(casted)44 msg := core.NewMessage(nil, []byte("us-west.servicename.webserver0.this.is.the.measurement 12.0 1497003802\r"), nil, core.InvalidStreamID)45 err = formatter.ApplyFormatter(msg)46 expect.NoError(err)47 expectKey := func(key, val string) {48 v, err := msg.GetMetadata().String(key)49 expect.NoError(err)50 expect.Equal(val, v)51 }52 expectKey("root/datacenter", "us-west")53 expectKey("root/service", "servicename")54 expectKey("root/host", "webserver0")55 expectKey("root/measurement", "this.is.the.measurement")56 expectKey("root/value", "12.0")57 expectKey("root/time", "1497003802")58}59func TestPattern(t *testing.T) {60 expect := ttesting.NewExpect(t)61 config := core.NewPluginConfig("", "format.Grok")62 config.Override("Patterns", []string{`(?P<datacenter>[^\.]+?)\.(?P<service>[^\.]+?)\.(?P<host>[^\.]+?)\.(?P<measurement>[^\s]+?)\s%{NUMBER:value:float}\s%{INT:time}`})63 plugin, err := core.NewPluginWithConfig(config)64 expect.NoError(err)65 formatter, casted := plugin.(*Grok)66 expect.True(casted)67 msg := core.NewMessage(nil, []byte("us-west.servicename.webserver0.this.is.the.measurement 12.0 1497003802\r"), nil, core.InvalidStreamID)68 err = formatter.ApplyFormatter(msg)69 expect.NoError(err)70 expectKey := func(key, val string) {71 v, err := msg.GetMetadata().String(key)72 expect.NoError(err)73 expect.Equal(val, v)74 }75 expectKey("datacenter", "us-west")76 expectKey("service", "servicename")77 expectKey("host", "webserver0")78 expectKey("measurement", "this.is.the.measurement")79 expectKey("value", "12.0")80 expectKey("time", "1497003802")81}82func TestMultiplePatternsOrder(t *testing.T) {83 expect := ttesting.NewExpect(t)84 config := core.NewPluginConfig("", "format.Grok")85 config.Override("Patterns", []string{86 `(?P<datacenter>[^\.]+?)\.(?P<service>[^\.]+?)\.(?P<host>[^\.]+?)\.statsd\.gauge-(?P<nomatch>[^\.]+?)\.(?P<measurement>[^\s]+?)\s%{NUMBER:value_gauge:float}\s*%{INT:time}.*`,87 `(?P<datacenter>[^\.]+?)\.(?P<service>[^\.]+?)\.(?P<host>[^\.]+?)\.statsd\.latency-(?P<application>[^\.]+?)\.(?P<measurement>[^\s]+?)\s%{NUMBER:value_latency:float}\s*%{INT:time}.*`,88 `(?P<datacenter>[^\.]+?)\.(?P<service>[^\.]+?)\.(?P<host>[^\.]+?)\.(?P<measurement>[^\s]+?)\s%{NUMBER:value:float}\s*%{INT:time}.*`,89 })90 plugin, err := core.NewPluginWithConfig(config)91 expect.NoError(err)92 formatter, casted := plugin.(*Grok)93 expect.True(casted)94 msg := core.NewMessage(nil, []byte("us-east.www.webserver1.statsd.latency-appname.another.test-measurement 42.1 1497005802\r"), nil, core.InvalidStreamID)95 err = formatter.ApplyFormatter(msg)96 expect.NoError(err)97 expectKey := func(key, val string) {98 v, err := msg.GetMetadata().String(key)99 expect.NoError(err)100 expect.Equal(val, v)101 }102 expectKey("datacenter", "us-east")103 expectKey("service", "www")104 expectKey("host", "webserver1")105 expectKey("application", "appname")106 expectKey("measurement", "another.test-measurement")107 expectKey("value_latency", "42.1")108 expectKey("time", "1497005802")109}110func TestNoMatch(t *testing.T) {111 expect := ttesting.NewExpect(t)112 config := core.NewPluginConfig("", "format.Grok")113 config.Override("Patterns", []string{114 `%{INT:random}`,115 })116 plugin, err := core.NewPluginWithConfig(config)117 expect.NoError(err)118 formatter, casted := plugin.(*Grok)119 expect.True(casted)120 msg := core.NewMessage(nil, []byte("nonumber"), nil, core.InvalidStreamID)121 err = formatter.ApplyFormatter(msg)122 expect.NotNil(err)123}...
copy_test.go
Source:copy_test.go
1// Copyright 2015-2018 trivago N.V.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14package format15import (16 "testing"17 "github.com/trivago/gollum/core"18 "github.com/trivago/tgo/tcontainer"19 "github.com/trivago/tgo/ttesting"20)21func TestCopyReplace(t *testing.T) {22 expect := ttesting.NewExpect(t)23 config := core.NewPluginConfig("", "format.Copy")24 config.Override("Source", "foo")25 plugin, err := core.NewPluginWithConfig(config)26 expect.NoError(err)27 formatter, casted := plugin.(*Copy)28 expect.True(casted)29 msg := core.NewMessage(nil, []byte("not applied"), tcontainer.MarshalMap{"foo": []byte("foo")}, core.InvalidStreamID)30 err = formatter.ApplyFormatter(msg)31 expect.NoError(err)32 expect.Equal("foo", msg.String())33}34func TestCopyAddKey(t *testing.T) {35 expect := ttesting.NewExpect(t)36 config := core.NewPluginConfig("", "format.Copy")37 config.Override("Source", "")38 config.Override("Target", "foo")39 plugin, err := core.NewPluginWithConfig(config)40 expect.NoError(err)41 formatter, casted := plugin.(*Copy)42 expect.True(casted)43 msg := core.NewMessage(nil, []byte("test"), nil, core.InvalidStreamID)44 err = formatter.ApplyFormatter(msg)45 expect.NoError(err)46 val, err := msg.GetMetadata().Bytes("foo")47 expect.NoError(err)48 expect.Equal("test", msg.String())49 expect.Equal("test", string(val))50}51func TestCopyAppend(t *testing.T) {52 expect := ttesting.NewExpect(t)53 config := core.NewPluginConfig("", "format.Copy")54 config.Override("Source", "foo")55 config.Override("Mode", "append")56 config.Override("Separator", " ")57 plugin, err := core.NewPluginWithConfig(config)58 expect.NoError(err)59 formatter, casted := plugin.(*Copy)60 expect.True(casted)61 msg := core.NewMessage(nil, []byte("test"), tcontainer.MarshalMap{"foo": []byte("foo")}, core.InvalidStreamID)62 err = formatter.ApplyFormatter(msg)63 expect.NoError(err)64 expect.Equal("test foo", msg.String())65}66func TestCopyPrepend(t *testing.T) {67 expect := ttesting.NewExpect(t)68 config := core.NewPluginConfig("", "format.Copy")69 config.Override("Source", "foo")70 config.Override("Mode", "prepend")71 plugin, err := core.NewPluginWithConfig(config)72 expect.NoError(err)73 formatter, casted := plugin.(*Copy)74 expect.True(casted)75 msg := core.NewMessage(nil, []byte("test"), tcontainer.MarshalMap{"foo": []byte("foo")}, core.InvalidStreamID)76 err = formatter.ApplyFormatter(msg)77 expect.NoError(err)78 expect.Equal("footest", msg.String())79}80func TestCopyMetadataIntegrity(t *testing.T) {81 expect := ttesting.NewExpect(t)82 config := core.NewPluginConfig("", "format.Copy")83 config.Override("Source", "")84 config.Override("Target", "foo")85 plugin, err := core.NewPluginWithConfig(config)86 expect.NoError(err)87 formatter, casted := plugin.(*Copy)88 expect.True(casted)89 msg := core.NewMessage(nil, []byte("payload"), nil, core.InvalidStreamID)90 err = formatter.ApplyFormatter(msg)91 expect.NoError(err)92 foo, err := msg.GetMetadata().Bytes("foo")93 expect.NoError(err)94 expect.Equal("payload", msg.String())95 expect.Equal("payload", string(foo))96 msg.StorePayload([]byte("xxx"))97 foo, err = msg.GetMetadata().Bytes("foo")98 expect.NoError(err)99 expect.Equal("xxx", msg.String())100 expect.Equal("payload", string(foo))101}102func TestCopyPayloadIntegrity(t *testing.T) {103 expect := ttesting.NewExpect(t)104 config := core.NewPluginConfig("", "format.Copy")105 config.Override("Source", "foo")106 plugin, err := core.NewPluginWithConfig(config)107 expect.NoError(err)108 formatter, casted := plugin.(*Copy)109 expect.True(casted)110 msg := core.NewMessage(nil, []byte{}, nil, core.InvalidStreamID)111 msg.GetMetadata().Set("foo", []byte("metadata"))112 err = formatter.ApplyFormatter(msg)113 expect.NoError(err)114 foo, err := msg.GetMetadata().Bytes("foo")115 expect.NoError(err)116 expect.Equal("metadata", msg.String())117 expect.Equal("metadata", string(foo))118 msg.GetMetadata().Set("foo", []byte("xxx"))119 foo, err = msg.GetMetadata().Bytes("foo")120 expect.NoError(err)121 expect.Equal("metadata", msg.String())122 expect.Equal("xxx", string(foo))123}...
streamroute_test.go
Source:streamroute_test.go
1// Copyright 2015-2018 trivago N.V.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14package format15import (16 "testing"17 "github.com/trivago/gollum/core"18 "github.com/trivago/tgo/ttesting"19)20func TestStreamRoute(t *testing.T) {21 expect := ttesting.NewExpect(t)22 config := core.NewPluginConfig("", "format.StreamRoute")23 plugin, err := core.NewPluginWithConfig(config)24 expect.NoError(err)25 formatter, casted := plugin.(*StreamRoute)26 expect.True(casted)27 msg := core.NewMessage(nil, []byte(core.LogInternalStream+":test"), nil, core.InvalidStreamID)28 err = formatter.ApplyFormatter(msg)29 expect.NoError(err)30 expect.Equal("test", msg.String())31 expect.Equal(core.LogInternalStreamID, msg.GetStreamID())32}33func TestStreamRouteNoStreamName(t *testing.T) {34 expect := ttesting.NewExpect(t)35 config := core.NewPluginConfig("", "format.StreamRoute")36 plugin, err := core.NewPluginWithConfig(config)37 expect.NoError(err)38 formatter, casted := plugin.(*StreamRoute)39 expect.True(casted)40 msg := core.NewMessage(nil, []byte(":test"), nil, core.InvalidStreamID)41 err = formatter.ApplyFormatter(msg)42 expect.NoError(err)43 expect.Equal("test", msg.String())44 expect.Equal(core.InvalidStreamID, msg.GetStreamID())45}46func TestStreamRouteFormat(t *testing.T) {47 expect := ttesting.NewExpect(t)48 config := core.NewPluginConfig("", "format.StreamRoute")49 config.Override("StreamModulator", []interface{}{50 map[interface{}]interface{}{51 "format.Envelope": map[string]string{52 "Prefix": "_",53 "Postfix": "_",54 },55 },56 })57 plugin, err := core.NewPluginWithConfig(config)58 expect.NoError(err)59 formatter, casted := plugin.(*StreamRoute)60 expect.True(casted)61 msg := core.NewMessage(nil, []byte("GOLLUM:test"), nil, core.InvalidStreamID)62 err = formatter.ApplyFormatter(msg)63 expect.NoError(err)64 expect.Equal("test", string(msg.GetPayload()))65 expect.Equal(core.LogInternalStream, core.StreamRegistry.GetStreamName(msg.GetStreamID()))66}67func TestStreamTarget(t *testing.T) {68 expect := ttesting.NewExpect(t)69 config := core.NewPluginConfig("", "format.StreamRoute")70 config.Override("ApplyTo", "foo")71 plugin, err := core.NewPluginWithConfig(config)72 expect.NoError(err)73 formatter, casted := plugin.(*StreamRoute)74 expect.True(casted)75 msg := core.NewMessage(nil, []byte("payload"), nil, core.InvalidStreamID)76 msg.GetMetadata().Set("foo", []byte(core.LogInternalStream+":test"))77 err = formatter.ApplyFormatter(msg)78 expect.NoError(err)79 foo, err := msg.GetMetadata().Bytes("foo")80 expect.NoError(err)81 expect.Equal("payload", msg.String())82 expect.Equal("test", string(foo))83 expect.Equal(core.LogInternalStreamID, msg.GetStreamID())84}...
EXPECT
Using AI Code Generation
1func main() {2 EXPECT(a, b)3}4func main() {5 EXPECT(a, b)6}7func main() {8 EXPECT(a, b)9}10func main() {11 EXPECT(a, b)12}13func main() {14 EXPECT(a, b)15}16func main() {17 EXPECT(a, b)18}19func main() {20 EXPECT(a, b)21}22func main() {23 EXPECT(a, b)24}25func main() {26 EXPECT(a, b)27}28func main() {29 EXPECT(a, b)30}31func main() {32 EXPECT(a, b)33}34func main() {35 EXPECT(a, b)36}37func main() {
EXPECT
Using AI Code Generation
1import (2func TestExpect(t *testing.T) {3 fmt.Println("This is a test")4}5import (6func TestExpect(t *testing.T) {7 fmt.Println("This is a test")8}9import (10func TestExpect(t *testing.T) {11 fmt.Println("This is a test")12}13import (14func TestExpect(t *testing.T) {15 fmt.Println("This is a test")16}17import (18func TestExpect(t *testing.T) {19 fmt.Println("This is a test")20}21import (22func TestExpect(t *testing.T) {23 fmt.Println("This is a test")24}25import (26func TestExpect(t *testing.T) {27 fmt.Println("This is a test")28}29import (30func TestExpect(t *testing.T) {31 fmt.Println("This is a test")32}33import (34func TestExpect(t *testing.T) {35 fmt.Println("This is a test")36}37import (38func TestExpect(t *testing.T) {39 fmt.Println("This is a test")40}
EXPECT
Using AI Code Generation
1import (2func main() {3 cmd := exec.Command("sh", "-c", "echo 'hello world'")4 e, _, err := expect.Spawn(cmd, -1)5 if err != nil {6 panic(err)7 }8 defer e.Close()9 ch := make(chan string)10 go func() {11 for {12 res, err := e.Expect(expect.EOF, expect.Timeout(time.Second*1))13 if err != nil {14 panic(err)15 }16 if res == 0 {17 }18 }19 close(ch)20 }()21 for res := range ch {22 fmt.Println("Result:", res)23 }24}
EXPECT
Using AI Code Generation
1import (2func TestExpect(t *testing.T) {3 t.Run("test expect", func(t *testing.T) {4 t.Expect(4).ToBe(result)5 })6}7import (8func TestExpect(t *testing.T) {9 t.Run("test expect", func(t *testing.T) {10 t.Expect(4).ToBe(result)11 })12}13import (14func TestExpect(t *testing.T) {15 t.Run("test expect", func(t *testing.T) {16 t.Expect(4).ToBe(result)17 })18}19import (20func TestExpect(t *testing.T) {21 t.Run("test expect", func(t *testing.T) {22 t.Expect(4).ToBe(result)23 })24}25import (26func TestExpect(t *testing.T) {27 t.Run("test expect", func(t *testing.T) {28 t.Expect(4).ToBe(result)29 })30}31import (
EXPECT
Using AI Code Generation
1import (2func main() {3fmt.Println("a is of type", reflect.TypeOf(a))4fmt.Println("b is of type", reflect.TypeOf(b))5fmt.Println("c is of type", reflect.TypeOf(c))6fmt.Println("d is of type", reflect.TypeOf(d))7}8import (9func main() {10fmt.Println("a is not of type", reflect.TypeOf(b))11fmt.Println("b is not of type", reflect.TypeOf(c))12fmt.Println("c is not of type", reflect.TypeOf(d))13fmt.Println("d is not of type", reflect.TypeOf(a))14}15import (16func main() {17fmt.Println("a is equal to", reflect.TypeOf(a))18fmt.Println("b is equal to", reflect.TypeOf(b))19fmt.Println("c is equal to", reflect.TypeOf(c))20fmt.Println("d is equal to", reflect.TypeOf(d))21}
EXPECT
Using AI Code Generation
1func main() {2 wd := agouti.ChromeDriver()3 wd.Start()4 wd.FindElement(agouti.Name("q")).SendKeys("Agouti")5 wd.FindElement(agouti.Name("btnG")).Click()6 wd.FindElement(agouti.Name("q")).Check("Agouti")7 wd.FindElement(agouti.Name("q")).Uncheck("Agouti")8 wd.FindElement(agouti.Name("q")).Select("Agouti")9 wd.FindElement(agouti.Name("q")).Deselect("Agouti")10 wd.FindElement(agouti.Name("q")).AttachFile("/path/to/file")11 wd.FindElement(agouti.Name("q")).Clear()12 wd.FindElement(agouti.Name("q")).Text()13 wd.FindElement(agouti.Name("q")).Value()14 wd.FindElement(agouti.Name("q")).TagName()15 wd.FindElement(agouti.Name("q")).Attribute("attribute")16 wd.FindElement(agouti.Name("q")).CSSProperty("property")17 wd.FindElement(agouti.Name("q")).Size()18 wd.FindElement(agouti.Name("q")).Location()19 wd.FindElement(agouti.Name("q")).LocationInView()
EXPECT
Using AI Code Generation
1import "core"2func main() {3 c := core.new()4 c.EXPECT("some string")5}6import "core"7func main() {8 c := core.new()9 c.EXPECT("some string")10}11import "core"12func main() {13 c := core.new()14 c.EXPECT("some string")15}16import "core"17func main() {18 c := core.new()19 c.EXPECT("some string")20}21import "core"22func main() {23 c := core.new()24 c.EXPECT("some string")25}26import "core"27func main() {28 c := core.new()29 c.EXPECT("some string")30}31import "core"32func main() {33 c := core.new()34 c.EXPECT("some
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!