Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.112.2
v1.112.3
115 changes: 115 additions & 0 deletions cmd/gardener-controller-manager/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,41 @@ import (
"net/http"
"os"
goruntime "runtime"
"slices"
"strconv"
"strings"
"time"

"github.com/go-logr/logr"
"github.com/spf13/cobra"
"go.uber.org/automaxprocs/maxprocs"
"golang.org/x/exp/maps"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/component-base/version/verflag"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/manager"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"

"github.com/gardener/gardener/cmd/gardener-controller-manager/app/bootstrappers"
"github.com/gardener/gardener/cmd/utils/initrun"
"github.com/gardener/gardener/pkg/api/indexer"
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
seedmanagementv1alpha1 "github.com/gardener/gardener/pkg/apis/seedmanagement/v1alpha1"
"github.com/gardener/gardener/pkg/client/kubernetes"
controllermanagerconfigv1alpha1 "github.com/gardener/gardener/pkg/controllermanager/apis/config/v1alpha1"
"github.com/gardener/gardener/pkg/controllermanager/controller"
"github.com/gardener/gardener/pkg/controllerutils/routes"
"github.com/gardener/gardener/pkg/features"
gardenerhealthz "github.com/gardener/gardener/pkg/healthz"
"github.com/gardener/gardener/pkg/utils/flow"
gardenerutils "github.com/gardener/gardener/pkg/utils/gardener"
)

// Name is a const for the name of this component.
Expand Down Expand Up @@ -144,6 +157,108 @@ func run(ctx context.Context, log logr.Logger, cfg *controllermanagerconfigv1alp
return fmt.Errorf("failed adding controllers to manager: %w", err)
}

// TODO(rfranzke): Remove this after Gardener v1.114 has been released and add code that cleans up all legacy
// `seed.gardener.cloud/<name>=true` labels from these objects.
if err := mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
var fns []flow.TaskFn

prepareEmptyPatchTasks := func(list client.ObjectList, seedNamesFromObject func(obj client.Object) ([]*string, error)) error {
if err := mgr.GetClient().List(ctx, list); err != nil {
return fmt.Errorf("failed listing objects: %w", err)
}

return meta.EachListItem(list, func(o runtime.Object) error {
fns = append(fns, func(ctx context.Context) error {
obj := o.(client.Object)

if slices.ContainsFunc(maps.Keys(obj.GetLabels()), func(s string) bool {
return strings.HasPrefix(s, v1beta1constants.LabelPrefixSeedName)
}) {
return nil
}

gvk, err := apiutil.GVKForObject(obj, mgr.GetScheme())
if err != nil {
return fmt.Errorf("could not get GroupVersionKind from object %v: %w", obj, err)
}

mgr.GetLogger().Info("Adding new seed name labels", "gvk", gvk, "objectKey", client.ObjectKeyFromObject(obj))

seedNames, err := seedNamesFromObject(obj)
if err != nil {
return err
}

patch := client.MergeFrom(obj.DeepCopyObject().(client.Object))
gardenerutils.MaintainSeedNameLabels(obj, seedNames...)
return mgr.GetClient().Patch(ctx, obj, patch)
})
return nil
})
}

if err := prepareEmptyPatchTasks(&gardencorev1beta1.BackupEntryList{}, func(obj client.Object) ([]*string, error) {
backupEntry := obj.(*gardencorev1beta1.BackupEntry)
return []*string{backupEntry.Spec.SeedName, backupEntry.Status.SeedName}, nil
}); err != nil {
return fmt.Errorf("failed computing tasks for backup entries: %w", err)
}

if err := prepareEmptyPatchTasks(&gardencorev1beta1.ShootList{}, func(obj client.Object) ([]*string, error) {
shoot := obj.(*gardencorev1beta1.Shoot)
return []*string{shoot.Spec.SeedName, shoot.Status.SeedName}, nil
}); err != nil {
return fmt.Errorf("failed computing tasks for shoots: %w", err)
}

if err := prepareEmptyPatchTasks(&gardencorev1beta1.SeedList{}, func(obj client.Object) ([]*string, error) {
seed := obj.(*gardencorev1beta1.Seed)

seedNames := []*string{&seed.Name}

managedSeed := &seedmanagementv1alpha1.ManagedSeed{ObjectMeta: metav1.ObjectMeta{Name: seed.Name, Namespace: v1beta1constants.GardenNamespace}}
if err := mgr.GetClient().Get(ctx, client.ObjectKeyFromObject(managedSeed), managedSeed); err != nil {
if !apierrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to get managed seed %q: %v", seed.Name, err)
}
} else if managedSeed.Spec.Shoot != nil {
shoot := &gardencorev1beta1.Shoot{ObjectMeta: metav1.ObjectMeta{Name: managedSeed.Spec.Shoot.Name, Namespace: managedSeed.Namespace}}
if err := mgr.GetClient().Get(ctx, client.ObjectKeyFromObject(shoot), shoot); err != nil {
return nil, fmt.Errorf("failed to get shoot %s for managed seed %q: %v", managedSeed.Spec.Shoot.Name, managedSeed.Name, err)
}
seedNames = append(seedNames, shoot.Spec.SeedName)
}

return seedNames, nil
}); err != nil {
return fmt.Errorf("failed computing tasks for seeds: %w", err)
}

if err := prepareEmptyPatchTasks(&seedmanagementv1alpha1.ManagedSeedList{}, func(obj client.Object) ([]*string, error) {
managedSeed := obj.(*seedmanagementv1alpha1.ManagedSeed)

if managedSeed.Spec.Shoot == nil {
return nil, nil
}

shoot := &gardencorev1beta1.Shoot{ObjectMeta: metav1.ObjectMeta{Name: managedSeed.Spec.Shoot.Name, Namespace: managedSeed.Namespace}}
if err := mgr.GetClient().Get(ctx, client.ObjectKeyFromObject(shoot), shoot); err != nil {
if apierrors.IsNotFound(err) {
return nil, nil
}
return nil, err
}

return []*string{shoot.Spec.SeedName}, nil
}); err != nil {
return fmt.Errorf("failed computing tasks for managed seeds: %w", err)
}

return flow.Parallel(fns...)(ctx)
})); err != nil {
return fmt.Errorf("failed adding seed name label migration runnable to manager: %w", err)
}

log.Info("Starting manager")
return mgr.Start(ctx)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/gardenlet/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func (g *garden) Start(ctx context.Context) error {
&gardencorev1beta1.ControllerInstallation{}: {
Field: fields.SelectorFromSet(fields.Set{gardencore.SeedRefName: g.config.SeedConfig.SeedTemplate.Name}),
},
// TODO(rfranzke): Enable the label selector for Seeds after Gardener v1.113 has been released.
// TODO(rfranzke): Enable the label selector for Seeds after Gardener v1.114 has been released.
// &gardencorev1beta1.Seed{}: {
// Label: labels.SelectorFromSet(labels.Set{v1beta1constants.LabelPrefixSeedName + g.config.SeedConfig.SeedTemplate.Name: "true"}),
// },
Expand Down
8 changes: 4 additions & 4 deletions docs/concepts/apiserver-admission-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ Rejects the deletion if `Shoot`(s) reference the seed cluster.
_(enabled by default)_

This admission controller reacts on `CREATE` and `UPDATE` operations for `Seed`s.
It maintains the `seed.gardener.cloud/<name>` labels for it.
More specifically, it adds that the `seed.gardener.cloud/<name>=true` label where `<name>` is
- the name of the `Seed` resource (a `Seed` named `foo` will get label `seed.gardener.cloud/foo=true`).
- the name of the parent `Seed` resource in case it is a `ManagedSeed` (a `Seed` named `foo` that is created by a `ManagedSeed` which references a `Shoot` running a `Seed` called `bar` will get label `seed.gardener.cloud/bar=true`).
It maintains the `name.seed.gardener.cloud/<name>` labels for it.
More specifically, it adds that the `name.seed.gardener.cloud/<name>=true` label where `<name>` is
- the name of the `Seed` resource (a `Seed` named `foo` will get label `name.seed.gardener.cloud/foo=true`).
- the name of the parent `Seed` resource in case it is a `ManagedSeed` (a `Seed` named `foo` that is created by a `ManagedSeed` which references a `Shoot` running a `Seed` called `bar` will get label `name.seed.gardener.cloud/bar=true`).

## `ShootDNS`

Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/core/v1beta1/constants/types_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,8 @@ const (
LabelSecretBindingReference = "reference.gardener.cloud/secretbinding"
// LabelCredentialsBindingReference is used to identify credentials which are referred by a CredentialsBinding (not necessarily in the same namespace).
LabelCredentialsBindingReference = "reference.gardener.cloud/credentialsbinding"
// LabelPrefixSeedName is the prefix for the label key describing the name of a seed, e.g. seed.gardener.cloud/my-seed=true.
LabelPrefixSeedName = "seed.gardener.cloud/"
// LabelPrefixSeedName is the prefix for the label key describing the name of a seed, e.g. name.seed.gardener.cloud/my-seed=true.
LabelPrefixSeedName = "name.seed.gardener.cloud/"

// LabelExtensionExtensionTypePrefix is used to prefix extension label for extension types.
LabelExtensionExtensionTypePrefix = "extensions.extensions.gardener.cloud/"
Expand Down
8 changes: 4 additions & 4 deletions pkg/apiserver/registry/core/backupentry/strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,16 @@ var _ = Describe("#Canonicalize", func() {
Context("seed names", func() {
It("should correctly add the seed labels", func() {
metav1.SetMetaDataLabel(&backupEntry.ObjectMeta, "foo", "bar")
metav1.SetMetaDataLabel(&backupEntry.ObjectMeta, "seed.gardener.cloud/foo", "true")
metav1.SetMetaDataLabel(&backupEntry.ObjectMeta, "name.seed.gardener.cloud/foo", "true")
backupEntry.Spec.SeedName = ptr.To("spec-seed")
backupEntry.Status.SeedName = ptr.To("status-seed")

backupentryregistry.NewStrategy().Canonicalize(backupEntry)

Expect(backupEntry.Labels).To(Equal(map[string]string{
"foo": "bar",
"seed.gardener.cloud/spec-seed": "true",
"seed.gardener.cloud/status-seed": "true",
"foo": "bar",
"name.seed.gardener.cloud/spec-seed": "true",
"name.seed.gardener.cloud/status-seed": "true",
}))
})
})
Expand Down
8 changes: 4 additions & 4 deletions pkg/apiserver/registry/core/shoot/strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -840,16 +840,16 @@ var _ = Describe("Strategy", func() {
Context("seed names", func() {
It("should correctly add the seed labels", func() {
metav1.SetMetaDataLabel(&shoot.ObjectMeta, "foo", "bar")
metav1.SetMetaDataLabel(&shoot.ObjectMeta, "seed.gardener.cloud/foo", "true")
metav1.SetMetaDataLabel(&shoot.ObjectMeta, "name.seed.gardener.cloud/foo", "true")
shoot.Spec.SeedName = ptr.To("spec-seed")
shoot.Status.SeedName = ptr.To("status-seed")

strategy.Canonicalize(shoot)

Expect(shoot.Labels).To(Equal(map[string]string{
"foo": "bar",
"seed.gardener.cloud/spec-seed": "true",
"seed.gardener.cloud/status-seed": "true",
"foo": "bar",
"name.seed.gardener.cloud/spec-seed": "true",
"name.seed.gardener.cloud/status-seed": "true",
}))
})
})
Expand Down
40 changes: 25 additions & 15 deletions pkg/component/extensions/extension/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package extension

import (
"context"
"sync"
"time"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -118,6 +119,7 @@ type extension struct {
waitSevereThreshold time.Duration
waitTimeout time.Duration

lock sync.Mutex
extensions map[string]*extensionsv1alpha1.Extension
}

Expand Down Expand Up @@ -461,23 +463,12 @@ func (e *extension) forEach(
) []flow.TaskFn {
fns := make([]flow.TaskFn, 0, len(e.values.Extensions))

for _, ext := range e.values.Extensions {
if !filterFn(ext) {
for _, extensionTemplate := range e.values.Extensions {
if !filterFn(extensionTemplate) {
continue
}
extensionTemplate := ext

extensionObj, ok := e.extensions[extensionTemplate.Name]
if !ok {
extensionObj = &extensionsv1alpha1.Extension{
ObjectMeta: metav1.ObjectMeta{
Name: extensionTemplate.Name,
Namespace: e.values.Namespace,
},
}
// store object for later usage (we want to pass a filled object to WaitUntil*)
e.extensions[extensionTemplate.Name] = extensionObj
}

extensionObj := e.initializeExtensionObject(extensionTemplate.Name)

fns = append(fns, func(ctx context.Context) error {
return fn(ctx, extensionObj, extensionTemplate.Spec.Type, extensionTemplate.Spec.ProviderConfig, extensionTemplate.Spec.Class, extensionTemplate.Timeout)
Expand All @@ -491,3 +482,22 @@ func (e *extension) forEach(
func (e *extension) Extensions() map[string]Extension {
return e.values.Extensions
}

func (e *extension) initializeExtensionObject(name string) *extensionsv1alpha1.Extension {
e.lock.Lock()
defer e.lock.Unlock()

extensionObj, ok := e.extensions[name]
if !ok {
extensionObj = &extensionsv1alpha1.Extension{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: e.values.Namespace,
},
}
// store object for later usage (we want to pass a filled object to WaitUntil*)
e.extensions[name] = extensionObj
}

return extensionObj
}
10 changes: 5 additions & 5 deletions pkg/utils/gardener/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ var _ = Describe("Identity", func() {
Describe("#MaintainSeedNameLabels", func() {
It("should maintain the labels", func() {
obj := &gardencorev1beta1.Shoot{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"seed.gardener.cloud/old-seed": "true"}},
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"name.seed.gardener.cloud/old-seed": "true"}},
Spec: gardencorev1beta1.ShootSpec{SeedName: ptr.To("spec-seed")},
Status: gardencorev1beta1.ShootStatus{SeedName: ptr.To("status-seed")},
}

MaintainSeedNameLabels(obj, obj.Spec.SeedName, obj.Status.SeedName)

Expect(obj.Labels).To(And(
HaveKeyWithValue("seed.gardener.cloud/spec-seed", "true"),
HaveKeyWithValue("seed.gardener.cloud/status-seed", "true"),
HaveKeyWithValue("name.seed.gardener.cloud/spec-seed", "true"),
HaveKeyWithValue("name.seed.gardener.cloud/status-seed", "true"),
))
})

Expand All @@ -39,12 +39,12 @@ var _ = Describe("Identity", func() {

MaintainSeedNameLabels(obj, obj.Spec.SeedName, obj.Status.SeedName)

Expect(obj.Labels).To(HaveKeyWithValue("seed.gardener.cloud/seed", "true"))
Expect(obj.Labels).To(HaveKeyWithValue("name.seed.gardener.cloud/seed", "true"))
})

It("should maintain the labels when spec and status names are empty", func() {
obj := &gardencorev1beta1.Shoot{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar", "seed.gardener.cloud/old-seed": "true"}},
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar", "name.seed.gardener.cloud/old-seed": "true"}},
}

MaintainSeedNameLabels(obj, obj.Spec.SeedName, obj.Status.SeedName)
Expand Down
8 changes: 4 additions & 4 deletions plugin/pkg/managedseed/validator/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,18 +379,18 @@ var _ = Describe("ManagedSeed", func() {
Expect(admissionHandler.Admit(context.TODO(), getManagedSeedAttributes(managedSeed), nil)).To(Succeed())

Expect(managedSeed.Labels).To(And(
HaveKeyWithValue("seed.gardener.cloud/parent-seed", "true"),
HaveKeyWithValue("name.seed.gardener.cloud/parent-seed", "true"),
))
})

It("should remove unneeded labels", func() {
metav1.SetMetaDataLabel(&seed.ObjectMeta, "seed.gardener.cloud/foo", "true")
metav1.SetMetaDataLabel(&seed.ObjectMeta, "name.seed.gardener.cloud/foo", "true")

Expect(admissionHandler.Admit(context.TODO(), getManagedSeedAttributes(managedSeed), nil)).To(Succeed())

Expect(managedSeed.Labels).To(And(
HaveKeyWithValue("seed.gardener.cloud/parent-seed", "true"),
Not(HaveKey("seed.gardener.cloud/foo")),
HaveKeyWithValue("name.seed.gardener.cloud/parent-seed", "true"),
Not(HaveKey("name.seed.gardener.cloud/foo")),
))
})
})
Expand Down
Loading