From 7453188e2a173d578c55df76a9299b8ade998fbd Mon Sep 17 00:00:00 2001 From: Jose Date: Thu, 13 Apr 2023 09:18:35 +0200 Subject: [PATCH] Support generation of ClusterRoleBinding resources Fix https://github.com/quarkusio/quarkus/issues/32572 --- .../asciidoc/deploying-to-kubernetes.adoc | 8 ++- .../spi/KubernetesRoleBindingBuildItem.java | 48 ------------- .../io/quarkus/kubernetes/spi/RoleRef.java | 19 +++++ .../io/quarkus/kubernetes/spi/Subject.java | 31 ++++++++ ...ddClusterRoleBindingResourceDecorator.java | 71 +++++++++++++++++++ .../AddRoleBindingResourceDecorator.java | 13 ++-- .../deployment/ClusterRoleBindingConfig.java | 42 +++++++++++ .../kubernetes/deployment/Constants.java | 1 + .../deployment/KubernetesCommonHelper.java | 46 +++++++++--- .../kubernetes/deployment/RbacConfig.java | 6 ++ .../KubernetesWithRbacFullTest.java | 14 ++++ .../kubernetes-with-rbac-full.properties | 6 +- 12 files changed, 238 insertions(+), 67 deletions(-) create mode 100644 extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/RoleRef.java create mode 100644 extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Subject.java create mode 100644 extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddClusterRoleBindingResourceDecorator.java create mode 100644 extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java diff --git a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc index ff8d7d80eea721..09c41151aa4fab 100644 --- a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc +++ b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc @@ -907,7 +907,7 @@ quarkus.kubernetes.rbac.role-bindings.my-role-binding.role-name=my-role <2> Also, the service account "my-service-account" will be generated. <3> And we can configure the generated RoleBinding resource by selecting the role to be used and the subject. -Finally, we can also generate the cluster wide role resource of "ClusterRole" kind as follows: +Finally, we can also generate the cluster wide role resource of "ClusterRole" kind and a "ClusterRoleBinding" resource as follows: [source,properties] ---- @@ -917,11 +917,13 @@ quarkus.kubernetes.rbac.cluster-roles.my-cluster-role.policy-rules.0.resources=d quarkus.kubernetes.rbac.cluster-roles.my-cluster-role.policy-rules.0.verbs=get,watch,list # Bind the ClusterRole "my-cluster-role" with the application service account -quarkus.kubernetes.rbac.role-bindings.my-role-binding.role-name=my-cluster-role <2> +quarkus.kubernetes.rbac.cluster-role-bindings.my-cluster-role-binding.subjects.manager.kind=Group +quarkus.kubernetes.rbac.cluster-role-bindings.my-cluster-role-binding.subjects.manager.api-group=rbac.authorization.k8s.io +quarkus.kubernetes.rbac.cluster-role-bindings.my-cluster-role-binding.role-name=my-cluster-role <2> ---- <1> In this example, the cluster role "my-cluster-role" will be generated with the specified policy rules. -<2> As we have configured only one role, this property is not really necessary. +<2> Because we're configuring a ClusterRoleBinding resource, we assume that the role name links to a ClusterRole resource. === Deploying to Minikube diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java index 0e220489348bd4..0242d90192277b 100644 --- a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesRoleBindingBuildItem.java @@ -79,52 +79,4 @@ public RoleRef getRoleRef() { public Subject[] getSubjects() { return subjects; } - - public static final class RoleRef { - private final boolean clusterWide; - private final String name; - - public RoleRef(String name, boolean clusterWide) { - this.name = name; - this.clusterWide = clusterWide; - } - - public boolean isClusterWide() { - return clusterWide; - } - - public String getName() { - return name; - } - } - - public static final class Subject { - private final String apiGroup; - private final String kind; - private final String name; - private final String namespace; - - public Subject(String apiGroup, String kind, String name, String namespace) { - this.apiGroup = apiGroup; - this.kind = kind; - this.name = name; - this.namespace = namespace; - } - - public String getApiGroup() { - return apiGroup; - } - - public String getKind() { - return kind; - } - - public String getName() { - return name; - } - - public String getNamespace() { - return namespace; - } - } } diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/RoleRef.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/RoleRef.java new file mode 100644 index 00000000000000..f4650b844761d8 --- /dev/null +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/RoleRef.java @@ -0,0 +1,19 @@ +package io.quarkus.kubernetes.spi; + +public class RoleRef { + private final boolean clusterWide; + private final String name; + + public RoleRef(String name, boolean clusterWide) { + this.name = name; + this.clusterWide = clusterWide; + } + + public boolean isClusterWide() { + return clusterWide; + } + + public String getName() { + return name; + } +} diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Subject.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Subject.java new file mode 100644 index 00000000000000..98c6721a567434 --- /dev/null +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/Subject.java @@ -0,0 +1,31 @@ +package io.quarkus.kubernetes.spi; + +public class Subject { + private final String apiGroup; + private final String kind; + private final String name; + private final String namespace; + + public Subject(String apiGroup, String kind, String name, String namespace) { + this.apiGroup = apiGroup; + this.kind = kind; + this.name = name; + this.namespace = namespace; + } + + public String getApiGroup() { + return apiGroup; + } + + public String getKind() { + return kind; + } + + public String getName() { + return name; + } + + public String getNamespace() { + return namespace; + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddClusterRoleBindingResourceDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddClusterRoleBindingResourceDecorator.java new file mode 100644 index 00000000000000..6cb38c8eecec5d --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddClusterRoleBindingResourceDecorator.java @@ -0,0 +1,71 @@ +package io.quarkus.kubernetes.deployment; + +import static io.quarkus.kubernetes.deployment.Constants.CLUSTER_ROLE; +import static io.quarkus.kubernetes.deployment.Constants.CLUSTER_ROLE_BINDING; +import static io.quarkus.kubernetes.deployment.Constants.RBAC_API_GROUP; +import static io.quarkus.kubernetes.deployment.Constants.RBAC_API_VERSION; +import static io.quarkus.kubernetes.deployment.Constants.ROLE; + +import java.util.HashMap; +import java.util.Map; + +import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator; +import io.dekorate.utils.Strings; +import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBindingBuilder; +import io.quarkus.kubernetes.spi.RoleRef; +import io.quarkus.kubernetes.spi.Subject; + +public class AddClusterRoleBindingResourceDecorator extends ResourceProvidingDecorator { + + private final String deploymentName; + private final String name; + private final Map labels; + private final RoleRef roleRef; + private final Subject[] subjects; + + public AddClusterRoleBindingResourceDecorator(String deploymentName, String name, Map labels, + RoleRef roleRef, + Subject... subjects) { + this.deploymentName = deploymentName; + this.name = name; + this.labels = labels; + this.roleRef = roleRef; + this.subjects = subjects; + } + + public void visit(KubernetesListBuilder list) { + if (contains(list, RBAC_API_VERSION, CLUSTER_ROLE_BINDING, name)) { + return; + } + + Map clusterRoleBindingLabels = new HashMap<>(); + clusterRoleBindingLabels.putAll(labels); + getDeploymentMetadata(list, deploymentName) + .map(ObjectMeta::getLabels) + .ifPresent(clusterRoleBindingLabels::putAll); + + ClusterRoleBindingBuilder builder = new ClusterRoleBindingBuilder() + .withNewMetadata() + .withName(name) + .withLabels(clusterRoleBindingLabels) + .endMetadata() + .withNewRoleRef() + .withKind(roleRef.isClusterWide() ? CLUSTER_ROLE : ROLE) + .withName(roleRef.getName()) + .withApiGroup(RBAC_API_GROUP) + .endRoleRef(); + + for (Subject subject : subjects) { + builder.addNewSubject() + .withApiGroup(subject.getApiGroup()) + .withKind(subject.getKind()) + .withName(Strings.defaultIfEmpty(subject.getName(), deploymentName)) + .withNamespace(subject.getNamespace()) + .endSubject(); + } + + list.addToItems(builder.build()); + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleBindingResourceDecorator.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleBindingResourceDecorator.java index 7be1897199680a..4db922a841a59f 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleBindingResourceDecorator.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddRoleBindingResourceDecorator.java @@ -14,19 +14,20 @@ import io.fabric8.kubernetes.api.model.KubernetesListBuilder; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder; -import io.quarkus.kubernetes.spi.KubernetesRoleBindingBuildItem; +import io.quarkus.kubernetes.spi.RoleRef; +import io.quarkus.kubernetes.spi.Subject; public class AddRoleBindingResourceDecorator extends ResourceProvidingDecorator { private final String deploymentName; private final String name; private final Map labels; - private final KubernetesRoleBindingBuildItem.RoleRef roleRef; - private final KubernetesRoleBindingBuildItem.Subject[] subjects; + private final RoleRef roleRef; + private final Subject[] subjects; public AddRoleBindingResourceDecorator(String deploymentName, String name, Map labels, - KubernetesRoleBindingBuildItem.RoleRef roleRef, - KubernetesRoleBindingBuildItem.Subject... subjects) { + RoleRef roleRef, + Subject... subjects) { this.deploymentName = deploymentName; this.name = name; this.labels = labels; @@ -56,7 +57,7 @@ public void visit(KubernetesListBuilder list) { .withApiGroup(RBAC_API_GROUP) .endRoleRef(); - for (KubernetesRoleBindingBuildItem.Subject subject : subjects) { + for (Subject subject : subjects) { builder.addNewSubject() .withApiGroup(subject.getApiGroup()) .withKind(subject.getKind()) diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java new file mode 100644 index 00000000000000..02876949cd20a7 --- /dev/null +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java @@ -0,0 +1,42 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.Map; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class ClusterRoleBindingConfig { + + /** + * Name of the ClusterRoleBinding resource to be generated. If not provided, it will use the application name plus the role + * ref name. + */ + @ConfigItem + public Optional name; + + /** + * Labels to add into the RoleBinding resource. + */ + @ConfigItem + public Map labels; + + /** + * The name of the Role resource to use by the RoleRef element in the generated ClusterRoleBinding resource. + */ + @ConfigItem + public String roleName; + + /** + * If the Role sets in the `role-name` property is cluster wide or not. + */ + @ConfigItem(defaultValue = "true") + public Boolean clusterWide; + + /** + * List of subjects elements to use in the generated ClusterRoleBinding resource. + */ + @ConfigItem + public Map subjects; +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java index 101d3a5ff7a63b..416a9aa97d6050 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java @@ -12,6 +12,7 @@ public final class Constants { public static final String ROLE = "Role"; public static final String CLUSTER_ROLE = "ClusterRole"; public static final String ROLE_BINDING = "RoleBinding"; + public static final String CLUSTER_ROLE_BINDING = "ClusterRoleBinding"; public static final String SERVICE_ACCOUNT = "ServiceAccount"; public static final String DEPLOYMENT_GROUP = "apps"; public static final String DEPLOYMENT_VERSION = "v1"; diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java index ec91ba8b7f9616..85308171dc3ba2 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java @@ -96,6 +96,8 @@ import io.quarkus.kubernetes.spi.KubernetesRoleBindingBuildItem; import io.quarkus.kubernetes.spi.KubernetesRoleBuildItem; import io.quarkus.kubernetes.spi.KubernetesServiceAccountBuildItem; +import io.quarkus.kubernetes.spi.RoleRef; +import io.quarkus.kubernetes.spi.Subject; public class KubernetesCommonHelper { @@ -358,17 +360,17 @@ private static Collection createRbacDecorators(String name, String rbName = rb.getValue().name.orElse(rb.getKey()); RoleBindingConfig roleBinding = rb.getValue(); - List subjects = new ArrayList<>(); + List subjects = new ArrayList<>(); if (roleBinding.subjects.isEmpty()) { requiresServiceAccount = true; - subjects.add(new KubernetesRoleBindingBuildItem.Subject(null, SERVICE_ACCOUNT, + subjects.add(new Subject(null, SERVICE_ACCOUNT, defaultIfEmpty(defaultServiceAccount, config.getServiceAccount().orElse(name)), defaultServiceAccountNamespace)); } else { for (Map.Entry s : roleBinding.subjects.entrySet()) { String subjectName = s.getValue().name.orElse(s.getKey()); SubjectConfig subject = s.getValue(); - subjects.add(new KubernetesRoleBindingBuildItem.Subject(subject.apiGroup.orElse(null), + subjects.add(new Subject(subject.apiGroup.orElse(null), subject.kind, subjectName, subject.namespace.orElse(null))); @@ -384,8 +386,34 @@ private static Collection createRbacDecorators(String name, result.add(new DecoratorBuildItem(target, new AddRoleBindingResourceDecorator(name, rbName, roleBinding.labels, - new KubernetesRoleBindingBuildItem.RoleRef(roleName, clusterWide), - subjects.toArray(new KubernetesRoleBindingBuildItem.Subject[0])))); + new RoleRef(roleName, clusterWide), + subjects.toArray(new Subject[0])))); + } + + // Add cluster role bindings from configuration + for (Map.Entry rb : config.getRbacConfig().clusterRoleBindings.entrySet()) { + String rbName = rb.getValue().name.orElse(rb.getKey()); + ClusterRoleBindingConfig clusterRoleBinding = rb.getValue(); + + List subjects = new ArrayList<>(); + if (clusterRoleBinding.subjects.isEmpty()) { + throw new IllegalStateException("No subjects have been set in the ClusterRoleBinding resource!"); + } + + for (Map.Entry s : clusterRoleBinding.subjects.entrySet()) { + String subjectName = s.getValue().name.orElse(s.getKey()); + SubjectConfig subject = s.getValue(); + subjects.add(new Subject(subject.apiGroup.orElse(null), + subject.kind, + subjectName, + subject.namespace.orElse(null))); + } + + result.add(new DecoratorBuildItem(target, new AddClusterRoleBindingResourceDecorator(name, + rbName, + clusterRoleBinding.labels, + new RoleRef(clusterRoleBinding.roleName, clusterRoleBinding.clusterWide), + subjects.toArray(new Subject[0])))); } // if no role bindings were created, then automatically create one if: @@ -396,8 +424,8 @@ private static Collection createRbacDecorators(String name, result.add(new DecoratorBuildItem(target, new AddRoleBindingResourceDecorator(name, name, Collections.emptyMap(), - new KubernetesRoleBindingBuildItem.RoleRef(defaultRoleName, defaultClusterWide), - new KubernetesRoleBindingBuildItem.Subject(null, SERVICE_ACCOUNT, + new RoleRef(defaultRoleName, defaultClusterWide), + new Subject(null, SERVICE_ACCOUNT, defaultIfEmpty(defaultServiceAccount, config.getServiceAccount().orElse(name)), defaultServiceAccountNamespace)))); } else if (kubernetesClientRequiresRbacGeneration) { @@ -407,8 +435,8 @@ private static Collection createRbacDecorators(String name, result.add(new DecoratorBuildItem(target, new AddRoleBindingResourceDecorator(name, name + "-" + DEFAULT_ROLE_NAME_VIEW, Collections.emptyMap(), - new KubernetesRoleBindingBuildItem.RoleRef(DEFAULT_ROLE_NAME_VIEW, true), - new KubernetesRoleBindingBuildItem.Subject(null, SERVICE_ACCOUNT, + new RoleRef(DEFAULT_ROLE_NAME_VIEW, true), + new Subject(null, SERVICE_ACCOUNT, defaultIfEmpty(defaultServiceAccount, config.getServiceAccount().orElse(name)), defaultServiceAccountNamespace)))); } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RbacConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RbacConfig.java index e47638d2734422..20fc8e72b07da9 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RbacConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RbacConfig.java @@ -31,4 +31,10 @@ public class RbacConfig { */ @ConfigItem Map roleBindings; + + /** + * List of cluster role bindings to generate. + */ + @ConfigItem + Map clusterRoleBindings; } diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithRbacFullTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithRbacFullTest.java index e93f96d6054183..41bdd6c3e594f0 100644 --- a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithRbacFullTest.java +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithRbacFullTest.java @@ -19,6 +19,7 @@ import io.fabric8.kubernetes.api.model.ServiceAccount; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.rbac.ClusterRole; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; import io.fabric8.kubernetes.api.model.rbac.Role; import io.fabric8.kubernetes.api.model.rbac.RoleBinding; import io.fabric8.kubernetes.api.model.rbac.Subject; @@ -95,6 +96,15 @@ public void assertGeneratedResources() throws IOException { assertEquals("ServiceAccount", subject.getKind()); assertEquals("user", subject.getName()); assertEquals("projectc", subject.getNamespace()); + + // cluster role binding + ClusterRoleBinding clusterRoleBinding = getClusterRoleBindingByName(kubernetesList, "my-cluster-role-binding"); + assertEquals("secret-reader", clusterRoleBinding.getRoleRef().getName()); + assertEquals("ClusterRole", clusterRoleBinding.getRoleRef().getKind()); + Subject clusterSubject = clusterRoleBinding.getSubjects().get(0); + assertEquals("Group", clusterSubject.getKind()); + assertEquals("manager", clusterSubject.getName()); + assertEquals("rbac.authorization.k8s.io", clusterSubject.getApiGroup()); } private int lastIndexOfKind(String content, String... kinds) { @@ -132,6 +142,10 @@ private RoleBinding getRoleBindingByName(List kubernetesList, Strin return getResourceByName(kubernetesList, RoleBinding.class, rbName); } + private ClusterRoleBinding getClusterRoleBindingByName(List kubernetesList, String rbName) { + return getResourceByName(kubernetesList, ClusterRoleBinding.class, rbName); + } + private T getResourceByName(List kubernetesList, Class clazz, String name) { Optional resource = kubernetesList.stream() .filter(r -> r.getMetadata().getName().equals(name)) diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-rbac-full.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-rbac-full.properties index 5fe2b89c3c3a78..e87936940e556d 100644 --- a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-rbac-full.properties +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/kubernetes-with-rbac-full.properties @@ -15,4 +15,8 @@ quarkus.kubernetes.rbac.service-accounts.user.namespace=projectc quarkus.kubernetes.rbac.role-bindings.my-role-binding.subjects.user.kind=ServiceAccount quarkus.kubernetes.rbac.role-bindings.my-role-binding.subjects.user.namespace=projectc quarkus.kubernetes.rbac.role-bindings.my-role-binding.role-name=pod-writer -quarkus.kubernetes.rbac.role-bindings.my-role-binding.cluster-wide=false \ No newline at end of file +quarkus.kubernetes.rbac.role-bindings.my-role-binding.cluster-wide=false + +quarkus.kubernetes.rbac.cluster-role-bindings.my-cluster-role-binding.subjects.manager.kind=Group +quarkus.kubernetes.rbac.cluster-role-bindings.my-cluster-role-binding.subjects.manager.api-group=rbac.authorization.k8s.io +quarkus.kubernetes.rbac.cluster-role-bindings.my-cluster-role-binding.role-name=secret-reader \ No newline at end of file