From e8de9a111ae89e3eaad72bef802a23395fdff59c Mon Sep 17 00:00:00 2001 From: shuting Date: Thu, 16 May 2019 14:09:02 -0700 Subject: [PATCH] Finished Generate() logic to actual generating the resource --- .../policy-namespace-patch-cmgCG-sgCG.yaml | 51 ++++++++++--------- kubeclient/kubeclient.go | 20 +++++--- main.go | 2 +- pkg/apis/policy/v1alpha1/types.go | 6 +-- pkg/apis/policy/v1alpha1/utils.go | 5 +- pkg/engine/generation.go | 39 ++++++++------ pkg/webhooks/server.go | 8 +++ 7 files changed, 78 insertions(+), 53 deletions(-) diff --git a/examples/ConfigMapGenerator-SecretGenerator/policy-namespace-patch-cmgCG-sgCG.yaml b/examples/ConfigMapGenerator-SecretGenerator/policy-namespace-patch-cmgCG-sgCG.yaml index 922eeee05a..a400677700 100644 --- a/examples/ConfigMapGenerator-SecretGenerator/policy-namespace-patch-cmgCG-sgCG.yaml +++ b/examples/ConfigMapGenerator-SecretGenerator/policy-namespace-patch-cmgCG-sgCG.yaml @@ -3,42 +3,47 @@ # To apply this policy you need to create secret and configMap in "default" namespace # and then create a namespace -apiVersion : policy.nirmata.io/v1alpha1 +apiVersion : kubepolicy.nirmata.io/v1alpha1 kind : Policy metadata : name : "policy-ns-patch-cmg-sg" spec : - failurePolicy: stopOnError rules: - - resource : + - name: "patchNamespace2" + resource : kind : Namespace selector: matchLabels: LabelForSelector : "namespace2" - patch: - - path: "/metadata/labels/isMutatedByPolicy" - op: add - value: "true" + mutate: + patches: + - path: "/metadata/labels/isMutatedByPolicy" + op: add + value: "true" - - resource : + - name: "copyCM" + resource : kind : Namespace selector: matchLabels: LabelForSelector : "namespace2" - configMapGenerator : + generate : + kind: ConfigMap name : copied-cm copyFrom : namespace : default name : game-config data : secretData: "data from cmg" - - - resource : + + - name: "generateCM" + resource : kind : Namespace selector: matchLabels: LabelForSelector : "namespace2" - configMapGenerator : + generate : + kind: ConfigMap name : generated-cm data : secretData: "very sensitive data from cmg" @@ -49,13 +54,12 @@ spec : image.public.key=771 rsa.public.key=42 - - resource : + - name: "generateSecret" + resource : kind : Namespace - selector: - matchLabels: - LabelForSelector : "namespace2" - - secretGenerator : + name: ns2 + generate : + kind: Secret name : generated-secrets data : foo : bar @@ -66,13 +70,12 @@ spec : foo1=bar1 foo2=bar2 - - resource : + - name: "copySecret" + resource : kind : Namespace - selector: - matchLabels: - LabelForSelector : "namespace2" - - secretGenerator : + name: ns2 + generate : + kind: Secret name : copied-secrets copyFrom : namespace : default diff --git a/kubeclient/kubeclient.go b/kubeclient/kubeclient.go index f32d6950b6..98797907bc 100644 --- a/kubeclient/kubeclient.go +++ b/kubeclient/kubeclient.go @@ -67,10 +67,12 @@ func (kc *KubeClient) GenerateConfigMap(generator types.Generation, namespace st var err error - kc.logger.Printf("Copying data from configmap %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name) - configMap, err = kc.client.CoreV1().ConfigMaps(generator.CopyFrom.Namespace).Get(generator.CopyFrom.Name, metav1.GetOptions{}) - if err != nil { - return err + if generator.CopyFrom != nil { + kc.logger.Printf("Copying data from configmap %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name) + configMap, err = kc.client.CoreV1().ConfigMaps(generator.CopyFrom.Namespace).Get(generator.CopyFrom.Name, metav1.GetOptions{}) + if err != nil { + return err + } } configMap.ObjectMeta = metav1.ObjectMeta{ @@ -101,10 +103,12 @@ func (kc *KubeClient) GenerateSecret(generator types.Generation, namespace strin var err error - kc.logger.Printf("Copying data from secret %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name) - secret, err = kc.client.CoreV1().Secrets(generator.CopyFrom.Namespace).Get(generator.CopyFrom.Name, metav1.GetOptions{}) - if err != nil { - return err + if generator.CopyFrom != nil { + kc.logger.Printf("Copying data from secret %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name) + secret, err = kc.client.CoreV1().Secrets(generator.CopyFrom.Namespace).Get(generator.CopyFrom.Name, metav1.GetOptions{}) + if err != nil { + return err + } } secret.ObjectMeta = metav1.ObjectMeta{ diff --git a/main.go b/main.go index abf9b47711..9bd39c21a5 100644 --- a/main.go +++ b/main.go @@ -59,7 +59,7 @@ func main() { log.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - server, err := webhooks.NewWebhookServer(tlsPair, policyInformer.Lister(), nil) + server, err := webhooks.NewWebhookServer(tlsPair, policyInformer.Lister(), kubeclient, nil) if err != nil { log.Fatalf("Unable to create webhook server: %v\n", err) } diff --git a/pkg/apis/policy/v1alpha1/types.go b/pkg/apis/policy/v1alpha1/types.go index dd7d8cd43d..0bc0699eef 100644 --- a/pkg/apis/policy/v1alpha1/types.go +++ b/pkg/apis/policy/v1alpha1/types.go @@ -60,9 +60,9 @@ type Validation struct { // Generation describes which resources will be created when other resource is created type Generation struct { - Kind string `json:"kind"` - Name string `json:"name"` - CopyFrom `json:"copyFrom"` + Kind string `json:"kind"` + Name string `json:"name"` + CopyFrom *CopyFrom `json:"copyFrom,omitempty"` Data map[string]string `json:"data"` Labels map[string]string `json:"labels"` } diff --git a/pkg/apis/policy/v1alpha1/utils.go b/pkg/apis/policy/v1alpha1/utils.go index 653513a232..dd0374bb26 100644 --- a/pkg/apis/policy/v1alpha1/utils.go +++ b/pkg/apis/policy/v1alpha1/utils.go @@ -78,7 +78,10 @@ func (pcg *Generation) Validate() error { return errors.New("Name or/and Kind of generator is not specified") } - return pcg.CopyFrom.Validate() + if pcg.CopyFrom != nil { + return pcg.CopyFrom.Validate() + } + return nil } // DeepCopyInto is declared because k8s:deepcopy-gen is diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index e5cb259942..11f70ae346 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -4,6 +4,7 @@ import ( "fmt" "log" + kubeClient "github.com/nirmata/kube-policy/kubeclient" kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" "github.com/nirmata/kube-policy/pkg/engine/mutation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -16,14 +17,12 @@ type GenerationResponse struct { // Generate should be called to process generate rules on the resource // TODO: extend kubeclient(will change to dynamic client) to create resources -func Generate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) []GenerationResponse { +func Generate(policy kubepolicy.Policy, rawResource []byte, kubeClient *kubeClient.KubeClient, gvk metav1.GroupVersionKind) { // configMapGenerator and secretGenerator can be applied only to namespaces if gvk.Kind != "Namespace" { - return nil + return } - var generateResps []GenerationResponse - for i, rule := range policy.Spec.Rules { // Checks for preconditions @@ -48,31 +47,39 @@ func Generate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers continue } - generateResps, err = applyRuleGenerator(rawResource, rule.Generation) + err = applyRuleGenerator(rawResource, rule.Generation, kubeClient) if err != nil { log.Printf("Failed to apply rule generator: %v", err) - } else { - generateResps = append(generateResps, generateResps...) } } - - return generateResps } // Applies "configMapGenerator" and "secretGenerator" described in PolicyRule // TODO: plan to support all kinds of generator -func applyRuleGenerator(rawResource []byte, generator *kubepolicy.Generation) ([]GenerationResponse, error) { - var generateResps []GenerationResponse +func applyRuleGenerator(rawResource []byte, generator *kubepolicy.Generation, kubeClient *kubeClient.KubeClient) error { if generator == nil { - return nil, nil + return nil } err := generator.Validate() if err != nil { - return nil, fmt.Errorf("Generator for '%s' is invalid: %s", generator.Kind, err) + return fmt.Errorf("Generator for '%s/%s' is invalid: %s", generator.Kind, generator.Name, err) } - namespaceName := mutation.ParseNameFromObject(rawResource) - generateResps = append(generateResps, GenerationResponse{generator, namespaceName}) - return generateResps, nil + namespace := mutation.ParseNameFromObject(rawResource) + switch generator.Kind { + case "ConfigMap": + err = kubeClient.GenerateConfigMap(*generator, namespace) + case "Secret": + err = kubeClient.GenerateSecret(*generator, namespace) + default: + err = fmt.Errorf("Unsupported config Kind '%s'", generator.Kind) + } + + if err != nil { + return fmt.Errorf("Unable to apply generator for %s '%s/%s' : %v", generator.Kind, namespace, generator.Name, err) + } + + log.Printf("Successfully applied generator %s/%s", generator.Kind, generator.Name) + return nil } diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index b74a6a36e8..807f6bc23b 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -13,6 +13,7 @@ import ( "time" "github.com/nirmata/kube-policy/config" + kubeClient "github.com/nirmata/kube-policy/kubeclient" policylister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1" engine "github.com/nirmata/kube-policy/pkg/engine" "github.com/nirmata/kube-policy/pkg/engine/mutation" @@ -27,6 +28,7 @@ import ( type WebhookServer struct { server http.Server policyLister policylister.PolicyLister + kubeClient *kubeClient.KubeClient logger *log.Logger } @@ -35,6 +37,7 @@ type WebhookServer struct { func NewWebhookServer( tlsPair *tlsutils.TlsPemPair, policyLister policylister.PolicyLister, + kubeClient *kubeClient.KubeClient, logger *log.Logger) (*WebhookServer, error) { if logger == nil { logger = log.New(os.Stdout, "Webhook Server: ", log.LstdFlags) @@ -53,6 +56,7 @@ func NewWebhookServer( ws := &WebhookServer{ policyLister: policyLister, + kubeClient: kubeClient, logger: logger, } @@ -174,6 +178,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 allowed := true for _, policy := range policies { + // validation ws.logger.Printf("Validating resource with policy %s with %d rules", policy.ObjectMeta.Name, len(policy.Spec.Rules)) if ok := engine.Validate(*policy, request.Object.Raw, request.Kind); !ok { @@ -183,6 +188,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } else { ws.logger.Println("Validation is successful") } + + // generation + engine.Generate(*policy, request.Object.Raw, ws.kubeClient, request.Kind) } return &v1beta1.AdmissionResponse{