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

227 lines
6.8 KiB
Go
Raw Normal View History

2019-05-13 18:17:28 -07:00
package engine
2019-05-09 22:26:22 -07:00
import (
2019-06-25 22:53:18 -07:00
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
2019-05-21 11:00:09 -07:00
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
2019-06-25 22:53:18 -07:00
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/info"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2019-07-23 23:34:03 -04:00
v1helper "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
2019-05-09 22:26:22 -07:00
)
2019-07-23 23:34:03 -04:00
func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy) map[string]resourceInfo {
2019-07-17 23:13:28 -07:00
// key uid
resourceMap := map[string]resourceInfo{}
2019-06-25 22:53:18 -07:00
for _, rule := range policy.Spec.Rules {
2019-07-23 23:34:03 -04:00
// Match
for _, k := range rule.MatchResources.Kinds {
2019-06-25 22:53:18 -07:00
// kind -> resource
gvr := client.DiscoveryClient.GetGVRFromKind(k)
2019-07-23 23:34:03 -04:00
// Namespace
namespace := "default"
2019-07-23 23:34:03 -04:00
if rule.MatchResources.Namespace != nil {
namespace = *rule.MatchResources.Namespace
}
if k == "Namespace" {
namespace = ""
}
2019-07-23 23:34:03 -04:00
// Check if exclude namespace is not clashing
if rule.ExcludeResources.Namespace != nil && *rule.ExcludeResources.Namespace == namespace {
// as the namespace is excluded
continue
}
2019-07-23 23:34:03 -04:00
// List resources
list, err := client.ListResource(k, namespace, rule.MatchResources.Selector)
2019-06-25 22:53:18 -07:00
if err != nil {
2019-07-23 23:34:03 -04:00
glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.MatchResources.Selector.String())
2019-06-25 22:53:18 -07:00
glog.Errorf("unable to apply policy %s rule %s. err: %s", policy.Name, rule.Name, err)
continue
}
2019-07-23 23:34:03 -04:00
var selector labels.Selector
// exclude label selector
if rule.ExcludeResources.Selector != nil {
selector, err = v1helper.LabelSelectorAsSelector(rule.ExcludeResources.Selector)
if err != nil {
glog.Error(err)
}
}
2019-06-25 22:53:18 -07:00
for _, res := range list.Items {
2019-07-23 23:34:03 -04:00
// exclude label selectors
if selector != nil {
set := labels.Set(res.GetLabels())
if selector.Matches(set) {
// if matches
continue
}
}
var name *string
// match
// name
// wild card matching
name = rule.MatchResources.Name
2019-06-25 22:53:18 -07:00
if name != nil {
2019-07-23 23:34:03 -04:00
// if does not match then we skip
2019-06-25 22:53:18 -07:00
if !wildcard.Match(*name, res.GetName()) {
continue
}
}
2019-07-23 23:34:03 -04:00
// exclude
// name
// wild card matching
name = rule.ExcludeResources.Name
if name != nil {
// if matches then we skip
if wildcard.Match(*name, res.GetName()) {
continue
}
}
gvk := res.GroupVersionKind()
ri := resourceInfo{Resource: res, Gvk: &metav1.GroupVersionKind{Group: gvk.Group,
2019-06-25 22:53:18 -07:00
Version: gvk.Version,
Kind: gvk.Kind}}
2019-07-17 23:13:28 -07:00
resourceMap[string(res.GetUID())] = ri
2019-06-25 22:53:18 -07:00
}
}
}
2019-07-23 23:34:03 -04:00
return resourceMap
}
// ProcessExisting checks for mutation and validation violations of existing resources
func ProcessExisting(client *client.Client, policy *types.Policy) []*info.PolicyInfo {
glog.Infof("Applying policy %s on existing resources", policy.Name)
// key uid
resourceMap := ListResourcesThatApplyToPolicy(client, policy)
// for _, rule := range policy.Spec.Rules {
// for _, k := range rule.Kinds {
// // kind -> resource
// gvr := client.DiscoveryClient.GetGVRFromKind(k)
// // label selectors
// // namespace ? should it be default or allow policy to specify it
// namespace := "default"
// if rule.ResourceDescription.Namespace != nil {
// namespace = *rule.ResourceDescription.Namespace
// }
// if k == "Namespace" {
// namespace = ""
// }
// list, err := client.ListResource(k, namespace, rule.ResourceDescription.Selector)
// if err != nil {
// glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.Selector.String())
// glog.Errorf("unable to apply policy %s rule %s. err: %s", policy.Name, rule.Name, err)
// continue
// }
// for _, res := range list.Items {
// name := rule.ResourceDescription.Name
// gvk := res.GroupVersionKind()
// if name != nil {
// // wild card matching
// if !wildcard.Match(*name, res.GetName()) {
// continue
// }
// }
// ri := resourceInfo{resource: res, gvk: &metav1.GroupVersionKind{Group: gvk.Group,
// Version: gvk.Version,
// Kind: gvk.Kind}}
// resourceMap[string(res.GetUID())] = ri
// }
// }
// }
2019-06-25 22:53:18 -07:00
policyInfos := []*info.PolicyInfo{}
// for the filtered resource apply policy
2019-07-17 23:13:28 -07:00
for _, v := range resourceMap {
2019-06-25 22:53:18 -07:00
2019-07-17 23:13:28 -07:00
policyInfo, err := applyPolicy(client, policy, v)
2019-06-25 22:53:18 -07:00
if err != nil {
2019-07-23 23:34:03 -04:00
glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.Resource.GetName(), v.Resource.GetNamespace())
2019-06-25 22:53:18 -07:00
glog.Error(err)
continue
}
policyInfos = append(policyInfos, policyInfo)
}
return policyInfos
}
func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) (*info.PolicyInfo, error) {
2019-07-23 23:34:03 -04:00
policyInfo := info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction)
2019-06-25 22:53:18 -07:00
glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
2019-07-23 23:34:03 -04:00
rawResource, err := res.Resource.MarshalJSON()
2019-06-25 22:53:18 -07:00
if err != nil {
return nil, err
}
// Mutate
2019-07-23 23:34:03 -04:00
mruleInfos, err := mutation(policy, rawResource, res.Gvk)
2019-06-25 22:53:18 -07:00
policyInfo.AddRuleInfos(mruleInfos)
if err != nil {
return nil, err
}
// Validation
2019-07-23 23:34:03 -04:00
vruleInfos, err := Validate(*policy, rawResource, *res.Gvk)
2019-06-25 22:53:18 -07:00
policyInfo.AddRuleInfos(vruleInfos)
if err != nil {
return nil, err
}
2019-07-23 23:34:03 -04:00
if res.Gvk.Kind == "Namespace" {
// Generation
2019-07-26 08:11:20 -04:00
gruleInfos := Generate(client, policy, res.Resource)
policyInfo.AddRuleInfos(gruleInfos)
}
2019-06-25 22:53:18 -07:00
return policyInfo, nil
}
func mutation(p *types.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) {
patches, ruleInfos := Mutate(*p, rawResource, *gvk)
2019-07-18 10:22:20 -07:00
if len(ruleInfos) == 0 {
// no rules were processed
return nil, nil
}
2019-07-23 00:10:18 -04:00
// if there are any errors return
for _, r := range ruleInfos {
if !r.IsSuccessful() {
return ruleInfos, nil
}
}
// if there are no patches // for overlay
if len(patches) == 0 {
return ruleInfos, nil
}
2019-06-25 22:53:18 -07:00
// option 2: (original Resource + patch) compare with (original resource)
mergePatches := JoinPatches(patches)
// merge the patches
patch, err := jsonpatch.DecodePatch(mergePatches)
if err != nil {
return nil, err
}
// apply the patches returned by mutate to the original resource
patchedResource, err := patch.Apply(rawResource)
if err != nil {
return nil, err
}
// compare (original Resource + patch) vs (original resource)
// to verify if they are equal
2019-06-26 15:31:18 -07:00
ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation)
2019-06-25 22:53:18 -07:00
if !jsonpatch.Equal(patchedResource, rawResource) {
//resource does not match so there was a mutation rule violated
// TODO : check the rule name "mutation rules"
ruleInfo.Fail()
ruleInfo.Add("resource does not satisfy mutation rules")
} else {
ruleInfo.Add("resource satisfys the mutation rule")
}
ruleInfos = append(ruleInfos, ruleInfo)
return ruleInfos, nil
2019-05-14 18:20:41 -07:00
}