Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Generate role binding based on generated role #839

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 97 additions & 3 deletions pkg/rbac/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,107 @@ func GenerateRoles(ctx *genall.GenerationContext, roleName string) ([]interface{
return objs, nil
}

// GenerateRoleBindings generate a slice of objs representing either a ClusterRoleBinding or a RoleBinding object
// according to the given roles
func GenerateRoleBindings(roles []interface{}, roleName string) ([]interface{}, error) {
var objs []interface{}
for _, role := range roles {
if role, ok := role.(rbacv1.ClusterRole); ok {
objs = append(objs, rbacv1.ClusterRoleBinding{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRoleBinding",
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%sbinding", roleName),
},
Subjects: []rbacv1.Subject{
{
// keep consistent with kubebuilder's default
Kind: rbacv1.ServiceAccountKind,
Name: "controller-manager",
Namespace: "system",
},
},
RoleRef: rbacv1.RoleRef{
Kind: role.Kind,
Name: role.Name,
APIGroup: rbacv1.SchemeGroupVersion.Group,
},
})
}

if role, ok := role.(rbacv1.Role); ok {
objs = append(objs, rbacv1.RoleBinding{
TypeMeta: metav1.TypeMeta{
Kind: "RoleBinding",
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%sbinding", roleName),
Namespace: role.Namespace,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I just got into the issue you refer to in this PR. Is the kustomize namespace replacement considered here? If I manually generate a role like this in my case, the namespace would be replaced with the one set in the main kustomization.yaml file.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 'controller-tools' is mainly used to parse the annotations of kubebuilder and generate the manifest, no kustomization.

Ref the kubebuilder, it do have kustomization, https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugins/common/kustomize/v2/scaffolds/internal/templates/config/rbac/kustomization.go.

If we want to overwrite the namespace, i prefer to use an additional config of https://kubectl.docs.kubernetes.io/references/kustomize/builtins/#_namespacetransformer.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you stating that you would change how the namespace is set by the projects consuming controller-runtime (kube-builder, operator-sdk, ...) as a follow-up that uses something like unsetOnly: true and setRoleBindingSubjects: allServiceAccounts?

},
Subjects: []rbacv1.Subject{
{
// keep consistent with kubebuilder's default
Kind: rbacv1.ServiceAccountKind,
Name: "controller-manager",
Namespace: "system",
},
},
RoleRef: rbacv1.RoleRef{
Kind: role.Kind,
Name: role.Name,
APIGroup: rbacv1.SchemeGroupVersion.Group,
},
})
}
}
return objs, nil
}

// Generate generates RBAC manifests.
func (g Generator) Generate(ctx *genall.GenerationContext) error {
objs, err := GenerateRoles(ctx, g.RoleName)
roles, err := GenerateRoles(ctx, g.RoleName)
if err != nil {
return err
}

if len(objs) == 0 {
err = g.writeRoles(ctx, roles)
if err != nil {
return err
}

roleBindings, err := GenerateRoleBindings(roles, g.RoleName)
if err != nil {
return err
}

return g.writeRoleBindings(ctx, roleBindings)
}

// writeRoles writes the given roles to the file "role.yaml".
func (g Generator) writeRoles(ctx *genall.GenerationContext, roles []interface{}) error {
if len(roles) == 0 {
return nil
}

var headerText string
if g.HeaderFile != "" {
headerBytes, err := ctx.ReadFile(g.HeaderFile)
if err != nil {
return err
}
headerText = string(headerBytes)
}
headerText = strings.ReplaceAll(headerText, " YEAR", " "+g.Year)

return ctx.WriteYAML("role.yaml", headerText, roles, genall.WithTransform(genall.TransformRemoveCreationTimestamp))
}

// writeRoleBindings writes the given roleBindings to the file "role_binding.yaml".
func (g Generator) writeRoleBindings(ctx *genall.GenerationContext, roleBindings []interface{}) error {
if len(roleBindings) == 0 {
return nil
}

Expand All @@ -279,5 +373,5 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
}
headerText = strings.ReplaceAll(headerText, " YEAR", " "+g.Year)

return ctx.WriteYAML("role.yaml", headerText, objs, genall.WithTransform(genall.TransformRemoveCreationTimestamp))
return ctx.WriteYAML("role_binding.yaml", headerText, roleBindings, genall.WithTransform(genall.TransformRemoveCreationTimestamp))
}
33 changes: 28 additions & 5 deletions pkg/rbac/parser_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ var _ = Describe("ClusterRole generated by the RBAC Generator", func() {
Roots: pkgs,
}

By("generating a ClusterRole")
objs, err := rbac.GenerateRoles(ctx, "manager-role")
By("generating ClusterRoles and Roles")
roles, err := rbac.GenerateRoles(ctx, "manager-role")
Expect(err).NotTo(HaveOccurred())

By("loading the desired YAML")
By("loading the desired role YAML")
expectedFile, err := ioutil.ReadFile("role.yaml")
Expect(err).NotTo(HaveOccurred())

By("parsing the desired YAML")
By("parsing the desired role YAML")
for i, expectedRoleBytes := range bytes.Split(expectedFile, []byte("\n---\n"))[1:] {
By(fmt.Sprintf("comparing the generated Role and expected Role (Pair %d)", i))
obj := objs[i]
obj := roles[i]
switch obj := obj.(type) {
case rbacv1.ClusterRole:
var expectedClusterRole rbacv1.ClusterRole
Expand All @@ -68,6 +68,29 @@ var _ = Describe("ClusterRole generated by the RBAC Generator", func() {
}
}

By("generating ClusterRoleBindings and RoleBindings")
roleBindings, err := rbac.GenerateRoleBindings(roles, "manager-role")
Expect(err).NotTo(HaveOccurred())

By("loading the desired role binding YAML")
expectedFile, err = ioutil.ReadFile("role_binding.yaml")
Expect(err).NotTo(HaveOccurred())

By("parsing the desired role binding YAML")
for i, expectedRoleBindingBytes := range bytes.Split(expectedFile, []byte("\n---\n"))[1:] {
By(fmt.Sprintf("comparing the generated RoleBinding and expected RoleBinding (Pair %d)", i))
obj := roleBindings[i]
switch obj := obj.(type) {
case rbacv1.ClusterRoleBinding:
var expectedClusterRoleBinding rbacv1.ClusterRoleBinding
Expect(yaml.Unmarshal(expectedRoleBindingBytes, &expectedClusterRoleBinding)).To(Succeed())
Expect(obj).To(Equal(expectedClusterRoleBinding), "type not as expected, check pkg/rbac/testdata/README.md for more details.\n\nDiff:\n\n%s", cmp.Diff(obj, expectedClusterRoleBinding))
default:
var expectedRoleBinding rbacv1.RoleBinding
Expect(yaml.Unmarshal(expectedRoleBindingBytes, &expectedRoleBinding)).To(Succeed())
Expect(obj).To(Equal(expectedRoleBinding), "type not as expected, check pkg/rbac/testdata/README.md for more details.\n\nDiff:\n\n%s", cmp.Diff(obj, expectedRoleBinding))
}
}
})
}
})
44 changes: 44 additions & 0 deletions pkg/rbac/testdata/role_binding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: manager-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: manager-rolebinding
namespace: park
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: manager-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: manager-rolebinding
namespace: zoo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: manager-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system
Loading