diff --git a/hack/compare-kcm-controllers.sh b/hack/compare-kcm-controllers.sh new file mode 100755 index 00000000000..6152296f167 --- /dev/null +++ b/hack/compare-kcm-controllers.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# +# Copyright (c) 2022 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 file +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +usage() { + echo "Usage:" + echo "> compare-kcm-controllers.sh [ -h | ]" + echo + echo ">> For example: compare-kcm-controllers.sh 1.22 1.23" + + exit 0 +} + +if [ "$1" == "-h" ] || [ "$#" -ne 2 ]; then + usage +fi + +versions=("$1" "$2") + +out_dir=dev/temp +mkdir -p "${out_dir}" + +for version in "${versions[@]}"; do + rm -rf "${out_dir}/kubernetes-${version}" + rm -f "${out_dir}/kcm-controllers-${version}.txt" + + git clone --depth 1 --filter=blob:none --sparse https://github.com/kubernetes/kubernetes -b "release-${version}" "${out_dir}/kubernetes-${version}" + pushd "${out_dir}/kubernetes-${version}" > /dev/null + git sparse-checkout set "cmd/kube-controller-manager/app" + popd > /dev/null + + cat "${out_dir}/kubernetes-${version}/cmd/kube-controller-manager/app/"*.go |\ + sed -rn "s/.*[Client|Config]OrDie\(\"(.*)\"\).*/\1/p" |\ + grep -vE "informers|discovery" |\ + sort |\ + uniq > "${out_dir}/kcm-controllers-${version}.txt" +done + +echo +echo "kube-controller-manager controllers added in $2 compared to $1:" +diff "${out_dir}/kcm-controllers-$1.txt" "${out_dir}/kcm-controllers-$2.txt" | grep '>' | awk '{print $2}' +echo +echo "kube-controller-manager controllers removed in $2 compared to $1:" +diff "${out_dir}/kcm-controllers-$1.txt" "${out_dir}/kcm-controllers-$2.txt" | grep '<' | awk '{print $2}' diff --git a/pkg/gardenlet/controller/shoot/shoot_control_reconcile.go b/pkg/gardenlet/controller/shoot/shoot_control_reconcile.go index e4d763cfb68..7c8b27e1760 100644 --- a/pkg/gardenlet/controller/shoot/shoot_control_reconcile.go +++ b/pkg/gardenlet/controller/shoot/shoot_control_reconcile.go @@ -410,6 +410,11 @@ func (r *shootReconciler) runReconcileShootFlow(ctx context.Context, o *operatio Fn: flow.TaskFn(botanist.Shoot.Components.SystemComponents.Namespaces.Deploy).RetryUntilTimeout(defaultInterval, defaultTimeout).SkipIf(o.Shoot.HibernationEnabled), Dependencies: flow.NewTaskIDs(deployGardenerResourceManager, waitUntilOperatingSystemConfigReady), }) + _ = g.Add(flow.Task{ + Name: "Deploying shoot system resources", + Fn: flow.TaskFn(botanist.Shoot.Components.SystemComponents.Resources.Deploy).RetryUntilTimeout(defaultInterval, defaultTimeout).SkipIf(o.Shoot.HibernationEnabled), + Dependencies: flow.NewTaskIDs(deployGardenerResourceManager, waitUntilOperatingSystemConfigReady), + }) _ = g.Add(flow.Task{ Name: "Deploying CoreDNS system component", Fn: flow.TaskFn(func(ctx context.Context) error { diff --git a/pkg/operation/botanist/botanist.go b/pkg/operation/botanist/botanist.go index 311b77c6e29..29b6f5342a8 100644 --- a/pkg/operation/botanist/botanist.go +++ b/pkg/operation/botanist/botanist.go @@ -159,6 +159,7 @@ func New(ctx context.Context, o *operation.Operation) (*Botanist, error) { if err != nil { return nil, err } + o.Shoot.Components.SystemComponents.Resources = b.DefaultShootSystem() o.Shoot.Components.SystemComponents.VPNShoot, err = b.DefaultVPNShoot() if err != nil { return nil, err diff --git a/pkg/operation/botanist/component/shootsystem/shootsystem.go b/pkg/operation/botanist/component/shootsystem/shootsystem.go new file mode 100644 index 00000000000..64a24a1e19b --- /dev/null +++ b/pkg/operation/botanist/component/shootsystem/shootsystem.go @@ -0,0 +1,177 @@ +// Copyright (c) 2022 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 file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shootsystem + +import ( + "context" + "time" + + resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" + "github.com/gardener/gardener/pkg/client/kubernetes" + "github.com/gardener/gardener/pkg/operation/botanist/component" + "github.com/gardener/gardener/pkg/utils/managedresources" + versionutils "github.com/gardener/gardener/pkg/utils/version" + + "github.com/Masterminds/semver" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + // ManagedResourceName is the name of the ManagedResource containing the resource specifications. + ManagedResourceName = "shoot-core-system" +) + +// Values is a set of configuration values for the system resources. +type Values struct { + // KubernetesVersion is the Kubernetes version of the shoot cluster. + KubernetesVersion *semver.Version +} + +// New creates a new instance of DeployWaiter for shoot system resources. +func New( + client client.Client, + namespace string, + values Values, +) component.DeployWaiter { + return &shootSystem{ + client: client, + namespace: namespace, + values: values, + } +} + +type shootSystem struct { + client client.Client + namespace string + values Values +} + +func (s *shootSystem) Deploy(ctx context.Context) error { + data, err := s.computeResourcesData() + if err != nil { + return err + } + + return managedresources.CreateForShoot(ctx, s.client, s.namespace, ManagedResourceName, false, data) +} + +func (s *shootSystem) Destroy(ctx context.Context) error { + return managedresources.DeleteForShoot(ctx, s.client, s.namespace, ManagedResourceName) +} + +// TimeoutWaitForManagedResource is the timeout used while waiting for the ManagedResources to become healthy +// or deleted. +var TimeoutWaitForManagedResource = 2 * time.Minute + +func (s *shootSystem) Wait(ctx context.Context) error { + timeoutCtx, cancel := context.WithTimeout(ctx, TimeoutWaitForManagedResource) + defer cancel() + + return managedresources.WaitUntilHealthy(timeoutCtx, s.client, s.namespace, ManagedResourceName) +} + +func (s *shootSystem) WaitCleanup(ctx context.Context) error { + timeoutCtx, cancel := context.WithTimeout(ctx, TimeoutWaitForManagedResource) + defer cancel() + + return managedresources.WaitUntilDeleted(timeoutCtx, s.client, s.namespace, ManagedResourceName) +} + +func (s *shootSystem) computeResourcesData() (map[string][]byte, error) { + var ( + registry = managedresources.NewRegistry(kubernetes.ShootScheme, kubernetes.ShootCodec, kubernetes.ShootSerializer) + + kubeControllerManagerServiceAccounts []client.Object + ) + + for _, name := range s.getServiceAccountNamesToInvalidate() { + kubeControllerManagerServiceAccounts = append(kubeControllerManagerServiceAccounts, &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceSystem, + Annotations: map[string]string{resourcesv1alpha1.KeepObject: "true"}, + }, + AutomountServiceAccountToken: pointer.Bool(false), + }) + } + + return registry.AddAllAndSerialize(kubeControllerManagerServiceAccounts...) +} + +func (s *shootSystem) getServiceAccountNamesToInvalidate() []string { + // Well-known {kube,cloud}-controller-manager controllers using a token for ServiceAccounts in the shoot + // To maintain this list for each new Kubernetes version: + // * Run hack/compare-kcm-controllers.sh (e.g. 'hack/compare-kcm-controllers.sh 1.22 1.23'). + // It will present 2 lists of controllers: those added and those removed in compared to . + // * Double check whether such ServiceAccount indeed appears in the kube-system namespace when creating a cluster + // with . Note that it sometimes might be hidden behind a default-off feature gate. + // If it appears, add all added controllers to the list if the Kubernetes version is high enough. + // * For any removed controllers, add them only to the Kubernetes version if it is low enough. + kubeControllerManagerServiceAccountNames := []string{ + "attachdetach-controller", + "bootstrap-signer", + "certificate-controller", + "clusterrole-aggregation-controller", + "controller-discovery", + "cronjob-controller", + "daemon-set-controller", + "deployment-controller", + "disruption-controller", + "endpoint-controller", + "endpointslice-controller", + "expand-controller", + "generic-garbage-collector", + "horizontal-pod-autoscaler", + "job-controller", + "metadata-informers", + "namespace-controller", + "node-controller", + "persistent-volume-binder", + "pod-garbage-collector", + "pv-protection-controller", + "pvc-protection-controller", + "replicaset-controller", + "replication-controller", + "resourcequota-controller", + "root-ca-cert-publisher", + "route-controller", + "service-account-controller", + "service-controller", + "shared-informers", + "statefulset-controller", + "token-cleaner", + "tokens-controller", + "ttl-after-finished-controller", + "ttl-controller", + } + + if versionutils.ConstraintK8sGreaterEqual119.Check(s.values.KubernetesVersion) { + kubeControllerManagerServiceAccountNames = append(kubeControllerManagerServiceAccountNames, + "endpointslicemirroring-controller", + "ephemeral-volume-controller", + ) + } + + if versionutils.ConstraintK8sGreaterEqual120.Check(s.values.KubernetesVersion) { + kubeControllerManagerServiceAccountNames = append(kubeControllerManagerServiceAccountNames, + "storage-version-garbage-collector", + ) + } + + return append(kubeControllerManagerServiceAccountNames, "default") +} diff --git a/pkg/operation/botanist/component/shootsystem/shootsystem_suite_test.go b/pkg/operation/botanist/component/shootsystem/shootsystem_suite_test.go new file mode 100644 index 00000000000..ab244158d90 --- /dev/null +++ b/pkg/operation/botanist/component/shootsystem/shootsystem_suite_test.go @@ -0,0 +1,27 @@ +// Copyright (c) 2022 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 file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shootsystem_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestShootSystem(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Botanist Component ShootSystem Suite") +} diff --git a/pkg/operation/botanist/component/shootsystem/shootsystem_test.go b/pkg/operation/botanist/component/shootsystem/shootsystem_test.go new file mode 100644 index 00000000000..263be69fbe7 --- /dev/null +++ b/pkg/operation/botanist/component/shootsystem/shootsystem_test.go @@ -0,0 +1,312 @@ +// Copyright (c) 2022 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 file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shootsystem_test + +import ( + "context" + + gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" + "github.com/gardener/gardener/pkg/client/kubernetes" + "github.com/gardener/gardener/pkg/operation/botanist/component" + . "github.com/gardener/gardener/pkg/operation/botanist/component/shootsystem" + "github.com/gardener/gardener/pkg/utils/retry" + retryfake "github.com/gardener/gardener/pkg/utils/retry/fake" + "github.com/gardener/gardener/pkg/utils/test" + . "github.com/gardener/gardener/pkg/utils/test/matchers" + + "github.com/Masterminds/semver" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var _ = Describe("ShootSystem", func() { + var ( + ctx = context.TODO() + + managedResourceName = "shoot-core-system" + namespace = "some-namespace" + kubernetesVersion = "1.17.1" + + c client.Client + values Values + component component.DeployWaiter + + managedResource *resourcesv1alpha1.ManagedResource + managedResourceSecret *corev1.Secret + ) + + BeforeEach(func() { + c = fakeclient.NewClientBuilder().WithScheme(kubernetes.SeedScheme).Build() + values = Values{ + KubernetesVersion: semver.MustParse(kubernetesVersion), + } + component = New(c, namespace, values) + + managedResource = &resourcesv1alpha1.ManagedResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: managedResourceName, + Namespace: namespace, + }, + } + managedResourceSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "managedresource-" + managedResource.Name, + Namespace: namespace, + }, + } + }) + + Describe("#Deploy", func() { + serviceAccountYAMLFor := func(name string) string { + return `apiVersion: v1 +automountServiceAccountToken: false +kind: ServiceAccount +metadata: + annotations: + resources.gardener.cloud/keep-object: "true" + creationTimestamp: null + name: ` + name + ` + namespace: kube-system +` + } + + JustBeforeEach(func() { + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(MatchError(apierrors.NewNotFound(schema.GroupResource{Group: resourcesv1alpha1.SchemeGroupVersion.Group, Resource: "managedresources"}, managedResource.Name))) + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(MatchError(apierrors.NewNotFound(schema.GroupResource{Group: corev1.SchemeGroupVersion.Group, Resource: "secrets"}, managedResourceSecret.Name))) + + Expect(component.Deploy(ctx)).To(Succeed()) + + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(Succeed()) + Expect(managedResource).To(DeepEqual(&resourcesv1alpha1.ManagedResource{ + TypeMeta: metav1.TypeMeta{ + APIVersion: resourcesv1alpha1.SchemeGroupVersion.String(), + Kind: "ManagedResource", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: managedResource.Name, + Namespace: managedResource.Namespace, + ResourceVersion: "1", + Labels: map[string]string{"origin": "gardener"}, + }, + Spec: resourcesv1alpha1.ManagedResourceSpec{ + InjectLabels: map[string]string{"shoot.gardener.cloud/no-cleanup": "true"}, + SecretRefs: []corev1.LocalObjectReference{{ + Name: managedResourceSecret.Name, + }}, + KeepObjects: pointer.BoolPtr(false), + }, + })) + + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(Succeed()) + Expect(managedResourceSecret.Type).To(Equal(corev1.SecretTypeOpaque)) + }) + + defaultKCMControllerSANames := []string{"attachdetach-controller", + "bootstrap-signer", + "certificate-controller", + "clusterrole-aggregation-controller", + "controller-discovery", + "cronjob-controller", + "daemon-set-controller", + "deployment-controller", + "disruption-controller", + "endpoint-controller", + "endpointslice-controller", + "expand-controller", + "generic-garbage-collector", + "horizontal-pod-autoscaler", + "job-controller", + "metadata-informers", + "namespace-controller", + "node-controller", + "persistent-volume-binder", + "pod-garbage-collector", + "pv-protection-controller", + "pvc-protection-controller", + "replicaset-controller", + "replication-controller", + "resourcequota-controller", + "root-ca-cert-publisher", + "route-controller", + "service-account-controller", + "service-controller", + "shared-informers", + "statefulset-controller", + "token-cleaner", + "tokens-controller", + "ttl-after-finished-controller", + "ttl-controller", + } + + Context("k8s < 1.19", func() { + It("should successfully deploy all resources", func() { + names := append(defaultKCMControllerSANames, "default") + + Expect(managedResourceSecret.Data).To(HaveLen(len(names))) + for _, name := range names { + Expect(string(managedResourceSecret.Data["serviceaccount__kube-system__"+name+".yaml"])).To(Equal(serviceAccountYAMLFor(name))) + } + }) + }) + + Context("1.19 <= k8s < 1.20", func() { + BeforeEach(func() { + values.KubernetesVersion = semver.MustParse("1.19.4") + component = New(c, namespace, values) + }) + + It("should successfully deploy all resources", func() { + names := append(defaultKCMControllerSANames, "default", "endpointslicemirroring-controller", "ephemeral-volume-controller") + + Expect(managedResourceSecret.Data).To(HaveLen(len(names))) + for _, name := range names { + Expect(string(managedResourceSecret.Data["serviceaccount__kube-system__"+name+".yaml"])).To(Equal(serviceAccountYAMLFor(name))) + } + }) + }) + + Context("k8s >= 1.20", func() { + BeforeEach(func() { + values.KubernetesVersion = semver.MustParse("1.21.4") + component = New(c, namespace, values) + }) + + It("should successfully deploy all resources", func() { + names := append(defaultKCMControllerSANames, "default", "endpointslicemirroring-controller", "ephemeral-volume-controller", "storage-version-garbage-collector") + + Expect(managedResourceSecret.Data).To(HaveLen(len(names))) + for _, name := range names { + Expect(string(managedResourceSecret.Data["serviceaccount__kube-system__"+name+".yaml"])).To(Equal(serviceAccountYAMLFor(name))) + } + }) + }) + }) + + Describe("#Destroy", func() { + It("should successfully destroy all resources", func() { + Expect(c.Create(ctx, managedResource)).To(Succeed()) + Expect(c.Create(ctx, managedResourceSecret)).To(Succeed()) + + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(Succeed()) + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(Succeed()) + + Expect(component.Destroy(ctx)).To(Succeed()) + + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(MatchError(apierrors.NewNotFound(schema.GroupResource{Group: resourcesv1alpha1.SchemeGroupVersion.Group, Resource: "managedresources"}, managedResource.Name))) + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(MatchError(apierrors.NewNotFound(schema.GroupResource{Group: corev1.SchemeGroupVersion.Group, Resource: "secrets"}, managedResourceSecret.Name))) + }) + }) + + Context("waiting functions", func() { + var ( + fakeOps *retryfake.Ops + resetVars func() + ) + + BeforeEach(func() { + fakeOps = &retryfake.Ops{MaxAttempts: 1} + resetVars = test.WithVars( + &retry.Until, fakeOps.Until, + &retry.UntilTimeout, fakeOps.UntilTimeout, + ) + }) + + AfterEach(func() { + resetVars() + }) + + Describe("#Wait", func() { + It("should fail because reading the ManagedResource fails", func() { + Expect(component.Wait(ctx)).To(MatchError(ContainSubstring("not found"))) + }) + + It("should fail because the ManagedResource doesn't become healthy", func() { + fakeOps.MaxAttempts = 2 + + Expect(c.Create(ctx, &resourcesv1alpha1.ManagedResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: managedResourceName, + Namespace: namespace, + Generation: 1, + }, + Status: resourcesv1alpha1.ManagedResourceStatus{ + ObservedGeneration: 1, + Conditions: []gardencorev1beta1.Condition{ + { + Type: resourcesv1alpha1.ResourcesApplied, + Status: gardencorev1beta1.ConditionFalse, + }, + { + Type: resourcesv1alpha1.ResourcesHealthy, + Status: gardencorev1beta1.ConditionFalse, + }, + }, + }, + })) + + Expect(component.Wait(ctx)).To(MatchError(ContainSubstring("is not healthy"))) + }) + + It("should successfully wait for the managed resource to become healthy", func() { + fakeOps.MaxAttempts = 2 + + Expect(c.Create(ctx, &resourcesv1alpha1.ManagedResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: managedResourceName, + Namespace: namespace, + Generation: 1, + }, + Status: resourcesv1alpha1.ManagedResourceStatus{ + ObservedGeneration: 1, + Conditions: []gardencorev1beta1.Condition{ + { + Type: resourcesv1alpha1.ResourcesApplied, + Status: gardencorev1beta1.ConditionTrue, + }, + { + Type: resourcesv1alpha1.ResourcesHealthy, + Status: gardencorev1beta1.ConditionTrue, + }, + }, + }, + })) + + Expect(component.Wait(ctx)).To(Succeed()) + }) + }) + + Describe("#WaitCleanup", func() { + It("should fail when the wait for the managed resource deletion times out", func() { + fakeOps.MaxAttempts = 2 + + Expect(c.Create(ctx, managedResource)).To(Succeed()) + + Expect(component.WaitCleanup(ctx)).To(MatchError(ContainSubstring("still exists"))) + }) + + It("should not return an error when it's already removed", func() { + Expect(component.WaitCleanup(ctx)).To(Succeed()) + }) + }) + }) +}) diff --git a/pkg/operation/botanist/shootsystem.go b/pkg/operation/botanist/shootsystem.go new file mode 100644 index 00000000000..784ea621c1a --- /dev/null +++ b/pkg/operation/botanist/shootsystem.go @@ -0,0 +1,29 @@ +// Copyright (c) 2022 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 file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package botanist + +import ( + "github.com/gardener/gardener/pkg/operation/botanist/component" + "github.com/gardener/gardener/pkg/operation/botanist/component/shootsystem" +) + +// DefaultShootSystem returns a deployer for the shoot system resources. +func (b *Botanist) DefaultShootSystem() component.DeployWaiter { + values := shootsystem.Values{ + KubernetesVersion: b.Shoot.KubernetesVersion, + } + + return shootsystem.New(b.K8sSeedClient.Client(), b.Shoot.SeedNamespace, values) +} diff --git a/pkg/operation/shoot/types.go b/pkg/operation/shoot/types.go index ed8437e5526..a3800907a57 100644 --- a/pkg/operation/shoot/types.go +++ b/pkg/operation/shoot/types.go @@ -149,6 +149,7 @@ type SystemComponents struct { CoreDNS coredns.Interface NodeLocalDNS nodelocaldns.Interface MetricsServer metricsserver.Interface + Resources component.DeployWaiter VPNShoot vpnshoot.Interface NodeProblemDetector component.DeployWaiter }