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:
parent
5a68653749
commit
fc6da9c9e6
3 changed files with 92 additions and 67 deletions
6
go.sum
6
go.sum
|
@ -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=
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue