diff --git a/api/v1alpha1/types_etcd.go b/api/v1alpha1/types_etcd.go index 8c2bf2e97..c6685d56d 100644 --- a/api/v1alpha1/types_etcd.go +++ b/api/v1alpha1/types_etcd.go @@ -473,5 +473,10 @@ func (e *Etcd) GetAsOwnerReference() metav1.OwnerReference { // GetRoleName returns the role name for the Etcd func (e *Etcd) GetRoleName() string { - return fmt.Sprintf("druid.gardener.cloud:etcd:%s", e.Name) + return fmt.Sprintf("%s:etcd:%s", GroupVersion.Group, e.Name) +} + +// GetRoleBindingName returns the rolebinding name for the Etcd +func (e *Etcd) GetRoleBindingName() string { + return fmt.Sprintf("%s:etcd:%s", GroupVersion.Group, e.Name) } diff --git a/api/v1alpha1/types_etcd_test.go b/api/v1alpha1/types_etcd_test.go index 396c715cf..9af6b79a6 100644 --- a/api/v1alpha1/types_etcd_test.go +++ b/api/v1alpha1/types_etcd_test.go @@ -144,8 +144,13 @@ var _ = Describe("Etcd", func() { Context("GetRoleName", func() { It("should return the role name for the Etcd", func() { - expected := "druid.gardener.cloud:etcd:foo" - Expect(created.GetRoleName()).To(Equal(expected)) + Expect(created.GetRoleName()).To(Equal(GroupVersion.Group + ":etcd:foo")) + }) + }) + + Context("GetRoleBindingName", func() { + It("should return the rolebinding name for the Etcd", func() { + Expect(created.GetRoleName()).To(Equal(GroupVersion.Group + ":etcd:foo")) }) }) }) diff --git a/charts/etcd/templates/etcd-rolebinding.yaml b/charts/etcd/templates/etcd-rolebinding.yaml deleted file mode 100644 index 790aa8a30..000000000 --- a/charts/etcd/templates/etcd-rolebinding.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ .Values.roleBindingName }} - namespace: {{ .Release.Namespace }} - ownerReferences: - - apiVersion: druid.gardener.cloud/v1alpha1 - blockOwnerDeletion: true - controller: true - kind: Etcd - name: {{ .Values.name }} - uid: {{ .Values.uid }} - labels: - name: etcd - instance: {{ .Values.name }} -{{- if .Values.labels }} -{{ toYaml .Values.labels | indent 4 }} -{{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ .Values.roleName }} -subjects: -- kind: ServiceAccount - name: {{ .Values.serviceAccountName }} - namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/controllers/etcd/reconciler.go b/controllers/etcd/reconciler.go index 8b7bd5bec..c5f36a267 100644 --- a/controllers/etcd/reconciler.go +++ b/controllers/etcd/reconciler.go @@ -18,7 +18,6 @@ import ( "context" "fmt" "path/filepath" - "reflect" druidv1alpha1 "github.com/gardener/etcd-druid/api/v1alpha1" "github.com/gardener/etcd-druid/controllers/utils" @@ -27,6 +26,7 @@ import ( componentlease "github.com/gardener/etcd-druid/pkg/component/etcd/lease" componentpdb "github.com/gardener/etcd-druid/pkg/component/etcd/poddisruptionbudget" componentrole "github.com/gardener/etcd-druid/pkg/component/etcd/role" + componentrolebinding "github.com/gardener/etcd-druid/pkg/component/etcd/rolebinding" componentservice "github.com/gardener/etcd-druid/pkg/component/etcd/service" componentserviceaccount "github.com/gardener/etcd-druid/pkg/component/etcd/serviceaccount" componentsts "github.com/gardener/etcd-druid/pkg/component/etcd/statefulset" @@ -40,7 +40,6 @@ import ( "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - rbac "k8s.io/api/rbac/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" @@ -280,6 +279,14 @@ func (r *Reconciler) delete(ctx context.Context, etcd *druidv1alpha1.Etcd) (ctrl }, err } + roleBindingValues := componentrolebinding.GenerateValues(etcd) + roleBindingDeployer := componentrolebinding.New(r.Client, roleBindingValues) + if err := roleBindingDeployer.Destroy(ctx); err != nil { + return ctrl.Result{ + Requeue: true, + }, err + } + if sets.NewString(etcd.Finalizers...).Has(common.FinalizerName) { logger.Info("Removing finalizer") if err := controllerutils.RemoveFinalizers(ctx, r.Client, etcd, common.FinalizerName); client.IgnoreNotFound(err) != nil { @@ -292,40 +299,6 @@ func (r *Reconciler) delete(ctx context.Context, etcd *druidv1alpha1.Etcd) (ctrl return ctrl.Result{}, nil } -func (r *Reconciler) reconcileRoleBinding(ctx context.Context, logger logr.Logger, etcd *druidv1alpha1.Etcd, values map[string]interface{}) error { - logger.Info("Reconciling rolebinding") - var err error - - decoded, err := r.chart.decodeRoleBinding(etcd.Name, etcd.Namespace, values) - if err != nil { - return err - } - - roleBindingObj := &rbac.RoleBinding{} - key := client.ObjectKeyFromObject(decoded) - if err := r.Get(ctx, key, roleBindingObj); err != nil { - if !apierrors.IsNotFound(err) { - return err - } - if err := r.Create(ctx, decoded); err != nil { - return err - } - logger.Info("Creating rolebinding", "rolebinding", kutil.Key(decoded.Namespace, decoded.Name).String()) - return nil - } - - if !reflect.DeepEqual(decoded.RoleRef, roleBindingObj.RoleRef) || !reflect.DeepEqual(decoded.Subjects, roleBindingObj.Subjects) { - roleBindingObjCopy := roleBindingObj.DeepCopy() - roleBindingObjCopy.RoleRef = decoded.RoleRef - roleBindingObjCopy.Subjects = decoded.Subjects - if err := r.Patch(ctx, roleBindingObjCopy, client.MergeFrom(roleBindingObj)); err != nil { - return err - } - } - - return err -} - func (r *Reconciler) reconcileEtcd(ctx context.Context, logger logr.Logger, etcd *druidv1alpha1.Etcd) reconcileResult { // Check if Spec.Replicas is odd or even. // TODO(timuthy): The following checks should rather be part of a validation. Also re-enqueuing doesn't make sense in case the values are invalid. @@ -381,11 +354,9 @@ func (r *Reconciler) reconcileEtcd(ctx context.Context, logger logr.Logger, etcd return reconcileResult{err: err} } - roleBindingValues, err := r.getMapFromEtcd(etcd, r.config.DisableEtcdServiceAccountAutomount) - if err != nil { - return reconcileResult{err: err} - } - err = r.reconcileRoleBinding(ctx, logger, etcd, roleBindingValues) + roleBindingValues := componentrolebinding.GenerateValues(etcd) + roleBindingDeployer := componentrolebinding.New(r.Client, roleBindingValues) + err = roleBindingDeployer.Deploy(ctx) if err != nil { return reconcileResult{err: err} } @@ -420,26 +391,6 @@ func (r *Reconciler) reconcileEtcd(ctx context.Context, logger logr.Logger, etcd return reconcileResult{svcName: &serviceValues.ClientServiceName, sts: sts, err: err} } -func (r *Reconciler) getMapFromEtcd(etcd *druidv1alpha1.Etcd, disableEtcdServiceAccountAutomount bool) (map[string]interface{}, error) { - pdbMinAvailable := 0 - if etcd.Spec.Replicas > 1 { - pdbMinAvailable = int(etcd.Spec.Replicas) - } - - values := map[string]interface{}{ - "name": etcd.Name, - "uid": etcd.UID, - "labels": etcd.Spec.Labels, - "pdbMinAvailable": pdbMinAvailable, - "serviceAccountName": etcd.GetServiceAccountName(), - "disableEtcdServiceAccountAutomount": disableEtcdServiceAccountAutomount, - "roleName": fmt.Sprintf("druid.gardener.cloud:etcd:%s", etcd.Name), - "roleBindingName": fmt.Sprintf("druid.gardener.cloud:etcd:%s", etcd.Name), - } - - return values, nil -} - func isPeerTLSChangedToEnabled(peerTLSEnabledStatusFromMembers bool, configMapValues *componentconfigmap.Values) bool { if peerTLSEnabledStatusFromMembers { return false diff --git a/pkg/component/etcd/rolebinding/rolebinding.go b/pkg/component/etcd/rolebinding/rolebinding.go new file mode 100644 index 000000000..bab8bc6c0 --- /dev/null +++ b/pkg/component/etcd/rolebinding/rolebinding.go @@ -0,0 +1,76 @@ +// Copyright (c) 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 rolebinding + +import ( + "context" + + "github.com/gardener/gardener/pkg/controllerutils" + + gardenercomponent "github.com/gardener/gardener/pkg/operation/botanist/component" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type component struct { + client client.Client + values *Values +} + +func (c component) Deploy(ctx context.Context) error { + roleBinding := c.emptyRoleBinding() + _, err := controllerutils.GetAndCreateOrStrategicMergePatch(ctx, c.client, roleBinding, func() error { + roleBinding.Name = c.values.Name + roleBinding.Namespace = c.values.Namespace + roleBinding.Labels = c.values.Labels + roleBinding.OwnerReferences = c.values.OwnerReferences + roleBinding.RoleRef = rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: c.values.RoleName, + } + roleBinding.Subjects = []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: c.values.ServiceAccountName, + Namespace: c.values.Namespace, + }, + } + return nil + }) + return err +} + +func (c component) Destroy(ctx context.Context) error { + return client.IgnoreNotFound(c.client.Delete(ctx, c.emptyRoleBinding())) +} + +func (c *component) emptyRoleBinding() *rbacv1.RoleBinding { + return &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: c.values.Name, + Namespace: c.values.Namespace, + }, + } +} + +// New creates a new role binding deployer instance. +func New(c client.Client, value *Values) gardenercomponent.Deployer { + return &component{ + client: c, + values: value, + } +} diff --git a/pkg/component/etcd/rolebinding/rolebinding_suite_test.go b/pkg/component/etcd/rolebinding/rolebinding_suite_test.go new file mode 100644 index 000000000..e46726e0a --- /dev/null +++ b/pkg/component/etcd/rolebinding/rolebinding_suite_test.go @@ -0,0 +1,27 @@ +// Copyright (c) 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 rolebinding + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestRoleBinding(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "RoleBinding Component Suite") +} diff --git a/pkg/component/etcd/rolebinding/rolebinding_test.go b/pkg/component/etcd/rolebinding/rolebinding_test.go new file mode 100644 index 000000000..3ad2045b1 --- /dev/null +++ b/pkg/component/etcd/rolebinding/rolebinding_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 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 rolebinding_test + +import ( + "context" + + "github.com/gardener/etcd-druid/api/v1alpha1" + "github.com/gardener/etcd-druid/pkg/client/kubernetes" + "github.com/gardener/etcd-druid/pkg/component/etcd/rolebinding" + + "github.com/gardener/gardener/pkg/operation/botanist/component" + . "github.com/gardener/gardener/pkg/utils/test/matchers" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var _ = Describe("RoleBinding Component", func() { + var ( + ctx context.Context + c client.Client + values *rolebinding.Values + roleBindingComponent component.Deployer + ) + + Context("#Deploy", func() { + + BeforeEach(func() { + ctx = context.Background() + c = fake.NewClientBuilder().WithScheme(kubernetes.Scheme).Build() + values = getTestRoleBindingValues() + roleBindingComponent = rolebinding.New(c, values) + + err := roleBindingComponent.Deploy(ctx) + Expect(err).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + err := roleBindingComponent.Destroy(ctx) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should create the RoleBinding with the expected values", func() { + By("verifying that the RoleBinding is created on the K8s cluster as expected") + created := &rbacv1.RoleBinding{} + err := c.Get(ctx, getRoleBindingKeyFromValue(values), created) + Expect(err).NotTo(HaveOccurred()) + verifyRoleBindingValues(created, values) + }) + + It("should update the RoleBinding with the expected values", func() { + By("updating the RoleBinding") + values.Labels["new"] = "label" + err := roleBindingComponent.Deploy(ctx) + Expect(err).NotTo(HaveOccurred()) + + By("verifying that the RoleBinding is updated on the K8s cluster as expected") + updated := &rbacv1.RoleBinding{} + err = c.Get(ctx, getRoleBindingKeyFromValue(values), updated) + Expect(err).NotTo(HaveOccurred()) + verifyRoleBindingValues(updated, values) + }) + + It("should not return an error when there is nothing to update the RoleBinding", func() { + err := roleBindingComponent.Deploy(ctx) + Expect(err).NotTo(HaveOccurred()) + updated := &rbacv1.RoleBinding{} + err = c.Get(ctx, getRoleBindingKeyFromValue(values), updated) + Expect(err).NotTo(HaveOccurred()) + verifyRoleBindingValues(updated, values) + }) + + It("should return an error when the update fails", func() { + values.Name = "" + err := roleBindingComponent.Deploy(ctx) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("Required value: name is required")) + }) + }) + + Context("#Destroy", func() { + + BeforeEach(func() { + ctx = context.Background() + c = fake.NewClientBuilder().WithScheme(kubernetes.Scheme).Build() + values = getTestRoleBindingValues() + + roleBindingComponent = rolebinding.New(c, values) + err := roleBindingComponent.Deploy(ctx) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should delete the RoleBinding", func() { + By("deleting the RoleBinding") + err := roleBindingComponent.Destroy(ctx) + Expect(err).NotTo(HaveOccurred()) + + roleBinding := &rbacv1.RoleBinding{} + By("verifying that the RoleBinding is deleted from the K8s cluster as expected") + Expect(c.Get(ctx, getRoleBindingKeyFromValue(values), roleBinding)).To(BeNotFoundError()) + }) + + It("should not return an error when there is nothing to delete", func() { + By("deleting the RoleBinding") + err := roleBindingComponent.Destroy(ctx) + Expect(err).NotTo(HaveOccurred()) + + By("verifying that attempting to delete the RoleBinding again returns no error") + err = roleBindingComponent.Destroy(ctx) + Expect(err).NotTo(HaveOccurred()) + }) + }) +}) + +func verifyRoleBindingValues(expected *rbacv1.RoleBinding, values *rolebinding.Values) { + Expect(expected.Name).To(Equal(values.Name)) + Expect(expected.Labels).To(Equal(values.Labels)) + Expect(expected.Namespace).To(Equal(values.Namespace)) + Expect(expected.Namespace).To(Equal(values.Namespace)) + Expect(expected.OwnerReferences).To(Equal(values.OwnerReferences)) + Expect(expected.Subjects).To(Equal([]rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: values.ServiceAccountName, + Namespace: values.Namespace, + }, + })) + Expect(expected.RoleRef).To(Equal(rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: values.RoleName, + })) +} +func getRoleBindingKeyFromValue(values *rolebinding.Values) types.NamespacedName { + return client.ObjectKey{Name: values.Name, Namespace: values.Namespace} +} + +func getTestRoleBindingValues() *rolebinding.Values { + return &rolebinding.Values{ + Name: "test-rolebinding", + Namespace: "test-namespace", + Labels: map[string]string{ + "foo": "bar", + }, + RoleName: "test-role", + ServiceAccountName: "test-serviceaccount", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: v1alpha1.GroupVersion.String(), + Kind: "etcd", + Name: "test-etcd", + UID: "123-456-789", + Controller: pointer.Bool(true), + BlockOwnerDeletion: pointer.Bool(true), + }, + }, + } +} diff --git a/pkg/component/etcd/rolebinding/values.go b/pkg/component/etcd/rolebinding/values.go new file mode 100644 index 000000000..936933bf0 --- /dev/null +++ b/pkg/component/etcd/rolebinding/values.go @@ -0,0 +1,34 @@ +// Copyright (c) 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 rolebinding + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// Values defines the fields used to create a RoleBinding for Etcd. +type Values struct { + // Name is the name of the RoleBinding. + Name string + // Namespace is the namespace of the RoleBinding. + Namespace string + // RoleName is the role name of the RoleBinding. It is assumed that the role exists in the namespace where the etcd custom resource is created. + RoleName string + // ServiceAccountName is the service account subject name for the RoleBinding. + // It is assumed that the ServiceAccount exists in the namespace where the etcd custom resource is created. + ServiceAccountName string + // OwnerReferences are the OwnerReferences of the RoleBinding. + OwnerReferences []metav1.OwnerReference + // Labels are the labels of the RoleBinding. + Labels map[string]string +} diff --git a/pkg/component/etcd/rolebinding/values_helper.go b/pkg/component/etcd/rolebinding/values_helper.go new file mode 100644 index 000000000..a6c35a716 --- /dev/null +++ b/pkg/component/etcd/rolebinding/values_helper.go @@ -0,0 +1,34 @@ +// Copyright (c) 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 rolebinding + +import ( + druidv1alpha1 "github.com/gardener/etcd-druid/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GenerateValues generates `serviceaccount.Values` for the serviceaccount component with the given `etcd` object. +func GenerateValues(etcd *druidv1alpha1.Etcd) *Values { + return &Values{ + Name: etcd.GetRoleBindingName(), + Namespace: etcd.Namespace, + Labels: etcd.GetDefaultLabels(), + OwnerReferences: []metav1.OwnerReference{ + etcd.GetAsOwnerReference(), + }, + RoleName: etcd.GetRoleName(), + ServiceAccountName: etcd.GetServiceAccountName(), + } +} diff --git a/pkg/component/etcd/rolebinding/values_helper_test.go b/pkg/component/etcd/rolebinding/values_helper_test.go new file mode 100644 index 000000000..6e8b83eff --- /dev/null +++ b/pkg/component/etcd/rolebinding/values_helper_test.go @@ -0,0 +1,61 @@ +// Copyright (c) 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 rolebinding_test + +import ( + "github.com/gardener/etcd-druid/api/v1alpha1" + "github.com/gardener/etcd-druid/pkg/component/etcd/rolebinding" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("RoleBinding", func() { + var ( + etcd = &v1alpha1.Etcd{ + ObjectMeta: metav1.ObjectMeta{ + Name: "etcd-name", + Namespace: "etcd-namespace", + UID: "etcd-uid", + }, + Spec: v1alpha1.EtcdSpec{ + Labels: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + }, + } + expected = &rolebinding.Values{ + Name: etcd.GetRoleBindingName(), + Namespace: etcd.Namespace, + Labels: map[string]string{ + "name": "etcd", + "instance": etcd.Name, + }, + RoleName: etcd.GetRoleName(), + ServiceAccountName: etcd.GetServiceAccountName(), + OwnerReferences: []metav1.OwnerReference{ + etcd.GetAsOwnerReference(), + }, + } + ) + + Context("Generate Values", func() { + It("should generate correct values", func() { + values := rolebinding.GenerateValues(etcd) + Expect(values).To(Equal(expected)) + }) + }) +})