mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-05 15:37:19 +00:00
feat: add table output to cli apply command (#7757)
* feat: add table output to cli apply command Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * factorise Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
parent
b6fb496d9b
commit
9bc540e454
7 changed files with 257 additions and 134 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/kyverno/kyverno/api/kyverno"
|
"github.com/kyverno/kyverno/api/kyverno"
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
|
||||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
|
||||||
|
@ -146,12 +147,14 @@ More info: https://kyverno.io/docs/kyverno-cli/
|
||||||
|
|
||||||
func Command() *cobra.Command {
|
func Command() *cobra.Command {
|
||||||
var cmd *cobra.Command
|
var cmd *cobra.Command
|
||||||
|
var removeColor, compact, table bool
|
||||||
applyCommandConfig := &ApplyCommandConfig{}
|
applyCommandConfig := &ApplyCommandConfig{}
|
||||||
cmd = &cobra.Command{
|
cmd = &cobra.Command{
|
||||||
Use: "apply",
|
Use: "apply",
|
||||||
Short: "Applies policies on resources.",
|
Short: "Applies policies on resources.",
|
||||||
Example: applyHelp,
|
Example: applyHelp,
|
||||||
RunE: func(cmd *cobra.Command, policyPaths []string) (err error) {
|
RunE: func(cmd *cobra.Command, policyPaths []string) (err error) {
|
||||||
|
color.InitColors(removeColor)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
|
@ -168,6 +171,8 @@ func Command() *cobra.Command {
|
||||||
printSkippedAndInvalidPolicies(skipInvalidPolicies)
|
printSkippedAndInvalidPolicies(skipInvalidPolicies)
|
||||||
if applyCommandConfig.PolicyReport {
|
if applyCommandConfig.PolicyReport {
|
||||||
printReport(responses, applyCommandConfig.AuditWarn)
|
printReport(responses, applyCommandConfig.AuditWarn)
|
||||||
|
} else if table {
|
||||||
|
printTable(compact, applyCommandConfig.AuditWarn, responses...)
|
||||||
} else {
|
} else {
|
||||||
printViolations(rc)
|
printViolations(rc)
|
||||||
}
|
}
|
||||||
|
@ -192,6 +197,9 @@ func Command() *cobra.Command {
|
||||||
cmd.Flags().BoolVar(&applyCommandConfig.AuditWarn, "audit-warn", false, "If set to true, will flag audit policies as warnings instead of failures")
|
cmd.Flags().BoolVar(&applyCommandConfig.AuditWarn, "audit-warn", false, "If set to true, will flag audit policies as warnings instead of failures")
|
||||||
cmd.Flags().IntVar(&applyCommandConfig.warnExitCode, "warn-exit-code", 0, "Set the exit code for warnings; if failures or errors are found, will exit 1")
|
cmd.Flags().IntVar(&applyCommandConfig.warnExitCode, "warn-exit-code", 0, "Set the exit code for warnings; if failures or errors are found, will exit 1")
|
||||||
cmd.Flags().BoolVar(&applyCommandConfig.warnNoPassed, "warn-no-pass", false, "Specify if warning exit code should be raised if no objects satisfied a policy; can be used together with --warn-exit-code flag")
|
cmd.Flags().BoolVar(&applyCommandConfig.warnNoPassed, "warn-no-pass", false, "Specify if warning exit code should be raised if no objects satisfied a policy; can be used together with --warn-exit-code flag")
|
||||||
|
cmd.Flags().BoolVar(&removeColor, "remove-color", false, "Remove any color from output")
|
||||||
|
cmd.Flags().BoolVar(&compact, "compact", true, "Does not show detailed results")
|
||||||
|
cmd.Flags().BoolVarP(&table, "table", "t", false, "Show results in table format")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
66
cmd/cli/kubectl-kyverno/apply/table.go
Normal file
66
cmd/cli/kubectl-kyverno/apply/table.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package apply
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kyverno/kyverno/api/kyverno"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/output/table"
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printTable(compact, auditWarn bool, engineResponses ...engineapi.EngineResponse) {
|
||||||
|
var resultsTable table.Table
|
||||||
|
id := 1
|
||||||
|
for _, engineResponse := range engineResponses {
|
||||||
|
var policyNamespace, policyName string
|
||||||
|
var ann map[string]string
|
||||||
|
|
||||||
|
isVAP := engineResponse.IsValidatingAdmissionPolicy()
|
||||||
|
|
||||||
|
if isVAP {
|
||||||
|
policy := engineResponse.ValidatingAdmissionPolicy()
|
||||||
|
policyNamespace = policy.GetNamespace()
|
||||||
|
policyName = policy.GetName()
|
||||||
|
ann = policy.GetAnnotations()
|
||||||
|
} else {
|
||||||
|
policy := engineResponse.Policy()
|
||||||
|
policyNamespace = policy.GetNamespace()
|
||||||
|
policyName = policy.GetName()
|
||||||
|
ann = policy.GetAnnotations()
|
||||||
|
}
|
||||||
|
resourceKind := engineResponse.Resource.GetKind()
|
||||||
|
resourceNamespace := engineResponse.Resource.GetNamespace()
|
||||||
|
resourceName := engineResponse.Resource.GetName()
|
||||||
|
|
||||||
|
for _, ruleResponse := range engineResponse.PolicyResponse.Rules {
|
||||||
|
var row table.Row
|
||||||
|
row.ID = id
|
||||||
|
id++
|
||||||
|
row.Policy = color.Policy(policyNamespace, policyName)
|
||||||
|
if !isVAP {
|
||||||
|
row.Rule = color.Rule(ruleResponse.Name())
|
||||||
|
}
|
||||||
|
row.Resource = color.Resource(resourceKind, resourceNamespace, resourceName)
|
||||||
|
if ruleResponse.Status() == engineapi.RuleStatusPass {
|
||||||
|
row.Result = color.ResultPass()
|
||||||
|
} else if ruleResponse.Status() == engineapi.RuleStatusFail {
|
||||||
|
if scored, ok := ann[kyverno.AnnotationPolicyScored]; ok && scored == "false" {
|
||||||
|
row.Result = color.ResultWarn()
|
||||||
|
} else if auditWarn && engineResponse.GetValidationFailureAction().Audit() {
|
||||||
|
row.Result = color.ResultWarn()
|
||||||
|
} else {
|
||||||
|
row.Result = color.ResultFail()
|
||||||
|
}
|
||||||
|
} else if ruleResponse.Status() == engineapi.RuleStatusWarn {
|
||||||
|
row.Result = color.ResultWarn()
|
||||||
|
} else if ruleResponse.Status() == engineapi.RuleStatusError {
|
||||||
|
row.Result = color.ResultError()
|
||||||
|
} else if ruleResponse.Status() == engineapi.RuleStatusSkip {
|
||||||
|
row.Result = color.ResultSkip()
|
||||||
|
}
|
||||||
|
row.Message = ruleResponse.Message()
|
||||||
|
resultsTable.Add(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printer := table.NewTablePrinter()
|
||||||
|
printer.Print(resultsTable.Rows(compact))
|
||||||
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/kataras/tablewriter"
|
|
||||||
"github.com/lensesio/tableprinter"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
boldGreen *color.Color
|
|
||||||
boldRed *color.Color
|
|
||||||
boldYellow *color.Color
|
|
||||||
boldFgCyan *color.Color
|
|
||||||
headerBgColor int
|
|
||||||
headerFgColor int
|
|
||||||
)
|
|
||||||
|
|
||||||
func initColors(noColor bool) {
|
|
||||||
toggleColor := func(c *color.Color) *color.Color {
|
|
||||||
if noColor {
|
|
||||||
c.DisableColor()
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
boldGreen = toggleColor(color.New(color.FgGreen).Add(color.Bold))
|
|
||||||
boldRed = toggleColor(color.New(color.FgRed).Add(color.Bold))
|
|
||||||
boldYellow = toggleColor(color.New(color.FgYellow).Add(color.Bold))
|
|
||||||
boldFgCyan = toggleColor(color.New(color.FgCyan).Add(color.Bold))
|
|
||||||
if !noColor {
|
|
||||||
headerBgColor = tablewriter.BgBlackColor
|
|
||||||
headerFgColor = tablewriter.FgGreenColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTablePrinter() *tableprinter.Printer {
|
|
||||||
printer := tableprinter.New(os.Stdout)
|
|
||||||
printer.BorderTop, printer.BorderBottom, printer.BorderLeft, printer.BorderRight = true, true, true, true
|
|
||||||
printer.CenterSeparator = "│"
|
|
||||||
printer.ColumnSeparator = "│"
|
|
||||||
printer.RowSeparator = "─"
|
|
||||||
printer.RowCharLimit = 300
|
|
||||||
printer.HeaderBgColor = headerBgColor
|
|
||||||
printer.HeaderFgColor = headerFgColor
|
|
||||||
printer.RowLengthTitle = func(rowsLength int) bool {
|
|
||||||
return rowsLength > 10
|
|
||||||
}
|
|
||||||
return printer
|
|
||||||
}
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/manifest"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/manifest"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/output/table"
|
||||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
"github.com/kyverno/kyverno/pkg/openapi"
|
||||||
|
@ -27,7 +29,7 @@ func Command() *cobra.Command {
|
||||||
Long: longHelp,
|
Long: longHelp,
|
||||||
Example: exampleHelp,
|
Example: exampleHelp,
|
||||||
RunE: func(cmd *cobra.Command, dirPath []string) (err error) {
|
RunE: func(cmd *cobra.Command, dirPath []string) (err error) {
|
||||||
initColors(removeColor)
|
color.InitColors(removeColor)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
|
@ -63,47 +65,6 @@ func Command() *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
type Table struct {
|
|
||||||
rows []Row
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) Rows(compact bool) interface{} {
|
|
||||||
if !compact {
|
|
||||||
return t.rows
|
|
||||||
}
|
|
||||||
var rows []CompactRow
|
|
||||||
for _, row := range t.rows {
|
|
||||||
rows = append(rows, row.CompactRow)
|
|
||||||
}
|
|
||||||
return rows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) AddFailed(rows ...Row) {
|
|
||||||
for _, row := range rows {
|
|
||||||
if row.isFailure {
|
|
||||||
t.rows = append(t.rows, row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) Add(rows ...Row) {
|
|
||||||
t.rows = append(t.rows, rows...)
|
|
||||||
}
|
|
||||||
|
|
||||||
type CompactRow struct {
|
|
||||||
isFailure bool
|
|
||||||
ID int `header:"id"`
|
|
||||||
Policy string `header:"policy"`
|
|
||||||
Rule string `header:"rule"`
|
|
||||||
Resource string `header:"resource"`
|
|
||||||
Result string `header:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Row struct {
|
|
||||||
CompactRow `header:"inline"`
|
|
||||||
Message string `header:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type resultCounts struct {
|
type resultCounts struct {
|
||||||
Skip int
|
Skip int
|
||||||
Pass int
|
Pass int
|
||||||
|
@ -136,7 +97,7 @@ func testCommandExecute(
|
||||||
fmt.Printf("\n No test yamls available \n")
|
fmt.Printf("\n No test yamls available \n")
|
||||||
}
|
}
|
||||||
rc = &resultCounts{}
|
rc = &resultCounts{}
|
||||||
var table Table
|
var table table.Table
|
||||||
for _, p := range policies {
|
for _, p := range policies {
|
||||||
if reports, tests, err := applyPoliciesFromPath(
|
if reports, tests, err := applyPoliciesFromPath(
|
||||||
fs,
|
fs,
|
||||||
|
@ -152,7 +113,7 @@ func testCommandExecute(
|
||||||
} else if t, err := printTestResult(reports, tests, rc, failOnly, compact); err != nil {
|
} else if t, err := printTestResult(reports, tests, rc, failOnly, compact); err != nil {
|
||||||
return rc, sanitizederror.NewWithError("failed to print test result:", err)
|
return rc, sanitizederror.NewWithError("failed to print test result:", err)
|
||||||
} else {
|
} else {
|
||||||
table.AddFailed(t.rows...)
|
table.AddFailed(t.RawRows...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(errors) > 0 && log.Log.V(1).Enabled() {
|
if len(errors) > 0 && log.Log.V(1).Enabled() {
|
||||||
|
@ -177,25 +138,25 @@ func testCommandExecute(
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, testResults []api.TestResults, rc *resultCounts, failOnly bool, compact bool) (Table, error) {
|
func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, testResults []api.TestResults, rc *resultCounts, failOnly bool, compact bool) (table.Table, error) {
|
||||||
printer := newTablePrinter()
|
printer := table.NewTablePrinter()
|
||||||
var table Table
|
var resultsTable table.Table
|
||||||
var countDeprecatedResource int
|
var countDeprecatedResource int
|
||||||
testCount := 1
|
testCount := 1
|
||||||
for _, v := range testResults {
|
for _, v := range testResults {
|
||||||
var row Row
|
var row table.Row
|
||||||
row.ID = testCount
|
row.ID = testCount
|
||||||
if v.Resources == nil {
|
if v.Resources == nil {
|
||||||
testCount++
|
testCount++
|
||||||
}
|
}
|
||||||
row.Policy = boldFgCyan.Sprint(v.Policy)
|
row.Policy = color.Policy("", v.Policy)
|
||||||
row.Rule = boldFgCyan.Sprint(v.Rule)
|
row.Rule = color.Rule(v.Rule)
|
||||||
|
|
||||||
if v.Resources != nil {
|
if v.Resources != nil {
|
||||||
for _, resource := range v.Resources {
|
for _, resource := range v.Resources {
|
||||||
row.ID = testCount
|
row.ID = testCount
|
||||||
testCount++
|
testCount++
|
||||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(resource)
|
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||||
var ruleNameInResultKey string
|
var ruleNameInResultKey string
|
||||||
if !v.IsVap {
|
if !v.IsVap {
|
||||||
if v.AutoGeneratedRule != "" {
|
if v.AutoGeneratedRule != "" {
|
||||||
|
@ -227,11 +188,10 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
} else {
|
} else {
|
||||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, resource)
|
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, resource)
|
||||||
}
|
}
|
||||||
|
row.Policy = color.Policy(ns, v.Policy)
|
||||||
row.Policy = boldFgCyan.Sprint(ns) + "/" + boldFgCyan.Sprint(v.Policy)
|
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(resource)
|
|
||||||
} else if v.Namespace != "" {
|
} else if v.Namespace != "" {
|
||||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(resource)
|
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||||
|
|
||||||
if !v.IsVap {
|
if !v.IsVap {
|
||||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||||
|
@ -245,10 +205,10 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
testRes = val
|
testRes = val
|
||||||
} else {
|
} else {
|
||||||
log.Log.V(2).Info("result not found", "key", resultKey)
|
log.Log.V(2).Info("result not found", "key", resultKey)
|
||||||
row.Result = boldYellow.Sprint("Not found")
|
row.Result = color.NotFound()
|
||||||
rc.Fail++
|
rc.Fail++
|
||||||
row.isFailure = true
|
row.IsFailure = true
|
||||||
table.Add(row)
|
resultsTable.Add(row)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
row.Message = testRes.Message
|
row.Message = testRes.Message
|
||||||
|
@ -257,7 +217,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
}
|
}
|
||||||
|
|
||||||
if testRes.Result == v.Result {
|
if testRes.Result == v.Result {
|
||||||
row.Result = boldGreen.Sprint("Pass")
|
row.Result = color.ResultPass()
|
||||||
if testRes.Result == policyreportv1alpha2.StatusSkip {
|
if testRes.Result == policyreportv1alpha2.StatusSkip {
|
||||||
rc.Skip++
|
rc.Skip++
|
||||||
} else {
|
} else {
|
||||||
|
@ -265,22 +225,22 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Log.V(2).Info("result mismatch", "expected", v.Result, "received", testRes.Result, "key", resultKey)
|
log.Log.V(2).Info("result mismatch", "expected", v.Result, "received", testRes.Result, "key", resultKey)
|
||||||
row.Result = boldRed.Sprint("Fail")
|
row.Result = color.ResultFail()
|
||||||
rc.Fail++
|
rc.Fail++
|
||||||
row.isFailure = true
|
row.IsFailure = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if failOnly {
|
if failOnly {
|
||||||
if row.Result == boldRed.Sprintf("Fail") || row.Result == "Fail" {
|
if row.Result == color.ResultFail() || row.Result == "Fail" {
|
||||||
table.Add(row)
|
resultsTable.Add(row)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
table.Add(row)
|
resultsTable.Add(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if v.Resource != "" {
|
} else if v.Resource != "" {
|
||||||
countDeprecatedResource++
|
countDeprecatedResource++
|
||||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(v.Resource)
|
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||||
var ruleNameInResultKey string
|
var ruleNameInResultKey string
|
||||||
if !v.IsVap {
|
if !v.IsVap {
|
||||||
if v.AutoGeneratedRule != "" {
|
if v.AutoGeneratedRule != "" {
|
||||||
|
@ -313,10 +273,10 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, v.Resource)
|
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, v.Resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
row.Policy = boldFgCyan.Sprint(ns) + "/" + boldFgCyan.Sprint(v.Policy)
|
row.Policy = color.Policy(ns, v.Policy)
|
||||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(v.Resource)
|
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||||
} else if v.Namespace != "" {
|
} else if v.Namespace != "" {
|
||||||
row.Resource = boldFgCyan.Sprint(v.Namespace) + "/" + boldFgCyan.Sprint(v.Kind) + "/" + boldFgCyan.Sprint(v.Resource)
|
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||||
|
|
||||||
if !v.IsVap {
|
if !v.IsVap {
|
||||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||||
|
@ -330,10 +290,10 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
testRes = val
|
testRes = val
|
||||||
} else {
|
} else {
|
||||||
log.Log.V(2).Info("result not found", "key", resultKey)
|
log.Log.V(2).Info("result not found", "key", resultKey)
|
||||||
row.Result = boldYellow.Sprint("Not found")
|
row.Result = color.NotFound()
|
||||||
rc.Fail++
|
rc.Fail++
|
||||||
row.isFailure = true
|
row.IsFailure = true
|
||||||
table.Add(row)
|
resultsTable.Add(row)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +304,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
}
|
}
|
||||||
|
|
||||||
if testRes.Result == v.Result {
|
if testRes.Result == v.Result {
|
||||||
row.Result = boldGreen.Sprint("Pass")
|
row.Result = color.ResultPass()
|
||||||
if testRes.Result == policyreportv1alpha2.StatusSkip {
|
if testRes.Result == policyreportv1alpha2.StatusSkip {
|
||||||
rc.Skip++
|
rc.Skip++
|
||||||
} else {
|
} else {
|
||||||
|
@ -352,31 +312,31 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Log.V(2).Info("result mismatch", "expected", v.Result, "received", testRes.Result, "key", resultKey)
|
log.Log.V(2).Info("result mismatch", "expected", v.Result, "received", testRes.Result, "key", resultKey)
|
||||||
row.Result = boldRed.Sprint("Fail")
|
row.Result = color.ResultFail()
|
||||||
rc.Fail++
|
rc.Fail++
|
||||||
row.isFailure = true
|
row.IsFailure = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if failOnly {
|
if failOnly {
|
||||||
if row.Result == boldRed.Sprintf("Fail") || row.Result == "Fail" {
|
if row.Result == color.ResultFail() || row.Result == "Fail" {
|
||||||
table.Add(row)
|
resultsTable.Add(row)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
table.Add(row)
|
resultsTable.Add(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
printer.Print(table.Rows(compact))
|
printer.Print(resultsTable.Rows(compact))
|
||||||
return table, nil
|
return resultsTable, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printFailedTestResult(table Table, compact bool) {
|
func printFailedTestResult(resultsTable table.Table, compact bool) {
|
||||||
printer := newTablePrinter()
|
printer := table.NewTablePrinter()
|
||||||
for i := range table.rows {
|
for i := range resultsTable.RawRows {
|
||||||
table.rows[i].ID = i + 1
|
resultsTable.RawRows[i].ID = i + 1
|
||||||
}
|
}
|
||||||
fmt.Printf("Aggregated Failed Test Cases : ")
|
fmt.Printf("Aggregated Failed Test Cases : ")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
printer.Print(table.Rows(compact))
|
printer.Print(resultsTable.Rows(compact))
|
||||||
}
|
}
|
||||||
|
|
74
cmd/cli/kubectl-kyverno/utils/color/color.go
Normal file
74
cmd/cli/kubectl-kyverno/utils/color/color.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/kataras/tablewriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
BoldGreen *color.Color
|
||||||
|
BoldRed *color.Color
|
||||||
|
BoldYellow *color.Color
|
||||||
|
BoldFgCyan *color.Color
|
||||||
|
HeaderBgColor int
|
||||||
|
HeaderFgColor int
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitColors(noColor bool) {
|
||||||
|
toggleColor := func(c *color.Color) *color.Color {
|
||||||
|
if noColor {
|
||||||
|
c.DisableColor()
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
BoldGreen = toggleColor(color.New(color.FgGreen).Add(color.Bold))
|
||||||
|
BoldRed = toggleColor(color.New(color.FgRed).Add(color.Bold))
|
||||||
|
BoldYellow = toggleColor(color.New(color.FgYellow).Add(color.Bold))
|
||||||
|
BoldFgCyan = toggleColor(color.New(color.FgCyan).Add(color.Bold))
|
||||||
|
if !noColor {
|
||||||
|
HeaderBgColor = tablewriter.BgBlackColor
|
||||||
|
HeaderFgColor = tablewriter.FgGreenColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Policy(namespace, name string) string {
|
||||||
|
if namespace == "" {
|
||||||
|
return BoldFgCyan.Sprint(name)
|
||||||
|
}
|
||||||
|
return BoldFgCyan.Sprint(namespace) + "/" + BoldFgCyan.Sprint(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Rule(name string) string {
|
||||||
|
return BoldFgCyan.Sprint(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Resource(kind, namespace, name string) string {
|
||||||
|
if namespace == "" {
|
||||||
|
return BoldFgCyan.Sprint(kind) + "/" + BoldFgCyan.Sprint(name)
|
||||||
|
}
|
||||||
|
return BoldFgCyan.Sprint(namespace) + "/" + BoldFgCyan.Sprint(kind) + "/" + BoldFgCyan.Sprint(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NotFound() string {
|
||||||
|
return BoldYellow.Sprint("Not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResultPass() string {
|
||||||
|
return BoldGreen.Sprint("Pass")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResultFail() string {
|
||||||
|
return BoldRed.Sprint("Fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResultWarn() string {
|
||||||
|
return BoldYellow.Sprint("Warn")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResultError() string {
|
||||||
|
return BoldRed.Sprint("Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResultSkip() string {
|
||||||
|
return BoldFgCyan.Sprint("Skip")
|
||||||
|
}
|
23
cmd/cli/kubectl-kyverno/utils/output/table/printer.go
Normal file
23
cmd/cli/kubectl-kyverno/utils/output/table/printer.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package table
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
|
||||||
|
"github.com/lensesio/tableprinter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTablePrinter() *tableprinter.Printer {
|
||||||
|
printer := tableprinter.New(os.Stdout)
|
||||||
|
printer.BorderTop, printer.BorderBottom, printer.BorderLeft, printer.BorderRight = true, true, true, true
|
||||||
|
printer.CenterSeparator = "│"
|
||||||
|
printer.ColumnSeparator = "│"
|
||||||
|
printer.RowSeparator = "─"
|
||||||
|
printer.RowCharLimit = 300
|
||||||
|
printer.HeaderBgColor = color.HeaderBgColor
|
||||||
|
printer.HeaderFgColor = color.HeaderFgColor
|
||||||
|
printer.RowLengthTitle = func(rowsLength int) bool {
|
||||||
|
return rowsLength > 10
|
||||||
|
}
|
||||||
|
return printer
|
||||||
|
}
|
42
cmd/cli/kubectl-kyverno/utils/output/table/table.go
Normal file
42
cmd/cli/kubectl-kyverno/utils/output/table/table.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package table
|
||||||
|
|
||||||
|
type Table struct {
|
||||||
|
RawRows []Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) Rows(compact bool) interface{} {
|
||||||
|
if !compact {
|
||||||
|
return t.RawRows
|
||||||
|
}
|
||||||
|
var rows []CompactRow
|
||||||
|
for _, row := range t.RawRows {
|
||||||
|
rows = append(rows, row.CompactRow)
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) AddFailed(rows ...Row) {
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.IsFailure {
|
||||||
|
t.RawRows = append(t.RawRows, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) Add(rows ...Row) {
|
||||||
|
t.RawRows = append(t.RawRows, rows...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompactRow struct {
|
||||||
|
IsFailure bool
|
||||||
|
ID int `header:"id"`
|
||||||
|
Policy string `header:"policy"`
|
||||||
|
Rule string `header:"rule"`
|
||||||
|
Resource string `header:"resource"`
|
||||||
|
Result string `header:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Row struct {
|
||||||
|
CompactRow `header:"inline"`
|
||||||
|
Message string `header:"message"`
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue