1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/engine/generation.go

187 lines
8.5 KiB
Go
Raw Normal View History

2019-05-13 18:17:28 -07:00
package engine
import (
"encoding/json"
2019-08-19 16:40:10 -07:00
"time"
"fmt"
2019-05-31 17:59:36 -07:00
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2019-07-26 08:11:20 -04:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
2019-07-26 08:11:20 -04:00
//Generate apply generation rules on a resource
2019-10-08 10:57:24 -07:00
func Generate(client *client.Client, policy kyverno.ClusterPolicy, ns unstructured.Unstructured) (response EngineResponse) {
2019-08-19 16:40:10 -07:00
startTime := time.Now()
2019-08-26 13:34:42 -07:00
// policy information
func() {
// set policy information
response.PolicyResponse.Policy = policy.Name
// resource details
response.PolicyResponse.Resource.Name = ns.GetName()
response.PolicyResponse.Resource.Kind = ns.GetKind()
response.PolicyResponse.Resource.APIVersion = ns.GetAPIVersion()
}()
2019-08-19 16:40:10 -07:00
glog.V(4).Infof("started applying generation rules of policy %q (%v)", policy.Name, startTime)
defer func() {
2019-08-26 13:34:42 -07:00
response.PolicyResponse.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying generation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
glog.V(4).Infof("Generation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
2019-08-19 16:40:10 -07:00
}()
incrementAppliedRuleCount := func() {
2019-08-19 16:40:10 -07:00
// rules applied succesfully count
2019-08-26 13:34:42 -07:00
response.PolicyResponse.RulesAppliedCount++
2019-08-19 16:40:10 -07:00
}
for _, rule := range policy.Spec.Rules {
2019-10-21 14:22:31 -07:00
if !rule.HasGenerate() {
continue
}
2019-08-14 14:56:53 -07:00
glog.V(4).Infof("applying policy %s generate rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName())
2019-08-26 13:34:42 -07:00
ruleResponse := applyRuleGenerator(client, ns, rule, policy.GetCreationTimestamp())
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
incrementAppliedRuleCount()
}
2019-08-19 18:57:19 -07:00
return response
}
2019-08-26 13:34:42 -07:00
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, rule kyverno.Rule, policyCreationTime metav1.Time) (response RuleResponse) {
startTime := time.Now()
glog.V(4).Infof("started applying generation rule %q (%v)", rule.Name, startTime)
response.Name = rule.Name
response.Type = Generation.String()
defer func() {
response.RuleStats.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying generation rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
}()
var err error
2019-07-26 08:11:20 -04:00
resource := &unstructured.Unstructured{}
var rdata map[string]interface{}
// To manage existing resource , we compare the creation time for the default resource to be generate and policy creation time
processExisting := func() bool {
nsCreationTime := ns.GetCreationTimestamp()
return nsCreationTime.Before(&policyCreationTime)
}()
2019-08-26 13:34:42 -07:00
if rule.Generation.Data != nil {
2019-08-14 14:56:53 -07:00
glog.V(4).Info("generate rule: creates new resource")
2019-07-26 08:11:20 -04:00
// 1> Check if resource exists
2019-08-26 13:34:42 -07:00
obj, err := client.GetResource(rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
2019-07-26 08:11:20 -04:00
if err == nil {
2019-08-26 13:34:42 -07:00
glog.V(4).Infof("generate rule: resource %s/%s/%s already present. checking if it contains the required configuration", rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
2019-07-26 08:11:20 -04:00
// 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
2019-08-26 13:34:42 -07:00
ok, err := checkResource(rule.Generation.Data, obj)
2019-07-26 08:11:20 -04:00
if err != nil {
glog.V(4).Infof("generate rule: unable to check if configuration %v, is present in resource '%s/%s' in namespace '%s'", rule.Generation.Data, rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
response.Success = false
response.Message = fmt.Sprintf("unable to check if configuration %v, is present in resource '%s/%s' in namespace '%s'", rule.Generation.Data, rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
return response
2019-07-26 08:11:20 -04:00
}
if !ok {
glog.V(4).Infof("generate rule: configuration %v not present in resource '%s/%s' in namespace '%s'", rule.Generation.Data, rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
response.Success = false
response.Message = fmt.Sprintf("configuration %v not present in resource '%s/%s' in namespace '%s'", rule.Generation.Data, rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
return response
2019-07-26 08:11:20 -04:00
}
2019-08-26 13:34:42 -07:00
response.Success = true
response.Message = fmt.Sprintf("required configuration %v is present in resource '%s/%s' in namespace '%s'", rule.Generation.Data, rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
return response
2019-07-26 08:11:20 -04:00
}
2019-08-26 13:34:42 -07:00
rdata, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&rule.Generation.Data)
2019-07-26 08:11:20 -04:00
if err != nil {
glog.Error(err)
2019-08-26 13:34:42 -07:00
response.Success = false
response.Message = fmt.Sprintf("failed to parse the specified resource spec %v: %v", rule.Generation.Data, err)
return response
2019-07-26 08:11:20 -04:00
}
}
2019-08-26 13:34:42 -07:00
if rule.Generation.Clone != (kyverno.CloneFrom{}) {
2019-08-14 14:56:53 -07:00
glog.V(4).Info("generate rule: clone resource")
2019-07-26 08:11:20 -04:00
// 1> Check if resource exists
2019-08-26 13:34:42 -07:00
_, err := client.GetResource(rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
2019-07-26 08:11:20 -04:00
if err == nil {
glog.V(4).Infof("generate rule: resource '%s/%s' already present in namespace '%s'", rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
response.Success = true
response.Message = fmt.Sprintf("resource '%s/%s' already present in namespace '%s'", rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
return response
2019-07-26 08:11:20 -04:00
}
2019-08-14 14:56:53 -07:00
// 2> If clone already exists return
2019-08-26 13:34:42 -07:00
resource, err = client.GetResource(rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
2019-07-26 08:11:20 -04:00
if err != nil {
glog.V(4).Infof("generate rule: clone reference resource '%s/%s' not present in namespace '%s': %v", rule.Generation.Kind, rule.Generation.Clone.Name, rule.Generation.Clone.Namespace, err)
2019-08-26 13:34:42 -07:00
response.Success = false
response.Message = fmt.Sprintf("clone reference resource '%s/%s' not present in namespace '%s': %v", rule.Generation.Kind, rule.Generation.Clone.Name, rule.Generation.Clone.Namespace, err)
2019-08-26 13:34:42 -07:00
return response
2019-07-26 08:11:20 -04:00
}
glog.V(4).Infof("generate rule: clone reference resource '%s/%s' present in namespace '%s'", rule.Generation.Kind, rule.Generation.Clone.Name, rule.Generation.Clone.Namespace)
2019-07-26 08:11:20 -04:00
rdata = resource.UnstructuredContent()
}
if processExisting {
glog.V(4).Infof("resource '%s/%s' not found in existing namespace '%s'", rule.Generation.Kind, rule.Generation.Name, ns.GetName())
2019-08-26 13:34:42 -07:00
response.Success = false
response.Message = fmt.Sprintf("resource '%s/%s' not found in existing namespace '%s'", rule.Generation.Kind, rule.Generation.Name, ns.GetName())
// for existing resources we generate an error which indirectly generates a policy violation
2019-08-26 13:34:42 -07:00
return response
}
2019-07-26 08:11:20 -04:00
resource.SetUnstructuredContent(rdata)
2019-08-26 13:34:42 -07:00
resource.SetName(rule.Generation.Name)
2019-07-26 08:11:20 -04:00
resource.SetNamespace(ns.GetName())
// Reset resource version
resource.SetResourceVersion("")
2019-08-26 13:34:42 -07:00
_, err = client.CreateResource(rule.Generation.Kind, ns.GetName(), resource, false)
if err != nil {
2019-08-26 13:34:42 -07:00
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", rule.Generation.Kind, resource.GetNamespace(), resource.GetName(), err)
response.Success = false
response.Message = fmt.Sprintf("unable to create resource %s/%s/%s: %v", rule.Generation.Kind, resource.GetNamespace(), resource.GetName(), err)
return response
}
2019-08-26 13:34:42 -07:00
glog.V(4).Infof("generate rule: created resource %s/%s/%s", rule.Generation.Kind, resource.GetNamespace(), resource.GetName())
response.Success = true
response.Message = fmt.Sprintf("created resource %s/%s/%s", rule.Generation.Kind, resource.GetNamespace(), resource.GetName())
return response
}
2019-07-26 08:11:20 -04:00
2019-08-14 14:56:53 -07:00
//checkResource checks if the config is present in th eresource
2019-07-26 08:11:20 -04:00
func checkResource(config interface{}, resource *unstructured.Unstructured) (bool, error) {
var err error
2019-07-26 08:11:20 -04:00
objByte, err := resource.MarshalJSON()
if err != nil {
// unable to parse the json
return false, err
}
err = resource.UnmarshalJSON(objByte)
2019-07-26 08:11:20 -04:00
if err != nil {
// unable to parse the json
2019-07-26 08:11:20 -04:00
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
2019-07-26 08:11:20 -04:00
}