2021-02-07 20:26:56 -08:00
package test
2021-02-01 16:22:41 +05:30
import (
"encoding/json"
2021-02-07 20:26:56 -08:00
"fmt"
2022-09-30 15:25:19 +08:00
"io"
2021-02-07 20:26:56 -08:00
"net/url"
2021-02-01 16:22:41 +05:30
"os"
2022-04-27 17:19:40 +05:30
"path"
2021-02-01 16:22:41 +05:30
"path/filepath"
2021-10-01 14:16:33 +05:30
"regexp"
2021-02-07 20:26:56 -08:00
"sort"
2021-02-01 16:22:41 +05:30
"strings"
2021-02-07 20:26:56 -08:00
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
2022-05-17 13:12:43 +02:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2022-04-25 20:20:40 +08:00
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
2022-05-17 13:12:43 +02:00
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
2022-12-03 19:56:09 +01:00
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/manifest"
2022-04-14 17:50:18 +05:30
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
2022-03-28 16:01:27 +02:00
"github.com/kyverno/kyverno/pkg/autogen"
2022-04-13 18:15:04 +05:30
"github.com/kyverno/kyverno/pkg/background/generate"
2022-08-31 14:03:47 +08:00
"github.com/kyverno/kyverno/pkg/clients/dclient"
2023-01-30 12:41:09 +01:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2021-02-01 16:22:41 +05:30
"github.com/kyverno/kyverno/pkg/openapi"
2021-02-07 20:26:56 -08:00
policy2 "github.com/kyverno/kyverno/pkg/policy"
2022-12-03 19:56:09 +01:00
gitutils "github.com/kyverno/kyverno/pkg/utils/git"
2021-02-07 20:26:56 -08:00
"github.com/spf13/cobra"
2022-11-29 09:04:49 +01:00
"golang.org/x/exp/slices"
2021-02-07 20:26:56 -08:00
corev1 "k8s.io/api/core/v1"
2021-10-01 14:16:33 +05:30
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2021-02-07 20:26:56 -08:00
"k8s.io/apimachinery/pkg/util/yaml"
2022-12-09 22:15:23 +05:30
"sigs.k8s.io/controller-runtime/pkg/log"
2021-02-01 16:22:41 +05:30
)
// Command returns version command
func Command ( ) * cobra . Command {
2021-02-18 01:00:41 +05:30
var cmd * cobra . Command
2022-03-09 13:10:53 +05:30
var testCase string
var fileName , gitBranch string
2022-12-03 19:56:09 +01:00
var registryAccess , failOnly , removeColor , manifestValidate , manifestMutate bool
2021-02-18 01:00:41 +05:30
cmd = & cobra . Command {
2022-02-21 10:53:29 +05:30
Use : "test <path_to_folder_Containing_test.yamls> [flags]\n kyverno test <path_to_gitRepository_with_dir> --git-branch <branchName>\n kyverno test --manifest-mutate > kyverno-test.yaml\n kyverno test --manifest-validate > kyverno-test.yaml" ,
// Args: cobra.ExactArgs(1),
2022-12-27 22:46:01 +08:00
Short : "Run tests from directory." ,
2021-10-01 14:16:33 +05:30
Long : longHelp ,
Example : exampleHelp ,
2021-02-01 23:34:15 +05:30
RunE : func ( cmd * cobra . Command , dirPath [ ] string ) ( err error ) {
2021-02-01 16:22:41 +05:30
defer func ( ) {
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
log . Log . Error ( err , "failed to sanitize" )
err = fmt . Errorf ( "internal error" )
}
}
} ( )
2022-12-03 19:56:09 +01:00
if manifestMutate {
manifest . PrintMutate ( )
} else if manifestValidate {
manifest . PrintValidate ( )
} else {
store . SetRegistryAccess ( registryAccess )
2023-04-12 14:51:03 +02:00
_ , err = testCommandExecute ( dirPath , fileName , gitBranch , testCase , failOnly , removeColor , false )
2022-12-03 19:56:09 +01:00
if err != nil {
log . Log . V ( 3 ) . Info ( "a directory is required" )
return err
}
2021-02-01 16:22:41 +05:30
}
return nil
} ,
}
2021-12-17 12:01:34 +05:30
cmd . Flags ( ) . StringVarP ( & fileName , "file-name" , "f" , "kyverno-test.yaml" , "test filename" )
2021-12-20 11:39:53 +05:30
cmd . Flags ( ) . StringVarP ( & gitBranch , "git-branch" , "b" , "" , "test github repository branch" )
2022-03-09 13:10:53 +05:30
cmd . Flags ( ) . StringVarP ( & testCase , "test-case-selector" , "t" , "" , ` run some specific test cases by passing a string argument in double quotes to this flag like - "policy=<policy_name>, rule=<rule_name>, resource=<resource_name". The argument could be any combination of policy, rule and resource. ` )
2022-12-03 19:56:09 +01:00
cmd . Flags ( ) . BoolVarP ( & manifestMutate , "manifest-mutate" , "" , false , "prints out a template test manifest for a mutate policy" )
cmd . Flags ( ) . BoolVarP ( & manifestValidate , "manifest-validate" , "" , false , "prints out a template test manifest for a validate policy" )
2022-03-16 09:56:47 +05:30
cmd . Flags ( ) . BoolVarP ( & registryAccess , "registry" , "" , false , "If set to true, access the image registry using local docker credentials to populate external data" )
2022-07-22 20:02:12 +05:30
cmd . Flags ( ) . BoolVarP ( & failOnly , "fail-only" , "" , false , "If set to true, display all the failing test only as output for the test command" )
2022-08-19 19:11:19 +05:30
cmd . Flags ( ) . BoolVarP ( & removeColor , "remove-color" , "" , false , "Remove any color from output" )
2021-02-18 01:00:41 +05:30
return cmd
2021-02-01 16:22:41 +05:30
}
type Table struct {
2021-02-07 20:26:56 -08:00
ID int ` header:"#" `
2021-09-02 23:11:35 +05:30
Policy string ` header:"policy" `
Rule string ` header:"rule" `
Resource string ` header:"resource" `
2021-02-07 20:26:56 -08:00
Result string ` header:"result" `
2021-02-01 16:22:41 +05:30
}
2021-03-05 05:39:18 +05:30
type resultCounts struct {
2021-09-02 23:11:35 +05:30
Skip int
Pass int
Fail int
2021-03-05 05:39:18 +05:30
}
2023-03-23 10:32:01 +01:00
var ftable [ ] Table
2022-06-02 23:01:46 +05:30
2023-04-12 14:51:03 +02:00
func testCommandExecute (
dirPath [ ] string ,
fileName string ,
gitBranch string ,
testCase string ,
failOnly bool ,
removeColor bool ,
auditWarn bool ,
) ( rc * resultCounts , err error ) {
2023-04-12 17:17:51 +02:00
// check input dir
2021-02-01 23:34:15 +05:30
if len ( dirPath ) == 0 {
2022-05-09 18:55:35 +02:00
return rc , sanitizederror . NewWithError ( "a directory is required" , err )
2021-02-07 20:26:56 -08:00
}
2023-04-12 17:17:51 +02:00
// parse filter
filter := parseFilter ( testCase )
// init openapi manager
2023-02-09 16:15:51 +01:00
openApiManager , err := openapi . NewManager ( log . Log )
2022-02-24 15:34:12 +00:00
if err != nil {
return rc , fmt . Errorf ( "unable to create open api controller, %w" , err )
}
2023-04-12 17:17:51 +02:00
var errors [ ] error
fs := memfs . New ( )
rc = & resultCounts { }
var testYamlCount int
2022-05-10 21:28:45 +02:00
if strings . Contains ( dirPath [ 0 ] , "https://" ) {
2021-03-05 05:39:18 +05:30
gitURL , err := url . Parse ( dirPath [ 0 ] )
2021-02-01 16:22:41 +05:30
if err != nil {
2021-03-05 05:39:18 +05:30
return rc , sanitizederror . NewWithError ( "failed to parse URL" , err )
2021-02-01 16:22:41 +05:30
}
2021-09-03 16:41:13 +05:30
2021-03-05 05:39:18 +05:30
pathElems := strings . Split ( gitURL . Path [ 1 : ] , "/" )
2021-09-02 23:11:35 +05:30
if len ( pathElems ) <= 1 {
2021-12-20 11:39:53 +05:30
err := fmt . Errorf ( "invalid URL path %s - expected https://github.com/:owner/:repository/:branch (without --git-branch flag) OR https://github.com/:owner/:repository/:directory (with --git-branch flag)" , gitURL . Path )
2021-03-05 05:39:18 +05:30
fmt . Printf ( "Error: failed to parse URL \nCause: %s\n" , err )
os . Exit ( 1 )
2021-02-01 16:22:41 +05:30
}
2021-09-03 16:41:13 +05:30
2021-03-12 01:16:36 +05:30
gitURL . Path = strings . Join ( [ ] string { pathElems [ 0 ] , pathElems [ 1 ] } , "/" )
2021-03-05 05:39:18 +05:30
repoURL := gitURL . String ( )
2021-12-20 11:39:53 +05:30
var gitPathToYamls string
if gitBranch == "" {
gitPathToYamls = "/"
if string ( dirPath [ 0 ] [ len ( dirPath [ 0 ] ) - 1 ] ) == "/" {
gitBranch = strings . ReplaceAll ( dirPath [ 0 ] , repoURL + "/" , "" )
} else {
gitBranch = strings . ReplaceAll ( dirPath [ 0 ] , repoURL , "" )
}
if gitBranch == "" {
gitBranch = "main"
} else if string ( gitBranch [ 0 ] ) == "/" {
gitBranch = gitBranch [ 1 : ]
}
} else {
if string ( dirPath [ 0 ] [ len ( dirPath [ 0 ] ) - 1 ] ) == "/" {
gitPathToYamls = strings . ReplaceAll ( dirPath [ 0 ] , repoURL + "/" , "/" )
} else {
gitPathToYamls = strings . ReplaceAll ( dirPath [ 0 ] , repoURL , "/" )
}
2021-09-02 23:11:35 +05:30
}
2021-09-03 16:41:13 +05:30
2022-12-03 19:56:09 +01:00
_ , cloneErr := gitutils . Clone ( repoURL , fs , gitBranch )
2021-03-12 01:16:36 +05:30
if cloneErr != nil {
fmt . Printf ( "Error: failed to clone repository \nCause: %s\n" , cloneErr )
log . Log . V ( 3 ) . Info ( fmt . Sprintf ( "failed to clone repository %v as it is not valid" , repoURL ) , "error" , cloneErr )
2021-03-05 05:39:18 +05:30
os . Exit ( 1 )
2021-02-01 16:22:41 +05:30
}
2021-09-03 16:41:13 +05:30
2022-12-03 19:56:09 +01:00
policyYamls , err := gitutils . ListYamls ( fs , gitPathToYamls )
2021-02-01 16:22:41 +05:30
if err != nil {
2021-03-05 05:39:18 +05:30
return rc , sanitizederror . NewWithError ( "failed to list YAMLs in repository" , err )
2021-02-01 16:22:41 +05:30
}
sort . Strings ( policyYamls )
2021-09-03 16:41:13 +05:30
2021-02-01 16:22:41 +05:30
for _ , yamlFilePath := range policyYamls {
2021-02-07 20:26:56 -08:00
file , err := fs . Open ( yamlFilePath )
2021-05-03 08:20:22 -04:00
if err != nil {
errors = append ( errors , sanitizederror . NewWithError ( "Error: failed to open file" , err ) )
continue
}
2021-09-03 16:41:13 +05:30
2022-04-27 17:19:40 +05:30
if path . Base ( file . Name ( ) ) == fileName {
2021-03-12 01:16:36 +05:30
testYamlCount ++
2021-02-18 01:00:41 +05:30
policyresoucePath := strings . Trim ( yamlFilePath , fileName )
2022-09-30 15:25:19 +08:00
bytes , err := io . ReadAll ( file )
2021-02-18 01:00:41 +05:30
if err != nil {
2021-05-03 08:20:22 -04:00
errors = append ( errors , sanitizederror . NewWithError ( "Error: failed to read file" , err ) )
2021-02-18 01:00:41 +05:30
continue
}
2021-09-03 16:41:13 +05:30
2021-02-18 01:00:41 +05:30
policyBytes , err := yaml . ToJSON ( bytes )
if err != nil {
2021-05-03 08:20:22 -04:00
errors = append ( errors , sanitizederror . NewWithError ( "failed to convert to JSON" , err ) )
2021-02-18 01:00:41 +05:30
continue
}
2023-04-12 17:17:51 +02:00
if err := applyPoliciesFromPath ( fs , policyBytes , true , policyresoucePath , rc , openApiManager , filter , failOnly , removeColor , auditWarn ) ; err != nil {
2021-03-05 05:39:18 +05:30
return rc , sanitizederror . NewWithError ( "failed to apply test command" , err )
2021-02-18 01:00:41 +05:30
}
2021-02-01 16:22:41 +05:30
}
2021-05-03 08:20:22 -04:00
}
2021-09-03 16:41:13 +05:30
2021-05-03 08:20:22 -04:00
if testYamlCount == 0 {
fmt . Printf ( "\n No test yamls available \n" )
2021-02-01 16:22:41 +05:30
}
} else {
2022-01-14 14:15:59 +05:30
var testFiles int
2021-02-01 23:34:15 +05:30
path := filepath . Clean ( dirPath [ 0 ] )
2023-04-12 17:17:51 +02:00
errors = getLocalDirTestFiles ( fs , path , fileName , rc , & testFiles , openApiManager , filter , failOnly , removeColor , auditWarn )
2022-01-14 14:15:59 +05:30
if testFiles == 0 {
fmt . Printf ( "\n No test files found. Please provide test YAML files named kyverno-test.yaml \n" )
}
2021-05-03 08:20:22 -04:00
}
2021-09-03 16:41:13 +05:30
2021-05-03 08:20:22 -04:00
if len ( errors ) > 0 && log . Log . V ( 1 ) . Enabled ( ) {
2021-10-03 22:36:06 -07:00
fmt . Printf ( "test errors: \n" )
2021-05-03 08:20:22 -04:00
for _ , e := range errors {
fmt . Printf ( " %v \n" , e . Error ( ) )
2021-02-01 16:22:41 +05:30
}
}
2021-10-03 22:36:06 -07:00
2022-07-22 20:02:12 +05:30
if ! failOnly {
fmt . Printf ( "\nTest Summary: %d tests passed and %d tests failed\n" , rc . Pass + rc . Skip , rc . Fail )
} else {
fmt . Printf ( "\nTest Summary: %d out of %d tests failed\n" , rc . Fail , rc . Pass + rc . Skip + rc . Fail )
}
2022-06-02 23:01:46 +05:30
fmt . Printf ( "\n" )
2022-05-02 03:52:40 +05:30
2022-07-22 20:02:12 +05:30
if rc . Fail > 0 && ! failOnly {
2022-12-07 13:02:43 +01:00
printFailedTestResult ( removeColor )
2021-03-05 05:39:18 +05:30
os . Exit ( 1 )
}
os . Exit ( 0 )
return rc , nil
2021-02-01 16:22:41 +05:30
}
2021-02-01 23:34:15 +05:30
2023-04-12 14:51:03 +02:00
func getLocalDirTestFiles (
fs billy . Filesystem ,
path string ,
fileName string ,
rc * resultCounts ,
testFiles * int ,
openApiManager openapi . Manager ,
2023-04-12 17:17:51 +02:00
filter filter ,
2023-04-12 14:51:03 +02:00
failOnly bool ,
removeColor bool ,
auditWarn bool ,
) [ ] error {
2021-05-03 08:20:22 -04:00
var errors [ ] error
2022-01-14 14:15:59 +05:30
2022-09-30 15:25:19 +08:00
files , err := os . ReadDir ( path )
2021-02-18 01:00:41 +05:30
if err != nil {
2021-05-03 08:20:22 -04:00
return [ ] error { fmt . Errorf ( "failed to read %v: %v" , path , err . Error ( ) ) }
2021-02-18 01:00:41 +05:30
}
for _ , file := range files {
if file . IsDir ( ) {
2023-04-12 17:17:51 +02:00
getLocalDirTestFiles ( fs , filepath . Join ( path , file . Name ( ) ) , fileName , rc , testFiles , openApiManager , filter , failOnly , removeColor , auditWarn )
2021-02-18 01:00:41 +05:30
continue
}
2022-03-22 08:39:08 +00:00
if file . Name ( ) == fileName {
2022-01-14 14:15:59 +05:30
* testFiles ++
2021-10-14 00:05:13 +02:00
// We accept the risk of including files here as we read the test dir only.
2022-09-30 15:25:19 +08:00
yamlFile , err := os . ReadFile ( filepath . Join ( path , file . Name ( ) ) ) // #nosec G304
2021-02-18 01:00:41 +05:30
if err != nil {
2021-05-03 08:20:22 -04:00
errors = append ( errors , sanitizederror . NewWithError ( "unable to read yaml" , err ) )
2021-02-18 01:00:41 +05:30
continue
}
valuesBytes , err := yaml . ToJSON ( yamlFile )
if err != nil {
2021-05-03 08:20:22 -04:00
errors = append ( errors , sanitizederror . NewWithError ( "failed to convert json" , err ) )
2021-02-18 01:00:41 +05:30
continue
}
2023-04-12 17:17:51 +02:00
if err := applyPoliciesFromPath ( fs , valuesBytes , false , path , rc , openApiManager , filter , failOnly , removeColor , auditWarn ) ; err != nil {
2021-05-03 08:20:22 -04:00
errors = append ( errors , sanitizederror . NewWithError ( fmt . Sprintf ( "failed to apply test command from file %s" , file . Name ( ) ) , err ) )
2021-02-18 01:00:41 +05:30
continue
}
}
}
2021-05-03 08:20:22 -04:00
return errors
2021-02-18 01:00:41 +05:30
}
2023-04-12 14:51:03 +02:00
func buildPolicyResults (
engineResponses [ ] engineapi . EngineResponse ,
testResults [ ] api . TestResults ,
policyResourcePath string ,
fs billy . Filesystem ,
isGit bool ,
auditWarn bool ,
) ( map [ string ] policyreportv1alpha2 . PolicyReportResult , [ ] api . TestResults ) {
results := map [ string ] policyreportv1alpha2 . PolicyReportResult { }
2021-09-07 20:23:03 +05:30
2022-01-23 05:54:22 -08:00
for _ , resp := range engineResponses {
2023-02-10 09:11:21 +01:00
policyName := resp . Policy . GetName ( )
2023-02-10 15:04:41 +01:00
resourceName := resp . Resource . GetName ( )
resourceKind := resp . Resource . GetKind ( )
resourceNamespace := resp . Resource . GetNamespace ( )
2023-02-10 09:11:21 +01:00
policyNamespace := resp . Policy . GetNamespace ( )
2021-07-06 10:44:43 +05:30
2021-05-07 19:27:15 -04:00
var rules [ ] string
for _ , rule := range resp . PolicyResponse . Rules {
2023-04-05 12:35:38 +02:00
rules = append ( rules , rule . Name ( ) )
2021-05-07 19:27:15 -04:00
}
2021-10-01 22:43:21 +05:30
2022-05-17 13:12:43 +02:00
result := policyreportv1alpha2 . PolicyReportResult {
2021-05-07 19:27:15 -04:00
Policy : policyName ,
2022-04-28 11:11:14 +02:00
Resources : [ ] corev1 . ObjectReference {
2021-05-07 19:27:15 -04:00
{
Name : resourceName ,
} ,
} ,
2021-10-03 01:31:05 -07:00
Message : buildMessage ( resp ) ,
2021-05-07 19:27:15 -04:00
}
2021-10-01 22:43:21 +05:30
2022-01-23 05:54:22 -08:00
var patchedResourcePath [ ] string
2021-09-07 20:23:03 +05:30
for i , test := range testResults {
2021-10-01 14:16:33 +05:30
var userDefinedPolicyNamespace string
var userDefinedPolicyName string
2021-10-01 22:43:21 +05:30
found , err := isNamespacedPolicy ( test . Policy )
if err != nil {
log . Log . V ( 3 ) . Info ( "error while checking the policy is namespaced or not" , "policy: " , test . Policy , "error: " , err )
continue
}
2021-10-01 14:16:33 +05:30
if found {
userDefinedPolicyNamespace , userDefinedPolicyName = getUserDefinedPolicyNameAndNamespace ( test . Policy )
test . Policy = userDefinedPolicyName
}
2022-06-20 12:08:13 +05:30
if test . Resources != nil {
if test . Policy == policyName {
2022-08-24 15:08:24 +02:00
// results[].namespace value implicit set same as metadata.namespace until and unless
// user provides explicit values for results[].namespace in test yaml file.
2022-06-20 12:08:13 +05:30
if test . Namespace == "" {
test . Namespace = resourceNamespace
testResults [ i ] . Namespace = resourceNamespace
}
for _ , resource := range test . Resources {
if resource == resourceName {
var resultsKey string
resultsKey = GetResultKeyAccordingToTestResults ( userDefinedPolicyNamespace , test . Policy , test . Rule , test . Namespace , test . Kind , resource )
2022-11-29 09:04:49 +01:00
if ! slices . Contains ( rules , test . Rule ) {
if ! slices . Contains ( rules , "autogen-" + test . Rule ) {
if ! slices . Contains ( rules , "autogen-cronjob-" + test . Rule ) {
2022-06-20 12:08:13 +05:30
result . Result = policyreportv1alpha2 . StatusSkip
} else {
testResults [ i ] . AutoGeneratedRule = "autogen-cronjob"
test . Rule = "autogen-cronjob-" + test . Rule
resultsKey = GetResultKeyAccordingToTestResults ( userDefinedPolicyNamespace , test . Policy , test . Rule , test . Namespace , test . Kind , resource )
}
} else {
testResults [ i ] . AutoGeneratedRule = "autogen"
test . Rule = "autogen-" + test . Rule
resultsKey = GetResultKeyAccordingToTestResults ( userDefinedPolicyNamespace , test . Policy , test . Rule , test . Namespace , test . Kind , resource )
}
if results [ resultsKey ] . Result == "" {
result . Result = policyreportv1alpha2 . StatusSkip
results [ resultsKey ] = result
}
}
patchedResourcePath = append ( patchedResourcePath , test . PatchedResource )
if _ , ok := results [ resultsKey ] ; ! ok {
results [ resultsKey ] = result
}
}
}
2022-06-15 17:59:13 +05:30
}
2022-06-20 12:08:13 +05:30
}
if test . Resource != "" {
if test . Policy == policyName && test . Resource == resourceName {
var resultsKey string
resultsKey = GetResultKeyAccordingToTestResults ( userDefinedPolicyNamespace , test . Policy , test . Rule , test . Namespace , test . Kind , test . Resource )
2022-11-29 09:04:49 +01:00
if ! slices . Contains ( rules , test . Rule ) {
if ! slices . Contains ( rules , "autogen-" + test . Rule ) {
if ! slices . Contains ( rules , "autogen-cronjob-" + test . Rule ) {
2022-06-20 12:08:13 +05:30
result . Result = policyreportv1alpha2 . StatusSkip
} else {
testResults [ i ] . AutoGeneratedRule = "autogen-cronjob"
test . Rule = "autogen-cronjob-" + test . Rule
resultsKey = GetResultKeyAccordingToTestResults ( userDefinedPolicyNamespace , test . Policy , test . Rule , test . Namespace , test . Kind , test . Resource )
}
2021-09-07 22:27:29 +05:30
} else {
2022-06-20 12:08:13 +05:30
testResults [ i ] . AutoGeneratedRule = "autogen"
test . Rule = "autogen-" + test . Rule
2021-10-01 14:16:33 +05:30
resultsKey = GetResultKeyAccordingToTestResults ( userDefinedPolicyNamespace , test . Policy , test . Rule , test . Namespace , test . Kind , test . Resource )
2021-09-07 22:27:29 +05:30
}
2022-06-20 12:08:13 +05:30
if results [ resultsKey ] . Result == "" {
result . Result = policyreportv1alpha2 . StatusSkip
results [ resultsKey ] = result
}
2021-10-01 14:16:33 +05:30
}
2021-10-01 22:43:21 +05:30
2022-06-20 12:08:13 +05:30
patchedResourcePath = append ( patchedResourcePath , test . PatchedResource )
if _ , ok := results [ resultsKey ] ; ! ok {
2021-10-01 14:16:33 +05:30
results [ resultsKey ] = result
2021-09-07 20:23:03 +05:30
}
2021-05-07 19:27:15 -04:00
}
}
2022-05-25 19:56:22 +05:30
for _ , rule := range resp . PolicyResponse . Rules {
2023-04-05 12:35:38 +02:00
if rule . RuleType ( ) != engineapi . Generation || test . Rule != rule . Name ( ) {
2022-05-25 19:56:22 +05:30
continue
}
var resultsKey [ ] string
var resultKey string
var result policyreportv1alpha2 . PolicyReportResult
2023-04-05 12:35:38 +02:00
resultsKey = GetAllPossibleResultsKey ( policyNamespace , policyName , rule . Name ( ) , resourceNamespace , resourceKind , resourceName )
2022-05-25 19:56:22 +05:30
for _ , key := range resultsKey {
if val , ok := results [ key ] ; ok {
result = val
resultKey = key
} else {
continue
}
2023-04-05 12:35:38 +02:00
if rule . Status ( ) == engineapi . RuleStatusSkip {
2022-05-25 19:56:22 +05:30
result . Result = policyreportv1alpha2 . StatusSkip
2023-04-05 12:35:38 +02:00
} else if rule . Status ( ) == engineapi . RuleStatusError {
2022-05-25 19:56:22 +05:30
result . Result = policyreportv1alpha2 . StatusError
} else {
var x string
result . Result = policyreportv1alpha2 . StatusFail
2023-04-05 12:35:38 +02:00
x = getAndCompareResource ( test . GeneratedResource , rule . GeneratedResource ( ) , isGit , policyResourcePath , fs , true )
2022-05-25 19:56:22 +05:30
if x == "pass" {
result . Result = policyreportv1alpha2 . StatusPass
}
}
results [ resultKey ] = result
}
}
2021-10-01 14:16:33 +05:30
2023-04-12 14:51:03 +02:00
for _ , rule := range resp . PolicyResponse . Rules {
if rule . RuleType ( ) != engineapi . Mutation || test . Rule != rule . Name ( ) {
2021-10-01 14:16:33 +05:30
continue
}
2021-10-07 15:25:44 -07:00
2023-04-12 14:51:03 +02:00
var resultsKey [ ] string
var resultKey string
var result policyreportv1alpha2 . PolicyReportResult
resultsKey = GetAllPossibleResultsKey ( policyNamespace , policyName , rule . Name ( ) , resourceNamespace , resourceKind , resourceName )
for _ , key := range resultsKey {
if val , ok := results [ key ] ; ok {
result = val
resultKey = key
} else {
continue
}
if rule . Status ( ) == engineapi . RuleStatusSkip {
result . Result = policyreportv1alpha2 . StatusSkip
} else if rule . Status ( ) == engineapi . RuleStatusError {
result . Result = policyreportv1alpha2 . StatusError
} else {
var x string
for _ , path := range patchedResourcePath {
result . Result = policyreportv1alpha2 . StatusFail
x = getAndCompareResource ( path , resp . PatchedResource , isGit , policyResourcePath , fs , false )
if x == "pass" {
result . Result = policyreportv1alpha2 . StatusPass
break
}
2021-10-07 15:25:44 -07:00
}
2021-10-01 14:16:33 +05:30
}
2021-10-07 15:25:44 -07:00
2023-04-12 14:51:03 +02:00
results [ resultKey ] = result
}
2021-10-01 14:16:33 +05:30
}
2021-09-07 20:23:03 +05:30
2023-04-12 14:51:03 +02:00
for _ , rule := range resp . PolicyResponse . Rules {
if rule . RuleType ( ) != engineapi . Validation && rule . RuleType ( ) != engineapi . ImageVerify || test . Rule != rule . Name ( ) {
2021-02-01 16:22:41 +05:30
continue
}
2021-09-07 22:27:29 +05:30
2023-04-12 14:51:03 +02:00
var resultsKey [ ] string
2021-10-01 14:16:33 +05:30
var resultKey string
2023-04-12 14:51:03 +02:00
var result policyreportv1alpha2 . PolicyReportResult
resultsKey = GetAllPossibleResultsKey ( policyNamespace , policyName , rule . Name ( ) , resourceNamespace , resourceKind , resourceName )
for _ , key := range resultsKey {
2022-02-13 20:35:11 -08:00
if val , ok := results [ key ] ; ok {
2021-10-01 14:16:33 +05:30
result = val
2022-02-13 20:35:11 -08:00
resultKey = key
2021-10-01 14:16:33 +05:30
} else {
continue
}
2021-09-07 22:27:29 +05:30
2023-04-12 14:51:03 +02:00
ann := resp . Policy . GetAnnotations ( )
if rule . Status ( ) == engineapi . RuleStatusSkip {
result . Result = policyreportv1alpha2 . StatusSkip
} else if rule . Status ( ) == engineapi . RuleStatusError {
result . Result = policyreportv1alpha2 . StatusError
} else if rule . Status ( ) == engineapi . RuleStatusPass {
result . Result = policyreportv1alpha2 . StatusPass
} else if rule . Status ( ) == engineapi . RuleStatusFail {
if scored , ok := ann [ kyvernov1 . AnnotationPolicyScored ] ; ok && scored == "false" {
result . Result = policyreportv1alpha2 . StatusWarn
} else if auditWarn && resp . GetValidationFailureAction ( ) . Audit ( ) {
result . Result = policyreportv1alpha2 . StatusWarn
} else {
result . Result = policyreportv1alpha2 . StatusFail
}
} else {
fmt . Println ( rule )
}
results [ resultKey ] = result
}
2021-02-01 16:22:41 +05:30
}
}
}
2021-09-07 20:23:03 +05:30
return results , testResults
2021-02-01 16:22:41 +05:30
}
2022-01-23 05:54:22 -08:00
func GetAllPossibleResultsKey ( policyNamespace , policy , rule , resourceNamespace , kind , resource string ) [ ] string {
2021-10-01 14:16:33 +05:30
var resultsKey [ ] string
resultKey1 := fmt . Sprintf ( "%s-%s-%s-%s" , policy , rule , kind , resource )
2022-01-23 05:54:22 -08:00
resultKey2 := fmt . Sprintf ( "%s-%s-%s-%s-%s" , policy , rule , resourceNamespace , kind , resource )
resultKey3 := fmt . Sprintf ( "%s-%s-%s-%s-%s" , policyNamespace , policy , rule , kind , resource )
resultKey4 := fmt . Sprintf ( "%s-%s-%s-%s-%s-%s" , policyNamespace , policy , rule , resourceNamespace , kind , resource )
2021-10-01 14:16:33 +05:30
resultsKey = append ( resultsKey , resultKey1 , resultKey2 , resultKey3 , resultKey4 )
return resultsKey
}
2021-10-01 22:43:21 +05:30
func GetResultKeyAccordingToTestResults ( policyNs , policy , rule , resourceNs , kind , resource string ) string {
2021-10-01 14:16:33 +05:30
var resultKey string
resultKey = fmt . Sprintf ( "%s-%s-%s-%s" , policy , rule , kind , resource )
2021-10-01 22:43:21 +05:30
if policyNs != "" && resourceNs != "" {
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s-%s" , policyNs , policy , rule , resourceNs , kind , resource )
} else if policyNs != "" {
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s" , policyNs , policy , rule , kind , resource )
} else if resourceNs != "" {
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s" , policy , rule , resourceNs , kind , resource )
2021-10-01 14:16:33 +05:30
}
return resultKey
}
func isNamespacedPolicy ( policyNames string ) ( bool , error ) {
return regexp . MatchString ( "^[a-z]*/[a-z]*" , policyNames )
}
func getUserDefinedPolicyNameAndNamespace ( policyName string ) ( string , string ) {
2021-10-01 22:54:10 +05:30
if strings . Contains ( policyName , "/" ) {
2021-10-29 16:06:03 +01:00
parts := strings . Split ( policyName , "/" )
namespace := parts [ 0 ]
policy := parts [ 1 ]
2021-10-01 22:54:10 +05:30
return namespace , policy
}
return "" , policyName
2021-10-01 14:16:33 +05:30
}
2022-05-25 19:56:22 +05:30
// getAndCompareResource --> Get the patchedResource or generatedResource from the path provided by user
// And compare this resource with engine generated resource.
func getAndCompareResource ( path string , engineResource unstructured . Unstructured , isGit bool , policyResourcePath string , fs billy . Filesystem , isGenerate bool ) string {
2021-10-01 14:16:33 +05:30
var status string
2022-05-25 19:56:22 +05:30
resourceType := "patchedResource"
if isGenerate {
resourceType = "generatedResource"
}
userResource , err := common . GetResourceFromPath ( fs , path , isGit , policyResourcePath , resourceType )
2021-10-01 14:16:33 +05:30
if err != nil {
2022-07-06 13:24:28 +05:30
fmt . Printf ( "Error: failed to load resources\nCause: %s\n" , err )
return ""
2021-10-01 14:16:33 +05:30
}
2022-05-25 19:56:22 +05:30
matched , err := generate . ValidateResourceWithPattern ( log . Log , engineResource . UnstructuredContent ( ) , userResource . UnstructuredContent ( ) )
2021-10-01 14:16:33 +05:30
if err != nil {
2022-05-25 19:56:22 +05:30
log . Log . V ( 3 ) . Info ( resourceType + " mismatch" , "error" , err . Error ( ) )
2021-10-01 14:16:33 +05:30
status = "fail"
2022-11-11 01:19:49 -06:00
} else if matched == "" {
2021-10-01 14:16:33 +05:30
status = "pass"
}
return status
}
2023-04-12 14:51:03 +02:00
func buildMessage ( resp engineapi . EngineResponse ) string {
2021-10-03 01:31:05 -07:00
var bldr strings . Builder
for _ , ruleResp := range resp . PolicyResponse . Rules {
2023-04-05 12:35:38 +02:00
fmt . Fprintf ( & bldr , " %s: %s \n" , ruleResp . Name ( ) , ruleResp . Status ( ) )
fmt . Fprintf ( & bldr , " %s \n" , ruleResp . Message ( ) )
2021-10-03 01:31:05 -07:00
}
return bldr . String ( )
}
2021-10-01 17:52:23 +05:30
func getFullPath ( paths [ ] string , policyResourcePath string , isGit bool ) [ ] string {
var pols [ ] string
2021-10-01 14:16:33 +05:30
var pol string
if ! isGit {
2021-10-01 17:52:23 +05:30
for _ , path := range paths {
pol = filepath . Join ( policyResourcePath , path )
pols = append ( pols , pol )
}
return pols
2021-10-01 14:16:33 +05:30
}
2021-10-01 17:52:23 +05:30
return paths
2021-10-01 14:16:33 +05:30
}
2023-04-12 14:51:03 +02:00
func applyPoliciesFromPath (
fs billy . Filesystem ,
policyBytes [ ] byte ,
isGit bool ,
policyResourcePath string ,
rc * resultCounts ,
openApiManager openapi . Manager ,
2023-04-12 17:17:51 +02:00
filter filter ,
2023-04-12 14:51:03 +02:00
failOnly bool ,
removeColor bool ,
auditWarn bool ,
) ( err error ) {
engineResponses := make ( [ ] engineapi . EngineResponse , 0 )
2022-05-17 16:40:51 +02:00
var dClient dclient . Interface
2022-12-03 19:56:09 +01:00
values := & api . Test { }
2021-02-02 18:43:19 +05:30
var variablesString string
2021-09-02 23:11:35 +05:30
var resultCounts common . ResultCounts
2021-02-01 16:22:41 +05:30
2022-04-12 09:30:49 +05:30
store . SetMock ( true )
2021-02-01 16:22:41 +05:30
if err := json . Unmarshal ( policyBytes , values ) ; err != nil {
return sanitizederror . NewWithError ( "failed to decode yaml" , err )
2021-02-07 20:26:56 -08:00
}
2021-02-18 01:00:41 +05:30
2023-04-12 17:17:51 +02:00
var filteredResults [ ] api . TestResults
for _ , res := range values . Results {
if filter ( res ) {
filteredResults = append ( filteredResults , res )
2022-03-09 13:10:53 +05:30
}
}
2023-04-12 17:17:51 +02:00
values . Results = filteredResults
2022-03-09 13:10:53 +05:30
if len ( values . Results ) == 0 {
return nil
}
2023-03-23 10:32:01 +01:00
fmt . Printf ( "\nExecuting %s...\n" , values . Name )
2022-03-09 13:10:53 +05:30
valuesFile := values . Variables
2022-04-12 09:30:49 +05:30
userInfoFile := values . UserInfo
2022-12-09 22:15:23 +05:30
variables , globalValMap , valuesMap , namespaceSelectorMap , subresources , err := common . GetVariable ( variablesString , values . Variables , fs , isGit , policyResourcePath )
2021-02-01 16:22:41 +05:30
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
2021-02-07 20:26:56 -08:00
return sanitizederror . NewWithError ( "failed to decode yaml" , err )
2021-02-01 16:22:41 +05:30
}
2021-02-07 20:26:56 -08:00
return err
2021-02-01 16:22:41 +05:30
}
2021-02-18 01:00:41 +05:30
2022-04-12 09:30:49 +05:30
// get the user info as request info from a different file
2022-04-25 20:20:40 +08:00
var userInfo v1beta1 . RequestInfo
2022-05-11 20:51:13 +05:30
2022-04-12 09:30:49 +05:30
if userInfoFile != "" {
2023-02-08 14:19:56 +01:00
userInfo , err = common . GetUserInfoFromPath ( fs , userInfoFile , isGit , policyResourcePath )
2022-04-12 09:30:49 +05:30
if err != nil {
fmt . Printf ( "Error: failed to load request info\nCause: %s\n" , err )
os . Exit ( 1 )
}
}
2021-10-01 17:52:23 +05:30
policyFullPath := getFullPath ( values . Policies , policyResourcePath , isGit )
resourceFullPath := getFullPath ( values . Resources , policyResourcePath , isGit )
2021-02-18 01:00:41 +05:30
2021-10-01 14:16:33 +05:30
for i , result := range values . Results {
2021-10-01 17:52:23 +05:30
arrPatchedResource := [ ] string { result . PatchedResource }
2022-05-25 19:56:22 +05:30
arrGeneratedResource := [ ] string { result . GeneratedResource }
arrCloneSourceResource := [ ] string { result . CloneSourceResource }
2021-10-01 17:52:23 +05:30
patchedResourceFullPath := getFullPath ( arrPatchedResource , policyResourcePath , isGit )
2022-05-25 19:56:22 +05:30
generatedResourceFullPath := getFullPath ( arrGeneratedResource , policyResourcePath , isGit )
CloneSourceResourceFullPath := getFullPath ( arrCloneSourceResource , policyResourcePath , isGit )
2021-10-01 17:52:23 +05:30
values . Results [ i ] . PatchedResource = patchedResourceFullPath [ 0 ]
2022-05-25 19:56:22 +05:30
values . Results [ i ] . GeneratedResource = generatedResourceFullPath [ 0 ]
values . Results [ i ] . CloneSourceResource = CloneSourceResourceFullPath [ 0 ]
2021-10-01 14:16:33 +05:30
}
2021-10-01 17:52:23 +05:30
policies , err := common . GetPoliciesFromPaths ( fs , policyFullPath , isGit , policyResourcePath )
2021-02-01 16:22:41 +05:30
if err != nil {
fmt . Printf ( "Error: failed to load policies\nCause: %s\n" , err )
os . Exit ( 1 )
}
2021-07-09 18:01:46 -07:00
2023-03-23 10:32:01 +01:00
var filteredPolicies [ ] kyvernov1 . PolicyInterface
2022-03-09 13:10:53 +05:30
for _ , p := range policies {
for _ , res := range values . Results {
2022-03-31 08:44:00 +02:00
if p . GetName ( ) == res . Policy {
2022-03-09 13:10:53 +05:30
filteredPolicies = append ( filteredPolicies , p )
break
}
}
}
2022-08-24 15:08:24 +02:00
ruleToCloneSourceResource := map [ string ] string { }
2022-03-09 13:10:53 +05:30
for _ , p := range filteredPolicies {
2023-03-23 10:32:01 +01:00
var filteredRules [ ] kyvernov1 . Rule
2022-03-09 13:10:53 +05:30
2022-03-28 16:01:27 +02:00
for _ , rule := range autogen . ComputeRules ( p ) {
2022-03-09 13:10:53 +05:30
for _ , res := range values . Results {
if rule . Name == res . Rule {
filteredRules = append ( filteredRules , rule )
2022-05-25 19:56:22 +05:30
if rule . HasGenerate ( ) {
ruleUnstr , err := generate . GetUnstrRule ( rule . Generation . DeepCopy ( ) )
if err != nil {
fmt . Printf ( "Error: failed to get unstructured rule\nCause: %s\n" , err )
break
}
genClone , _ , err := unstructured . NestedMap ( ruleUnstr . Object , "clone" )
if err != nil {
fmt . Printf ( "Error: failed to read data\nCause: %s\n" , err )
break
}
if len ( genClone ) != 0 {
ruleToCloneSourceResource [ rule . Name ] = res . CloneSourceResource
}
}
2022-03-09 13:10:53 +05:30
break
}
}
}
2022-03-31 08:44:00 +02:00
p . GetSpec ( ) . SetRules ( filteredRules )
2022-03-09 13:10:53 +05:30
}
policies = filteredPolicies
2022-10-26 01:43:46 +02:00
err = common . PrintMutatedPolicy ( policies )
2021-09-03 16:41:13 +05:30
if err != nil {
return sanitizederror . NewWithError ( "failed to print mutated policy" , err )
2021-09-02 23:11:35 +05:30
}
2021-09-03 16:41:13 +05:30
2022-10-26 01:43:46 +02:00
resources , err := common . GetResourceAccordingToResourcePath ( fs , resourceFullPath , false , policies , dClient , "" , false , isGit , policyResourcePath )
2021-02-07 20:26:56 -08:00
if err != nil {
fmt . Printf ( "Error: failed to load resources\nCause: %s\n" , err )
os . Exit ( 1 )
}
2021-07-09 18:01:46 -07:00
2023-03-22 19:12:34 +01:00
checkableResources := selectResourcesForCheck ( resources , values )
2022-12-02 20:44:26 +05:30
2021-02-07 20:26:56 -08:00
msgPolicies := "1 policy"
2022-10-26 01:43:46 +02:00
if len ( policies ) > 1 {
2021-02-07 20:26:56 -08:00
msgPolicies = fmt . Sprintf ( "%d policies" , len ( policies ) )
}
2021-07-09 18:01:46 -07:00
2021-02-07 20:26:56 -08:00
msgResources := "1 resource"
2023-03-22 19:12:34 +01:00
if len ( checkableResources ) > 1 {
msgResources = fmt . Sprintf ( "%d resources" , len ( checkableResources ) )
2021-02-07 20:26:56 -08:00
}
2021-07-09 18:01:46 -07:00
2023-03-22 19:12:34 +01:00
if len ( policies ) > 0 && len ( checkableResources ) > 0 {
2023-03-23 10:32:01 +01:00
fmt . Printf ( "applying %s to %s... \n" , msgPolicies , msgResources )
2021-02-07 20:26:56 -08:00
}
2021-07-09 18:01:46 -07:00
2022-10-26 01:43:46 +02:00
for _ , policy := range policies {
2023-03-01 14:52:20 +08:00
_ , err := policy2 . Validate ( policy , nil , nil , true , openApiManager )
2021-02-01 16:22:41 +05:30
if err != nil {
2022-03-31 08:44:00 +02:00
log . Log . Error ( err , "skipping invalid policy" , "name" , policy . GetName ( ) )
2021-02-07 20:26:56 -08:00
continue
2021-02-01 16:22:41 +05:30
}
2021-07-09 18:01:46 -07:00
2021-11-03 11:16:55 -07:00
matches := common . HasVariables ( policy )
2021-07-24 00:02:48 +05:30
variable := common . RemoveDuplicateAndObjectVariables ( matches )
2021-09-03 18:43:11 +05:30
if len ( variable ) > 0 {
if len ( variables ) == 0 {
// check policy in variable file
2022-03-31 08:44:00 +02:00
if valuesFile == "" || valuesMap [ policy . GetName ( ) ] == nil {
fmt . Printf ( "test skipped for policy %v (as required variables are not provided by the users) \n \n" , policy . GetName ( ) )
2021-09-03 18:43:11 +05:30
}
}
}
2022-12-09 22:15:23 +05:30
kindOnwhichPolicyIsApplied := common . GetKindsFromPolicy ( policy , subresources , dClient )
2021-09-02 23:11:35 +05:30
2023-03-22 19:12:34 +01:00
for _ , resource := range checkableResources {
2021-09-20 22:16:57 +05:30
thisPolicyResourceValues , err := common . CheckVariableForPolicy ( valuesMap , globalValMap , policy . GetName ( ) , resource . GetName ( ) , resource . GetKind ( ) , variables , kindOnwhichPolicyIsApplied , variable )
2021-09-03 16:41:13 +05:30
if err != nil {
2022-03-31 08:44:00 +02:00
return sanitizederror . NewWithError ( fmt . Sprintf ( "policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag" , policy . GetName ( ) , resource . GetName ( ) ) , err )
2021-02-01 16:22:41 +05:30
}
2022-10-19 22:09:15 +05:30
applyPolicyConfig := common . ApplyPolicyConfig {
Policy : policy ,
Resource : resource ,
MutateLogPath : "" ,
Variables : thisPolicyResourceValues ,
UserInfo : userInfo ,
PolicyReport : true ,
NamespaceSelectorMap : namespaceSelectorMap ,
Rc : & resultCounts ,
RuleToCloneSourceResource : ruleToCloneSourceResource ,
Client : dClient ,
2022-12-09 22:15:23 +05:30
Subresources : subresources ,
2022-10-19 22:09:15 +05:30
}
2023-04-12 14:51:03 +02:00
ers , err := common . ApplyPolicyOnResource ( applyPolicyConfig )
2021-02-07 20:26:56 -08:00
if err != nil {
2022-03-31 08:44:00 +02:00
return sanitizederror . NewWithError ( fmt . Errorf ( "failed to apply policy %v on resource %v" , policy . GetName ( ) , resource . GetName ( ) ) . Error ( ) , err )
2021-02-01 16:22:41 +05:30
}
2021-10-01 17:52:23 +05:30
engineResponses = append ( engineResponses , ers ... )
2021-02-01 16:22:41 +05:30
}
2021-02-07 20:26:56 -08:00
}
2023-04-12 14:51:03 +02:00
resultsMap , testResults := buildPolicyResults ( engineResponses , values . Results , policyResourcePath , fs , isGit , auditWarn )
2022-08-19 19:11:19 +05:30
resultErr := printTestResult ( resultsMap , testResults , rc , failOnly , removeColor )
2021-02-07 20:26:56 -08:00
if resultErr != nil {
2021-10-03 22:36:06 -07:00
return sanitizederror . NewWithError ( "failed to print test result:" , resultErr )
2021-02-07 20:26:56 -08:00
}
2021-10-03 22:36:06 -07:00
2021-02-01 16:22:41 +05:30
return
}
2023-03-22 19:12:34 +01:00
func selectResourcesForCheck ( resources [ ] * unstructured . Unstructured , values * api . Test ) [ ] * unstructured . Unstructured {
2023-03-23 10:32:01 +01:00
res , _ , _ := selectResourcesForCheckInternal ( resources , values )
return res
}
// selectResourcesForCheckInternal internal method to test duplicates and unused
func selectResourcesForCheckInternal ( resources [ ] * unstructured . Unstructured , values * api . Test ) ( [ ] * unstructured . Unstructured , int , int ) {
var duplicates int
var unused int
2023-03-22 19:12:34 +01:00
uniqResources := make ( map [ string ] * unstructured . Unstructured )
for i := range resources {
r := resources [ i ]
key := fmt . Sprintf ( "%s/%s/%s" , r . GetKind ( ) , r . GetName ( ) , r . GetNamespace ( ) )
if _ , ok := uniqResources [ key ] ; ok {
fmt . Println ( "skipping duplicate resource, resource :" , r )
2023-03-23 10:32:01 +01:00
duplicates ++
2023-03-22 19:12:34 +01:00
} else {
uniqResources [ key ] = r
}
}
selectedResources := map [ string ] * unstructured . Unstructured { }
for key := range uniqResources {
r := uniqResources [ key ]
for _ , res := range values . Results {
2023-03-23 10:32:01 +01:00
if res . Kind == r . GetKind ( ) {
for _ , testr := range res . Resources {
if r . GetName ( ) == testr {
selectedResources [ key ] = r
}
}
if r . GetName ( ) == res . Resource {
2023-03-22 19:12:34 +01:00
selectedResources [ key ] = r
}
}
}
}
var checkableResources [ ] * unstructured . Unstructured
for key := range selectedResources {
checkableResources = append ( checkableResources , selectedResources [ key ] )
2023-03-23 10:32:01 +01:00
delete ( uniqResources , key )
}
for _ , r := range uniqResources {
fmt . Println ( "skipping unused resource, resource :" , r )
unused ++
2023-03-22 19:12:34 +01:00
}
2023-03-23 10:32:01 +01:00
return checkableResources , duplicates , unused
2023-03-22 19:12:34 +01:00
}
2022-12-03 19:56:09 +01:00
func printTestResult ( resps map [ string ] policyreportv1alpha2 . PolicyReportResult , testResults [ ] api . TestResults , rc * resultCounts , failOnly , removeColor bool ) error {
2022-12-07 13:02:43 +01:00
printer := newTablePrinter ( removeColor )
2023-03-23 10:32:01 +01:00
var table [ ] Table
2021-10-01 22:43:21 +05:30
2022-08-24 15:08:24 +02:00
var countDeprecatedResource int
2022-10-11 18:00:11 +05:30
testCount := 1
for _ , v := range testResults {
2021-02-01 16:22:41 +05:30
res := new ( Table )
2022-10-11 18:00:11 +05:30
res . ID = testCount
if v . Resources == nil {
testCount ++
}
2022-12-07 13:02:43 +01:00
res . Policy = colorize ( removeColor , boldFgCyan , v . Policy )
res . Rule = colorize ( removeColor , boldFgCyan , v . Rule )
2021-10-01 22:43:21 +05:30
2022-06-20 12:08:13 +05:30
if v . Resources != nil {
for _ , resource := range v . Resources {
2022-10-11 18:00:11 +05:30
res . ID = testCount
testCount ++
2022-12-07 13:02:43 +01:00
res . Resource = colorize ( removeColor , boldFgCyan , v . Namespace ) + "/" + colorize ( removeColor , boldFgCyan , v . Kind ) + "/" + colorize ( removeColor , boldFgCyan , resource )
2022-06-20 12:08:13 +05:30
var ruleNameInResultKey string
if v . AutoGeneratedRule != "" {
ruleNameInResultKey = fmt . Sprintf ( "%s-%s" , v . AutoGeneratedRule , v . Rule )
} else {
ruleNameInResultKey = v . Rule
}
2021-10-01 22:43:21 +05:30
2022-06-20 12:08:13 +05:30
resultKey := fmt . Sprintf ( "%s-%s-%s-%s" , v . Policy , ruleNameInResultKey , v . Kind , resource )
found , _ := isNamespacedPolicy ( v . Policy )
var ns string
ns , v . Policy = getUserDefinedPolicyNameAndNamespace ( v . Policy )
if found && v . Namespace != "" {
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s-%s" , ns , v . Policy , ruleNameInResultKey , v . Namespace , v . Kind , resource )
} else if found {
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s" , ns , v . Policy , ruleNameInResultKey , v . Kind , resource )
2022-12-07 13:02:43 +01:00
res . Policy = colorize ( removeColor , boldFgCyan , ns ) + "/" + colorize ( removeColor , boldFgCyan , v . Policy )
res . Resource = colorize ( removeColor , boldFgCyan , v . Namespace ) + "/" + colorize ( removeColor , boldFgCyan , v . Kind ) + "/" + colorize ( removeColor , boldFgCyan , resource )
2022-06-20 12:08:13 +05:30
} else if v . Namespace != "" {
2022-12-07 13:02:43 +01:00
res . Resource = colorize ( removeColor , boldFgCyan , v . Namespace ) + "/" + colorize ( removeColor , boldFgCyan , v . Kind ) + "/" + colorize ( removeColor , boldFgCyan , resource )
2022-06-20 12:08:13 +05:30
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s" , v . Policy , ruleNameInResultKey , v . Namespace , v . Kind , resource )
}
var testRes policyreportv1alpha2 . PolicyReportResult
if val , ok := resps [ resultKey ] ; ok {
testRes = val
} else {
log . Log . V ( 2 ) . Info ( "result not found" , "key" , resultKey )
2022-12-07 13:02:43 +01:00
res . Result = colorize ( removeColor , boldYellow , "Not found" )
2022-06-20 12:08:13 +05:30
rc . Fail ++
table = append ( table , * res )
ftable = append ( ftable , * res )
continue
}
if v . Result == "" && v . Status != "" {
v . Result = v . Status
}
if testRes . Result == v . Result {
2022-12-07 13:02:43 +01:00
res . Result = colorize ( removeColor , boldGreen , "Pass" )
2022-08-19 19:11:19 +05:30
if testRes . Result == policyreportv1alpha2 . StatusSkip {
2022-06-20 12:08:13 +05:30
rc . Skip ++
} else {
rc . Pass ++
}
} else {
log . Log . V ( 2 ) . Info ( "result mismatch" , "expected" , v . Result , "received" , testRes . Result , "key" , resultKey )
2022-12-07 13:02:43 +01:00
res . Result = colorize ( removeColor , boldRed , "Fail" )
2022-06-20 12:08:13 +05:30
rc . Fail ++
ftable = append ( ftable , * res )
}
2022-07-22 20:02:12 +05:30
if failOnly {
2022-08-19 19:11:19 +05:30
if res . Result == boldRed . Sprintf ( "Fail" ) || res . Result == "Fail" {
2022-07-22 20:02:12 +05:30
table = append ( table , * res )
}
} else {
table = append ( table , * res )
}
2022-06-20 12:08:13 +05:30
}
} else if v . Resource != "" {
countDeprecatedResource ++
2022-12-07 13:02:43 +01:00
res . Resource = colorize ( removeColor , boldFgCyan , v . Namespace ) + "/" + colorize ( removeColor , boldFgCyan , v . Kind ) + "/" + colorize ( removeColor , boldFgCyan , v . Resource )
2022-06-20 12:08:13 +05:30
var ruleNameInResultKey string
if v . AutoGeneratedRule != "" {
ruleNameInResultKey = fmt . Sprintf ( "%s-%s" , v . AutoGeneratedRule , v . Rule )
} else {
ruleNameInResultKey = v . Rule
}
2021-10-01 22:43:21 +05:30
2022-06-20 12:08:13 +05:30
resultKey := fmt . Sprintf ( "%s-%s-%s-%s" , v . Policy , ruleNameInResultKey , v . Kind , v . Resource )
found , _ := isNamespacedPolicy ( v . Policy )
var ns string
ns , v . Policy = getUserDefinedPolicyNameAndNamespace ( v . Policy )
if found && v . Namespace != "" {
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s-%s" , ns , v . Policy , ruleNameInResultKey , v . Namespace , v . Kind , v . Resource )
} else if found {
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s" , ns , v . Policy , ruleNameInResultKey , v . Kind , v . Resource )
2022-12-07 13:02:43 +01:00
res . Policy = colorize ( removeColor , boldFgCyan , ns ) + "/" + colorize ( removeColor , boldFgCyan , v . Policy )
res . Resource = colorize ( removeColor , boldFgCyan , v . Namespace ) + "/" + colorize ( removeColor , boldFgCyan , v . Kind ) + "/" + colorize ( removeColor , boldFgCyan , v . Resource )
2022-06-20 12:08:13 +05:30
} else if v . Namespace != "" {
2022-12-07 13:02:43 +01:00
res . Resource = colorize ( removeColor , boldFgCyan , v . Namespace ) + "/" + colorize ( removeColor , boldFgCyan , v . Kind ) + "/" + colorize ( removeColor , boldFgCyan , v . Resource )
2022-06-20 12:08:13 +05:30
resultKey = fmt . Sprintf ( "%s-%s-%s-%s-%s" , v . Policy , ruleNameInResultKey , v . Namespace , v . Kind , v . Resource )
}
2021-10-01 22:43:21 +05:30
2022-06-20 12:08:13 +05:30
var testRes policyreportv1alpha2 . PolicyReportResult
if val , ok := resps [ resultKey ] ; ok {
testRes = val
} else {
log . Log . V ( 2 ) . Info ( "result not found" , "key" , resultKey )
2022-12-07 13:02:43 +01:00
res . Result = colorize ( removeColor , boldYellow , "Not found" )
2022-06-20 12:08:13 +05:30
rc . Fail ++
table = append ( table , * res )
ftable = append ( ftable , * res )
continue
}
if v . Result == "" && v . Status != "" {
v . Result = v . Status
}
2021-10-01 22:43:21 +05:30
2022-06-20 12:08:13 +05:30
if testRes . Result == v . Result {
2022-12-07 13:02:43 +01:00
res . Result = colorize ( removeColor , boldGreen , "Pass" )
2022-08-19 19:11:19 +05:30
if testRes . Result == policyreportv1alpha2 . StatusSkip {
2022-06-20 12:08:13 +05:30
rc . Skip ++
} else {
rc . Pass ++
}
2021-05-07 19:27:15 -04:00
} else {
2022-06-20 12:08:13 +05:30
log . Log . V ( 2 ) . Info ( "result mismatch" , "expected" , v . Result , "received" , testRes . Result , "key" , resultKey )
2022-12-07 13:02:43 +01:00
res . Result = colorize ( removeColor , boldRed , "Fail" )
2022-06-20 12:08:13 +05:30
rc . Fail ++
ftable = append ( ftable , * res )
2021-02-01 16:22:41 +05:30
}
2021-10-01 22:43:21 +05:30
2022-07-22 20:02:12 +05:30
if failOnly {
2022-08-19 19:11:19 +05:30
if res . Result == boldRed . Sprintf ( "Fail" ) || res . Result == "Fail" {
2022-07-22 20:02:12 +05:30
table = append ( table , * res )
}
} else {
table = append ( table , * res )
}
2022-06-20 12:08:13 +05:30
}
2021-02-07 20:26:56 -08:00
}
2021-10-01 14:16:33 +05:30
fmt . Printf ( "\n" )
2021-02-01 16:22:41 +05:30
printer . Print ( table )
return nil
2021-02-07 20:26:56 -08:00
}
2022-08-19 19:11:19 +05:30
2022-12-07 13:02:43 +01:00
func printFailedTestResult ( removeColor bool ) {
printer := newTablePrinter ( removeColor )
2022-06-02 23:01:46 +05:30
for i , v := range ftable {
v . ID = i + 1
}
fmt . Printf ( "Aggregated Failed Test Cases : " )
fmt . Printf ( "\n" )
printer . Print ( ftable )
}