Skip to content

Commit

Permalink
pipeline: add renderRBAC func
Browse files Browse the repository at this point in the history
Signed-off-by: Xieql <[email protected]>
  • Loading branch information
Xieql committed Dec 1, 2023
1 parent 984b852 commit e6453d0
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 0 deletions.
32 changes: 32 additions & 0 deletions pkg/fleet-manager/backup/render/manifests/rbac.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: "{{ .ServiceAccountName }}"
namespace: "{{ .PipelineNamespace }}"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: "{{ .RoleBindingName }}"
namespace: "{{ .PipelineNamespace }}"
subjects:
- kind: ServiceAccount
name: "{{ .ServiceAccountName }}"
namespace: "{{ .PipelineNamespace }}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-roles # add role for handle EventListener, configmap, secret and so on. `tekton-triggers-eventlistener-roles` is provided by Tekton
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: "{{ .ClusterRoleBindingName }}"
subjects:
- kind: ServiceAccount
name: "{{ .ServiceAccountName }}"
namespace: "{{ .PipelineNamespace }}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-clusterroles # add role for handle pod. `tekton-triggers-eventlistener-clusterroles` is provided by Tekton
45 changes: 45 additions & 0 deletions pkg/fleet-manager/backup/render/rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package render

import (
"fmt"
"io/fs"
)

const (
ServiceAccountNamePrefix = "kurator-pipeline-robot-"
RoleBindingNameSuffix = "-binding"
ClusterRoleBindingNameSuffix = "-clusterbinding"
// RBACTemplateFileName is the name of the RBAC template file.
RBACTemplateFileName = "rbac.tpl"
RBACTemplateName = "pipeline rbac template"
)

// RBACConfig contains the configuration data required for the RBAC template.
// Both PipelineName and PipelineNamespace are required.
type RBACConfig struct {
PipelineName string // Name of the pipeline.
PipelineNamespace string // Kubernetes namespace where the pipeline is deployed.
}

// ServiceAccountName generates the service account name using the pipeline name and namespace.
func (rbac RBACConfig) ServiceAccountName() string {
return ServiceAccountNamePrefix + rbac.PipelineName + "-" + rbac.PipelineNamespace
}

// RoleBindingName generates the role binding name using the service account name.
func (rbac RBACConfig) RoleBindingName() string {
return rbac.ServiceAccountName() + RoleBindingNameSuffix
}

// ClusterRoleBindingName generates the cluster role binding name using the service account name.
func (rbac RBACConfig) ClusterRoleBindingName() string {
return rbac.ServiceAccountName() + ClusterRoleBindingNameSuffix
}

// renderRBAC renders the RBAC configuration using a specified template.
func renderRBAC(fsys fs.FS, cfg RBACConfig) ([]byte, error) {
if cfg.PipelineName == "" || cfg.PipelineNamespace == "" {
return nil, fmt.Errorf("invalid RBACConfig: PipelineName and PipelineNamespace must not be empty")
}
return renderPipelineTemplate(fsys, RBACTemplateFileName, RBACTemplateName, cfg)
}
80 changes: 80 additions & 0 deletions pkg/fleet-manager/backup/render/rbac_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package render

import (
"os"
"testing"

"github.com/stretchr/testify/assert"

"kurator.dev/kurator/pkg/fleet-manager/manifests"
)

var manifestFS = manifests.BuiltinOrDir("manifests")

const expectedRBACFilePath = "testdata/rbac/"

func TestRenderRBAC(t *testing.T) {
// Define test cases including both valid and error scenarios.
cases := []struct {
name string
cfg RBACConfig
expectError bool
expectedFile string
}{
{
name: "valid configuration",
cfg: RBACConfig{
PipelineName: "example",
PipelineNamespace: "default",
},
expectError: false,
expectedFile: "default-example.yaml",
},
{
name: "empty PipelineName",
cfg: RBACConfig{
PipelineName: "",
PipelineNamespace: "default",
},
expectError: true,
},
{
name: "empty PipelineNamespace",
cfg: RBACConfig{
PipelineName: "example",
PipelineNamespace: "",
},
expectError: true,
},
{
name: "invalid file system path",
cfg: RBACConfig{
PipelineName: "example",
PipelineNamespace: "default",
},
expectError: true,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
fs := manifestFS
// Use an invalid file system for the relevant test case.
if tc.name == "invalid file system path" {
fs = manifests.BuiltinOrDir("invalid-path")
}

result, err := renderRBAC(fs, tc.cfg)

if tc.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)

expected, err := os.ReadFile(expectedRBACFilePath + tc.expectedFile)
assert.NoError(t, err)
assert.Equal(t, string(expected), string(result))
}
})
}
}
32 changes: 32 additions & 0 deletions pkg/fleet-manager/backup/render/testdata/rbac/default-example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: "kurator-pipeline-robot-example-default"
namespace: "default"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: "kurator-pipeline-robot-example-default-binding"
namespace: "default"
subjects:
- kind: ServiceAccount
name: "kurator-pipeline-robot-example-default"
namespace: "default"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-roles # add role for handle EventListener, configmap, secret and so on. `tekton-triggers-eventlistener-roles` is provided by Tekton
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: "kurator-pipeline-robot-example-default-clusterbinding"
subjects:
- kind: ServiceAccount
name: "kurator-pipeline-robot-example-default"
namespace: "default"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-clusterroles # add role for handle pod. `tekton-triggers-eventlistener-clusterroles` is provided by Tekton
50 changes: 50 additions & 0 deletions pkg/fleet-manager/backup/render/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package render

import (
"bytes"
"html/template"
"io/fs"

"github.com/Masterminds/sprig/v3"
"sigs.k8s.io/yaml"
)

// renderPipelineTemplate reads, parses, and renders a template file using the provided configuration data.
func renderPipelineTemplate(fsys fs.FS, tplFileName, tplName string, cfg interface{}) ([]byte, error) {
out, err := fs.ReadFile(fsys, tplFileName)
if err != nil {
return nil, err
}

t := template.New(tplName)

tpl, err := t.Funcs(funMap()).Parse(string(out))
if err != nil {
return nil, err
}

var b bytes.Buffer

if err := tpl.Execute(&b, cfg); err != nil {
return nil, err
}

return b.Bytes(), nil
}

// funMap returns a map of functions for use in the template.
func funMap() template.FuncMap {
m := sprig.TxtFuncMap()
m["toYaml"] = toYaml
return m
}

// toYaml converts a given value to its YAML representation.
func toYaml(value interface{}) string {
y, err := yaml.Marshal(value)
if err != nil {
return ""
}

return string(y)
}

0 comments on commit e6453d0

Please sign in to comment.