diff --git a/pkg/rbac/parser.go b/pkg/rbac/parser.go index c8bbe9d54..052533843 100644 --- a/pkg/rbac/parser.go +++ b/pkg/rbac/parser.go @@ -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, + }, + 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 } @@ -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)) } diff --git a/pkg/rbac/parser_integration_test.go b/pkg/rbac/parser_integration_test.go index a56c1ec6a..945162ffa 100644 --- a/pkg/rbac/parser_integration_test.go +++ b/pkg/rbac/parser_integration_test.go @@ -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 @@ -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)) + } + } }) } }) diff --git a/pkg/rbac/testdata/role_binding.yaml b/pkg/rbac/testdata/role_binding.yaml new file mode 100644 index 000000000..4d91d8d32 --- /dev/null +++ b/pkg/rbac/testdata/role_binding.yaml @@ -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