2019-05-13 18:17:28 -07:00
|
|
|
package engine
|
2019-05-13 21:27:47 +03:00
|
|
|
|
|
|
|
import (
|
2019-07-26 08:11:20 -04:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2019-05-13 21:27:47 +03:00
|
|
|
|
2019-05-31 17:59:36 -07:00
|
|
|
"github.com/golang/glog"
|
2019-07-26 08:11:20 -04:00
|
|
|
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
2019-05-29 14:12:09 -07:00
|
|
|
client "github.com/nirmata/kyverno/pkg/dclient"
|
2019-06-25 18:16:02 -07:00
|
|
|
"github.com/nirmata/kyverno/pkg/info"
|
2019-07-26 08:11:20 -04:00
|
|
|
"github.com/nirmata/kyverno/pkg/utils"
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
2019-05-13 21:27:47 +03:00
|
|
|
)
|
|
|
|
|
2019-07-26 08:11:20 -04:00
|
|
|
//Generate apply generation rules on a resource
|
|
|
|
func Generate(client *client.Client, policy *v1alpha1.Policy, ns unstructured.Unstructured) []*info.RuleInfo {
|
2019-06-25 18:16:02 -07:00
|
|
|
ris := []*info.RuleInfo{}
|
2019-05-20 14:48:38 +03:00
|
|
|
for _, rule := range policy.Spec.Rules {
|
2019-06-25 18:16:02 -07:00
|
|
|
if rule.Generation == nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-06-25 23:58:28 -07:00
|
|
|
ri := info.NewRuleInfo(rule.Name, info.Generation)
|
2019-08-07 18:13:43 -07:00
|
|
|
err := applyRuleGenerator(client, ns, rule.Generation, policy.Spec.ValidationFailureAction)
|
2019-05-14 18:20:41 -07:00
|
|
|
if err != nil {
|
2019-06-25 18:16:02 -07:00
|
|
|
ri.Fail()
|
2019-06-26 18:04:50 -07:00
|
|
|
ri.Addf("Rule %s: Failed to apply rule generator, err %v.", rule.Name, err)
|
2019-06-25 18:16:02 -07:00
|
|
|
} else {
|
2019-06-26 18:04:50 -07:00
|
|
|
ri.Addf("Rule %s: Generation succesfully.", rule.Name)
|
2019-05-13 21:27:47 +03:00
|
|
|
}
|
2019-06-25 18:16:02 -07:00
|
|
|
ris = append(ris, ri)
|
2019-07-26 08:11:20 -04:00
|
|
|
|
2019-05-13 21:27:47 +03:00
|
|
|
}
|
2019-06-25 18:16:02 -07:00
|
|
|
return ris
|
2019-05-13 21:27:47 +03:00
|
|
|
}
|
|
|
|
|
2019-08-07 18:13:43 -07:00
|
|
|
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen *v1alpha1.Generation, validationFailureAction string) error {
|
2019-05-20 17:25:57 +03:00
|
|
|
var err error
|
2019-07-26 08:11:20 -04:00
|
|
|
resource := &unstructured.Unstructured{}
|
|
|
|
var rdata map[string]interface{}
|
|
|
|
|
|
|
|
if gen.Data != nil {
|
|
|
|
// 1> Check if resource exists
|
|
|
|
obj, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
|
|
|
if err == nil {
|
|
|
|
// 2> If already exsists, then verify the content is contained
|
|
|
|
// found the resource
|
|
|
|
// check if the rule is create, if yes, then verify if the specified configuration is present in the resource
|
|
|
|
ok, err := checkResource(gen.Data, obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
return errors.New("rule configuration not present in resource")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
rdata, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&gen.Data)
|
|
|
|
if err != nil {
|
|
|
|
glog.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if gen.Clone != nil {
|
|
|
|
// 1> Check if resource exists
|
|
|
|
_, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
|
|
|
if err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// 2> If already exists return
|
|
|
|
resource, err = client.GetResource(gen.Kind, gen.Clone.Namespace, gen.Clone.Name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rdata = resource.UnstructuredContent()
|
|
|
|
}
|
|
|
|
resource.SetUnstructuredContent(rdata)
|
|
|
|
resource.SetName(gen.Name)
|
|
|
|
resource.SetNamespace(ns.GetName())
|
|
|
|
// Reset resource version
|
|
|
|
resource.SetResourceVersion("")
|
2019-08-07 18:24:55 -07:00
|
|
|
// TODO based on https://github.com/nirmata/kyverno/issues/268
|
|
|
|
// if validationFailureAction != "audit" {
|
|
|
|
// // if not audit, then enforce..
|
|
|
|
// // with enforce we will block the creation of resource and instead generate an error
|
|
|
|
// // the error will then create a policyViolation so that the resource owner can add the defaults
|
|
|
|
// return errors.New("policy flag validationFailureAction:'audit' blocked the creation of default resource for the namespace")
|
|
|
|
// }
|
2019-08-07 18:13:43 -07:00
|
|
|
// for "audit" mode, the resource will create the resource
|
|
|
|
// but wont generate a policy violation as the generate controller doesnt know if the generate request
|
2019-08-07 18:24:55 -07:00
|
|
|
// is a new resource via admission controller or via syncing its cache after a controller
|
2019-07-26 08:11:20 -04:00
|
|
|
_, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false)
|
2019-05-16 14:09:02 -07:00
|
|
|
if err != nil {
|
2019-07-26 08:11:20 -04:00
|
|
|
return err
|
2019-05-13 21:27:47 +03:00
|
|
|
}
|
2019-05-16 14:09:02 -07:00
|
|
|
return nil
|
2019-05-13 21:27:47 +03:00
|
|
|
}
|
2019-07-26 08:11:20 -04:00
|
|
|
|
|
|
|
func checkResource(config interface{}, resource *unstructured.Unstructured) (bool, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
objByte, err := resource.MarshalJSON()
|
|
|
|
if err != nil {
|
|
|
|
// unable to parse the json
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
err = resource.UnmarshalJSON(objByte)
|
|
|
|
if err != nil {
|
|
|
|
// unable to parse the json
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
// marshall and unmarshall json to verify if its right format
|
|
|
|
configByte, err := json.Marshal(config)
|
|
|
|
if err != nil {
|
|
|
|
// unable to marshall the config
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
var configData interface{}
|
|
|
|
err = json.Unmarshal(configByte, &configData)
|
|
|
|
if err != nil {
|
|
|
|
// unable to unmarshall
|
|
|
|
return false, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
var objData interface{}
|
|
|
|
err = json.Unmarshal(objByte, &objData)
|
|
|
|
if err != nil {
|
|
|
|
// unable to unmarshall
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the config is a subset of resource
|
|
|
|
return utils.JSONsubsetValue(configData, objData), nil
|
|
|
|
}
|