From f7a8fd3d76e5d34120c272dd3b0fd4b75e6e8279 Mon Sep 17 00:00:00 2001 From: Alexander Pavel Date: Mon, 22 Oct 2018 11:16:07 -0700 Subject: [PATCH] commands/.../cmdutil: deduplicate updateRoleForResource and support ClusterRole (#646) --- commands/operator-sdk/cmd/add/api.go | 72 +------------- commands/operator-sdk/cmd/add/crd.go | 2 +- commands/operator-sdk/cmd/cmdutil/util.go | 114 ++++++++++++++++++++++ commands/operator-sdk/cmd/new.go | 70 +------------ 4 files changed, 117 insertions(+), 141 deletions(-) diff --git a/commands/operator-sdk/cmd/add/api.go b/commands/operator-sdk/cmd/add/api.go index 34dcde12d5..ce8e1ccaa3 100644 --- a/commands/operator-sdk/cmd/add/api.go +++ b/commands/operator-sdk/cmd/add/api.go @@ -15,22 +15,14 @@ package add import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" "log" - "path/filepath" "github.com/operator-framework/operator-sdk/commands/operator-sdk/cmd/cmdutil" "github.com/operator-framework/operator-sdk/commands/operator-sdk/cmd/generate" "github.com/operator-framework/operator-sdk/pkg/scaffold" "github.com/operator-framework/operator-sdk/pkg/scaffold/input" - "github.com/ghodss/yaml" "github.com/spf13/cobra" - rbacv1 "k8s.io/api/rbac/v1" - cgoscheme "k8s.io/client-go/kubernetes/scheme" ) var ( @@ -99,72 +91,10 @@ func apiRun(cmd *cobra.Command, args []string) { } // update deploy/role.yaml for the given resource r. - if err := updateRoleForResource(r, absProjectPath); err != nil { + if err := cmdutil.UpdateRoleForResource(r, absProjectPath); err != nil { log.Fatalf("failed to update the RBAC manifest for the resource (%v, %v): %v", r.APIVersion, r.Kind, err) } // Run k8s codegen for deepcopy generate.K8sCodegen() } - -func updateRoleForResource(r *scaffold.Resource, absProjectPath string) error { - // append rbac rule to deploy/role.yaml - roleFilePath := filepath.Join(absProjectPath, "deploy", "role.yaml") - roleYAML, err := ioutil.ReadFile(roleFilePath) - if err != nil { - return fmt.Errorf("failed to read role manifest %v: %v", roleFilePath, err) - } - obj, _, err := cgoscheme.Codecs.UniversalDeserializer().Decode(roleYAML, nil, nil) - if err != nil { - return fmt.Errorf("failed to decode role manifest %v: %v", roleFilePath, err) - } - switch role := obj.(type) { - // TODO: use rbac/v1. - case *rbacv1.Role: - pr := &rbacv1.PolicyRule{} - apiGroupFound := false - for i := range role.Rules { - if role.Rules[i].APIGroups[0] == r.FullGroup { - apiGroupFound = true - pr = &role.Rules[i] - break - } - } - // check if the resource already exists - for _, resource := range pr.Resources { - if resource == r.Resource { - log.Printf("deploy/role.yaml RBAC rules already up to date for the resource (%v, %v)", r.APIVersion, r.Kind) - return nil - } - } - - pr.Resources = append(pr.Resources, r.Resource) - // create a new apiGroup if not found. - if !apiGroupFound { - pr.APIGroups = []string{r.FullGroup} - // Using "*" to allow access to the resource and all its subresources e.g "memcacheds" and "memcacheds/finalizers" - // https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement - pr.Resources = []string{"*"} - pr.Verbs = []string{"*"} - role.Rules = append(role.Rules, *pr) - } - // update role.yaml - d, err := json.Marshal(&role) - if err != nil { - return fmt.Errorf("failed to marshal role(%+v): %v", role, err) - } - m := &map[string]interface{}{} - err = yaml.Unmarshal(d, m) - data, err := yaml.Marshal(m) - if err != nil { - return fmt.Errorf("failed to marshal role(%+v): %v", role, err) - } - if err := ioutil.WriteFile(roleFilePath, data, cmdutil.DefaultFileMode); err != nil { - return fmt.Errorf("failed to update %v: %v", roleFilePath, err) - } - default: - return errors.New("failed to parse role.yaml as a role") - } - // not reachable - return nil -} diff --git a/commands/operator-sdk/cmd/add/crd.go b/commands/operator-sdk/cmd/add/crd.go index e56319534e..185242a3a2 100644 --- a/commands/operator-sdk/cmd/add/crd.go +++ b/commands/operator-sdk/cmd/add/crd.go @@ -83,7 +83,7 @@ func crdFunc(cmd *cobra.Command, args []string) { } // update deploy/role.yaml for the given resource r. - if err := updateRoleForResource(resource, cfg.AbsProjectPath); err != nil { + if err := cmdutil.UpdateRoleForResource(resource, cfg.AbsProjectPath); err != nil { log.Fatalf("failed to update the RBAC manifest for the resource (%v, %v): %v", resource.APIVersion, resource.Kind, err) } } diff --git a/commands/operator-sdk/cmd/cmdutil/util.go b/commands/operator-sdk/cmd/cmdutil/util.go index 8a18e70014..64d2c1a8f5 100644 --- a/commands/operator-sdk/cmd/cmdutil/util.go +++ b/commands/operator-sdk/cmd/cmdutil/util.go @@ -15,10 +15,20 @@ package cmdutil import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" "log" "os" "path/filepath" "strings" + + "github.com/operator-framework/operator-sdk/pkg/scaffold" + + yaml "gopkg.in/yaml.v2" + rbacv1 "k8s.io/api/rbac/v1" + cgoscheme "k8s.io/client-go/kubernetes/scheme" ) const ( @@ -93,3 +103,107 @@ func GetOperatorType() OperatorType { } return OperatorTypeGo } + +func UpdateRoleForResource(r *scaffold.Resource, absProjectPath string) error { + // append rbac rule to deploy/role.yaml + roleFilePath := filepath.Join(absProjectPath, "deploy", "role.yaml") + roleYAML, err := ioutil.ReadFile(roleFilePath) + if err != nil { + return fmt.Errorf("failed to read role manifest %v: %v", roleFilePath, err) + } + obj, _, err := cgoscheme.Codecs.UniversalDeserializer().Decode(roleYAML, nil, nil) + if err != nil { + return fmt.Errorf("failed to decode role manifest %v: %v", roleFilePath, err) + } + switch role := obj.(type) { + // TODO: use rbac/v1. + case *rbacv1.Role: + pr := &rbacv1.PolicyRule{} + apiGroupFound := false + for i := range role.Rules { + if role.Rules[i].APIGroups[0] == r.FullGroup { + apiGroupFound = true + pr = &role.Rules[i] + break + } + } + // check if the resource already exists + for _, resource := range pr.Resources { + if resource == r.Resource { + log.Printf("deploy/role.yaml RBAC rules already up to date for the resource (%v, %v)", r.APIVersion, r.Kind) + return nil + } + } + + pr.Resources = append(pr.Resources, r.Resource) + // create a new apiGroup if not found. + if !apiGroupFound { + pr.APIGroups = []string{r.FullGroup} + // Using "*" to allow access to the resource and all its subresources e.g "memcacheds" and "memcacheds/finalizers" + // https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement + pr.Resources = []string{"*"} + pr.Verbs = []string{"*"} + role.Rules = append(role.Rules, *pr) + } + // update role.yaml + d, err := json.Marshal(&role) + if err != nil { + return fmt.Errorf("failed to marshal role(%+v): %v", role, err) + } + m := &map[string]interface{}{} + err = yaml.Unmarshal(d, m) + data, err := yaml.Marshal(m) + if err != nil { + return fmt.Errorf("failed to marshal role(%+v): %v", role, err) + } + if err := ioutil.WriteFile(roleFilePath, data, DefaultFileMode); err != nil { + return fmt.Errorf("failed to update %v: %v", roleFilePath, err) + } + case *rbacv1.ClusterRole: + pr := &rbacv1.PolicyRule{} + apiGroupFound := false + for i := range role.Rules { + if role.Rules[i].APIGroups[0] == r.FullGroup { + apiGroupFound = true + pr = &role.Rules[i] + break + } + } + // check if the resource already exists + for _, resource := range pr.Resources { + if resource == r.Resource { + log.Printf("deploy/role.yaml RBAC rules already up to date for the resource (%v, %v)", r.APIVersion, r.Kind) + return nil + } + } + + pr.Resources = append(pr.Resources, r.Resource) + // create a new apiGroup if not found. + if !apiGroupFound { + pr.APIGroups = []string{r.FullGroup} + // Using "*" to allow access to the resource and all its subresources e.g "memcacheds" and "memcacheds/finalizers" + // https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement + pr.Resources = []string{"*"} + pr.Verbs = []string{"*"} + role.Rules = append(role.Rules, *pr) + } + // update role.yaml + d, err := json.Marshal(&role) + if err != nil { + return fmt.Errorf("failed to marshal role(%+v): %v", role, err) + } + m := &map[string]interface{}{} + err = yaml.Unmarshal(d, m) + data, err := yaml.Marshal(m) + if err != nil { + return fmt.Errorf("failed to marshal role(%+v): %v", role, err) + } + if err := ioutil.WriteFile(roleFilePath, data, DefaultFileMode); err != nil { + return fmt.Errorf("failed to update %v: %v", roleFilePath, err) + } + default: + return errors.New("failed to parse role.yaml as a role") + } + // not reachable + return nil +} diff --git a/commands/operator-sdk/cmd/new.go b/commands/operator-sdk/cmd/new.go index 8990ed16e9..c918854f85 100644 --- a/commands/operator-sdk/cmd/new.go +++ b/commands/operator-sdk/cmd/new.go @@ -15,8 +15,6 @@ package cmd import ( - "encoding/json" - "errors" "fmt" "io/ioutil" "log" @@ -31,9 +29,6 @@ import ( "github.com/operator-framework/operator-sdk/pkg/scaffold/input" "github.com/spf13/cobra" - yaml "gopkg.in/yaml.v2" - rbacv1 "k8s.io/api/rbac/v1" - cgoscheme "k8s.io/client-go/kubernetes/scheme" ) func NewNewCmd() *cobra.Command { @@ -221,7 +216,7 @@ func doAnsibleScaffold() { } // update deploy/role.yaml for the given resource r. - if err := updateRoleForResource(resource, cfg.AbsProjectPath); err != nil { + if err := cmdutil.UpdateRoleForResource(resource, cfg.AbsProjectPath); err != nil { log.Fatalf("failed to update the RBAC manifest for the resource (%v, %v): %v", resource.APIVersion, resource.Kind, err) } } @@ -307,66 +302,3 @@ func initGit() { execCmd(os.Stdout, "git", "commit", "-q", "-m", "INITIAL COMMIT") fmt.Fprintln(os.Stdout, "Run git init done") } - -// Copied from add/api.go command -func updateRoleForResource(r *scaffold.Resource, absProjectPath string) error { - // append rbac rule to deploy/role.yaml - roleFilePath := filepath.Join(absProjectPath, "deploy", "role.yaml") - roleYAML, err := ioutil.ReadFile(roleFilePath) - if err != nil { - return fmt.Errorf("failed to read role manifest %v: %v", roleFilePath, err) - } - obj, _, err := cgoscheme.Codecs.UniversalDeserializer().Decode(roleYAML, nil, nil) - if err != nil { - return fmt.Errorf("failed to decode role manifest %v: %v", roleFilePath, err) - } - switch role := obj.(type) { - // TODO: handle cluster roles for operators to watch every namespace. - case *rbacv1.Role: - pr := &rbacv1.PolicyRule{} - apiGroupFound := false - for i := range role.Rules { - if role.Rules[i].APIGroups[0] == r.FullGroup { - apiGroupFound = true - pr = &role.Rules[i] - break - } - } - // check if the resource already exists - for _, resource := range pr.Resources { - if resource == r.Resource { - log.Printf("deploy/role.yaml RBAC rules already up to date for the resource (%v, %v)", r.APIVersion, r.Kind) - return nil - } - } - - pr.Resources = append(pr.Resources, r.Resource) - // create a new apiGroup if not found. - if !apiGroupFound { - pr.APIGroups = []string{r.FullGroup} - // Using "*" to allow access to the resource and all its subresources e.g "memcacheds" and "memcacheds/finalizers" - // https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement - pr.Resources = []string{"*"} - pr.Verbs = []string{"*"} - role.Rules = append(role.Rules, *pr) - } - // update role.yaml - d, err := json.Marshal(&role) - if err != nil { - return fmt.Errorf("failed to marshal role(%+v): %v", role, err) - } - m := &map[string]interface{}{} - err = yaml.Unmarshal(d, m) - data, err := yaml.Marshal(m) - if err != nil { - return fmt.Errorf("failed to marshal role(%+v): %v", role, err) - } - if err := ioutil.WriteFile(roleFilePath, data, cmdutil.DefaultFileMode); err != nil { - return fmt.Errorf("failed to update %v: %v", roleFilePath, err) - } - default: - return errors.New("failed to parse role.yaml as a role") - } - // not reachable - return nil -}