1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

improve CLI validation reports

This commit is contained in:
Jim Bugwadia 2020-08-18 21:03:00 -07:00
parent 5a68653749
commit fc6da9c9e6
3 changed files with 92 additions and 67 deletions

6
go.sum
View file

@ -45,6 +45,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ahmetb/gen-crd-api-reference-docs v0.2.0 h1:YI/cAcRdNAHArfhGKcmCY5qMa32k/UyCZagLgabC5JY=
@ -184,6 +185,7 @@ github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@ -663,6 +665,7 @@ github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qo
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190805055040-f9202b1cfdeb/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
@ -671,6 +674,7 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@ -818,6 +822,7 @@ github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4do
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20190704165056-9c2d0518ed81/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.0.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -834,6 +839,7 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH
github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4=
github.com/santhosh-tekuri/jsonschema/v2 v2.1.0/go.mod h1:yzJzKUGV4RbWqWIBBP4wSOBqavX5saE02yirLS0OTyg=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/secure-io/sio-go v0.3.0 h1:QKGb6rGJeiExac9wSWxnWPYo8O8OFN7lxXQvHshX6vo=
github.com/secure-io/sio-go v0.3.0/go.mod h1:D3KmXgKETffyYxBdFRN+Hpd2WzhzqS0EQwT3XWsAcBU=
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do=
github.com/segmentio/analytics-go v3.0.1+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48=

View file

@ -37,17 +37,26 @@ import (
log "sigs.k8s.io/controller-runtime/pkg/log"
)
type resultCounts struct {
pass int
fail int
warn int
error int
skip int
}
func Command() *cobra.Command {
var cmd *cobra.Command
var resourcePaths []string
var cluster bool
var mutatelogPath string
var mutateLogPath string
kubernetesConfig := genericclioptions.NewConfigFlags(true)
cmd = &cobra.Command{
Use: "apply",
Short: "Applies policies on resources",
Short: "applies policies on resources",
Example: fmt.Sprintf("To apply on a resource:\nkyverno apply /path/to/policy.yaml /path/to/folderOfPolicies --resource=/path/to/resource1 --resource=/path/to/resource2\n\nTo apply on a cluster\nkyverno apply /path/to/policy.yaml /path/to/folderOfPolicies --cluster"),
RunE: func(cmd *cobra.Command, policyPaths []string) (err error) {
defer func() {
@ -60,12 +69,12 @@ func Command() *cobra.Command {
}()
if len(resourcePaths) == 0 && !cluster {
return sanitizedError.NewWithError("resource file or cluster required", err)
return sanitizedError.NewWithError(fmt.Sprintf("resource file(s) or cluster required"), err)
}
var mutatelogPathIsDir bool
if mutatelogPath != "" {
spath := strings.Split(mutatelogPath, "/")
if mutateLogPath != "" {
spath := strings.Split(mutateLogPath, "/")
sfileName := strings.Split(spath[len(spath)-1], ".")
if sfileName[len(sfileName)-1] == "yml" || sfileName[len(sfileName)-1] == "yaml" {
mutatelogPathIsDir = false
@ -73,7 +82,7 @@ func Command() *cobra.Command {
mutatelogPathIsDir = true
}
err = createFileOrFolder(mutatelogPath, mutatelogPathIsDir)
err = createFileOrFolder(mutateLogPath, mutatelogPathIsDir)
if err != nil {
if !sanitizedError.IsErrorSanitized(err) {
return sanitizedError.NewWithError("failed to create file/folder.", err)
@ -90,18 +99,6 @@ func Command() *cobra.Command {
return err
}
for _, policy := range policies {
err := policy2.Validate(utils.MarshalPolicy(*policy), nil, true, openAPIController)
if err != nil {
fmt.Printf("Policy %v is not valid: %v\n", policy.Name, err)
os.Exit(1)
}
if common.PolicyHasVariables(*policy) {
return sanitizedError.NewWithError(fmt.Sprintf("invalid policy %s. 'apply' does not support policies with variables", policy.Name), err)
}
}
var dClient *client.Client
if cluster {
restConfig, err := kubernetesConfig.ToRESTConfig()
@ -119,31 +116,61 @@ func Command() *cobra.Command {
return sanitizedError.NewWithError("failed to load resources", err)
}
newPolicies, err := mutatePolices(policies)
if err != nil {
return sanitizedError.NewWithError("failed to mutate policy", err)
mutatedPolicies, err := mutatePolices(policies)
msgPolicies := "1 policy"
if len(mutatedPolicies) > 1 {
msgPolicies = fmt.Sprintf("%d policies", len(policies))
}
for i, policy := range newPolicies {
for j, resource := range resources {
if !(j == 0 && i == 0) {
fmt.Printf("\n\n==========================================================================================\n")
}
msgResources := "1 resource"
if len(resources) > 1 {
msgResources = fmt.Sprintf("%d resources", len(resources))
}
err = applyPolicyOnResource(policy, resource, mutatelogPath, mutatelogPathIsDir)
fmt.Printf("\napplying %s to %s \n", msgPolicies, msgResources)
if len(mutatedPolicies) == 0 || len(resources) == 0 {
return
}
rc := &resultCounts{}
for _, policy := range mutatedPolicies {
err := policy2.Validate(utils.MarshalPolicy(*policy), nil, true, openAPIController)
if err != nil {
rc.skip += len(resources)
fmt.Printf("\nskipping policy %v as it is not valid: %v\n", policy.Name, err)
continue
}
if common.PolicyHasVariables(*policy) {
rc.skip += len(resources)
fmt.Printf("\nskipping policy %s as policies with variables are not supported\n", policy.Name)
continue
}
for _, resource := range resources {
applyPolicyOnResource(policy, resource, rc)
if err != nil {
return sanitizedError.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
}
}
}
fmt.Printf("\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n",
rc.pass, rc.fail, rc.warn, rc.error, rc.skip)
if rc.fail > 0 || rc.error > 0 {
os.Exit(1)
}
return nil
},
}
cmd.Flags().StringArrayVarP(&resourcePaths, "resource", "r", []string{}, "Path to resource files")
cmd.Flags().BoolVarP(&cluster, "cluster", "c", false, "Checks if policies should be applied to cluster in the current context")
cmd.Flags().StringVarP(&mutatelogPath, "output", "o", "", "Prints the mutated resources in provided file/directory")
cmd.Flags().StringVarP(&mutateLogPath, "output", "o", "", "Prints the mutated resources in provided file/directory")
return cmd
}
@ -273,55 +300,45 @@ func getResource(path string) ([]*unstructured.Unstructured, error) {
}
// applyPolicyOnResource - function to apply policy on resource
func applyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured, mutatelogPath string, mutatelogPathIsDir bool) error {
fmt.Printf("\n\nApplying Policy %s on Resource %s/%s/%s\n", policy.Name, resource.GetNamespace(), resource.GetKind(), resource.GetName())
func applyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured, rc *resultCounts) {
responseError := false
resPath := fmt.Sprintf("%s/%s/%s", resource.GetNamespace(), resource.GetKind(), resource.GetName())
log.Log.V(3).Info("applying policy on resource", "policy", policy.Name, "resource", resPath)
mutateResponse := engine.Mutate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
if !mutateResponse.IsSuccessful() {
fmt.Printf("\n\nMutation:")
fmt.Printf("\nFailed to apply mutation")
fmt.Printf("Failed to apply mutate policy %s -> resource %s", policy.Name, resPath)
for i, r := range mutateResponse.PolicyResponse.Rules {
fmt.Printf("\n%d. %s", i+1, r.Message)
}
fmt.Printf("\n\n")
responseError = true
} else {
if len(mutateResponse.PolicyResponse.Rules) > 0 {
yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object)
if err != nil {
return err
rc.error++
}
if mutatelogPath == "" {
fmt.Printf("\n\nMutation:\nMutation has been applied succesfully")
fmt.Printf("\n\n" + string(yamlEncodedResource))
fmt.Printf("\n\n")
} else {
err := printMutatedOutput(mutatelogPath, mutatelogPathIsDir, string(yamlEncodedResource), resource.GetName()+"-mutated")
if err != nil {
return sanitizedError.NewWithError("failed to print mutated result", err)
}
fmt.Printf("\n\nMutation:\nMutation has been applied succesfully. Check the files.")
mutatedResource := string(yamlEncodedResource)
if len(strings.TrimSpace(mutatedResource)) > 0 {
fmt.Printf("mutate policy %s applied to %s:", policy.Name, resPath)
fmt.Printf("\n" + mutatedResource)
fmt.Printf("\n")
}
} else {
fmt.Printf("\n\nMutation:\nMutation skipped. Resource not matches the policy")
}
}
validateResponse := engine.Validate(engine.PolicyContext{Policy: *policy, NewResource: mutateResponse.PatchedResource})
if !validateResponse.IsSuccessful() {
fmt.Printf("\n\nValidation:")
fmt.Printf("\nResource is invalid")
fmt.Printf("\npolicy %s -> resource %s failed: \n", policy.Name, resPath)
for i, r := range validateResponse.PolicyResponse.Rules {
fmt.Printf("\n%d. %s", i+1, r.Message)
}
fmt.Printf("\n\n")
} else {
if len(validateResponse.PolicyResponse.Rules) > 0 {
fmt.Printf("\n\nValidation:")
fmt.Printf("\nResource is valid")
fmt.Printf("\n\n")
if !r.Success {
fmt.Printf("%d. %s: %s \n", i+1, r.Name, r.Message)
}
}
responseError = true
}
var policyHasGenerate bool
@ -334,20 +351,22 @@ func applyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
if policyHasGenerate {
generateResponse := engine.Generate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
if len(generateResponse.PolicyResponse.Rules) > 0 {
fmt.Printf("\n\nGenerate:")
fmt.Printf("\nResource is valid")
fmt.Printf("\n\n")
log.Log.V(3).Info("generate resource is valid", "policy", policy.Name, "resource", resPath)
} else {
fmt.Printf("\n\nGenerate:")
fmt.Printf("\nResource is invalid")
fmt.Printf("generate policy %s resource %s is invalid \n", policy.Name, resPath)
for i, r := range generateResponse.PolicyResponse.Rules {
fmt.Printf("\n%d. %s", i+1, r.Message)
fmt.Printf("%d. %s \b", i+1, r.Message)
}
fmt.Printf("\n\n")
responseError = true
}
}
return nil
if responseError == true {
rc.fail++
} else {
rc.pass++
}
}
// mutatePolicies - function to apply mutation on policies
@ -394,7 +413,7 @@ func printMutatedOutput(mutatelogPath string, mutatelogPathIsDir bool, yaml stri
return nil
}
// createFileOrFolder - creating file or folder accoring to path provided
// createFileOrFolder - creating file or folder according to path provided
func createFileOrFolder(mutatelogPath string, mutatelogPathIsDir bool) error {
mutatelogPath = filepath.Clean(mutatelogPath)
_, err := os.Stat(mutatelogPath)

View file

@ -61,7 +61,7 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, error error) {
}
if errString != "" {
fmt.Println("falied to extract policies")
fmt.Println("failed to extract policies: %s", errString)
os.Exit(2)
}