1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 18:38:40 +00:00

Add permission command to generate ClusterRole and ClusterRoleBinding ()

* Add permission command to generate ClusterRole and ClusterRoleBinding

Signed-off-by: Mohdcode <mohdkamaal2019@gmail.com>

* Add permission command to generate ClusterRole and ClusterRoleBinding

Signed-off-by: Mohdcode <mohdkamaal2019@gmail.com>

* Update command_test.go

Signed-off-by: Mohd Kamaal <102820439+Mohdcode@users.noreply.github.com>

---------

Signed-off-by: Mohdcode <mohdkamaal2019@gmail.com>
Signed-off-by: Mohd Kamaal <102820439+Mohdcode@users.noreply.github.com>
Co-authored-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
Mohd Kamaal 2024-10-10 19:33:16 +05:30 committed by GitHub
parent 8bf704edc5
commit 733063bb24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 338 additions and 0 deletions

View file

@ -4,6 +4,7 @@ import (
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/exception"
metricsconfig "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/metrics-config"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/role"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/test"
userinfo "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/user-info"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/values"
@ -28,6 +29,7 @@ func Command() *cobra.Command {
test.Command(),
userinfo.Command(),
values.Command(),
role.Command(),
)
return cmd
}

View file

@ -0,0 +1,102 @@
package role
import (
"fmt"
"log"
"os"
"text/template"
"github.com/Masterminds/sprig/v3"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/templates"
"github.com/spf13/cobra"
)
type options struct {
Verbs []string
Controllers []string
ApiGroup string
ResourceTypes []string
Name string
}
func Command() *cobra.Command {
var verbs []string
var path string
var opts options
cmd := &cobra.Command{
Use: "cluster-role [name] ",
Short: "Create an aggregated role for given resource types",
Long: `This command generates a Kubernetes ClusterRole for specified resource types.
The output is printed to stdout by default or saved to a specified file.
Required flags include 'api-groups', 'verbs', and 'resources'.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
// Validate input arguments
if args[0] == "" {
return fmt.Errorf("name argument is required")
}
opts.Name = args[0]
if opts.ApiGroup == "" {
return fmt.Errorf("required flag(s) \"api-groups\" not set")
}
if len(opts.ResourceTypes) == 0 {
return fmt.Errorf("required flag(s) \"resources\" not set")
}
if len(verbs) == 0 {
return fmt.Errorf("required flag(s) \"verbs\" not set")
}
if len(opts.Controllers) == 0 || (len(opts.Controllers) == 1 && opts.Controllers[0] == "") {
return fmt.Errorf("invalid controller provided")
}
// Handle 'all' verb
if verbs[0] == "all" {
verbs = []string{"create", "get", "update", "delete", "list", "watch"}
}
opts.Verbs = verbs
// Parse the role template
tmpl, err := template.New("aggregatedRole").Funcs(sprig.HermeticTxtFuncMap()).Parse(templates.AggregatedRoleTemplate)
if err != nil {
return fmt.Errorf("failed to parse template: %w", err)
}
// Set output: file or stdout
output := cmd.OutOrStdout()
if path != "" {
file, err := os.Create(path)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer file.Close()
output = file
}
// Execute template with name and options
return tmpl.Execute(output, opts)
},
}
// Define flags
cmd.Flags().StringArrayVar(&opts.Controllers, "controllers", []string{"background-controller"}, "List of controllers for the ClusterRole (default = background-controller)")
cmd.Flags().StringVarP(&path, "output", "o", "", "Output file path (prints to console if not set)")
cmd.Flags().StringVarP(&opts.ApiGroup, "api-groups", "g", "", "API group for the resource (required)")
cmd.Flags().StringArrayVar(&verbs, "verbs", nil, "A comma separated list of verbs or 'all' for all verbs")
cmd.Flags().StringArrayVar(&opts.ResourceTypes, "resources", nil, "A comma separated list of resources (required)")
// Mark required flags
if err := cmd.MarkFlagRequired("api-groups"); err != nil {
log.Println("WARNING", err)
}
if err := cmd.MarkFlagRequired("verbs"); err != nil {
log.Println("WARNING", err)
}
if err := cmd.MarkFlagRequired("resources"); err != nil {
log.Println("WARNING", err)
}
return cmd
}

View file

@ -0,0 +1,171 @@
package role
import (
"bytes"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
tempDir := t.TempDir()
templateFile := filepath.Join(tempDir, "templates", "aggregated-role.yaml")
// Sample template content for testing
templateContent := `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno-{{.Name}}-permission
labels:
{{- range .Controllers }}
rbac.kyverno.io/aggregate-to-{{ . }}: "true"
{{- end }}
rules:
- apiGroups: ["{{.ApiGroup}}"]
resources: ["{{.ResourceTypes | join \",\"}}"]
verbs: [{{range .Verbs}}"{{.}}",{{end}}]
`
// Write the template file to the temporary directory
err := os.MkdirAll(filepath.Dir(templateFile), os.ModePerm)
assert.NoError(t, err)
err = os.WriteFile(templateFile, []byte(templateContent), 0644)
assert.NoError(t, err)
// Define test cases
tests := []struct {
name string
args []string
expectedFile string
errorMsg string
}{
{
name: "ValidCommandWithMultipleControllers",
args: []string{"name1", "--resources=crontabs", "--api-groups=stable.example.com", "--verbs=get,list", "--controllers=controller1", "--controllers=controller2"},
expectedFile: "stdout",
},
{
name: "ValidCommandWithDefaultController",
args: []string{"name2", "--resources=pods", "--api-groups=core", "--verbs=get,list"},
expectedFile: "stdout",
},
{
name: "MissingResources",
args: []string{"name3", "--api-groups=stable.example.com", "--verbs=get,list"},
errorMsg: "required flag(s) \"resources\" not set",
},
{
name: "MissingApiGroup",
args: []string{"name4", "--resources=crontabs", "--verbs=get,list"},
errorMsg: "required flag(s) \"api-groups\" not set",
},
{
name: "MissingVerbs",
args: []string{"name5", "--resources=crontabs", "--api-groups=stable.example.com"},
errorMsg: "required flag(s) \"verbs\" not set",
},
{
name: "AllVerbExpands",
args: []string{"name6", "--resources=pods", "--api-groups=core", "--verbs=all"},
expectedFile: "stdout",
},
{
name: "OutputToFile",
args: []string{"name7", "--resources=pods", "--api-groups=core", "--verbs=get,list", "--output=" + filepath.Join(tempDir, "test-output.yaml")},
expectedFile: "test-output.yaml",
},
{
name: "NoFlags",
args: []string{"name10"},
errorMsg: "required flag(s) \"api-groups\", \"resources\", \"verbs\" not set",
},
{
name: "InvalidController",
args: []string{"name8", "--resources=pods", "--api-groups=core", "--verbs=get,list", "--controllers="},
errorMsg: "invalid controller provided",
},
{
name: "MultipleResources",
args: []string{"name11", "--resources=pods,services", "--api-groups=core", "--verbs=get,list"},
expectedFile: "stdout",
},
{
name: "SingleVerb",
args: []string{"name12", "--resources=pods", "--api-groups=core", "--verbs=get"},
expectedFile: "stdout",
},
{
name: "NoApiGroup",
args: []string{"name13", "--resources=pods", "--verbs=get"},
errorMsg: "required flag(s) \"api-groups\" not set",
},
{
name: "EmptyName",
args: []string{"", "--resources=pods", "--api-groups=stable.example.com", "--verbs=get,list"},
errorMsg: "name argument is required",
},
{
name: "DifferentVerbCombinations",
args: []string{"name14", "--resources=pods", "--api-groups=core", "--verbs=create,delete"},
expectedFile: "stdout",
},
{
name: "ValidCommandWithMixedControllers",
args: []string{"name15", "--resources=pods", "--api-groups=core", "--verbs=get,list", "--controllers=controller1,controller2"},
expectedFile: "stdout",
},
{
name: "AllFlagsWithComplexInput",
args: []string{"nameComplex", "--resources=pods,services", "--api-groups=core", "--verbs=get,list"},
expectedFile: "stdout",
},
{
name: "OutputFileCreationFailure",
args: []string{"nameOutputFail", "--resources=pods", "--api-groups=core", "--verbs=get,list", "--output=/invalid/path/test-output.yaml"},
errorMsg: "failed to create file: ",
},
{
name: "SpecialCharacterName",
args: []string{"name@#%", "--resources=pods", "--api-groups=core", "--verbs=get"},
expectedFile: "stdout",
},
}
// Iterate over the test cases
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
cmd := Command()
cmd.SetArgs(tc.args)
// Prepare a buffer to capture stdout
var stdoutBuffer bytes.Buffer
cmd.SetOut(&stdoutBuffer)
// Execute the command and handle errors
err = cmd.Execute()
if tc.errorMsg != "" {
assert.ErrorContains(t, err, tc.errorMsg)
return
}
assert.NoError(t, err)
// Check the output based on expected result
if tc.expectedFile == "stdout" {
output := stdoutBuffer.String()
assert.Contains(t, output, fmt.Sprintf("name: kyverno-%s-permission", tc.args[0]))
} else {
expectedFilePath := filepath.Join(tempDir, tc.expectedFile)
_, err := os.Stat(expectedFilePath)
assert.NoError(t, err)
// Clean up the created file
_ = os.Remove(expectedFilePath)
}
})
}
}

View file

@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno-{{.Name}}-permission
labels:
{{- range .Controllers }}
rbac.kyverno.io/aggregate-to-{{ . }}: "true"
{{- end }}
rules:
- apiGroups: ["{{.ApiGroup}}"]
resources: ["{{.ResourceTypes | join ","}}"]
verbs: [{{- range $index, $verb := .Verbs}}{{if $index}}, {{end}}"{{$verb}}"{{end}}]

View file

@ -18,3 +18,6 @@ var ExceptionTemplate string
//go:embed metrics-config.yaml
var MetricsConfigTemplate string
//go:embed aggregated-role.yaml
var AggregatedRoleTemplate string

View file

@ -53,6 +53,7 @@ kyverno create [flags]
### SEE ALSO
* [kyverno](kyverno.md) - Kubernetes Native Policy Management.
* [kyverno create cluster-role](kyverno_create_cluster-role.md) - Create an aggregated role for given resource types
* [kyverno create exception](kyverno_create_exception.md) - Create a Kyverno policy exception file.
* [kyverno create metrics-config](kyverno_create_metrics-config.md) - Create a Kyverno metrics-config file.
* [kyverno create test](kyverno_create_test.md) - Create a Kyverno test file.

View file

@ -0,0 +1,47 @@
## kyverno create cluster-role
Create an aggregated role for given resource types
### Synopsis
This command generates a Kubernetes ClusterRole for specified resource types.
The output is printed to stdout by default or saved to a specified file.
Required flags include 'api-groups', 'verbs', and 'resources'.
```
kyverno create cluster-role [name] [flags]
```
### Options
```
-g, --api-groups string API group for the resource (required)
--controllers stringArray List of controllers for the ClusterRole (default = background-controller) (default [background-controller])
-h, --help help for cluster-role
-o, --output string Output file path (prints to console if not set)
--resources stringArray A comma separated list of resources (required)
--verbs stringArray A comma separated list of verbs or 'all' for all verbs
```
### Options inherited from parent commands
```
--add_dir_header If true, adds the file directory to the header of the log messages
--alsologtostderr log to standard error as well as files (no effect when -logtostderr=true)
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory (no effect when -logtostderr=true)
--log_file string If non-empty, use this log file (no effect when -logtostderr=true)
--log_file_max_size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files (default true)
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files (no effect when -logtostderr=true)
--stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true) (default 2)
-v, --v Level number for the log level verbosity
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```
### SEE ALSO
* [kyverno create](kyverno_create.md) - Helps with the creation of various Kyverno resources.