mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
validation of policy against crd
This commit is contained in:
parent
a827f88dc7
commit
70b13d06dc
3 changed files with 186 additions and 2 deletions
3
go.mod
3
go.mod
|
@ -10,6 +10,9 @@ require (
|
|||
github.com/evanphx/json-patch/v5 v5.0.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||
github.com/go-logr/logr v0.1.0
|
||||
github.com/go-openapi/spec v0.19.5
|
||||
github.com/go-openapi/strfmt v0.19.5
|
||||
github.com/go-openapi/validate v0.19.8
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 // indirect
|
||||
github.com/googleapis/gnostic v0.3.1
|
||||
|
|
|
@ -4,9 +4,17 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
|
||||
"github.com/googleapis/gnostic/compiler"
|
||||
yaml_v2 "gopkg.in/yaml.v2"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
@ -211,3 +219,159 @@ func MutatePolicy(policy *v1.ClusterPolicy, logger logr.Logger) (*v1.ClusterPoli
|
|||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func ValidatePolicyAgainstCrd(policy *v1.ClusterPolicy, path string) error {
|
||||
log := log.Log
|
||||
path = filepath.Clean(path)
|
||||
|
||||
fileDesc, err := os.Stat(path)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to describe crd file")
|
||||
return err
|
||||
}
|
||||
|
||||
if fileDesc.IsDir() {
|
||||
return errors.New("crd path should be a file")
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to crd read file")
|
||||
return err
|
||||
}
|
||||
|
||||
var crd unstructured.Unstructured
|
||||
err = json.Unmarshal(bytes, &crd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// crdDefinitionPrior represents CRDs version prior to 1.16
|
||||
var crdDefinitionPrior struct {
|
||||
Spec struct {
|
||||
Names struct {
|
||||
Kind string `json:"kind"`
|
||||
} `json:"names"`
|
||||
Validation struct {
|
||||
OpenAPIV3Schema interface{} `json:"openAPIV3Schema"`
|
||||
} `json:"validation"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
|
||||
// crdDefinitionNew represents CRDs version 1.16+
|
||||
var crdDefinitionNew struct {
|
||||
Spec struct {
|
||||
Names struct {
|
||||
Kind string `json:"kind"`
|
||||
} `json:"names"`
|
||||
Versions []struct {
|
||||
Schema struct {
|
||||
OpenAPIV3Schema interface{} `json:"openAPIV3Schema"`
|
||||
} `json:"schema"`
|
||||
Storage bool `json:"storage"`
|
||||
} `json:"versions"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
|
||||
crdRaw, _ := json.Marshal(crd.Object)
|
||||
_ = json.Unmarshal(crdRaw, &crdDefinitionPrior)
|
||||
|
||||
openV3schema := crdDefinitionPrior.Spec.Validation.OpenAPIV3Schema
|
||||
crdName := crdDefinitionPrior.Spec.Names.Kind
|
||||
|
||||
if openV3schema == nil {
|
||||
_ = json.Unmarshal(crdRaw, &crdDefinitionNew)
|
||||
for _, crdVersion := range crdDefinitionNew.Spec.Versions {
|
||||
if crdVersion.Storage {
|
||||
openV3schema = crdVersion.Schema.OpenAPIV3Schema
|
||||
crdName = crdDefinitionNew.Spec.Names.Kind
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
schemaRaw, _ := json.Marshal(openV3schema)
|
||||
if len(schemaRaw) < 1 {
|
||||
//log.Log.V(3).Info("could not parse crd schema", "name", crdName)
|
||||
return err
|
||||
}
|
||||
|
||||
schemaRaw, err = addingDefaultFieldsToSchema(schemaRaw)
|
||||
if err != nil {
|
||||
//log.Log.Error(err, "could not parse crd schema", "name", crdName)
|
||||
return err
|
||||
}
|
||||
|
||||
schema := new(spec.Schema)
|
||||
_ = json.Unmarshal(schemaRaw, schema)
|
||||
|
||||
input := map[string]interface{}{}
|
||||
|
||||
// JSON data to validate
|
||||
//inputJSON := `{"name": "Ivan","address-1": "sesame street"}`
|
||||
//_ = json.Unmarshal([]byte(inputJSON), &input)
|
||||
|
||||
// strfmt.Default is the registry of recognized formats
|
||||
err = validate.AgainstSchema(schema, policy, strfmt.Default)
|
||||
if err != nil {
|
||||
fmt.Printf("JSON does not validate against schema: %v", err)
|
||||
} else {
|
||||
fmt.Printf("OK")
|
||||
}
|
||||
|
||||
//var schema yaml_v2.MapSlice
|
||||
//_ = yaml_v2.Unmarshal(schemaRaw, &schema)
|
||||
//
|
||||
//parsedSchema, err := openapi_v2.NewSchema(schema, compiler.NewContext("schema", nil))
|
||||
//if err != nil {
|
||||
// //log.Log.Error(err, "could not parse crd schema", "name", crdName)
|
||||
// return
|
||||
//}
|
||||
|
||||
|
||||
|
||||
//var spec yaml_v2.MapSlice
|
||||
//err := yaml_v2.Unmarshal([]byte(data.SwaggerDoc), &spec)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//crdDoc, err := openapi_v2.NewDocument(spec, compiler.NewContext("$root", nil))
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//crdDoc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addingDefaultFieldsToSchema will add any default missing fields like apiVersion, metadata
|
||||
func addingDefaultFieldsToSchema(schemaRaw []byte) ([]byte, error) {
|
||||
var schema struct {
|
||||
Properties map[string]interface{} `json:"properties"`
|
||||
}
|
||||
_ = json.Unmarshal(schemaRaw, &schema)
|
||||
|
||||
if len(schema.Properties) < 1 {
|
||||
return nil, errors.New("crd schema has no properties")
|
||||
}
|
||||
|
||||
if schema.Properties["apiVersion"] == nil {
|
||||
apiVersionDefRaw := `{"description":"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources","type":"string"}`
|
||||
apiVersionDef := make(map[string]interface{})
|
||||
_ = json.Unmarshal([]byte(apiVersionDefRaw), &apiVersionDef)
|
||||
schema.Properties["apiVersion"] = apiVersionDef
|
||||
}
|
||||
|
||||
if schema.Properties["metadata"] == nil {
|
||||
metadataDefRaw := `{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta","description":"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"}`
|
||||
metadataDef := make(map[string]interface{})
|
||||
_ = json.Unmarshal([]byte(metadataDefRaw), &metadataDef)
|
||||
schema.Properties["metadata"] = metadataDef
|
||||
}
|
||||
|
||||
schemaWithDefaultFields, _ := json.Marshal(schema)
|
||||
|
||||
return schemaWithDefaultFields, nil
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
)
|
||||
|
||||
func Command() *cobra.Command {
|
||||
var outputType string
|
||||
var outputType, crdPath string
|
||||
cmd := &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "Validates kyverno policies",
|
||||
|
@ -31,7 +31,7 @@ func Command() *cobra.Command {
|
|||
if err != nil {
|
||||
if !sanitizedError.IsErrorSanitized(err) {
|
||||
log.Log.Error(err, "failed to sanitize")
|
||||
err = fmt.Errorf("Internal error")
|
||||
err = fmt.Errorf("internal error")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -49,6 +49,22 @@ func Command() *cobra.Command {
|
|||
|
||||
invalidPolicyFound := false
|
||||
for _, policy := range policies {
|
||||
//if common.PolicyHasVariables(*policy) {
|
||||
// invalidPolicyFound = true
|
||||
// fmt.Printf("Policy %s is invalid.\n", policy.Name)
|
||||
// log.Log.Error(errors.New("'validate' does not support policies with variables"), "Policy "+policy.Name+" is invalid")
|
||||
// continue
|
||||
//}
|
||||
|
||||
// if crd is passed, then validate policy against the crd
|
||||
if crdPath != "" {
|
||||
err := common.ValidatePolicyAgainstCrd(*policy, crdPath)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "policy "+policy.Name+" is invalid")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
err := policy2.Validate(utils.MarshalPolicy(*policy), nil, true, openAPIController)
|
||||
if err != nil {
|
||||
fmt.Printf("Policy %s is invalid.\n", policy.Name)
|
||||
|
@ -84,5 +100,6 @@ func Command() *cobra.Command {
|
|||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(&outputType, "output", "o", "", "Prints the mutated policy")
|
||||
cmd.Flags().StringVarP(&crdPath, "crd", "c", "", "Path to resource files")
|
||||
return cmd
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue