From 236ac9c2161b78ad11404a703c621859a0835aba Mon Sep 17 00:00:00 2001 From: Rokibul Hasan Date: Tue, 7 Jan 2025 12:22:11 +0600 Subject: [PATCH] Add flag for JSON output in policy reports (#11840) * Add flag for JSON output in policy reports Signed-off-by: Rokibul Hasan * make codegen-docs-all Signed-off-by: Rokibul Hasan --------- Signed-off-by: Rokibul Hasan Co-authored-by: shuting --- .../kubectl-kyverno/commands/apply/command.go | 6 ++- .../kubectl-kyverno/commands/apply/print.go | 44 +++++++++++++------ docs/user/cli/commands/kyverno_apply.md | 1 + 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/cmd/cli/kubectl-kyverno/commands/apply/command.go b/cmd/cli/kubectl-kyverno/commands/apply/command.go index c17a740a47..a70be571e9 100644 --- a/cmd/cli/kubectl-kyverno/commands/apply/command.go +++ b/cmd/cli/kubectl-kyverno/commands/apply/command.go @@ -57,6 +57,7 @@ type ApplyCommandConfig struct { UserInfoPath string Cluster bool PolicyReport bool + OutputFormat string Stdin bool RegistryAccess bool AuditWarn bool @@ -93,9 +94,9 @@ func Command() *cobra.Command { cmd.SilenceErrors = true printSkippedAndInvalidPolicies(out, skipInvalidPolicies) if applyCommandConfig.PolicyReport { - printReports(out, responses, applyCommandConfig.AuditWarn) + printReports(out, responses, applyCommandConfig.AuditWarn, applyCommandConfig.OutputFormat) } else if applyCommandConfig.GenerateExceptions { - printExceptions(out, responses, applyCommandConfig.AuditWarn, applyCommandConfig.GeneratedExceptionTTL) + printExceptions(out, responses, applyCommandConfig.AuditWarn, applyCommandConfig.OutputFormat, applyCommandConfig.GeneratedExceptionTTL) } else if table { printTable(out, detailedResults, applyCommandConfig.AuditWarn, responses...) } else { @@ -146,6 +147,7 @@ func Command() *cobra.Command { cmd.Flags().StringSliceVarP(&applyCommandConfig.Variables, "set", "s", nil, "Variables that are required") cmd.Flags().StringVarP(&applyCommandConfig.ValuesFile, "values-file", "f", "", "File containing values for policy variables") cmd.Flags().BoolVarP(&applyCommandConfig.PolicyReport, "policy-report", "p", false, "Generates policy report when passed (default policyviolation)") + cmd.Flags().StringVarP(&applyCommandConfig.OutputFormat, "output-format", "", "yaml", "Specifies the policy report format (json or yaml). Default: yaml.") cmd.Flags().StringVarP(&applyCommandConfig.Namespace, "namespace", "n", "", "Optional Policy parameter passed with cluster flag") cmd.Flags().BoolVarP(&applyCommandConfig.Stdin, "stdin", "i", false, "Optional mutate policy parameter to pipe directly through to kubectl") cmd.Flags().BoolVar(&applyCommandConfig.RegistryAccess, "registry", false, "If set to true, access the image registry using local docker credentials to populate external data") diff --git a/cmd/cli/kubectl-kyverno/commands/apply/print.go b/cmd/cli/kubectl-kyverno/commands/apply/print.go index f38a23fdd5..b96b5c1f1c 100644 --- a/cmd/cli/kubectl-kyverno/commands/apply/print.go +++ b/cmd/cli/kubectl-kyverno/commands/apply/print.go @@ -40,26 +40,36 @@ func printSkippedAndInvalidPolicies(out io.Writer, skipInvalidPolicies SkippedIn } } -func printReports(out io.Writer, engineResponses []engineapi.EngineResponse, auditWarn bool) { +func printReports(out io.Writer, engineResponses []engineapi.EngineResponse, auditWarn bool, outputFormat string) { clustered, namespaced := report.ComputePolicyReports(auditWarn, engineResponses...) - if len(clustered) > 0 { - report := report.MergeClusterReports(clustered) - yamlReport, _ := yaml.Marshal(report) - fmt.Fprintln(out, string(yamlReport)) + + printReport := func(report interface{}) { + var output []byte + if outputFormat == "json" { + output, _ = json.Marshal(report) + } else { + output, _ = yaml.Marshal(report) + } + fmt.Fprintln(out, string(output)) } + + if len(clustered) > 0 { + clusterReport := report.MergeClusterReports(clustered) + printReport(clusterReport) + } + for _, r := range namespaced { - fmt.Fprintln(out, string("---")) - yamlReport, _ := yaml.Marshal(r) - fmt.Fprintln(out, string(yamlReport)) + fmt.Fprintln(out, "---") + printReport(r) } } -func printExceptions(out io.Writer, engineResponses []engineapi.EngineResponse, auditWarn bool, ttl time.Duration) { +func printExceptions(out io.Writer, engineResponses []engineapi.EngineResponse, auditWarn bool, outputFormat string, ttl time.Duration) { clustered, _ := report.ComputePolicyReports(auditWarn, engineResponses...) for _, report := range clustered { for _, result := range report.Results { if result.Result == "fail" { - if err := printException(out, result, ttl); err != nil { + if err := printException(out, result, ttl, outputFormat); err != nil { log.Error(err) } } @@ -67,7 +77,7 @@ func printExceptions(out io.Writer, engineResponses []engineapi.EngineResponse, } } -func printException(out io.Writer, result v1alpha2.PolicyReportResult, ttl time.Duration) error { +func printException(out io.Writer, result v1alpha2.PolicyReportResult, ttl time.Duration, outputFormat string) error { for _, r := range result.Resources { name := strings.Join([]string{result.Policy, result.Rule, r.Namespace, r.Name}, "-") @@ -141,13 +151,21 @@ func printException(out io.Writer, result v1alpha2.PolicyReportResult, ttl time. exception.Spec.PodSecurity = pssList } - exceptionYAML, err := yaml.Marshal(exception) + var exceptionReport []byte + marshal := func(report interface{}) ([]byte, error) { + if outputFormat == "json" { + return json.Marshal(report) + } + return yaml.Marshal(report) + } + + exceptionReport, err := marshal(exception) if err != nil { return err } fmt.Fprint(out, "---\n") - fmt.Fprint(out, string(exceptionYAML)) + fmt.Fprint(out, string(exceptionReport)) fmt.Fprint(out, "\n") } diff --git a/docs/user/cli/commands/kyverno_apply.md b/docs/user/cli/commands/kyverno_apply.md index c1976beba2..f9bcb08b3d 100644 --- a/docs/user/cli/commands/kyverno_apply.md +++ b/docs/user/cli/commands/kyverno_apply.md @@ -52,6 +52,7 @@ kyverno apply [flags] --kubeconfig string path to kubeconfig file with authorization and master location information -n, --namespace string Optional Policy parameter passed with cluster flag -o, --output string Prints the mutated/generated resources in provided file/directory + --output-format string Specifies the policy report format (json or yaml). Default: yaml. (default "yaml") -p, --policy-report Generates policy report when passed (default policyviolation) --registry If set to true, access the image registry using local docker credentials to populate external data --remove-color Remove any color from output