1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

generation: check existing resources

This commit is contained in:
shivkumar dudhani 2019-07-05 11:24:18 -07:00
parent 63c1874016
commit 3fbcb992a1
5 changed files with 257 additions and 38 deletions

14
main.go
View file

@ -2,7 +2,6 @@ package main
import (
"flag"
"fmt"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/config"
@ -36,19 +35,6 @@ func main() {
if err != nil {
glog.Fatalf("Error creating client: %v\n", err)
}
// test Code
rGVR := client.DiscoveryClient.GetGVRFromKind("ConfigMap")
obj, err := client.GetResource(rGVR.Resource, "ns2", "default-config")
if err != nil {
fmt.Println(err)
}
data, err := obj.MarshalJSON()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))
// test Code
policyInformerFactory, err := sharedinformer.NewSharedInformerFactory(clientConfig)
if err != nil {

View file

@ -1,26 +1,29 @@
package engine
import (
"encoding/json"
"errors"
"fmt"
"github.com/golang/glog"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/info"
"github.com/nirmata/kyverno/pkg/utils"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
//GenerateNew apply generation rules on a resource
func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns *corev1.Namespace, processExisting bool) []*info.RuleInfo {
func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns *corev1.Namespace) []*info.RuleInfo {
ris := []*info.RuleInfo{}
for _, rule := range policy.Spec.Rules {
if rule.Generation == nil {
continue
}
ri := info.NewRuleInfo(rule.Name, info.Generation)
err := applyRuleGeneratorNew(client, ns, rule.Generation, processExisting)
err := applyRuleGeneratorNew(client, ns, rule.Generation)
if err != nil {
ri.Fail()
ri.Addf("Rule %s: Failed to apply rule generator, err %v.", rule.Name, err)
@ -33,39 +36,45 @@ func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns *corev1.Name
return ris
}
func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1alpha1.Generation, processExisting bool) error {
func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1alpha1.Generation) error {
var err error
resource := &unstructured.Unstructured{}
var rdata map[string]interface{}
// get resource from kind
rGVR := client.DiscoveryClient.GetGVRFromKind(gen.Kind)
if rGVR.Resource == "" {
return fmt.Errorf("Kind to Resource Name conversion failed for %s", gen.Kind)
}
// If processing Existing resource, we only check if the resource
// already exists
if processExisting {
obj, err := client.GetResource(rGVR.Resource, ns.Name, gen.Name)
if err != nil {
return err
}
data := []byte{}
if err := obj.UnmarshalJSON(data); err != nil {
fmt.Println(err)
}
fmt.Println(string(data))
}
var rdata map[string]interface{}
// data -> create new resource
if gen.Data != nil {
// 1> Check if resource exists
obj, err := client.GetResource(rGVR.Resource, ns.Name, 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
}
}
// clone -> copy from existing resource
if gen.Clone != nil {
// 1> Check if resource exists
_, err := client.GetResource(rGVR.Resource, ns.Name, gen.Name)
if err == nil {
return nil
}
// 2> If already exists return
resource, err = client.GetResource(rGVR.Resource, gen.Clone.Namespace, gen.Clone.Name)
if err != nil {
return err
@ -84,3 +93,41 @@ func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1a
}
return nil
}
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
}

View file

@ -1,8 +1,6 @@
package gencontroller
import (
"fmt"
"github.com/golang/glog"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kyverno/pkg/engine"
@ -13,7 +11,6 @@ import (
func (c *Controller) processNamespace(ns *corev1.Namespace) error {
//Get all policies and then verify if the namespace matches any of the defined selectors
fmt.Println(ns.Name)
policies, err := c.listPolicies(ns)
if err != nil {
return err
@ -37,7 +34,6 @@ func (c *Controller) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, err
// Check if the policy contains a generatoin rule
for _, r := range p.Spec.Rules {
if r.Generation != nil {
fmt.Println(p.Name)
// Check if the resource meets the description
if namespaceMeetsRuleDescription(ns, r.ResourceDescription) {
fpolicies = append(fpolicies, p)
@ -56,7 +52,7 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) err
ns.Name,
"") // Namespace has no namespace..WOW
ruleInfos := engine.GenerateNew(c.client, p, ns, false)
ruleInfos := engine.GenerateNew(c.client, p, ns)
policyInfo.AddRuleInfos(ruleInfos)
if !policyInfo.IsSuccessful() {
glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name)

View file

@ -7,6 +7,7 @@ import (
"k8s.io/client-go/rest"
)
//NewKubeInformerFactory returns a kubeinformer
func NewKubeInformerFactory(cfg *rest.Config) kubeinformers.SharedInformerFactory {
// kubernetes client
kubeClient, err := kubernetes.NewForConfig(cfg)

189
pkg/utils/json.go Normal file
View file

@ -0,0 +1,189 @@
package utils
import (
"github.com/golang/glog"
)
//JSONsubsetValue checks if JSON a is contained in JSON b
func JSONsubsetValue(a interface{}, b interface{}) bool {
switch typed := a.(type) {
case bool:
bv, ok := b.(bool)
if !ok {
glog.Errorf("expected bool found %T", b)
return false
}
av, _ := a.(bool)
if av == bv {
return true
}
case int:
bv, ok := b.(int)
if !ok {
glog.Errorf("expected int found %T", b)
return false
}
av, _ := a.(int)
if av == bv {
return true
}
case float64:
bv, ok := b.(float64)
if !ok {
glog.Errorf("expected float64 found %T", b)
return false
}
av, _ := a.(float64)
if av == bv {
return true
}
case string:
bv, ok := b.(string)
if !ok {
glog.Errorf("expected string found %T", b)
return false
}
av, _ := a.(string)
if av == bv {
return true
}
case map[string]interface{}:
bv, ok := b.(map[string]interface{})
if !ok {
glog.Errorf("expected map[string]interface{} found %T", b)
return false
}
av, _ := a.(map[string]interface{})
return subsetMap(av, bv)
case []interface{}:
// TODO: verify the logic
bv, ok := b.([]interface{})
if !ok {
glog.Errorf("expected []interface{} found %T", b)
return false
}
av, _ := a.([]interface{})
return subsetSlice(av, bv)
default:
glog.Errorf("Unspported type %s", typed)
}
return false
}
func subsetMap(a, b map[string]interface{}) bool {
// check if keys are present
for k := range a {
if _, ok := b[k]; !ok {
glog.Errorf("key %s, not present in resource", k)
return false
}
}
// check if values for the keys match
for ak, av := range a {
bv := b[ak]
if !JSONsubsetValue(av, bv) {
return false
}
}
return true
}
func contains(a interface{}, b []interface{}) bool {
switch typed := a.(type) {
case bool:
for _, bv := range b {
bv, ok := bv.(bool)
if !ok {
return false
}
av, _ := a.(bool)
if bv == av {
return true
}
}
case int:
for _, bv := range b {
bv, ok := bv.(int)
if !ok {
return false
}
av, _ := a.(int)
if bv == av {
return true
}
}
case float64:
for _, bv := range b {
bv, ok := bv.(float64)
if !ok {
return false
}
av, _ := a.(float64)
if bv == av {
return true
}
}
case string:
for _, bv := range b {
bv, ok := bv.(string)
if !ok {
return false
}
av, _ := a.(string)
if bv == av {
return true
}
}
case map[string]interface{}:
for _, bv := range b {
bv, ok := bv.(map[string]interface{})
if !ok {
return false
}
av, _ := a.(map[string]interface{})
if subsetMap(av, bv) {
return true
}
}
case []interface{}:
for _, bv := range b {
bv, ok := bv.([]interface{})
if !ok {
return false
}
av, _ := a.([]interface{})
if JSONsubsetValue(av, bv) {
return true
}
}
default:
glog.Errorf("Unspported type %s", typed)
}
return false
}
func subsetSlice(a, b []interface{}) bool {
// if empty
if len(a) == 0 {
return true
}
// check if len is not greater
if len(a) > len(b) {
return false
}
for _, av := range a {
if !contains(av, b) {
return false
}
}
return true
}