1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-13 19:28:55 +00:00

feat: add create exception cli command (#7781)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-09-12 14:58:30 +02:00 committed by GitHub
parent 045e955a6e
commit a1d06b41df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 390 additions and 3 deletions

View file

@ -2,6 +2,7 @@ package create
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/test"
userinfo "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/user-info"
@ -20,6 +21,7 @@ func Command() *cobra.Command {
},
}
cmd.AddCommand(
exception.Command(),
metricsconfig.Command(),
test.Command(),
userinfo.Command(),

View file

@ -0,0 +1,117 @@
package exception
import (
"log"
"os"
"strings"
"text/template"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/api/kyverno/v2alpha1"
"github.com/kyverno/kyverno/api/kyverno/v2beta1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/create/templates"
"github.com/spf13/cobra"
)
type options struct {
Name string
Namespace string
Background bool
Exceptions []v2alpha1.Exception
Match v2beta1.MatchResources
}
func Command() *cobra.Command {
var path string
var rules, any, all []string
var options options
cmd := &cobra.Command{
Use: "exception [name]",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
tmpl, err := template.New("exception").Parse(templates.ExceptionTemplate)
if err != nil {
return err
}
options.Name = args[0]
for _, result := range rules {
result := parseRule(result)
if result != nil {
options.Exceptions = append(options.Exceptions, *result)
}
}
for _, result := range any {
result := parseResourceFilter(result)
if result != nil {
options.Match.Any = append(options.Match.Any, *result)
}
}
for _, result := range all {
result := parseResourceFilter(result)
if result != nil {
options.Match.All = append(options.Match.All, *result)
}
}
output := cmd.OutOrStdout()
if path != "" {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
output = file
}
return tmpl.Execute(output, options)
},
}
cmd.Flags().StringVarP(&path, "output", "o", "", "Output path (uses standard console output if not set)")
cmd.Flags().StringVar(&options.Namespace, "namespace", "", "Policy exception namespace")
cmd.Flags().BoolVarP(&options.Background, "background", "b", true, "Set to false when policy shouldn't be considered in background scans")
cmd.Flags().StringArrayVar(&rules, "policy-rules", nil, "Policy name, followed by rule names (`--policy-rules=policy,rule-1,rule-2,...`)")
cmd.Flags().StringArrayVar(&any, "any", nil, "List of resource filters")
cmd.Flags().StringArrayVar(&all, "all", nil, "List of resource filters")
if err := cmd.MarkFlagRequired("policy-rules"); err != nil {
log.Println("WARNING", err)
}
return cmd
}
func parseRule(in string) *v2alpha1.Exception {
parts := strings.Split(in, ",")
if len(parts) < 2 {
return nil
}
return &v2alpha1.Exception{
PolicyName: parts[0],
RuleNames: parts[1:],
}
}
func parseResourceFilter(in string) *kyvernov1.ResourceFilter {
parts := strings.Split(in, ",")
if len(parts) == 0 {
return nil
}
var result kyvernov1.ResourceFilter
for _, part := range parts {
kv := strings.Split(part, "=")
if len(kv) != 2 {
return nil
}
switch kv[0] {
case "kind":
result.Kinds = append(result.Kinds, kv[1])
case "name":
result.Names = append(result.Names, kv[1])
case "namespace":
result.Namespaces = append(result.Namespaces, kv[1])
case "operation":
result.Operations = append(result.Operations, kyvernov1.AdmissionOperation(kv[1]))
}
}
return &result
}

View file

@ -0,0 +1,101 @@
package exception
import (
"bytes"
"io"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
cmd.SetArgs([]string{"test", "--policy-rules", "policy,rule-1,rule-2"})
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithMultipleArgs(t *testing.T) {
cmd := Command()
cmd.SetArgs([]string{"test", "test2", "--policy-rules", "policy,rule-1,rule-2"})
err := cmd.Execute()
assert.Error(t, err)
}
func TestCommandWithoutPolicyRules(t *testing.T) {
cmd := Command()
cmd.SetArgs([]string{"test", "test2"})
err := cmd.Execute()
assert.Error(t, err)
}
func TestCommandWithAny(t *testing.T) {
cmd := Command()
cmd.SetArgs([]string{"test", "--policy-rules", "policy,rule-1,rule-2", "--any", "kind=Pod,kind=Deployment,name=test-*"})
b := bytes.NewBufferString("")
cmd.SetOut(b)
err := cmd.Execute()
assert.NoError(t, err)
out, err := io.ReadAll(b)
assert.NoError(t, err)
expected := `
apiVersion: kyverno.io/v2alpha1
kind: PolicyException
metadata:
name: test
namespace:
spec:
background: true
match:
any:
-
kinds:
- Pod
- Deployment
names:
- test-*
exceptions:
- policyName: policy
ruleNames:
- rule-1
- rule-2`
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out)))
}
func TestCommandWithAll(t *testing.T) {
cmd := Command()
cmd.SetArgs([]string{"test", "--policy-rules", "policy,rule-1,rule-2", "--all", "kind=Pod,kind=Deployment,name=test-*,namespace=test,operation=UPDATE"})
b := bytes.NewBufferString("")
cmd.SetOut(b)
err := cmd.Execute()
assert.NoError(t, err)
out, err := io.ReadAll(b)
assert.NoError(t, err)
expected := `
apiVersion: kyverno.io/v2alpha1
kind: PolicyException
metadata:
name: test
namespace:
spec:
background: true
match:
all:
-
kinds:
- Pod
- Deployment
names:
- test-*
namespaces:
- test
operations:
- UPDATE
exceptions:
- policyName: policy
ruleNames:
- rule-1
- rule-2`
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out)))
}

View file

@ -0,0 +1,15 @@
package exception
// TODO
var websiteUrl = ``
var description = []string{
`Create a Kyverno policy exception file.`,
}
var examples = [][]string{
{
"# Create a policy exception file",
`kyverno create exception my-exception --namespace my-ns --policy-rules "policy,rule-1,rule-2" --any "kind=Pod,kind=Deployment,name=test-*"`,
},
}

View file

@ -0,0 +1,81 @@
apiVersion: kyverno.io/v2alpha1
kind: PolicyException
metadata:
name: {{ .Name }}
namespace: {{ .Namespace }}
spec:
background: {{ .Background }}
match:
{{- with .Match.Any }}
any:
{{- range . }}
-
{{- with .Kinds }}
kinds:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- with .Names }}
names:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- with .Namespaces }}
namespaces:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- with .Operations }}
operations:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- with .Match.All }}
all:
{{- range . }}
-
{{- with .Kinds }}
kinds:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- with .Names }}
names:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- with .Namespaces }}
namespaces:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- with .Operations }}
operations:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- with .Exceptions }}
exceptions:
{{- range . }}
- policyName: {{ .PolicyName }}
ruleNames:
{{- range .RuleNames }}
- {{ . }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -13,5 +13,8 @@ var ValuesTemplate string
//go:embed user-info.yaml
var UserInfoTemplate string
//go:embed exception.yaml
var ExceptionTemplate string
//go:embed metrics-config.yaml
var MetricsConfigTemplate string

View file

@ -16,9 +16,9 @@ func Command() *cobra.Command {
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("Version: %s\n", version.Version())
fmt.Printf("Time: %s\n", version.Time())
fmt.Printf("Git commit ID: %s\n", version.Hash())
fmt.Fprintf(cmd.OutOrStdout(), "Version: %s\n", version.Version())
fmt.Fprintf(cmd.OutOrStdout(), "Time: %s\n", version.Time())
fmt.Fprintf(cmd.OutOrStdout(), "Git commit ID: %s\n", version.Hash())
return nil
},
}

View file

@ -1,15 +1,29 @@
package version
import (
"bytes"
"io"
"strings"
"testing"
"github.com/kyverno/kyverno/pkg/version"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
version.BuildVersion = "test"
cmd := Command()
b := bytes.NewBufferString("")
cmd.SetOut(b)
err := cmd.Execute()
assert.NoError(t, err)
out, err := io.ReadAll(b)
assert.NoError(t, err)
expected := `
Version: test
Time: ---
Git commit ID: ---`
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out)))
}
func TestCommandWithArgs(t *testing.T) {

View file

@ -53,6 +53,7 @@ kyverno create [flags]
### SEE ALSO
* [kyverno](kyverno.md) - Kubernetes Native Policy Management.
* [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.
* [kyverno create user-info](kyverno_create_user-info.md) - Create a Kyverno user-info file.

View file

@ -0,0 +1,53 @@
## kyverno create exception
Create a Kyverno policy exception file.
### Synopsis
Create a Kyverno policy exception file.
```
kyverno create exception [name] [flags]
```
### Examples
```
# Create a policy exception file
kyverno create exception my-exception --namespace my-ns --policy-rules "policy,rule-1,rule-2" --any "kind=Pod,kind=Deployment,name=test-*"
```
### Options
```
--all stringArray List of resource filters
--any stringArray List of resource filters
-b, --background Set to false when policy shouldn't be considered in background scans (default true)
-h, --help help for exception
--namespace string Policy exception namespace
-o, --output string Output path (uses standard console output if not set)
--policy-rules --policy-rules=policy,rule-1,rule-2,... Policy name, followed by rule names (--policy-rules=policy,rule-1,rule-2,...)
```
### 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=false) (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.