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 (#11211)
* 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:
parent
8bf704edc5
commit
733063bb24
7 changed files with 338 additions and 0 deletions
cmd/cli/kubectl-kyverno/commands/create
docs/user/cli/commands
|
@ -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
|
||||
}
|
||||
|
|
102
cmd/cli/kubectl-kyverno/commands/create/role/command.go
Normal file
102
cmd/cli/kubectl-kyverno/commands/create/role/command.go
Normal 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
|
||||
}
|
171
cmd/cli/kubectl-kyverno/commands/create/role/command_test.go
Normal file
171
cmd/cli/kubectl-kyverno/commands/create/role/command_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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}}]
|
|
@ -18,3 +18,6 @@ var ExceptionTemplate string
|
|||
|
||||
//go:embed metrics-config.yaml
|
||||
var MetricsConfigTemplate string
|
||||
|
||||
//go:embed aggregated-role.yaml
|
||||
var AggregatedRoleTemplate string
|
||||
|
|
|
@ -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.
|
||||
|
|
47
docs/user/cli/commands/kyverno_create_cluster-role.md
Normal file
47
docs/user/cli/commands/kyverno_create_cluster-role.md
Normal 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.
|
||||
|
Loading…
Add table
Reference in a new issue