diff --git a/pkg/kubernetes/secret/kubernetes.go b/pkg/kubernetes/secret/kubernetes.go index 9177417146..9bb1043d70 100644 --- a/pkg/kubernetes/secret/kubernetes.go +++ b/pkg/kubernetes/secret/kubernetes.go @@ -40,6 +40,9 @@ type getFn func(cli *kubernetes.Clientset, namespace, name string, opts metav1.G // to create secret type createFn func(cli *kubernetes.Clientset, namespace string, secret *corev1.Secret) (*corev1.Secret, error) +// listFn is a typed function that abstracts listing of secret instances +type listFn func(cli *kubernetes.Clientset, namespace string, opts metav1.ListOptions) (*corev1.SecretList, error) + // deleteFn is a typed function that abstracts // to delete secret type deleteFn func(cli *kubernetes.Clientset, namespace, name string, opts *metav1.DeleteOptions) error @@ -64,6 +67,7 @@ type Kubeclient struct { get getFn create createFn del deleteFn + list listFn } // KubeClientBuildOption defines the abstraction @@ -91,6 +95,12 @@ func (k *Kubeclient) withDefaults() { return cli.CoreV1().Secrets(namespace).Create(secret) } } + if k.list == nil { + k.list = func(cli *kubernetes.Clientset, namespace string, opts metav1.ListOptions) (*corev1.SecretList, error) { + return cli.CoreV1().Secrets(namespace).List(opts) + } + } + if k.del == nil { k.del = func(cli *kubernetes.Clientset, namespace, name string, opts *metav1.DeleteOptions) error { return cli.CoreV1().Secrets(namespace).Delete(name, opts) @@ -183,3 +193,12 @@ func (k *Kubeclient) Delete(name string, opts *metav1.DeleteOptions) error { } return k.del(cli, k.namespace, name, opts) } + +// List lists the secret if present in kubernetes cluster +func (k *Kubeclient) List(opts metav1.ListOptions) (*corev1.SecretList, error) { + cli, err := k.getClientsetOrCached() + if err != nil { + return nil, errors.Wrapf(err, "failed to lists secret in namespace: {%s}", k.namespace) + } + return k.list(cli, k.namespace, opts) +} diff --git a/pkg/kubernetes/webhook/validate/v1alpha1/kubernetes.go b/pkg/kubernetes/webhook/validate/v1alpha1/kubernetes.go index 923f8f7e90..ec490df96c 100644 --- a/pkg/kubernetes/webhook/validate/v1alpha1/kubernetes.go +++ b/pkg/kubernetes/webhook/validate/v1alpha1/kubernetes.go @@ -41,6 +41,9 @@ type listFunc func(cs *kubernetes.Clientset, opts metav1.ListOptions) (*admissio // getting validatingWebhookConfiguration instances type getFunc func(cs *kubernetes.Clientset, name string, opts metav1.GetOptions) (*admission.ValidatingWebhookConfiguration, error) +// delFunc is a typed function that abstracts deleting validatingWebhookConfiguration +type delFunc func(cli *kubernetes.Clientset, name string, opts *metav1.DeleteOptions) error + // createFn is a typed function that abstracts // to create admissionwebhook configuration type createFunc func(cli *kubernetes.Clientset, @@ -62,6 +65,7 @@ type Kubeclient struct { list listFunc create createFunc get getFunc + del delFunc } // KubeclientBuildOption defines the abstraction @@ -95,6 +99,13 @@ func (k *Kubeclient) withDefaults() { return cs.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(config) } } + if k.del == nil { + k.del = func(cs *kubernetes.Clientset, name string, opts *metav1.DeleteOptions) error { + return cs.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(name, opts) + } + + } + } // WithClientset sets the kubernetes clientset against @@ -173,3 +184,16 @@ func (k *Kubeclient) Create(config *admission.ValidatingWebhookConfiguration) (* } return k.create(cs, config) } + +// Delete deletes validatingWebhookConfiguration object for given name +func (k *Kubeclient) Delete(name string, options *metav1.DeleteOptions) error { + if strings.TrimSpace(name) == "" { + return errors.New("failed to delete validating config: missing name") + } + + cli, err := k.getClientOrCached() + if err != nil { + return err + } + return k.del(cli, name, options) +} diff --git a/pkg/webhook/configuration.go b/pkg/webhook/configuration.go index dda32b8fc2..b8e365a1be 100644 --- a/pkg/webhook/configuration.go +++ b/pkg/webhook/configuration.go @@ -26,6 +26,7 @@ import ( secret "github.com/openebs/maya/pkg/kubernetes/secret" svc "github.com/openebs/maya/pkg/kubernetes/service/v1alpha1" validate "github.com/openebs/maya/pkg/kubernetes/webhook/validate/v1alpha1" + "github.com/openebs/maya/pkg/version" "github.com/pkg/errors" "k8s.io/api/admissionregistration/v1beta1" appsv1 "k8s.io/api/apps/v1" @@ -43,6 +44,8 @@ const ( webhookHandlerName = "admission-webhook.openebs.io" validationPath = "/validate" validationPort = 8443 + webhookLabel = "openebs.io/component-name" + "=" + "admission-webhook" + webhooksvcLabel = "openebs.io/component-name" + "=" + "admission-webhook-svc" // AdmissionNameEnvVar is the constant for env variable ADMISSION_WEBHOOK_NAME // which is the name of the current admission webhook AdmissionNameEnvVar = "ADMISSION_WEBHOOK_NAME" @@ -98,6 +101,7 @@ func createWebhookService( Labels: map[string]string{ "app": "admission-webhook", "openebs.io/component-name": "admission-webhook-svc", + "openebs.io/version": version.GetVersion(), }, OwnerReferences: []metav1.OwnerReference{ownerReference}, }, @@ -188,6 +192,7 @@ func createAdmissionService( Labels: map[string]string{ "app": "admission-webhook", "openebs.io/component-name": "admission-webhook", + "openebs.io/version": version.GetVersion(), }, OwnerReferences: []metav1.OwnerReference{ownerReference}, }, @@ -241,6 +246,7 @@ func createCertsSecret( Labels: map[string]string{ "app": "admission-webhook", "openebs.io/component-name": "admission-webhook", + "openebs.io/version": version.GetVersion(), }, OwnerReferences: []metav1.OwnerReference{ownerReference}, }, @@ -282,6 +288,11 @@ func InitValidationServer( return err } + err = preUpgrade(openebsNamespace) + if err != nil { + return err + } + // Check to see if webhook secret is already present certSecret, err := GetSecret(openebsNamespace, validatorSecret) if err != nil { @@ -411,3 +422,51 @@ func GetAdmissionReference() (*metav1.OwnerReference, error) { Kind: "Deployment", }), nil } + +// preUpgrade checks for the required older webhook configs,older +// then 1.4.0 if exists delete them. +func preUpgrade(openebsNamespace string) error { + + secretlist, err := secret.NewKubeClient(secret.WithNamespace(openebsNamespace)).List(metav1.ListOptions{LabelSelector: webhookLabel}) + if err != nil { + return fmt.Errorf("failed to list old secret: %s", err.Error()) + } + + for _, scrt := range secretlist.Items { + if len(scrt.Labels["openebs.io/version"]) == 0 { + err = secret.NewKubeClient(secret.WithNamespace(openebsNamespace)).Delete(scrt.Name, &metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("failed to delete old secret %s: %s", scrt.Name, err.Error()) + } + } + } + + svcList, err := svc.NewKubeClient(svc.WithNamespace(openebsNamespace)).List(metav1.ListOptions{LabelSelector: webhooksvcLabel}) + if err != nil { + return fmt.Errorf("failed to list old service: %s", err.Error()) + } + + for _, service := range svcList.Items { + if len(service.Labels["openebs.io/version"]) == 0 { + err = svc.NewKubeClient(svc.WithNamespace(openebsNamespace)).Delete(service.Name, &metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("failed to delete old service %s: %s", service.Name, err.Error()) + } + } + } + webhookConfigList, err := validate.KubeClient().List(metav1.ListOptions{LabelSelector: webhookLabel}) + if err != nil { + return fmt.Errorf("failed to list older webhook config: %s", err.Error()) + } + + for _, config := range webhookConfigList.Items { + if len(config.Labels["openebs.io/version"]) == 0 { + err = validate.KubeClient().Delete(config.Name, &metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("failed to delete older webhook config %s: %s", config.Name, err.Error()) + } + } + } + + return nil +}