github.com/theishshah/operator-sdk@v0.6.0/pkg/scaffold/role.go (about) 1 // Copyright 2018 The Operator-SDK Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package scaffold 16 17 import ( 18 "encoding/json" 19 "errors" 20 "fmt" 21 "io/ioutil" 22 "path/filepath" 23 24 "github.com/operator-framework/operator-sdk/internal/util/fileutil" 25 "github.com/operator-framework/operator-sdk/pkg/scaffold/input" 26 27 log "github.com/sirupsen/logrus" 28 yaml "gopkg.in/yaml.v2" 29 rbacv1 "k8s.io/api/rbac/v1" 30 cgoscheme "k8s.io/client-go/kubernetes/scheme" 31 ) 32 33 const RoleYamlFile = "role.yaml" 34 35 type Role struct { 36 input.Input 37 38 IsClusterScoped bool 39 } 40 41 func (s *Role) GetInput() (input.Input, error) { 42 if s.Path == "" { 43 s.Path = filepath.Join(DeployDir, RoleYamlFile) 44 } 45 s.TemplateBody = roleTemplate 46 return s.Input, nil 47 } 48 49 func UpdateRoleForResource(r *Resource, absProjectPath string) error { 50 // append rbac rule to deploy/role.yaml 51 roleFilePath := filepath.Join(absProjectPath, DeployDir, RoleYamlFile) 52 roleYAML, err := ioutil.ReadFile(roleFilePath) 53 if err != nil { 54 return fmt.Errorf("failed to read role manifest %v: %v", roleFilePath, err) 55 } 56 obj, _, err := cgoscheme.Codecs.UniversalDeserializer().Decode(roleYAML, nil, nil) 57 if err != nil { 58 return fmt.Errorf("failed to decode role manifest %v: %v", roleFilePath, err) 59 } 60 switch role := obj.(type) { 61 // TODO: use rbac/v1. 62 case *rbacv1.Role: 63 pr := &rbacv1.PolicyRule{} 64 apiGroupFound := false 65 for i := range role.Rules { 66 if role.Rules[i].APIGroups[0] == r.FullGroup { 67 apiGroupFound = true 68 pr = &role.Rules[i] 69 break 70 } 71 } 72 // check if the resource already exists 73 for _, resource := range pr.Resources { 74 if resource == r.Resource { 75 log.Infof("RBAC rules in deploy/role.yaml already up to date for the resource (%v, %v)", r.APIVersion, r.Kind) 76 return nil 77 } 78 } 79 80 pr.Resources = append(pr.Resources, r.Resource) 81 // create a new apiGroup if not found. 82 if !apiGroupFound { 83 pr.APIGroups = []string{r.FullGroup} 84 // Using "*" to allow access to the resource and all its subresources e.g "memcacheds" and "memcacheds/finalizers" 85 // https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement 86 pr.Resources = []string{"*"} 87 pr.Verbs = []string{"*"} 88 role.Rules = append(role.Rules, *pr) 89 } 90 // update role.yaml 91 d, err := json.Marshal(&role) 92 if err != nil { 93 return fmt.Errorf("failed to marshal role(%+v): %v", role, err) 94 } 95 m := &map[string]interface{}{} 96 if err = yaml.Unmarshal(d, m); err != nil { 97 return fmt.Errorf("failed to unmarshal role(%+v): %v", role, err) 98 } 99 data, err := yaml.Marshal(m) 100 if err != nil { 101 return fmt.Errorf("failed to marshal role(%+v): %v", role, err) 102 } 103 if err := ioutil.WriteFile(roleFilePath, data, fileutil.DefaultFileMode); err != nil { 104 return fmt.Errorf("failed to update %v: %v", roleFilePath, err) 105 } 106 case *rbacv1.ClusterRole: 107 pr := &rbacv1.PolicyRule{} 108 apiGroupFound := false 109 for i := range role.Rules { 110 if role.Rules[i].APIGroups[0] == r.FullGroup { 111 apiGroupFound = true 112 pr = &role.Rules[i] 113 break 114 } 115 } 116 // check if the resource already exists 117 for _, resource := range pr.Resources { 118 if resource == r.Resource { 119 log.Infof("RBAC rules in deploy/role.yaml already up to date for the resource (%v, %v)", r.APIVersion, r.Kind) 120 return nil 121 } 122 } 123 124 pr.Resources = append(pr.Resources, r.Resource) 125 // create a new apiGroup if not found. 126 if !apiGroupFound { 127 pr.APIGroups = []string{r.FullGroup} 128 // Using "*" to allow access to the resource and all its subresources e.g "memcacheds" and "memcacheds/finalizers" 129 // https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement 130 pr.Resources = []string{"*"} 131 pr.Verbs = []string{"*"} 132 role.Rules = append(role.Rules, *pr) 133 } 134 // update role.yaml 135 d, err := json.Marshal(&role) 136 if err != nil { 137 return fmt.Errorf("failed to marshal role(%+v): %v", role, err) 138 } 139 m := &map[string]interface{}{} 140 err = yaml.Unmarshal(d, m) 141 data, err := yaml.Marshal(m) 142 if err != nil { 143 return fmt.Errorf("failed to marshal role(%+v): %v", role, err) 144 } 145 if err := ioutil.WriteFile(roleFilePath, data, fileutil.DefaultFileMode); err != nil { 146 return fmt.Errorf("failed to update %v: %v", roleFilePath, err) 147 } 148 default: 149 return errors.New("failed to parse role.yaml as a role") 150 } 151 // not reachable 152 return nil 153 } 154 155 const roleTemplate = `kind: {{if .IsClusterScoped}}Cluster{{end}}Role 156 apiVersion: rbac.authorization.k8s.io/v1 157 metadata: 158 name: {{.ProjectName}} 159 rules: 160 - apiGroups: 161 - "" 162 resources: 163 - pods 164 - services 165 - endpoints 166 - persistentvolumeclaims 167 - events 168 - configmaps 169 - secrets 170 verbs: 171 - "*" 172 - apiGroups: 173 - "" 174 resources: 175 - namespaces 176 verbs: 177 - get 178 - apiGroups: 179 - apps 180 resources: 181 - deployments 182 - daemonsets 183 - replicasets 184 - statefulsets 185 verbs: 186 - "*" 187 - apiGroups: 188 - monitoring.coreos.com 189 resources: 190 - servicemonitors 191 verbs: 192 - "get" 193 - "create" 194 - apiGroups: 195 - apps 196 resources: 197 - deployments/finalizers 198 resourceNames: 199 - {{ .ProjectName }} 200 verbs: 201 - "update" 202 `