Skip to content

Commit

Permalink
commands/.../cmdutil: deduplicate updateRoleForResource and support C…
Browse files Browse the repository at this point in the history
…lusterRole (#646)
  • Loading branch information
AlexNPavel authored Oct 22, 2018
1 parent 5c50126 commit f7a8fd3
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 141 deletions.
72 changes: 1 addition & 71 deletions commands/operator-sdk/cmd/add/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion commands/operator-sdk/cmd/add/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
114 changes: 114 additions & 0 deletions commands/operator-sdk/cmd/cmdutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
}
70 changes: 1 addition & 69 deletions commands/operator-sdk/cmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
package cmd

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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
}

0 comments on commit f7a8fd3

Please sign in to comment.