1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00
kyverno/cmd/cli/kubectl-kyverno/validate/command.go
Mritunjay Kumar Sharma b815caef5d
refactor cli code from pkg to cmd (#3591)
* refactor cli code from pkg to cmd

Signed-off-by: Mritunjay Sharma <mritunjaysharma394@gmail.com>

* fixes in imports

Signed-off-by: Mritunjay Sharma <mritunjaysharma394@gmail.com>

* fixes tests

Signed-off-by: Mritunjay Sharma <mritunjaysharma394@gmail.com>

* fixed conflicts

Signed-off-by: Mritunjay Sharma <mritunjaysharma394@gmail.com>

* moved non-commands to utils

Signed-off-by: Mritunjay Sharma <mritunjaysharma394@gmail.com>

Co-authored-by: Vyankatesh Kudtarkar <vyankateshkd@gmail.com>
2022-04-14 12:20:18 +00:00

203 lines
6.2 KiB
Go

package validate
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"os"
v1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/crds"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/pkg/openapi"
policy2 "github.com/kyverno/kyverno/pkg/policy"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/spf13/cobra"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/yaml"
)
// Command returns validate command
func Command() *cobra.Command {
var outputType string
var crdPaths []string
cmd := &cobra.Command{
Use: "validate",
Short: "Validates kyverno policies",
Example: "kyverno validate /path/to/policy.yaml /path/to/folderOfPolicies",
RunE: func(cmd *cobra.Command, policyPaths []string) (err error) {
defer func() {
if err != nil {
if !sanitizederror.IsErrorSanitized(err) {
log.Log.Error(err, "failed to sanitize")
err = fmt.Errorf("internal error")
}
}
}()
if outputType != "" {
if outputType != "yaml" && outputType != "json" {
return sanitizederror.NewWithError(fmt.Sprintf("%s format is not supported", outputType), errors.New("yaml and json are supported"))
}
}
if len(policyPaths) == 0 {
return sanitizederror.NewWithError(fmt.Sprintf("policy file(s) required"), err)
}
policies, err := getPolicyFromGivenPath(policyPaths)
if err != nil {
return sanitizederror.NewWithError("failed to parse policy", err)
}
v1crd, err := getPolicyCRD()
if err != nil {
return sanitizederror.NewWithError("failed to decode crd: ", err)
}
openAPIController, err := openapi.NewOpenAPIController()
if err != nil {
return sanitizederror.NewWithError("failed to initialize openAPIController", err)
}
// if CRD's are passed, add these to OpenAPIController
if len(crdPaths) > 0 {
crds, err := common.GetCRDs(crdPaths)
if err != nil {
fmt.Printf("\nError: crd is invalid. \nFile: %s \nCause: %s\n", crdPaths, err)
os.Exit(1)
}
for _, crd := range crds {
openAPIController.ParseCRD(*crd)
}
}
err = validatePolicies(policies, v1crd, openAPIController, outputType)
if err != nil {
return sanitizederror.NewWithError("failed to validate policies", err)
}
return nil
},
}
cmd.Flags().StringVarP(&outputType, "output", "o", "", "Prints the mutated policy in yaml or json format")
cmd.Flags().StringArrayVarP(&crdPaths, "crd", "c", []string{}, "Path to CRD files")
return cmd
}
func getPolicyFromGivenPath(policyPaths []string) (policies []v1.PolicyInterface, err error) {
var errs []error
if policyPaths[0] == "-" {
if common.IsInputFromPipe() {
policyStr := ""
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
policyStr = policyStr + scanner.Text() + "\n"
}
yamlBytes := []byte(policyStr)
policies, err = utils.GetPolicy(yamlBytes)
if err != nil {
return policies, sanitizederror.NewWithError("failed to parse policy", err)
}
}
} else {
policies, errs = common.GetPolicies(policyPaths)
if len(errs) > 0 && len(policies) == 0 {
return policies, sanitizederror.NewWithErrors("failed to parse policies", errs)
}
if len(errs) > 0 && log.Log.V(1).Enabled() {
fmt.Printf("ignoring errors: \n")
for _, e := range errs {
fmt.Printf(" %v \n", e.Error())
}
}
}
return policies, nil
}
func getPolicyCRD() (v1crd apiextensions.CustomResourceDefinitionSpec, err error) {
if err = json.Unmarshal([]byte(crds.PolicyCRD), &v1crd); err != nil {
return
}
return
}
func validatePolicyAccordingToPolicyCRD(policy v1.PolicyInterface, v1crd apiextensions.CustomResourceDefinitionSpec) (err error, errList field.ErrorList) {
policyBytes, err := json.Marshal(policy)
if err != nil {
return sanitizederror.NewWithError("failed to marshal policy", err), nil
}
u := &unstructured.Unstructured{}
err = u.UnmarshalJSON(policyBytes)
if err != nil {
return sanitizederror.NewWithError("failed to decode policy", err), nil
}
versions := v1crd.Versions
for _, version := range versions {
validator, _, err := apiservervalidation.NewSchemaValidator(&apiextensions.CustomResourceValidation{OpenAPIV3Schema: version.Schema.OpenAPIV3Schema})
if err != nil {
return sanitizederror.NewWithError("failed to create schema validator", err), nil
}
errList = apiservervalidation.ValidateCustomResource(nil, u.UnstructuredContent(), validator)
}
return
}
func validatePolicies(policies []v1.PolicyInterface, v1crd apiextensions.CustomResourceDefinitionSpec, openAPIController *openapi.Controller, outputType string) error {
invalidPolicyFound := false
for _, policy := range policies {
err, errorList := validatePolicyAccordingToPolicyCRD(policy, v1crd)
if err != nil {
return sanitizederror.NewWithError("failed to validate policy.", err)
}
if errorList == nil {
_, err = policy2.Validate(policy, nil, true, openAPIController)
}
fmt.Println("----------------------------------------------------------------------")
if errorList != nil || err != nil {
fmt.Printf("Policy %s is invalid.\n", policy.GetName())
if errorList != nil {
fmt.Printf("Error: invalid policy.\nCause: %s\n\n", errorList)
} else {
fmt.Printf("Error: invalid policy.\nCause: %s\n\n", err)
}
invalidPolicyFound = true
} else {
fmt.Printf("Policy %s is valid.\n\n", policy.GetName())
if outputType != "" {
logger := log.Log.WithName("validate")
p, err := common.MutatePolicy(policy, logger)
if err != nil {
if !sanitizederror.IsErrorSanitized(err) {
return sanitizederror.NewWithError("failed to mutate policy.", err)
}
return err
}
if outputType == "yaml" {
yamlPolicy, _ := yaml.Marshal(p)
fmt.Println(string(yamlPolicy))
} else {
jsonPolicy, _ := json.MarshalIndent(p, "", " ")
fmt.Println(string(jsonPolicy))
}
}
}
}
if invalidPolicyFound {
os.Exit(1)
}
return nil
}