diff --git a/webhooks/mutation.go b/webhooks/mutation.go index d060f56a25..ddc0c45098 100644 --- a/webhooks/mutation.go +++ b/webhooks/mutation.go @@ -4,29 +4,49 @@ import ( "errors" "fmt" "log" + "os" controller "github.com/nirmata/kube-policy/controller" kubeclient "github.com/nirmata/kube-policy/kubeclient" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rest "k8s.io/client-go/rest" ) // MutationWebhook is a data type that represents -// buisness logic for resource mutation +// business logic for resource mutation type MutationWebhook struct { - kubeclient *kubeclient.KubeClient - controller *controller.PolicyController - logger *log.Logger + kubeclient *kubeclient.KubeClient + controller *controller.PolicyController + registration *MutationWebhookRegistration + logger *log.Logger } -// NewMutationWebhook is a method that returns new instance -// of MutationWebhook struct -func NewMutationWebhook(kubeclient *kubeclient.KubeClient, controller *controller.PolicyController, logger *log.Logger) (*MutationWebhook, error) { - if kubeclient == nil || controller == nil || logger == nil { +// Registers mutation webhook in cluster and creates object for this webhook +func CreateMutationWebhook(clientConfig *rest.Config, kubeclient *kubeclient.KubeClient, controller *controller.PolicyController, logger *log.Logger) (*MutationWebhook, error) { + if clientConfig == nil || kubeclient == nil || controller == nil { return nil, errors.New("Some parameters are not set") } - return &MutationWebhook{kubeclient: kubeclient, controller: controller, logger: logger}, nil + + registration, err := NewMutationWebhookRegistration(clientConfig) + if err != nil { + return nil, err + } + + if logger == nil { + logger = log.New(os.Stdout, "Mutation WebHook: ", log.LstdFlags|log.Lshortfile) + } + return &MutationWebhook{ + kubeclient: kubeclient, + controller: controller, + registration: registration, + logger: logger, + }, nil +} + +func (mw *MutationWebhook) Deregister() error { + return mw.registration.Deregister() } // Mutate applies admission to request diff --git a/webhooks/registration.go b/webhooks/registration.go index b8390f471a..b83b4274de 100644 --- a/webhooks/registration.go +++ b/webhooks/registration.go @@ -1,53 +1,85 @@ package webhooks import ( + "errors" + "fmt" "io/ioutil" "github.com/nirmata/kube-policy/config" - adm "k8s.io/api/admissionregistration/v1beta1" + admregapi "k8s.io/api/admissionregistration/v1beta1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" - admreg "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" + admregclient "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" rest "k8s.io/client-go/rest" ) -func RegisterMutationWebhook(config *rest.Config) error { - registrationClient, err := admreg.NewForConfig(config) - if err != nil { - return err - } - - _, err = registrationClient.MutatingWebhookConfigurations().Create(constructWebhookConfig(config)) - if err != nil { - return err - } - - return nil +type MutationWebhookRegistration struct { + registrationClient *admregclient.AdmissionregistrationV1beta1Client } -func constructWebhookConfig(configuration *rest.Config) *adm.MutatingWebhookConfiguration { - return &adm.MutatingWebhookConfiguration{ +func NewMutationWebhookRegistration(clientConfig *rest.Config) (*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, + }, nil +} + +func (mwr *MutationWebhookRegistration) Deregister() error { + return mwr.registrationClient.MutatingWebhookConfigurations().Delete(config.MutationWebhookName, &meta.DeleteOptions{}) +} + +func 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") + } + + return &admregapi.MutatingWebhookConfiguration{ ObjectMeta: meta.ObjectMeta{ Name: config.WebhookConfigName, Labels: config.WebhookConfigLabels, }, - Webhooks: []adm.Webhook{ - adm.Webhook{ + Webhooks: []admregapi.Webhook{ + admregapi.Webhook{ Name: config.MutationWebhookName, - ClientConfig: adm.WebhookClientConfig{ - Service: &adm.ServiceReference{ + ClientConfig: admregapi.WebhookClientConfig{ + Service: &admregapi.ServiceReference{ Namespace: config.WebhookServiceNamespace, Name: config.WebhookServiceName, Path: &config.WebhookServicePath, }, - CABundle: ExtractCA(configuration), + CABundle: caData, }, - Rules: []adm.RuleWithOperations{ - adm.RuleWithOperations{ - Operations: []adm.OperationType{ - adm.Create, + Rules: []admregapi.RuleWithOperations{ + admregapi.RuleWithOperations{ + Operations: []admregapi.OperationType{ + admregapi.Create, }, - Rule: adm.Rule{ + Rule: admregapi.Rule{ APIGroups: []string{ "*", }, @@ -62,7 +94,7 @@ func constructWebhookConfig(configuration *rest.Config) *adm.MutatingWebhookConf }, }, }, - } + }, nil } func ExtractCA(config *rest.Config) (result []byte) {