diff --git a/cmd/cli/kubectl-kyverno/commands/fix/command.go b/cmd/cli/kubectl-kyverno/commands/fix/command.go index fee3f9ebf3..1e913b1f83 100644 --- a/cmd/cli/kubectl-kyverno/commands/fix/command.go +++ b/cmd/cli/kubectl-kyverno/commands/fix/command.go @@ -2,6 +2,7 @@ package fix import ( "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command" + "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/fix/policy" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/fix/test" "github.com/spf13/cobra" ) @@ -19,6 +20,7 @@ func Command() *cobra.Command { }, } cmd.AddCommand( + policy.Command(), test.Command(), ) return cmd diff --git a/cmd/cli/kubectl-kyverno/commands/fix/policy/command.go b/cmd/cli/kubectl-kyverno/commands/fix/policy/command.go new file mode 100644 index 0000000000..e5cb41ab11 --- /dev/null +++ b/cmd/cli/kubectl-kyverno/commands/fix/policy/command.go @@ -0,0 +1,26 @@ +package policy + +import ( + "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command" + "github.com/spf13/cobra" +) + +func Command() *cobra.Command { + var options options + cmd := &cobra.Command{ + Use: "policy [dir]...", + Short: command.FormatDescription(true, websiteUrl, true, description...), + Long: command.FormatDescription(false, websiteUrl, true, description...), + Example: command.FormatExamples(examples...), + Args: cobra.MinimumNArgs(1), + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := options.validate(args...); err != nil { + return err + } + return options.execute(cmd.OutOrStdout(), args...) + }, + } + cmd.Flags().BoolVar(&options.save, "save", false, "Save fixed file") + return cmd +} diff --git a/cmd/cli/kubectl-kyverno/commands/fix/policy/command_test.go b/cmd/cli/kubectl-kyverno/commands/fix/policy/command_test.go new file mode 100644 index 0000000000..387c67a585 --- /dev/null +++ b/cmd/cli/kubectl-kyverno/commands/fix/policy/command_test.go @@ -0,0 +1,65 @@ +package policy + +import ( + "bytes" + "io" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCommand(t *testing.T) { + cmd := Command() + assert.NotNil(t, cmd) + err := cmd.Execute() + assert.Error(t, err) +} + +func TestCommandInvalidFileName(t *testing.T) { + cmd := Command() + assert.NotNil(t, cmd) + cmd.SetArgs([]string{"foo", "-f", ""}) + err := cmd.Execute() + assert.Error(t, err) +} + +func TestCommandWithInvalidArg(t *testing.T) { + cmd := Command() + assert.NotNil(t, cmd) + b := bytes.NewBufferString("") + cmd.SetErr(b) + err := cmd.Execute() + assert.Error(t, err) + out, err := io.ReadAll(b) + assert.NoError(t, err) + expected := `Error: requires at least 1 arg(s), only received 0` + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out))) +} + +func TestCommandWithInvalidFlag(t *testing.T) { + cmd := Command() + assert.NotNil(t, cmd) + b := bytes.NewBufferString("") + cmd.SetErr(b) + cmd.SetArgs([]string{"--xxx"}) + err := cmd.Execute() + assert.Error(t, err) + out, err := io.ReadAll(b) + assert.NoError(t, err) + expected := `Error: unknown flag: --xxx` + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out))) +} + +func TestCommandHelp(t *testing.T) { + cmd := Command() + assert.NotNil(t, cmd) + b := bytes.NewBufferString("") + cmd.SetOut(b) + cmd.SetArgs([]string{"--help"}) + err := cmd.Execute() + assert.NoError(t, err) + out, err := io.ReadAll(b) + assert.NoError(t, err) + assert.True(t, strings.HasPrefix(string(out), cmd.Long)) +} diff --git a/cmd/cli/kubectl-kyverno/commands/fix/policy/doc.go b/cmd/cli/kubectl-kyverno/commands/fix/policy/doc.go new file mode 100644 index 0000000000..d20225ef83 --- /dev/null +++ b/cmd/cli/kubectl-kyverno/commands/fix/policy/doc.go @@ -0,0 +1,19 @@ +package policy + +// TODO +var websiteUrl = `` + +var description = []string{ + `Fix inconsistencies and deprecated usage in Kyverno policy files.`, +} + +var examples = [][]string{ + { + `# Fix Kyverno policy files`, + `KYVERNO_EXPERIMENTAL=true kyverno fix policy .`, + }, + { + `# Fix Kyverno policy files and save them back`, + `KYVERNO_EXPERIMENTAL=true kyverno fix policy . --save`, + }, +} diff --git a/cmd/cli/kubectl-kyverno/commands/fix/policy/options.go b/cmd/cli/kubectl-kyverno/commands/fix/policy/options.go new file mode 100644 index 0000000000..fc167f6973 --- /dev/null +++ b/cmd/cli/kubectl-kyverno/commands/fix/policy/options.go @@ -0,0 +1,161 @@ +package policy + +import ( + "errors" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "reflect" + + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/fix" + "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy" + gitutils "github.com/kyverno/kyverno/pkg/utils/git" + kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/yaml" +) + +type options struct { + save bool +} + +func (o options) validate(dirs ...string) error { + if len(dirs) == 0 { + return errors.New("at least one directory is required") + } + return nil +} + +func find(path string) ([]string, error) { + var files []string + err := filepath.Walk(path, func(file string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + if gitutils.IsYaml(info) { + files = append(files, file) + } + return nil + }) + if err != nil { + return nil, err + } + return files, nil +} + +func (o options) execute(out io.Writer, dirs ...string) error { + for _, dir := range dirs { + files, err := find(dir) + if err != nil { + return err + } + for _, file := range files { + o.processFile(out, file) + } + } + fmt.Fprintln(out, "Done.") + return nil +} + +func (o options) processFile(out io.Writer, path string) { + policies, vaps, err := policy.LoadWithLoader(policy.KubectlValidateLoader, nil, "", path) + if err != nil { + return + } + if len(policies) == 0 { + return + } + var fixed []kyvernov1.PolicyInterface + for _, policy := range policies { + copy := policy.CreateDeepCopy() + fmt.Fprintf(out, "Processing file (%s)...\n", path) + messages, err := fix.FixPolicy(copy) + for _, warning := range messages { + fmt.Fprintln(out, " WARNING:", warning) + } + if err != nil { + fmt.Fprintln(out, " ERROR:", err) + return + } + fixed = append(fixed, copy) + } + needsSave := !reflect.DeepEqual(policies, fixed) + if o.save && needsSave { + fmt.Fprintf(out, " Saving file (%s)...", path) + fmt.Fprintln(out) + var yamlBytes []byte + for _, policy := range fixed { + untyped, err := kubeutils.ObjToUnstructured(policy) + if err != nil { + fmt.Fprintf(out, " ERROR: converting to yaml: %s", err) + fmt.Fprintln(out) + return + } + // prune some fields + unstructured.RemoveNestedField(untyped.UnstructuredContent(), "status") + unstructured.RemoveNestedField(untyped.UnstructuredContent(), "metadata", "creationTimestamp") + unstructured.RemoveNestedField(untyped.UnstructuredContent(), "metadata", "generation") + unstructured.RemoveNestedField(untyped.UnstructuredContent(), "metadata", "uid") + rules, ok, err := unstructured.NestedFieldNoCopy(untyped.UnstructuredContent(), "spec", "rules") + if !ok || err != nil { + return + } + for _, rule := range rules.([]interface{}) { + rule := rule.(map[string]interface{}) + unstructured.RemoveNestedField(rule, "exclude", "resources") + unstructured.RemoveNestedField(rule, "match", "resources") + if item, _, _ := unstructured.NestedMap(rule, "generate", "clone"); len(item) == 0 { + unstructured.RemoveNestedField(rule, "generate", "clone") + } + if item, _, _ := unstructured.NestedMap(rule, "generate", "cloneList"); len(item) == 0 { + unstructured.RemoveNestedField(rule, "generate", "cloneList") + } + if item, _, _ := unstructured.NestedMap(rule, "generate"); len(item) == 0 { + unstructured.RemoveNestedField(rule, "generate") + } + if item, _, _ := unstructured.NestedMap(rule, "mutate"); len(item) == 0 { + unstructured.RemoveNestedField(rule, "mutate") + } + if item, _, _ := unstructured.NestedMap(rule, "exclude"); len(item) == 0 { + unstructured.RemoveNestedField(rule, "exclude") + } + if item, _, _ := unstructured.NestedMap(rule, "match"); len(item) == 0 { + unstructured.RemoveNestedField(rule, "match") + } + } + jsonBytes, err := untyped.MarshalJSON() + if err != nil { + fmt.Fprintf(out, " ERROR: converting to yaml: %s", err) + fmt.Fprintln(out) + return + } + finalBytes, err := yaml.JSONToYAML(jsonBytes) + if err != nil { + fmt.Fprintf(out, " ERROR: converting to yaml: %s", err) + fmt.Fprintln(out) + return + } + yamlBytes = append(yamlBytes, []byte("---\n")...) + yamlBytes = append(yamlBytes, finalBytes...) + } + for _, vap := range vaps { + finalBytes, err := yaml.Marshal(vap) + if err != nil { + fmt.Fprintf(out, " ERROR: converting to yaml: %s", err) + fmt.Fprintln(out) + return + } + yamlBytes = append(yamlBytes, []byte("---\n")...) + yamlBytes = append(yamlBytes, finalBytes...) + } + if err := os.WriteFile(path, yamlBytes, os.ModePerm); err != nil { + fmt.Fprintf(out, " ERROR: saving file (%s): %s", path, err) + fmt.Fprintln(out) + return + } + fmt.Fprintln(out, " OK") + } +} diff --git a/cmd/cli/kubectl-kyverno/fix/policy.go b/cmd/cli/kubectl-kyverno/fix/policy.go new file mode 100644 index 0000000000..55c7f44baa --- /dev/null +++ b/cmd/cli/kubectl-kyverno/fix/policy.go @@ -0,0 +1,82 @@ +package fix + +import ( + "fmt" + "reflect" + + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + apiutils "github.com/kyverno/kyverno/pkg/utils/api" +) + +func FixPolicy(policy kyvernov1.PolicyInterface) ([]string, error) { + var messages []string + spec := policy.GetSpec() + if spec.ValidationFailureAction.Enforce() { + spec.ValidationFailureAction = kyvernov1.Enforce + } else { + spec.ValidationFailureAction = kyvernov1.Audit + } + for i := range spec.Rules { + rule := &spec.Rules[i] + if !reflect.DeepEqual(rule.MatchResources.ResourceDescription, kyvernov1.ResourceDescription{}) || !reflect.DeepEqual(rule.MatchResources.UserInfo, kyvernov1.UserInfo{}) { + messages = append(messages, "match uses old syntax, moving to any") + rule.MatchResources.Any = append(rule.MatchResources.Any, kyvernov1.ResourceFilter{ + ResourceDescription: rule.MatchResources.ResourceDescription, + UserInfo: rule.MatchResources.UserInfo, + }) + rule.MatchResources.ResourceDescription = kyvernov1.ResourceDescription{} + rule.MatchResources.UserInfo = kyvernov1.UserInfo{} + } + if !reflect.DeepEqual(rule.ExcludeResources.ResourceDescription, kyvernov1.ResourceDescription{}) || !reflect.DeepEqual(rule.ExcludeResources.UserInfo, kyvernov1.UserInfo{}) { + messages = append(messages, "exclude uses old syntax, moving to any") + rule.ExcludeResources.Any = append(rule.ExcludeResources.Any, kyvernov1.ResourceFilter{ + ResourceDescription: rule.ExcludeResources.ResourceDescription, + UserInfo: rule.ExcludeResources.UserInfo, + }) + rule.ExcludeResources.ResourceDescription = kyvernov1.ResourceDescription{} + rule.ExcludeResources.UserInfo = kyvernov1.UserInfo{} + } + preconditions := rule.GetAnyAllConditions() + if preconditions != nil { + cond, err := apiutils.ApiextensionsJsonToKyvernoConditions(preconditions) + if err != nil { + return messages, err + } + var newCond *kyvernov1.AnyAllConditions + switch typedValue := cond.(type) { + case kyvernov1.AnyAllConditions: + newCond = &typedValue + case []kyvernov1.Condition: // backwards compatibility + newCond = &kyvernov1.AnyAllConditions{ + AllConditions: typedValue, + } + default: + return messages, fmt.Errorf("unknown preconditions type: %T", typedValue) + } + fixCondition := func(c *kyvernov1.Condition) { + switch c.Operator { + case "Equal": + messages = append(messages, "condition uses old operator `Equal`, updating") + c.Operator = "Equals" + case "NotEqual": + messages = append(messages, "condition uses old operator `NotEqual`, updating") + c.Operator = "NotEquals" + case "In": + messages = append(messages, "condition uses old operator `In`, updating") + c.Operator = "AllIn" + case "NotIn": + messages = append(messages, "condition uses old operator `NotIn`, updating") + c.Operator = "AnyNotIn" + } + } + for c := range newCond.AnyConditions { + fixCondition(&newCond.AnyConditions[c]) + } + for c := range newCond.AllConditions { + fixCondition(&newCond.AllConditions[c]) + } + rule.SetAnyAllConditions(newCond) + } + } + return messages, nil +} diff --git a/cmd/cli/kubectl-kyverno/policy/load.go b/cmd/cli/kubectl-kyverno/policy/load.go index 0bbed251fc..b25ef5965d 100644 --- a/cmd/cli/kubectl-kyverno/policy/load.go +++ b/cmd/cli/kubectl-kyverno/policy/load.go @@ -32,15 +32,67 @@ var ( clusterPolicyV2 = schema.GroupVersion(kyvernov2beta1.GroupVersion).WithKind("ClusterPolicy") vapV1 = v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicy") client = openapiclient.NewComposite( - openapiclient.NewHardcodedBuiltins("1.27"), + openapiclient.NewHardcodedBuiltins("1.28"), openapiclient.NewLocalCRDFiles(data.Crds(), data.CrdsFolder), ) + LegacyLoader = yamlutils.GetPolicy + KubectlValidateLoader = kubectlValidateLoader + defaultLoader = func(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { + if experimental.UseKubectlValidate() { + return KubectlValidateLoader(bytes) + } else { + return LegacyLoader(bytes) + } + } ) -func getPolicies(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { - if !experimental.UseKubectlValidate() { - return yamlutils.GetPolicy(bytes) +type loader = func([]byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) + +func Load(fs billy.Filesystem, resourcePath string, paths ...string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { + return LoadWithLoader(nil, fs, resourcePath, paths...) +} + +func LoadWithLoader(loader loader, fs billy.Filesystem, resourcePath string, paths ...string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { + if loader == nil { + loader = defaultLoader } + var pols []kyvernov1.PolicyInterface + var vaps []v1alpha1.ValidatingAdmissionPolicy + for _, path := range paths { + if source.IsStdin(path) { + p, v, err := stdinLoad(loader) + if err != nil { + return nil, nil, err + } + pols = append(pols, p...) + vaps = append(vaps, v...) + } else if fs != nil { + p, v, err := gitLoad(loader, fs, filepath.Join(resourcePath, path)) + if err != nil { + return nil, nil, err + } + pols = append(pols, p...) + vaps = append(vaps, v...) + } else if source.IsHttp(path) { + p, v, err := httpLoad(loader, path) + if err != nil { + return nil, nil, err + } + pols = append(pols, p...) + vaps = append(vaps, v...) + } else { + p, v, err := fsLoad(loader, path) + if err != nil { + return nil, nil, err + } + pols = append(pols, p...) + vaps = append(vaps, v...) + } + } + return pols, vaps, nil +} + +func kubectlValidateLoader(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { var policies []kyvernov1.PolicyInterface var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy documents, err := yamlutils.SplitDocuments(bytes) @@ -81,44 +133,7 @@ func getPolicies(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.Validati return policies, validatingAdmissionPolicies, nil } -func Load(fs billy.Filesystem, resourcePath string, paths ...string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { - var pols []kyvernov1.PolicyInterface - var vaps []v1alpha1.ValidatingAdmissionPolicy - for _, path := range paths { - if source.IsStdin(path) { - p, v, err := stdinLoad() - if err != nil { - return nil, nil, err - } - pols = append(pols, p...) - vaps = append(vaps, v...) - } else if fs != nil { - p, v, err := gitLoad(fs, filepath.Join(resourcePath, path)) - if err != nil { - return nil, nil, err - } - pols = append(pols, p...) - vaps = append(vaps, v...) - } else if source.IsHttp(path) { - p, v, err := httpLoad(path) - if err != nil { - return nil, nil, err - } - pols = append(pols, p...) - vaps = append(vaps, v...) - } else { - p, v, err := fsLoad(path) - if err != nil { - return nil, nil, err - } - pols = append(pols, p...) - vaps = append(vaps, v...) - } - } - return pols, vaps, nil -} - -func fsLoad(path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { +func fsLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { var pols []kyvernov1.PolicyInterface var vaps []v1alpha1.ValidatingAdmissionPolicy fi, err := os.Stat(filepath.Clean(path)) @@ -131,7 +146,7 @@ func fsLoad(path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmi return nil, nil, err } for _, file := range files { - p, v, err := fsLoad(filepath.Join(path, file.Name())) + p, v, err := fsLoad(loader, filepath.Join(path, file.Name())) if err != nil { return nil, nil, err } @@ -143,7 +158,7 @@ func fsLoad(path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmi if err != nil { return nil, nil, err } - p, v, err := getPolicies(fileBytes) + p, v, err := loader(fileBytes) if err != nil { return nil, nil, err } @@ -153,7 +168,7 @@ func fsLoad(path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmi return pols, vaps, nil } -func httpLoad(path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { +func httpLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { // We accept here that a random URL might be called based on user provided input. req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, path, nil) if err != nil { @@ -171,10 +186,10 @@ func httpLoad(path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAd if err != nil { return nil, nil, fmt.Errorf("failed to process %v: %v", path, err) } - return getPolicies(fileBytes) + return loader(fileBytes) } -func gitLoad(fs billy.Filesystem, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { +func gitLoad(loader loader, fs billy.Filesystem, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { file, err := fs.Open(path) if err != nil { return nil, nil, err @@ -183,14 +198,14 @@ func gitLoad(fs billy.Filesystem, path string) ([]kyvernov1.PolicyInterface, []v if err != nil { return nil, nil, err } - return getPolicies(fileBytes) + return loader(fileBytes) } -func stdinLoad() ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { +func stdinLoad(loader loader) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) { policyStr := "" scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { policyStr = policyStr + scanner.Text() + "\n" } - return getPolicies([]byte(policyStr)) + return loader([]byte(policyStr)) } diff --git a/cmd/cli/kubectl-kyverno/policy/load_test.go b/cmd/cli/kubectl-kyverno/policy/load_test.go index a5893e6348..ba095decde 100644 --- a/cmd/cli/kubectl-kyverno/policy/load_test.go +++ b/cmd/cli/kubectl-kyverno/policy/load_test.go @@ -5,7 +5,6 @@ import ( "github.com/go-git/go-billy/v5" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" - "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/experimental" "github.com/stretchr/testify/assert" "k8s.io/api/admissionregistration/v1alpha1" ) @@ -88,8 +87,7 @@ func TestLoadWithKubectlValidate(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - t.Setenv(experimental.KubectlValidateEnv, "true") - policies, vaps, err := Load(tt.fs, tt.resourcePath, tt.paths...) + policies, vaps, err := LoadWithLoader(KubectlValidateLoader, tt.fs, tt.resourcePath, tt.paths...) if (err != nil) != tt.wantErr { t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/docs/user/cli/kyverno_fix.md b/docs/user/cli/kyverno_fix.md index 521dde4ed2..25b89dc5e8 100644 --- a/docs/user/cli/kyverno_fix.md +++ b/docs/user/cli/kyverno_fix.md @@ -49,5 +49,6 @@ kyverno fix [flags] ### SEE ALSO * [kyverno](kyverno.md) - Kubernetes Native Policy Management. +* [kyverno fix policy](kyverno_fix_policy.md) - Fix inconsistencies and deprecated usage in Kyverno policy files. * [kyverno fix test](kyverno_fix_test.md) - Fix inconsistencies and deprecated usage in Kyverno test files. diff --git a/docs/user/cli/kyverno_fix_policy.md b/docs/user/cli/kyverno_fix_policy.md new file mode 100644 index 0000000000..5465aa8851 --- /dev/null +++ b/docs/user/cli/kyverno_fix_policy.md @@ -0,0 +1,53 @@ +## kyverno fix policy + +Fix inconsistencies and deprecated usage in Kyverno policy files. + +### Synopsis + +Fix inconsistencies and deprecated usage in Kyverno policy files. + + NOTE: This is an experimental command, use `KYVERNO_EXPERIMENTAL=true` to enable it. + +``` +kyverno fix policy [dir]... [flags] +``` + +### Examples + +``` + # Fix Kyverno policy files + KYVERNO_EXPERIMENTAL=true kyverno fix policy . + + # Fix Kyverno policy files and save them back + KYVERNO_EXPERIMENTAL=true kyverno fix policy . --save +``` + +### Options + +``` + -h, --help help for policy + --save Save fixed file +``` + +### 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 fix](kyverno_fix.md) - Fix inconsistencies and deprecated usage of Kyverno resources. +