From 670881c71d6da4fa7a619abdf937d9b67b382051 Mon Sep 17 00:00:00 2001 From: belyshevdenis Date: Mon, 25 Mar 2019 15:44:53 +0200 Subject: [PATCH] NK-51: Added Deployment as owner of MutatingWebhookConfiguration. This allows kubernetes to delete webhook config, when deployment deletes. --- config/config.go | 20 +++++++----- init.go | 2 +- kubeclient/kubeclient.go | 4 +++ main.go | 6 +--- webhooks/mutation.go | 11 ++++--- webhooks/registration.go | 68 ++++++++++++++++++++++++++-------------- 6 files changed, 69 insertions(+), 42 deletions(-) diff --git a/config/config.go b/config/config.go index 4ca7879709..30182e5ea2 100644 --- a/config/config.go +++ b/config/config.go @@ -2,16 +2,22 @@ package config const ( // These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml - WebhookServiceNamespace = "kube-system" - WebhookServiceName = "kube-policy-svc" + KubePolicyDeploymentName = "kube-policy-deployment" + KubePolicyNamespace = "kube-system" + WebhookServiceName = "kube-policy-svc" + WebhookConfigName = "nirmata-kube-policy-webhook-cfg" + MutationWebhookName = "webhook.nirmata.kube-policy" - WebhookConfigName = "nirmata-kube-policy-webhook-cfg" - MutationWebhookName = "webhook.nirmata.kube-policy" + // Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields + // Pull request: https://github.com/kubernetes/kubernetes/pull/63972 + // When pull request is closed, we should use TypeMeta struct instead of this constants + DeploymentKind = "Deployment" + DeploymentAPIVersion = "extensions/v1beta1" ) var ( - WebhookServicePath = "/mutate" - WebhookConfigLabels = map[string]string { + WebhookServicePath = "/mutate" + WebhookConfigLabels = map[string]string{ "app": "kube-policy", } -) \ No newline at end of file +) diff --git a/init.go b/init.go index 609b64af7b..f3c7913d92 100644 --- a/init.go +++ b/init.go @@ -76,7 +76,7 @@ func tlsPairFromCluster(configuration *rest.Config, client *kubeclient.KubeClien } certProps := utils.TlsCertificateProps{ Service: config.WebhookServiceName, - Namespace: config.WebhookServiceNamespace, + Namespace: config.KubePolicyNamespace, ApiServerHost: apiServerUrl.Hostname(), } diff --git a/kubeclient/kubeclient.go b/kubeclient/kubeclient.go index 9a5cf8001c..9b495bbedc 100644 --- a/kubeclient/kubeclient.go +++ b/kubeclient/kubeclient.go @@ -36,6 +36,10 @@ func NewKubeClient(config *rest.Config, logger *log.Logger) (*KubeClient, error) }, nil } +func (kc *KubeClient) GetClient() *kubernetes.Clientset { + return kc.client +} + // Generates new ConfigMap in given namespace. If the namespace does not exists yet, // waits until it is created for maximum namespaceCreationMaxWaitTime (see below) func (kc *KubeClient) GenerateConfigMap(generator types.PolicyConfigGenerator, namespace string) error { diff --git a/main.go b/main.go index 3b518e41cc..e0ba632a68 100644 --- a/main.go +++ b/main.go @@ -59,12 +59,8 @@ func main() { log.Println("Policy Controller has started") <-stopCh - server.Stop() - err = mutationWebhook.Deregister() - if err != nil { - log.Printf("Unable to deregister mutation webhook: %v", err) - } + server.Stop() log.Println("Policy Controller has stopped") } diff --git a/webhooks/mutation.go b/webhooks/mutation.go index ddc0c45098..b7a1893134 100644 --- a/webhooks/mutation.go +++ b/webhooks/mutation.go @@ -29,7 +29,12 @@ func CreateMutationWebhook(clientConfig *rest.Config, kubeclient *kubeclient.Kub return nil, errors.New("Some parameters are not set") } - registration, err := NewMutationWebhookRegistration(clientConfig) + registration, err := NewMutationWebhookRegistration(clientConfig, kubeclient) + if err != nil { + return nil, err + } + + err = registration.Register() if err != nil { return nil, err } @@ -45,10 +50,6 @@ func CreateMutationWebhook(clientConfig *rest.Config, kubeclient *kubeclient.Kub }, nil } -func (mw *MutationWebhook) Deregister() error { - return mw.registration.Deregister() -} - // Mutate applies admission to request func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { mw.logger.Printf("AdmissionReview for Kind=%v, Namespace=%v Name=%v UID=%v patchOperation=%v UserInfo=%v", diff --git a/webhooks/registration.go b/webhooks/registration.go index b83b4274de..6cd8a7ae1f 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -2,10 +2,10 @@ package webhooks import ( "errors" - "fmt" "io/ioutil" "github.com/nirmata/kube-policy/config" + kubeclient "github.com/nirmata/kube-policy/kubeclient" admregapi "k8s.io/api/admissionregistration/v1beta1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -15,60 +15,80 @@ import ( type MutationWebhookRegistration struct { registrationClient *admregclient.AdmissionregistrationV1beta1Client + kubeclient *kubeclient.KubeClient + clientConfig *rest.Config } -func NewMutationWebhookRegistration(clientConfig *rest.Config) (*MutationWebhookRegistration, error) { +func NewMutationWebhookRegistration(clientConfig *rest.Config, kubeclient *kubeclient.KubeClient) (*MutationWebhookRegistration, error) { registrationClient, err := admregclient.NewForConfig(clientConfig) if err != nil { return nil, err } - webhookConfig, err := constructWebhookConfig(clientConfig) - if err != nil { - return nil, err - } - - oldConfig, err := registrationClient.MutatingWebhookConfigurations().Get(config.WebhookConfigName, meta.GetOptions{}) - if oldConfig != nil && oldConfig.ObjectMeta.UID != "" { - // Normally webhook configuration should be deleted from cluster when controller end his work. - // But if old configuration is detected in cluster, it should be replaced by new one. - err = registrationClient.MutatingWebhookConfigurations().Delete(config.WebhookConfigName, &meta.DeleteOptions{}) - if err != nil { - return nil, errors.New(fmt.Sprintf("Failed to delete old webhook configuration: %v", err)) - } - } - - _, err = registrationClient.MutatingWebhookConfigurations().Create(webhookConfig) - if err != nil { - return nil, err - } - return &MutationWebhookRegistration{ registrationClient: registrationClient, + kubeclient: kubeclient, + clientConfig: clientConfig, }, nil } +func (mwr *MutationWebhookRegistration) Register() error { + webhookConfig, err := mwr.constructWebhookConfig(mwr.clientConfig) + if err != nil { + return err + } + + _, err = mwr.registrationClient.MutatingWebhookConfigurations().Create(webhookConfig) + if err != nil { + return err + } + + return nil +} + func (mwr *MutationWebhookRegistration) Deregister() error { return mwr.registrationClient.MutatingWebhookConfigurations().Delete(config.MutationWebhookName, &meta.DeleteOptions{}) } -func constructWebhookConfig(configuration *rest.Config) (*admregapi.MutatingWebhookConfiguration, error) { +func (mwr *MutationWebhookRegistration) constructWebhookConfig(configuration *rest.Config) (*admregapi.MutatingWebhookConfiguration, error) { caData := ExtractCA(configuration) if len(caData) == 0 { return nil, errors.New("Unable to extract CA data from configuration") } + // Here we must know our Deployment UID + kubePolicyDeployment, err := mwr.kubeclient. + GetClient(). + Apps(). + Deployments(config.KubePolicyNamespace). + Get(config.KubePolicyDeploymentName, meta.GetOptions{ + ResourceVersion: "1", + IncludeUninitialized: true, + }) + + if err != nil { + return nil, err + } + return &admregapi.MutatingWebhookConfiguration{ ObjectMeta: meta.ObjectMeta{ Name: config.WebhookConfigName, Labels: config.WebhookConfigLabels, + OwnerReferences: []meta.OwnerReference{ + meta.OwnerReference{ + APIVersion: config.DeploymentAPIVersion, + Kind: config.DeploymentKind, + Name: kubePolicyDeployment.ObjectMeta.Name, + UID: kubePolicyDeployment.ObjectMeta.UID, + }, + }, }, Webhooks: []admregapi.Webhook{ admregapi.Webhook{ Name: config.MutationWebhookName, ClientConfig: admregapi.WebhookClientConfig{ Service: &admregapi.ServiceReference{ - Namespace: config.WebhookServiceNamespace, + Namespace: config.KubePolicyNamespace, Name: config.WebhookServiceName, Path: &config.WebhookServicePath, },