diff --git a/documentation/kyverno-cli.md b/documentation/kyverno-cli.md index 1c7b7140b2..539d058b35 100644 --- a/documentation/kyverno-cli.md +++ b/documentation/kyverno-cli.md @@ -40,19 +40,24 @@ yay -S kyverno-git Prints the version of kyverno used by the CLI. -Example: +Example: + ``` kyverno version ``` ### Validate Validates a policy, can validate multiple policy resource description files or even an entire folder containing policy resource description -files. Currently supports files with resource description in yaml. +files. Currently supports files with resource description in yaml. The policies can also be passed from stdin. Example: ``` kyverno validate /path/to/policy1.yaml /path/to/policy2.yaml /path/to/folderFullOfPolicies ``` +Passing policy from stdin: +``` +kustomize build nginx/overlays/envs/prod/ | kyverno validate - +``` Use the -o flag to display the mutated policy. diff --git a/pkg/kyverno/apply/command.go b/pkg/kyverno/apply/command.go index 960812e015..00c5fb7570 100644 --- a/pkg/kyverno/apply/command.go +++ b/pkg/kyverno/apply/command.go @@ -9,6 +9,7 @@ import ( "reflect" "github.com/nirmata/kyverno/pkg/engine/context" + "github.com/nirmata/kyverno/pkg/openapi" "k8s.io/apimachinery/pkg/util/yaml" "os" @@ -151,7 +152,7 @@ func Command() *cobra.Command { } } - policies, openAPIController, err := common.GetPoliciesValidation(policyPaths) + policies, err := common.GetPoliciesValidation(policyPaths) if err != nil { if !sanitizedError.IsErrorSanitized(err) { return sanitizedError.NewWithError("failed to mutate policies.", err) @@ -159,6 +160,11 @@ func Command() *cobra.Command { return err } + openAPIController, err := openapi.NewOpenAPIController() + if err != nil { + return sanitizedError.NewWithError("failed to initialize openAPIController", err) + } + var dClient *client.Client if cluster { restConfig, err := kubernetesConfig.ToRESTConfig() @@ -173,7 +179,7 @@ func Command() *cobra.Command { var resources []*unstructured.Unstructured if resourcePaths[0] == "-" { - if isInputFromPipe() { + if common.IsInputFromPipe() { resourceStr := "" scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -588,8 +594,3 @@ func createFileOrFolder(mutateLogPath string, mutateLogPathIsDir bool) error { return nil } - -func isInputFromPipe() bool { - fileInfo, _ := os.Stdin.Stat() - return fileInfo.Mode()&os.ModeCharDevice == 0 -} diff --git a/pkg/kyverno/common/common.go b/pkg/kyverno/common/common.go index 7f0723fcc8..bf87909b92 100644 --- a/pkg/kyverno/common/common.go +++ b/pkg/kyverno/common/common.go @@ -20,7 +20,6 @@ import ( "github.com/go-logr/logr" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/kyverno/sanitizedError" - "github.com/nirmata/kyverno/pkg/openapi" "github.com/nirmata/kyverno/pkg/policymutation" "github.com/nirmata/kyverno/pkg/utils" log "sigs.k8s.io/controller-runtime/pkg/log" @@ -57,7 +56,12 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, error error) { policies = append(policies, policiesFromDir...) } else { - getPolicies, getErrors := utils.GetPolicy(path) + file, err := ioutil.ReadFile(path) + if err != nil { + log.Error(err, fmt.Sprintf("failed to load file: %v", path)) + return nil, sanitizedError.NewWithError(("failed to load file"), err) + } + getPolicies, getErrors := utils.GetPolicy(file) var errString string for _, err := range getErrors { if err != nil { @@ -78,21 +82,15 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, error error) { } //GetPoliciesValidation - validating policies -func GetPoliciesValidation(policyPaths []string) ([]*v1.ClusterPolicy, *openapi.Controller, error) { +func GetPoliciesValidation(policyPaths []string) ([]*v1.ClusterPolicy, error) { policies, err := GetPolicies(policyPaths) if err != nil { if !sanitizedError.IsErrorSanitized(err) { - return nil, nil, sanitizedError.NewWithError((fmt.Sprintf("failed to parse %v path/s.", policyPaths)), err) + return nil, sanitizedError.NewWithError((fmt.Sprintf("failed to parse %v path/s.", policyPaths)), err) } - return nil, nil, err + return nil, err } - - openAPIController, err := openapi.NewOpenAPIController() - if err != nil { - return nil, nil, err - } - - return policies, openAPIController, nil + return policies, nil } // PolicyHasVariables - check for variables in the policy @@ -242,3 +240,9 @@ func GetCRD(path string) (unstructuredCrds []*unstructured.Unstructured, err err return unstructuredCrds, nil } + +// IsInputFromPipe - check if input is passed using pipe +func IsInputFromPipe() bool { + fileInfo, _ := os.Stdin.Stat() + return fileInfo.Mode()&os.ModeCharDevice == 0 +} diff --git a/pkg/kyverno/validate/command.go b/pkg/kyverno/validate/command.go index dd39bddb46..99126c6fcb 100644 --- a/pkg/kyverno/validate/command.go +++ b/pkg/kyverno/validate/command.go @@ -1,11 +1,14 @@ package validate import ( + "bufio" "encoding/json" "errors" "fmt" "os" + v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "github.com/nirmata/kyverno/pkg/openapi" "github.com/nirmata/kyverno/pkg/utils" "github.com/nirmata/kyverno/pkg/kyverno/common" @@ -45,9 +48,46 @@ func Command() *cobra.Command { } } - policies, openAPIController, err := common.GetPoliciesValidation(policyPaths) + if len(policyPaths) == 0 { + return sanitizedError.NewWithError(fmt.Sprintf("policy file(s) required"), err) + } + + var policies []*v1.ClusterPolicy + if policyPaths[0] == "-" { + if common.IsInputFromPipe() { + policyStr := "" + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + policyStr = policyStr + scanner.Text() + "\n" + } + + yamlBytes := []byte(policyStr) + var getErrors []error + policies, getErrors = utils.GetPolicy(yamlBytes) + var errString string + + for _, err := range getErrors { + if err != nil { + errString += err.Error() + "\n" + } + } + if errString != "" { + return sanitizedError.NewWithError("failed to extract the resources", errors.New(errString)) + } + } + } else { + policies, err = common.GetPoliciesValidation(policyPaths) + if err != nil { + if !sanitizedError.IsErrorSanitized(err) { + return sanitizedError.NewWithError("failed to mutate policies.", err) + } + return err + } + } + + openAPIController, err := openapi.NewOpenAPIController() if err != nil { - return err + return sanitizedError.NewWithError("failed to initialize openAPIController", err) } // if CRD's are passed, add these to OpenAPIController diff --git a/pkg/policymutation/policymutation_test.go b/pkg/policymutation/policymutation_test.go index 19e057fd4e..49895f5859 100644 --- a/pkg/policymutation/policymutation_test.go +++ b/pkg/policymutation/policymutation_test.go @@ -1,6 +1,7 @@ package policymutation import ( + "io/ioutil" "os" "path/filepath" "strings" @@ -25,8 +26,11 @@ func Test_Exclude(t *testing.T) { dir, err := os.Getwd() baseDir := filepath.Dir(filepath.Dir(dir)) assert.NilError(t, err) - - policies, errs := utils.GetPolicy(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + file, err := ioutil.ReadFile(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + if err != nil { + t.Log(err) + } + policies, errs := utils.GetPolicy(file) if len(errs) != 0 { t.Log(errs) } @@ -53,8 +57,11 @@ func Test_CronJobOnly(t *testing.T) { dir, err := os.Getwd() baseDir := filepath.Dir(filepath.Dir(dir)) assert.NilError(t, err) - - policies, errs := utils.GetPolicy(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + file, err := ioutil.ReadFile(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + if err != nil { + t.Log(err) + } + policies, errs := utils.GetPolicy(file) if len(errs) != 0 { t.Log(errs) } @@ -83,7 +90,11 @@ func Test_CronJob_hasExclude(t *testing.T) { baseDir := filepath.Dir(filepath.Dir(dir)) assert.NilError(t, err) - policies, errs := utils.GetPolicy(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + file, err := ioutil.ReadFile(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + if err != nil { + t.Log(err) + } + policies, errs := utils.GetPolicy(file) if len(errs) != 0 { t.Log(errs) } @@ -115,8 +126,11 @@ func Test_CronJobAndDeployment(t *testing.T) { dir, err := os.Getwd() baseDir := filepath.Dir(filepath.Dir(dir)) assert.NilError(t, err) - - policies, errs := utils.GetPolicy(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + file, err := ioutil.ReadFile(baseDir + "/samples/best_practices/disallow_bind_mounts.yaml") + if err != nil { + t.Log(err) + } + policies, errs := utils.GetPolicy(file) if len(errs) != 0 { t.Log(errs) } diff --git a/pkg/utils/loadpolicy.go b/pkg/utils/loadpolicy.go index 175b26e642..ad21b32168 100644 --- a/pkg/utils/loadpolicy.go +++ b/pkg/utils/loadpolicy.go @@ -6,20 +6,13 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "k8s.io/apimachinery/pkg/util/yaml" ) // GetPolicy - Extracts policies from a YAML -func GetPolicy(path string) (clusterPolicies []*v1.ClusterPolicy, errors []error) { - file, err := ioutil.ReadFile(path) - if err != nil { - errors = append(errors, fmt.Errorf(fmt.Sprintf("failed to load file: %v. error: %v", path, err))) - return clusterPolicies, errors - } - +func GetPolicy(file []byte) (clusterPolicies []*v1.ClusterPolicy, errors []error) { policies, err := SplitYAMLDocuments(file) if err != nil { errors = append(errors, err) @@ -35,7 +28,7 @@ func GetPolicy(path string) (clusterPolicies []*v1.ClusterPolicy, errors []error policy := &v1.ClusterPolicy{} if err := json.Unmarshal(policyBytes, policy); err != nil { - errors = append(errors, fmt.Errorf(fmt.Sprintf("failed to decode policy in %s. error: %v", path, err))) + errors = append(errors, fmt.Errorf(fmt.Sprintf("failed to decode policy. error: %v", err))) continue }