api: add CSIProvisionerRBAC functions for the NFS-provisioner

Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
Niels de Vos 2024-01-26 17:00:55 +01:00 committed by mergify[bot]
parent 35da67be45
commit 6b13352c9b
7 changed files with 415 additions and 0 deletions

View File

@ -0,0 +1,48 @@
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-external-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "update", "delete", "patch"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["volumeattachments"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["storage.k8s.io"]
resources: ["volumeattachments/status"]
verbs: ["patch"]
- apiGroups: [""]
resources: ["persistentvolumeclaims/status"]
verbs: ["update", "patch"]
- apiGroups: ["storage.k8s.io"]
resources: ["csinodes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshotclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshotcontents"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshotcontents/status"]
verbs: ["update", "patch"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshots"]
verbs: ["get", "list"]

View File

@ -0,0 +1,13 @@
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: "{{ .ServiceAccount }}-role"
subjects:
- kind: ServiceAccount
name: "{{ .ServiceAccount }}"
namespace: "{{ .Namespace }}"
roleRef:
kind: ClusterRole
name: nfs-external-provisioner-runner
apiGroup: rbac.authorization.k8s.io

View File

@ -0,0 +1,14 @@
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: "{{ .Namespace }}"
name: nfs-external-provisioner-cfg
rules:
# remove this once we stop supporting v1.0.0
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "delete"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "watch", "list", "delete", "update", "create"]

View File

@ -0,0 +1,14 @@
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: "{{ .ServiceAccount }}-role-cfg"
namespace: "{{ .Namespace }}"
subjects:
- kind: ServiceAccount
name: "{{ .ServiceAccount }}"
namespace: "{{ .Namespace }}"
roleRef:
kind: Role
name: nfs-external-provisioner-cfg
apiGroup: rbac.authorization.k8s.io

View File

@ -0,0 +1,220 @@
/*
Copyright 2024 The Ceph-CSI Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package nfs
import (
"bytes"
_ "embed"
"fmt"
"text/template"
"strings"
"github.com/ghodss/yaml"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"github.com/ceph/ceph-csi/api/deploy/kubernetes"
)
//go:embed csi-provisioner-rbac-cr.yaml
var csiProvisionerClusterRole string
//go:embed csi-provisioner-rbac-crb.yaml
var csiProvisionerClusterRoleBinding string
//go:embed csi-provisioner-rbac-r.yaml
var csiProvisionerRole string
//go:embed csi-provisioner-rbac-rb.yaml
var csiProvisionerRoleBinding string
var CSIProvisionerRBACDefaults = kubernetes.CSIProvisionerRBACValues{
Namespace: "default",
ServiceAccount: "nfs-csi-provisioner",
}
// NewCSIProvisionerRBAC takes a driver name from the CSIProvisionerRBACValues
// struct and replaces the value in the template. A CSIProvisionerRBAC object
// is returned which can be used to create permissions for the provisioner in
// the Kubernetes cluster.
func NewCSIProvisionerRBAC(values kubernetes.CSIProvisionerRBACValues) (kubernetes.CSIProvisionerRBAC, error) {
sa := &corev1.ServiceAccount{}
sa.Namespace = values.Namespace
sa.Name = values.ServiceAccount
cr, err := newClusterRole(values)
if err != nil {
return nil, err
}
crb, err := newClusterRoleBinding(values)
if err != nil {
return nil, err
}
r, err := newRole(values)
if err != nil {
return nil, err
}
rb, err := newRoleBinding(values)
if err != nil {
return nil, err
}
return &csiProvisionerRBAC{
serviceAccount: sa,
clusterRole: cr,
clusterRoleBinding: crb,
role: r,
roleBinding: rb,
}, nil
}
func NewCSIProvisionerRBACYAML(values kubernetes.CSIProvisionerRBACValues) (string, error) {
docs := []string{}
data, err := newYAML("csiProvisionerClusterRole", csiProvisionerClusterRole, values)
if err != nil {
return "", err
}
docs = append(docs, data)
data, err = newYAML("csiProvisionerClusterRoleBinding", csiProvisionerClusterRoleBinding, values)
if err != nil {
return "", err
}
docs = append(docs, data)
data, err = newYAML("csiProvisionerRole", csiProvisionerRole, values)
if err != nil {
return "", err
}
docs = append(docs, data)
data, err = newYAML("csiProvisionerRoleBinding", csiProvisionerRoleBinding, values)
if err != nil {
return "", err
}
docs = append(docs, data)
return strings.Join(docs, "\n"), nil
}
func newYAML(name, data string, values kubernetes.CSIProvisionerRBACValues) (string, error) {
var buf bytes.Buffer
tmpl, err := template.New(name).Parse(data)
if err != nil {
return "", fmt.Errorf("failed to parse template: %w", err)
}
err = tmpl.Execute(&buf, values)
if err != nil {
return "", fmt.Errorf("failed to replace values in template: %w", err)
}
return buf.String(), nil
}
func newClusterRole(values kubernetes.CSIProvisionerRBACValues) (*rbacv1.ClusterRole, error) {
data, err := newYAML("csiProvisionerClusterRole", csiProvisionerClusterRole, values)
if err != nil {
return nil, err
}
cr := &rbacv1.ClusterRole{}
err = yaml.Unmarshal([]byte(data), cr)
if err != nil {
return nil, fmt.Errorf("failed convert YAML to %T: %w", cr, err)
}
return cr, nil
}
func newClusterRoleBinding(values kubernetes.CSIProvisionerRBACValues) (*rbacv1.ClusterRoleBinding, error) {
data, err := newYAML("csiProvisionerClusterRoleBinding", csiProvisionerClusterRoleBinding, values)
if err != nil {
return nil, err
}
crb := &rbacv1.ClusterRoleBinding{}
err = yaml.Unmarshal([]byte(data), crb)
if err != nil {
return nil, fmt.Errorf("failed convert YAML to %T: %w", crb, err)
}
return crb, nil
}
func newRole(values kubernetes.CSIProvisionerRBACValues) (*rbacv1.Role, error) {
data, err := newYAML("csiProvisionerRole", csiProvisionerRole, values)
if err != nil {
return nil, err
}
r := &rbacv1.Role{}
err = yaml.Unmarshal([]byte(data), r)
if err != nil {
return nil, fmt.Errorf("failed convert YAML to %T: %w", r, err)
}
return r, nil
}
func newRoleBinding(values kubernetes.CSIProvisionerRBACValues) (*rbacv1.RoleBinding, error) {
data, err := newYAML("csiProvisionerRoleBinding", csiProvisionerRoleBinding, values)
if err != nil {
return nil, err
}
rb := &rbacv1.RoleBinding{}
err = yaml.Unmarshal([]byte(data), rb)
if err != nil {
return nil, fmt.Errorf("failed convert YAML to %T: %w", rb, err)
}
return rb, nil
}
type csiProvisionerRBAC struct {
serviceAccount *corev1.ServiceAccount
clusterRole *rbacv1.ClusterRole
clusterRoleBinding *rbacv1.ClusterRoleBinding
role *rbacv1.Role
roleBinding *rbacv1.RoleBinding
}
func (rbac *csiProvisionerRBAC) GetServiceAccount() *corev1.ServiceAccount {
return rbac.serviceAccount
}
func (rbac *csiProvisionerRBAC) GetClusterRole() *rbacv1.ClusterRole {
return rbac.clusterRole
}
func (rbac *csiProvisionerRBAC) GetClusterRoleBinding() *rbacv1.ClusterRoleBinding {
return rbac.clusterRoleBinding
}
func (rbac *csiProvisionerRBAC) GetRole() *rbacv1.Role {
return rbac.role
}
func (rbac *csiProvisionerRBAC) GetRoleBinding() *rbacv1.RoleBinding {
return rbac.roleBinding
}

View File

@ -0,0 +1,67 @@
/*
Copyright 2024 The Ceph-CSI Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package nfs
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestNewCSIProvisionerRBAC(t *testing.T) {
rbac, err := NewCSIProvisionerRBAC(CSIProvisionerRBACDefaults)
require.NoError(t, err)
require.NotNil(t, rbac)
require.Equal(t, rbac.GetServiceAccount().Namespace, CSIProvisionerRBACDefaults.Namespace)
require.Equal(t, rbac.GetServiceAccount().Name, CSIProvisionerRBACDefaults.ServiceAccount)
}
func TestNewCSIProvisionerRBACYAML(t *testing.T) {
yaml, err := NewCSIProvisionerRBACYAML(CSIProvisionerRBACDefaults)
require.NoError(t, err)
require.NotEqual(t, "", yaml)
}
func TestNewClusterRole(t *testing.T) {
cr, err := newClusterRole(CSIProvisionerRBACDefaults)
require.NoError(t, err)
require.NotNil(t, cr)
}
func TestNewClusterRoleBinding(t *testing.T) {
crb, err := newClusterRoleBinding(CSIProvisionerRBACDefaults)
require.NoError(t, err)
require.NotNil(t, crb)
}
func TestNewRole(t *testing.T) {
r, err := newRole(CSIProvisionerRBACDefaults)
require.NoError(t, err)
require.NotNil(t, r)
}
func TestNewRoleBinding(t *testing.T) {
rb, err := newRoleBinding(CSIProvisionerRBACDefaults)
require.NoError(t, err)
require.NotNil(t, rb)
}

View File

@ -0,0 +1,39 @@
/*
Copyright 2024 The Ceph-CSI Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubernetes
import (
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
)
// CSIProvisionerRBAC describes the interface that is provided by different
// provisioner backends to get details about the required RBAC.
type CSIProvisionerRBAC interface {
GetServiceAccount() *corev1.ServiceAccount
GetClusterRole() *rbacv1.ClusterRole
GetClusterRoleBinding() *rbacv1.ClusterRoleBinding
GetRole() *rbacv1.Role
GetRoleBinding() *rbacv1.RoleBinding
}
// CSIProvisionerRBACValues contains values that can be passed to
// NewCSIProvisionerRBAC() functions for different provisioner backends.
type CSIProvisionerRBACValues struct {
Namespace string
ServiceAccount string
}