diff --git a/pkg/component/gardensystem/gardensystem.go b/pkg/component/gardensystem/runtime/runtime.go similarity index 97% rename from pkg/component/gardensystem/gardensystem.go rename to pkg/component/gardensystem/runtime/runtime.go index ca2a407798c..098c9fa38c2 100644 --- a/pkg/component/gardensystem/gardensystem.go +++ b/pkg/component/gardensystem/runtime/runtime.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gardensystem +package runtime import ( "context" @@ -31,7 +31,7 @@ import ( // ManagedResourceName is the name of the ManagedResource containing the resource specifications. const ManagedResourceName = "garden-system" -// New creates a new instance of DeployWaiter for garden system resources. +// New creates a new instance of DeployWaiter for runtime garden system resources. func New(client client.Client, namespace string) component.DeployWaiter { return &gardenSystem{ client: client, diff --git a/pkg/component/gardensystem/gardensystem_suite_test.go b/pkg/component/gardensystem/runtime/runtime_suite_test.go similarity index 88% rename from pkg/component/gardensystem/gardensystem_suite_test.go rename to pkg/component/gardensystem/runtime/runtime_suite_test.go index 634593a81c4..c8feae6b5d0 100644 --- a/pkg/component/gardensystem/gardensystem_suite_test.go +++ b/pkg/component/gardensystem/runtime/runtime_suite_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gardensystem_test +package runtime_test import ( "testing" @@ -21,7 +21,7 @@ import ( . "github.com/onsi/gomega" ) -func TestGardenSystem(t *testing.T) { +func TestRuntime(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Component GardenSystem Suite") + RunSpecs(t, "Component GardenSystem Runtime Suite") } diff --git a/pkg/component/gardensystem/gardensystem_test.go b/pkg/component/gardensystem/runtime/runtime_test.go similarity index 98% rename from pkg/component/gardensystem/gardensystem_test.go rename to pkg/component/gardensystem/runtime/runtime_test.go index efc9902ebb7..f557cf814c9 100644 --- a/pkg/component/gardensystem/gardensystem_test.go +++ b/pkg/component/gardensystem/runtime/runtime_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gardensystem_test +package runtime_test import ( "context" @@ -30,7 +30,7 @@ import ( gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" "github.com/gardener/gardener/pkg/component" - . "github.com/gardener/gardener/pkg/component/gardensystem" + . "github.com/gardener/gardener/pkg/component/gardensystem/runtime" operatorclient "github.com/gardener/gardener/pkg/operator/client" "github.com/gardener/gardener/pkg/resourcemanager/controller/garbagecollector/references" "github.com/gardener/gardener/pkg/utils/retry" @@ -39,7 +39,7 @@ import ( . "github.com/gardener/gardener/pkg/utils/test/matchers" ) -var _ = Describe("GardenSystem", func() { +var _ = Describe("Runtime", func() { var ( ctx = context.TODO() diff --git a/pkg/component/gardensystem/virtual/virtual.go b/pkg/component/gardensystem/virtual/virtual.go new file mode 100644 index 00000000000..c4d82e4fc04 --- /dev/null +++ b/pkg/component/gardensystem/virtual/virtual.go @@ -0,0 +1,608 @@ +// Copyright 2023 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 virtual + +import ( + "context" + "time" + + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + authenticationv1 "k8s.io/api/authentication/v1" + authorizationv1 "k8s.io/api/authorization/v1" + certificatesv1 "k8s.io/api/certificates/v1" + coordinationv1 "k8s.io/api/coordination/v1" + corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" + bootstraptokenapi "k8s.io/cluster-bootstrap/token/api" + apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" + operationsv1alpha1 "github.com/gardener/gardener/pkg/apis/operations/v1alpha1" + seedmanagementv1alpha1 "github.com/gardener/gardener/pkg/apis/seedmanagement/v1alpha1" + settingsv1alpha1 "github.com/gardener/gardener/pkg/apis/settings/v1alpha1" + "github.com/gardener/gardener/pkg/client/kubernetes" + "github.com/gardener/gardener/pkg/component" + "github.com/gardener/gardener/pkg/utils/managedresources" +) + +// ManagedResourceName is the name of the ManagedResource containing the resource specifications. +const ManagedResourceName = "garden-system-virtual" + +// New creates a new instance of DeployWaiter for virtual garden system resources. +func New(client client.Client, namespace string, values Values) component.DeployWaiter { + return &gardenSystem{ + client: client, + namespace: namespace, + values: values, + } +} + +type gardenSystem struct { + client client.Client + namespace string + values Values +} + +// Values contains values for the system resources. +type Values struct { + // SeedAuthorizerEnabled determines whether the seed authorizer is enabled. + SeedAuthorizerEnabled bool +} + +func (g *gardenSystem) Deploy(ctx context.Context) error { + data, err := g.computeResourcesData() + if err != nil { + return err + } + + return managedresources.CreateForSeed(ctx, g.client, g.namespace, ManagedResourceName, false, data) +} + +func (g *gardenSystem) Destroy(ctx context.Context) error { + return managedresources.DeleteForSeed(ctx, g.client, g.namespace, ManagedResourceName) +} + +// TimeoutWaitForManagedResource is the timeout used while waiting for the ManagedResources to become healthy +// or deleted. +var TimeoutWaitForManagedResource = 2 * time.Minute + +func (g *gardenSystem) Wait(ctx context.Context) error { + timeoutCtx, cancel := context.WithTimeout(ctx, TimeoutWaitForManagedResource) + defer cancel() + + return managedresources.WaitUntilHealthy(timeoutCtx, g.client, g.namespace, ManagedResourceName) +} + +func (g *gardenSystem) WaitCleanup(ctx context.Context) error { + timeoutCtx, cancel := context.WithTimeout(ctx, TimeoutWaitForManagedResource) + defer cancel() + + return managedresources.WaitUntilDeleted(timeoutCtx, g.client, g.namespace, ManagedResourceName) +} + +func (g *gardenSystem) computeResourcesData() (map[string][]byte, error) { + var ( + registry = managedresources.NewRegistry(kubernetes.SeedScheme, kubernetes.SeedCodec, kubernetes.SeedSerializer) + + namespaceGarden = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: v1beta1constants.GardenNamespace, + Labels: map[string]string{v1beta1constants.LabelApp: v1beta1constants.LabelGardener}, + }, + } + clusterRoleSeedBootstrapper = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:seed-bootstrapper", + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{certificatesv1.GroupName}, + Resources: []string{"certificatesigningrequests"}, + Verbs: []string{"create", "get"}, + }, + { + APIGroups: []string{certificatesv1.GroupName}, + Resources: []string{"certificatesigningrequests/seedclient"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleBindingSeedBootstrapper = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRoleSeedBootstrapper.Name, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: clusterRoleSeedBootstrapper.Name, + }, + Subjects: []rbacv1.Subject{{ + APIGroup: rbacv1.GroupName, + Kind: "Group", + Name: bootstraptokenapi.BootstrapDefaultGroup, + }}, + } + clusterRoleGardenerAdmin = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:admin", + Labels: map[string]string{v1beta1constants.GardenRole: "admin"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{ + gardencorev1beta1.GroupName, + seedmanagementv1alpha1.GroupName, + "dashboard.gardener.cloud", + settingsv1alpha1.GroupName, + operationsv1alpha1.GroupName, + }, + Resources: []string{"*"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{corev1.GroupName}, + Resources: []string{"events", "namespaces", "resourcequotas"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{eventsv1.GroupName}, + Resources: []string{"events"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{rbacv1.GroupName}, + Resources: []string{"clusterroles", "clusterrolebindings", "roles", "rolebindings"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{admissionregistrationv1.GroupName}, + Resources: []string{"mutatingwebhookconfigurations", "validatingwebhookconfigurations"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{apiregistrationv1.GroupName}, + Resources: []string{"apiservices"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{apiextensionsv1.GroupName}, + Resources: []string{"customresourcedefinitions"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{coordinationv1.GroupName}, + Resources: []string{"leases"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{certificatesv1.GroupName}, + Resources: []string{"certificatesigningrequests"}, + Verbs: []string{"*"}, + }, + }, + } + clusterRoleBindingGardenerAdmin = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRoleGardenerAdmin.Name, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: clusterRoleGardenerAdmin.Name, + }, + Subjects: []rbacv1.Subject{{ + APIGroup: rbacv1.GroupName, + Kind: "User", + Name: "system:kube-aggregator", + }}, + } + clusterRoleGardenerAdminAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:administrators", + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{v1beta1constants.GardenRole: "admin"}}, + {MatchLabels: map[string]string{v1beta1constants.GardenRole: "project-member"}}, + {MatchLabels: map[string]string{v1beta1constants.GardenRole: "project-serviceaccountmanager"}}, + }, + }, + } + clusterRoleGardenerViewer = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:viewer", + Labels: map[string]string{v1beta1constants.GardenRole: "viewer"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{gardencorev1beta1.GroupName}, + Resources: []string{ + "backupbuckets", + "backupentries", + "cloudprofiles", + "controllerinstallations", + "quotas", + "projects", + "seeds", + "shoots", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{ + seedmanagementv1alpha1.GroupName, + "dashboard.gardener.cloud", + settingsv1alpha1.GroupName, + operationsv1alpha1.GroupName, + }, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{corev1.GroupName}, + Resources: []string{"events", "namespaces", "resourcequotas"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{eventsv1.GroupName}, + Resources: []string{"events"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{rbacv1.GroupName}, + Resources: []string{"clusterroles", "clusterrolebindings", "roles", "rolebindings"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{admissionregistrationv1.GroupName}, + Resources: []string{"mutatingwebhookconfigurations", "validatingwebhookconfigurations"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{apiregistrationv1.GroupName}, + Resources: []string{"apiservices"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{apiextensionsv1.GroupName}, + Resources: []string{"customresourcedefinitions"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{coordinationv1.GroupName}, + Resources: []string{"leases"}, + Verbs: []string{"get", "list", "watch"}, + }, + }, + } + clusterRoleGardenerViewerAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:viewers", + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{v1beta1constants.GardenRole: "viewer"}}, + {MatchLabels: map[string]string{v1beta1constants.GardenRole: "project-viewer"}}, + }, + }, + } + clusterRoleReadGlobalResources = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:read-global-resources", + }, + Rules: []rbacv1.PolicyRule{{ + APIGroups: []string{gardencorev1beta1.GroupName}, + Resources: []string{ + "cloudprofiles", + "exposureclasses", + "seeds", + }, + Verbs: []string{"get", "list", "watch"}, + }}, + } + clusterRoleBindingReadGlobalResources = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRoleReadGlobalResources.Name, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: clusterRoleReadGlobalResources.Name, + }, + Subjects: []rbacv1.Subject{{ + APIGroup: rbacv1.GroupName, + Kind: "Group", + Name: user.AllAuthenticated, + }}, + } + clusterRoleUserAuth = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:user-auth", + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{authenticationv1.GroupName}, + Resources: []string{"tokenreviews"}, + Verbs: []string{"create"}, + }, + { + APIGroups: []string{authorizationv1.GroupName}, + Resources: []string{"selfsubjectaccessreviews"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleBindingUserAuth = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRoleUserAuth.Name, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: clusterRoleUserAuth.Name, + }, + Subjects: []rbacv1.Subject{{ + APIGroup: rbacv1.GroupName, + Kind: "Group", + Name: user.AllAuthenticated, + }}, + } + clusterRoleProjectCreation = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-creation", + }, + Rules: []rbacv1.PolicyRule{{ + APIGroups: []string{gardencorev1beta1.GroupName}, + Resources: []string{"projects"}, + Verbs: []string{"create"}, + }}, + } + labelKeyAggregateToProjectMember = "rbac.gardener.cloud/aggregate-to-project-member" + clusterRoleProjectMember = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-member-aggregation", + Labels: map[string]string{labelKeyAggregateToProjectMember: "true"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{corev1.GroupName}, + Resources: []string{ + "secrets", + "configmaps", + }, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{corev1.GroupName}, + Resources: []string{ + "events", + "resourcequotas", + "serviceaccounts", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{eventsv1.GroupName}, + Resources: []string{"events"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{gardencorev1beta1.GroupName}, + Resources: []string{ + "shoots", + "secretbindings", + "quotas", + }, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{settingsv1alpha1.GroupName}, + Resources: []string{"openidconnectpresets"}, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{operationsv1alpha1.GroupName}, + Resources: []string{"bastions"}, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{rbacv1.GroupName}, + Resources: []string{ + "roles", + "rolebindings", + }, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{gardencorev1beta1.GroupName}, + Resources: []string{"shoots/adminkubeconfig"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleProjectMemberAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-member", + Labels: map[string]string{v1beta1constants.GardenRole: "project-member"}, + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{labelKeyAggregateToProjectMember: "true"}}, + }, + }, + } + labelKeyAggregateToProjectServiceAccountManager = "rbac.gardener.cloud/aggregate-to-project-serviceaccountmanager" + clusterRoleProjectServiceAccountManager = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-serviceaccountmanager-aggregation", + Labels: map[string]string{labelKeyAggregateToProjectServiceAccountManager: "true"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{corev1.GroupName}, + Resources: []string{"serviceaccounts"}, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{corev1.GroupName}, + Resources: []string{"serviceaccounts/token"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleProjectServiceAccountManagerAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-serviceaccountmanager", + Labels: map[string]string{v1beta1constants.GardenRole: "project-serviceaccountmanager"}, + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{labelKeyAggregateToProjectServiceAccountManager: "true"}}, + }, + }, + } + labelKeyAggregateToProjectViewer = "rbac.gardener.cloud/aggregate-to-project-viewer" + clusterRoleProjectViewer = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-viewer-aggregation", + Labels: map[string]string{labelKeyAggregateToProjectViewer: "true"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{corev1.GroupName}, + Resources: []string{ + "events", + "configmaps", + "resourcequotas", + "serviceaccounts", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{eventsv1.GroupName}, + Resources: []string{"events"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{gardencorev1beta1.GroupName}, + Resources: []string{ + "shoots", + "secretbindings", + "quotas", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{settingsv1alpha1.GroupName}, + Resources: []string{"openidconnectpresets"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{operationsv1alpha1.GroupName}, + Resources: []string{"bastions"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{rbacv1.GroupName}, + Resources: []string{ + "roles", + "rolebindings", + }, + Verbs: []string{"get", "list", "watch"}, + }, + }, + } + clusterRoleProjectViewerAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-viewer", + Labels: map[string]string{v1beta1constants.GardenRole: "project-viewer"}, + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{labelKeyAggregateToProjectViewer: "true"}}, + }, + }, + } + ) + + if err := registry.Add( + namespaceGarden, + clusterRoleSeedBootstrapper, + clusterRoleBindingSeedBootstrapper, + clusterRoleGardenerAdmin, + clusterRoleBindingGardenerAdmin, + clusterRoleGardenerAdminAggregated, + clusterRoleGardenerViewer, + clusterRoleGardenerViewerAggregated, + clusterRoleReadGlobalResources, + clusterRoleBindingReadGlobalResources, + clusterRoleUserAuth, + clusterRoleBindingUserAuth, + clusterRoleProjectCreation, + clusterRoleProjectMember, + clusterRoleProjectMemberAggregated, + clusterRoleProjectServiceAccountManager, + clusterRoleProjectServiceAccountManagerAggregated, + clusterRoleProjectViewer, + clusterRoleProjectViewerAggregated, + ); err != nil { + return nil, err + } + + if !g.values.SeedAuthorizerEnabled { + var ( + clusterRoleSeeds = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:seeds", + }, + Rules: []rbacv1.PolicyRule{{ + APIGroups: []string{"*"}, + Resources: []string{"*"}, + Verbs: []string{"*"}, + }}, + } + clusterRoleBindingSeeds = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterRoleSeeds.Name, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: clusterRoleSeeds.Name, + }, + Subjects: []rbacv1.Subject{{ + APIGroup: rbacv1.GroupName, + Kind: "Group", + Name: v1beta1constants.SeedsGroup, + }}, + } + ) + + if err := registry.Add( + clusterRoleSeeds, + clusterRoleBindingSeeds, + ); err != nil { + return nil, err + } + } + + return registry.SerializedObjects(), nil +} diff --git a/pkg/component/gardensystem/virtual/virtual_suite_test.go b/pkg/component/gardensystem/virtual/virtual_suite_test.go new file mode 100644 index 00000000000..27c6e904554 --- /dev/null +++ b/pkg/component/gardensystem/virtual/virtual_suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2023 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 virtual_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestVirtual(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Component GardenSystem Virtual Suite") +} diff --git a/pkg/component/gardensystem/virtual/virtual_test.go b/pkg/component/gardensystem/virtual/virtual_test.go new file mode 100644 index 00000000000..5cef9d5186f --- /dev/null +++ b/pkg/component/gardensystem/virtual/virtual_test.go @@ -0,0 +1,735 @@ +// Copyright 2023 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 virtual_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/utils/pointer" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + + gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" + "github.com/gardener/gardener/pkg/component" + . "github.com/gardener/gardener/pkg/component/gardensystem/virtual" + componenttest "github.com/gardener/gardener/pkg/component/test" + operatorclient "github.com/gardener/gardener/pkg/operator/client" + "github.com/gardener/gardener/pkg/resourcemanager/controller/garbagecollector/references" + "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" +) + +var _ = Describe("Virtual", func() { + var ( + ctx = context.TODO() + + managedResourceName = "garden-system-virtual" + namespace = "some-namespace" + + c client.Client + component component.DeployWaiter + values Values + + managedResource *resourcesv1alpha1.ManagedResource + managedResourceSecret *corev1.Secret + + namespaceGarden *corev1.Namespace + clusterRoleSeedBootstrapper *rbacv1.ClusterRole + clusterRoleBindingSeedBootstrapper *rbacv1.ClusterRoleBinding + clusterRoleSeeds *rbacv1.ClusterRole + clusterRoleBindingSeeds *rbacv1.ClusterRoleBinding + clusterRoleGardenerAdmin *rbacv1.ClusterRole + clusterRoleBindingGardenerAdmin *rbacv1.ClusterRoleBinding + clusterRoleGardenerAdminAggregated *rbacv1.ClusterRole + clusterRoleGardenerViewer *rbacv1.ClusterRole + clusterRoleGardenerViewerAggregated *rbacv1.ClusterRole + clusterRoleReadGlobalResources *rbacv1.ClusterRole + clusterRoleBindingReadGlobalResources *rbacv1.ClusterRoleBinding + clusterRoleUserAuth *rbacv1.ClusterRole + clusterRoleBindingUserAuth *rbacv1.ClusterRoleBinding + clusterRoleProjectCreation *rbacv1.ClusterRole + clusterRoleProjectMember *rbacv1.ClusterRole + clusterRoleProjectMemberAggregated *rbacv1.ClusterRole + clusterRoleProjectServiceAccountManager *rbacv1.ClusterRole + clusterRoleProjectServiceAccountManagerAggregated *rbacv1.ClusterRole + clusterRoleProjectViewer *rbacv1.ClusterRole + clusterRoleProjectViewerAggregated *rbacv1.ClusterRole + ) + + BeforeEach(func() { + c = fakeclient.NewClientBuilder().WithScheme(operatorclient.RuntimeScheme).Build() + values = Values{} + 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, + }, + } + + namespaceGarden = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "garden", + Labels: map[string]string{"app": "gardener"}, + }, + } + clusterRoleSeedBootstrapper = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:seed-bootstrapper", + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"certificates.k8s.io"}, + Resources: []string{"certificatesigningrequests"}, + Verbs: []string{"create", "get"}, + }, + { + APIGroups: []string{"certificates.k8s.io"}, + Resources: []string{"certificatesigningrequests/seedclient"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleBindingSeedBootstrapper = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:seed-bootstrapper", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "gardener.cloud:system:seed-bootstrapper", + }, + Subjects: []rbacv1.Subject{{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Group", + Name: "system:bootstrappers", + }}, + } + clusterRoleSeeds = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:seeds", + }, + Rules: []rbacv1.PolicyRule{{ + APIGroups: []string{"*"}, + Resources: []string{"*"}, + Verbs: []string{"*"}, + }}, + } + clusterRoleBindingSeeds = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:seeds", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "gardener.cloud:system:seeds", + }, + Subjects: []rbacv1.Subject{{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Group", + Name: "gardener.cloud:system:seeds", + }}, + } + clusterRoleGardenerAdmin = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:admin", + Labels: map[string]string{"gardener.cloud/role": "admin"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{ + "core.gardener.cloud", + "seedmanagement.gardener.cloud", + "dashboard.gardener.cloud", + "settings.gardener.cloud", + "operations.gardener.cloud", + }, + Resources: []string{"*"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"events", "namespaces", "resourcequotas"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{"events.k8s.io"}, + Resources: []string{"events"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles", "clusterrolebindings", "roles", "rolebindings"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{"admissionregistration.k8s.io"}, + Resources: []string{"mutatingwebhookconfigurations", "validatingwebhookconfigurations"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{"apiregistration.k8s.io"}, + Resources: []string{"apiservices"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{"apiextensions.k8s.io"}, + Resources: []string{"customresourcedefinitions"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{"coordination.k8s.io"}, + Resources: []string{"leases"}, + Verbs: []string{"*"}, + }, + { + APIGroups: []string{"certificates.k8s.io"}, + Resources: []string{"certificatesigningrequests"}, + Verbs: []string{"*"}, + }, + }, + } + clusterRoleBindingGardenerAdmin = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "gardener.cloud:admin", + }, + Subjects: []rbacv1.Subject{{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "User", + Name: "system:kube-aggregator", + }}, + } + clusterRoleGardenerAdminAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:administrators", + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{"gardener.cloud/role": "admin"}}, + {MatchLabels: map[string]string{"gardener.cloud/role": "project-member"}}, + {MatchLabels: map[string]string{"gardener.cloud/role": "project-serviceaccountmanager"}}, + }, + }, + } + clusterRoleGardenerViewer = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:viewer", + Labels: map[string]string{"gardener.cloud/role": "viewer"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"core.gardener.cloud"}, + Resources: []string{ + "backupbuckets", + "backupentries", + "cloudprofiles", + "controllerinstallations", + "quotas", + "projects", + "seeds", + "shoots", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{ + "seedmanagement.gardener.cloud", + "dashboard.gardener.cloud", + "settings.gardener.cloud", + "operations.gardener.cloud", + }, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"events", "namespaces", "resourcequotas"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"events.k8s.io"}, + Resources: []string{"events"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles", "clusterrolebindings", "roles", "rolebindings"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"admissionregistration.k8s.io"}, + Resources: []string{"mutatingwebhookconfigurations", "validatingwebhookconfigurations"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"apiregistration.k8s.io"}, + Resources: []string{"apiservices"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"apiextensions.k8s.io"}, + Resources: []string{"customresourcedefinitions"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"coordination.k8s.io"}, + Resources: []string{"leases"}, + Verbs: []string{"get", "list", "watch"}, + }, + }, + } + clusterRoleGardenerViewerAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:viewers", + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{"gardener.cloud/role": "viewer"}}, + {MatchLabels: map[string]string{"gardener.cloud/role": "project-viewer"}}, + }, + }, + } + clusterRoleReadGlobalResources = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:read-global-resources", + }, + Rules: []rbacv1.PolicyRule{{ + APIGroups: []string{"core.gardener.cloud"}, + Resources: []string{ + "cloudprofiles", + "exposureclasses", + "seeds", + }, + Verbs: []string{"get", "list", "watch"}, + }}, + } + clusterRoleBindingReadGlobalResources = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:read-global-resources", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "gardener.cloud:system:read-global-resources", + }, + Subjects: []rbacv1.Subject{{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Group", + Name: "system:authenticated", + }}, + } + clusterRoleUserAuth = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:user-auth", + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"authentication.k8s.io"}, + Resources: []string{"tokenreviews"}, + Verbs: []string{"create"}, + }, + { + APIGroups: []string{"authorization.k8s.io"}, + Resources: []string{"selfsubjectaccessreviews"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleBindingUserAuth = &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:user-auth", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "gardener.cloud:system:user-auth", + }, + Subjects: []rbacv1.Subject{{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Group", + Name: "system:authenticated", + }}, + } + clusterRoleProjectCreation = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-creation", + }, + Rules: []rbacv1.PolicyRule{{ + APIGroups: []string{"core.gardener.cloud"}, + Resources: []string{"projects"}, + Verbs: []string{"create"}, + }}, + } + clusterRoleProjectMember = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-member-aggregation", + Labels: map[string]string{"rbac.gardener.cloud/aggregate-to-project-member": "true"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{ + "secrets", + "configmaps", + }, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{""}, + Resources: []string{ + "events", + "resourcequotas", + "serviceaccounts", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"events.k8s.io"}, + Resources: []string{"events"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"core.gardener.cloud"}, + Resources: []string{ + "shoots", + "secretbindings", + "quotas", + }, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{"settings.gardener.cloud"}, + Resources: []string{"openidconnectpresets"}, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{"operations.gardener.cloud"}, + Resources: []string{"bastions"}, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{ + "roles", + "rolebindings", + }, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{"core.gardener.cloud"}, + Resources: []string{"shoots/adminkubeconfig"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleProjectMemberAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-member", + Labels: map[string]string{"gardener.cloud/role": "project-member"}, + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{"rbac.gardener.cloud/aggregate-to-project-member": "true"}}, + }, + }, + } + clusterRoleProjectServiceAccountManager = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-serviceaccountmanager-aggregation", + Labels: map[string]string{"rbac.gardener.cloud/aggregate-to-project-serviceaccountmanager": "true"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"serviceaccounts"}, + Verbs: []string{"create", "delete", "deletecollection", "get", "list", "watch", "patch", "update"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"serviceaccounts/token"}, + Verbs: []string{"create"}, + }, + }, + } + clusterRoleProjectServiceAccountManagerAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-serviceaccountmanager", + Labels: map[string]string{"gardener.cloud/role": "project-serviceaccountmanager"}, + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{"rbac.gardener.cloud/aggregate-to-project-serviceaccountmanager": "true"}}, + }, + }, + } + clusterRoleProjectViewer = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-viewer-aggregation", + Labels: map[string]string{"rbac.gardener.cloud/aggregate-to-project-viewer": "true"}, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{ + "events", + "configmaps", + "resourcequotas", + "serviceaccounts", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"events.k8s.io"}, + Resources: []string{"events"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"core.gardener.cloud"}, + Resources: []string{ + "shoots", + "secretbindings", + "quotas", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"settings.gardener.cloud"}, + Resources: []string{"openidconnectpresets"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"operations.gardener.cloud"}, + Resources: []string{"bastions"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{ + "roles", + "rolebindings", + }, + Verbs: []string{"get", "list", "watch"}, + }, + }, + } + clusterRoleProjectViewerAggregated = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gardener.cloud:system:project-viewer", + Labels: map[string]string{"gardener.cloud/role": "project-viewer"}, + }, + AggregationRule: &rbacv1.AggregationRule{ + ClusterRoleSelectors: []metav1.LabelSelector{ + {MatchLabels: map[string]string{"rbac.gardener.cloud/aggregate-to-project-viewer": "true"}}, + }, + }, + } + }) + + Describe("#Deploy", func() { + JustBeforeEach(func() { + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(BeNotFoundError()) + + Expect(component.Deploy(ctx)).To(Succeed()) + + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(Succeed()) + expectedMr := &resourcesv1alpha1.ManagedResource{ + TypeMeta: metav1.TypeMeta{ + APIVersion: resourcesv1alpha1.SchemeGroupVersion.String(), + Kind: "ManagedResource", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: managedResource.Name, + Namespace: managedResource.Namespace, + Labels: map[string]string{"gardener.cloud/role": "seed-system-component"}, + ResourceVersion: "1", + }, + Spec: resourcesv1alpha1.ManagedResourceSpec{ + Class: pointer.String("seed"), + SecretRefs: []corev1.LocalObjectReference{{ + Name: managedResource.Spec.SecretRefs[0].Name, + }}, + KeepObjects: pointer.Bool(false), + }, + } + utilruntime.Must(references.InjectAnnotations(expectedMr)) + Expect(managedResource).To(DeepEqual(expectedMr)) + + managedResourceSecret.Name = managedResource.Spec.SecretRefs[0].Name + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(Succeed()) + Expect(managedResourceSecret.Type).To(Equal(corev1.SecretTypeOpaque)) + Expect(managedResourceSecret.Immutable).To(Equal(pointer.Bool(true))) + Expect(managedResourceSecret.Labels["resources.gardener.cloud/garbage-collectable-reference"]).To(Equal("true")) + }) + + It("should successfully deploy the resources when seed authorizer is disabled", func() { + Expect(managedResourceSecret.Data).To(HaveLen(21)) + Expect(string(managedResourceSecret.Data["namespace____garden.yaml"])).To(Equal(componenttest.Serialize(namespaceGarden))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_seed-bootstrapper.yaml"])).To(Equal(componenttest.Serialize(clusterRoleSeedBootstrapper))) + Expect(string(managedResourceSecret.Data["clusterrolebinding____gardener.cloud_system_seed-bootstrapper.yaml"])).To(Equal(componenttest.Serialize(clusterRoleBindingSeedBootstrapper))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_seeds.yaml"])).To(Equal(componenttest.Serialize(clusterRoleSeeds))) + Expect(string(managedResourceSecret.Data["clusterrolebinding____gardener.cloud_system_seeds.yaml"])).To(Equal(componenttest.Serialize(clusterRoleBindingSeeds))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_admin.yaml"])).To(Equal(componenttest.Serialize(clusterRoleGardenerAdmin))) + Expect(string(managedResourceSecret.Data["clusterrolebinding____gardener.cloud_admin.yaml"])).To(Equal(componenttest.Serialize(clusterRoleBindingGardenerAdmin))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_administrators.yaml"])).To(Equal(componenttest.Serialize(clusterRoleGardenerAdminAggregated))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_viewer.yaml"])).To(Equal(componenttest.Serialize(clusterRoleGardenerViewer))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_viewers.yaml"])).To(Equal(componenttest.Serialize(clusterRoleGardenerViewerAggregated))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_read-global-resources.yaml"])).To(Equal(componenttest.Serialize(clusterRoleReadGlobalResources))) + Expect(string(managedResourceSecret.Data["clusterrolebinding____gardener.cloud_system_read-global-resources.yaml"])).To(Equal(componenttest.Serialize(clusterRoleBindingReadGlobalResources))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_user-auth.yaml"])).To(Equal(componenttest.Serialize(clusterRoleUserAuth))) + Expect(string(managedResourceSecret.Data["clusterrolebinding____gardener.cloud_system_user-auth.yaml"])).To(Equal(componenttest.Serialize(clusterRoleBindingUserAuth))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_project-creation.yaml"])).To(Equal(componenttest.Serialize(clusterRoleProjectCreation))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_project-member.yaml"])).To(Equal(componenttest.Serialize(clusterRoleProjectMemberAggregated))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_project-member-aggregation.yaml"])).To(Equal(componenttest.Serialize(clusterRoleProjectMember))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_project-serviceaccountmanager.yaml"])).To(Equal(componenttest.Serialize(clusterRoleProjectServiceAccountManagerAggregated))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_project-serviceaccountmanager-aggregation.yaml"])).To(Equal(componenttest.Serialize(clusterRoleProjectServiceAccountManager))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_project-viewer.yaml"])).To(Equal(componenttest.Serialize(clusterRoleProjectViewerAggregated))) + Expect(string(managedResourceSecret.Data["clusterrole____gardener.cloud_system_project-viewer-aggregation.yaml"])).To(Equal(componenttest.Serialize(clusterRoleProjectViewer))) + }) + + Context("when seed authorizer is enabled", func() { + BeforeEach(func() { + values.SeedAuthorizerEnabled = true + component = New(c, namespace, values) + }) + + It("should successfully deploy the resources when seed authorizer is enabled", func() { + Expect(managedResourceSecret.Data).NotTo(HaveKey("clusterrole____gardener.cloud_system_seeds.yaml")) + Expect(managedResourceSecret.Data).NotTo(HaveKey("clusterrolebinding____gardener.cloud_system_seeds.yaml")) + }) + }) + }) + + 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(BeNotFoundError()) + Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(BeNotFoundError()) + }) + }) + + Context("waiting functions", func() { + var fakeOps *retryfake.Ops + + BeforeEach(func() { + fakeOps = &retryfake.Ops{MaxAttempts: 1} + DeferCleanup(test.WithVars( + &retry.Until, fakeOps.Until, + &retry.UntilTimeout, fakeOps.UntilTimeout, + )) + }) + + 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, + }, + }, + }, + })).To(Succeed()) + + 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, + }, + }, + }, + })).To(Succeed()) + + 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/care/garden_health.go b/pkg/operation/care/garden_health.go index 43b5616659a..425d199a8ff 100644 --- a/pkg/operation/care/garden_health.go +++ b/pkg/operation/care/garden_health.go @@ -33,7 +33,7 @@ import ( "github.com/gardener/gardener/pkg/client/kubernetes" "github.com/gardener/gardener/pkg/component/etcd" "github.com/gardener/gardener/pkg/component/gardeneraccess" - "github.com/gardener/gardener/pkg/component/gardensystem" + runtimegardensystem "github.com/gardener/gardener/pkg/component/gardensystem/runtime" "github.com/gardener/gardener/pkg/component/hvpa" "github.com/gardener/gardener/pkg/component/istio" "github.com/gardener/gardener/pkg/component/kubecontrollermanager" @@ -53,7 +53,7 @@ const virtualGardenPrefix = "virtual-garden-" var ( requiredGardenRuntimeManagedResources = sets.New( etcd.Druid, - gardensystem.ManagedResourceName, + runtimegardensystem.ManagedResourceName, kubestatemetrics.ManagedResourceName, fluentoperator.OperatorManagedResourceName, fluentoperator.CustomResourcesManagedResourceName+"-garden", diff --git a/pkg/operation/care/garden_health_test.go b/pkg/operation/care/garden_health_test.go index 4a1b56ccc37..81e5a99a442 100644 --- a/pkg/operation/care/garden_health_test.go +++ b/pkg/operation/care/garden_health_test.go @@ -32,7 +32,7 @@ import ( "github.com/gardener/gardener/pkg/client/kubernetes" "github.com/gardener/gardener/pkg/component/etcd" "github.com/gardener/gardener/pkg/component/gardeneraccess" - "github.com/gardener/gardener/pkg/component/gardensystem" + runtimegardensystem "github.com/gardener/gardener/pkg/component/gardensystem/runtime" "github.com/gardener/gardener/pkg/component/hvpa" "github.com/gardener/gardener/pkg/component/kubecontrollermanager" "github.com/gardener/gardener/pkg/component/kubestatemetrics" @@ -49,7 +49,7 @@ import ( var ( gardenManagedResources = []string{ etcd.Druid, - gardensystem.ManagedResourceName, + runtimegardensystem.ManagedResourceName, hvpa.ManagedResourceName, "istio-system", "virtual-garden-istio", diff --git a/pkg/operator/controller/garden/garden/components.go b/pkg/operator/controller/garden/garden/components.go index 59638ffe68b..2dbbfdfa709 100644 --- a/pkg/operator/controller/garden/garden/components.go +++ b/pkg/operator/controller/garden/garden/components.go @@ -42,7 +42,7 @@ import ( "github.com/gardener/gardener/pkg/component/apiserver" "github.com/gardener/gardener/pkg/component/etcd" "github.com/gardener/gardener/pkg/component/gardeneraccess" - "github.com/gardener/gardener/pkg/component/gardensystem" + runtimegardensystem "github.com/gardener/gardener/pkg/component/gardensystem/runtime" "github.com/gardener/gardener/pkg/component/hvpa" "github.com/gardener/gardener/pkg/component/istio" "github.com/gardener/gardener/pkg/component/kubeapiserver" @@ -288,7 +288,7 @@ func (r *Reconciler) newEtcdDruid() (component.DeployWaiter, error) { } func (r *Reconciler) newSystem() component.DeployWaiter { - return gardensystem.New(r.RuntimeClientSet.Client(), r.GardenNamespace) + return runtimegardensystem.New(r.RuntimeClientSet.Client(), r.GardenNamespace) } func (r *Reconciler) newEtcd( diff --git a/skaffold-operator.yaml b/skaffold-operator.yaml index 7d2f3aa466c..3fe2c228851 100644 --- a/skaffold-operator.yaml +++ b/skaffold-operator.yaml @@ -96,7 +96,7 @@ build: - pkg/component/extensions/worker - pkg/component/gardeneraccess - pkg/component/gardenerapiserver - - pkg/component/gardensystem + - pkg/component/gardensystem/runtime - pkg/component/hvpa - pkg/component/istio - pkg/component/kubeapiserver diff --git a/skaffold.yaml b/skaffold.yaml index 960a927bc3f..40d67d54662 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -792,7 +792,7 @@ build: - pkg/client/kubernetes/clientmap/builder - pkg/client/kubernetes/clientmap/internal - pkg/client/kubernetes/clientmap/keys - - pkg/component/gardensystem + - pkg/component/gardensystem/runtime - pkg/controller/networkpolicy - pkg/controller/networkpolicy/helper - pkg/controller/networkpolicy/hostnameresolver diff --git a/test/integration/operator/garden/care/care_test.go b/test/integration/operator/garden/care/care_test.go index 302ba46b371..8567844ea64 100644 --- a/test/integration/operator/garden/care/care_test.go +++ b/test/integration/operator/garden/care/care_test.go @@ -31,7 +31,7 @@ import ( resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" "github.com/gardener/gardener/pkg/component/etcd" "github.com/gardener/gardener/pkg/component/gardeneraccess" - "github.com/gardener/gardener/pkg/component/gardensystem" + runtimegardensystem "github.com/gardener/gardener/pkg/component/gardensystem/runtime" "github.com/gardener/gardener/pkg/component/hvpa" "github.com/gardener/gardener/pkg/component/kubecontrollermanager" "github.com/gardener/gardener/pkg/component/kubestatemetrics" @@ -46,7 +46,7 @@ var _ = Describe("Garden Care controller tests", func() { var ( requiredRuntimeClusterManagedResources = []string{ etcd.Druid, - gardensystem.ManagedResourceName, + runtimegardensystem.ManagedResourceName, hvpa.ManagedResourceName, vpa.ManagedResourceControlName, "istio-system",