github.com/verrazzano/verrazzano@v1.7.1/cluster-operator/apis/clusters/v1alpha1/verrazzanomanagedcluster_webhook.go (about) 1 // Copyright (c) 2021, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package v1alpha1 5 6 import ( 7 "context" 8 "fmt" 9 "net/url" 10 11 "github.com/verrazzano/verrazzano/pkg/vzcr" 12 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 13 "github.com/verrazzano/verrazzano/platform-operator/constants" 14 "go.uber.org/zap" 15 corev1 "k8s.io/api/core/v1" 16 "k8s.io/apimachinery/pkg/api/errors" 17 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 "k8s.io/apimachinery/pkg/runtime" 19 "k8s.io/apimachinery/pkg/types" 20 ctrl "sigs.k8s.io/controller-runtime" 21 "sigs.k8s.io/controller-runtime/pkg/client" 22 "sigs.k8s.io/controller-runtime/pkg/webhook" 23 ) 24 25 var getClientFunc = getClient 26 var _ webhook.Validator = &VerrazzanoManagedCluster{} 27 28 // SetupWebhookWithManager is used to let the controller manager know about the webhook 29 func (v *VerrazzanoManagedCluster) SetupWebhookWithManager(mgr ctrl.Manager) error { 30 return ctrl.NewWebhookManagedBy(mgr). 31 For(v). 32 Complete() 33 } 34 35 // ValidateCreate implements webhook.Validator so a webhook will be registered for the type 36 func (v *VerrazzanoManagedCluster) ValidateCreate() error { 37 log := zap.S().With("source", "webhook", "operation", "create", "resource", fmt.Sprintf("%s:%s", v.Namespace, v.Name)) 38 log.Debug("Validate create") 39 40 if v.ObjectMeta.Namespace != constants.VerrazzanoMultiClusterNamespace { 41 return fmt.Errorf("Namespace for the resource must be %s", constants.VerrazzanoMultiClusterNamespace) 42 } 43 client, err := getClientFunc() 44 if err != nil { 45 return err 46 } 47 vz, err := v.validateVerrazzanoInstalled(client) 48 if err != nil { 49 return err 50 } 51 52 // The secret and configmap are required fields _only_ if Rancher is disabled 53 if !vzcr.IsRancherEnabled(vz) { 54 err = v.validateSecret(client) 55 if err != nil { 56 return err 57 } 58 59 err = v.validateConfigMap(client) 60 if err != nil { 61 return err 62 } 63 } 64 return nil 65 } 66 67 // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type 68 func (v *VerrazzanoManagedCluster) ValidateUpdate(old runtime.Object) error { 69 log := zap.S().With("source", "webhook", "operation", "update", "resource", fmt.Sprintf("%s:%s", v.Namespace, v.Name)) 70 log.Debug("Validate update") 71 72 oldResource := old.(*VerrazzanoManagedCluster) 73 log.Debugf("oldResource: %v", oldResource) 74 log.Debugf("v: %v", v) 75 return nil 76 } 77 78 // ValidateDelete implements webhook.Validator so a webhook will be registered for the type 79 func (v *VerrazzanoManagedCluster) ValidateDelete() error { 80 // Webhook is not configured for deletes so function will not be called. 81 return nil 82 } 83 84 // getClient returns a controller runtime client for the Verrazzano resource 85 func getClient() (client.Client, error) { 86 config, err := ctrl.GetConfig() 87 if err != nil { 88 return nil, err 89 } 90 return client.New(config, client.Options{Scheme: newScheme()}) 91 } 92 93 // newScheme creates a new scheme that includes this package's object for use by client 94 func newScheme() *runtime.Scheme { 95 scheme := runtime.NewScheme() 96 AddToScheme(scheme) 97 corev1.AddToScheme(scheme) 98 scheme.AddKnownTypes(v1beta1.SchemeGroupVersion, &v1beta1.Verrazzano{}, &v1beta1.VerrazzanoList{}) 99 meta_v1.AddToGroupVersion(scheme, v1beta1.SchemeGroupVersion) 100 101 return scheme 102 } 103 104 // validateSecret enforces that the CA secret name was specified and that the secret exists 105 func (v VerrazzanoManagedCluster) validateSecret(client client.Client) error { 106 log := zap.S().With("source", "webhook", "operation", "update", "resource", fmt.Sprintf("%s:%s", v.Namespace, v.Name)) 107 log.Debug("Validate secret") 108 109 if len(v.Spec.CASecret) == 0 { 110 log.Debugf("No CA secret in namespace %s defined, using well-known CA", constants.VerrazzanoMultiClusterNamespace) 111 return nil 112 } 113 secret := corev1.Secret{} 114 nsn := types.NamespacedName{ 115 Namespace: constants.VerrazzanoMultiClusterNamespace, 116 Name: v.Spec.CASecret, 117 } 118 err := client.Get(context.TODO(), nsn, &secret) 119 if err == nil { 120 return nil 121 } 122 if errors.IsNotFound(err) { 123 return fmt.Errorf("The CA secret %s does not exist in namespace %s", v.Spec.CASecret, constants.VerrazzanoMultiClusterNamespace) 124 } 125 return fmt.Errorf("Error getting the CA secret %s namespace %s. Error: %s", v.Spec.CASecret, constants.VerrazzanoMultiClusterNamespace, err.Error()) 126 } 127 128 // validateConfigMap enforces that the verrazzano-admin-cluster secret name exists and server key has non-empty value 129 func (v VerrazzanoManagedCluster) validateConfigMap(client client.Client) error { 130 cm := corev1.ConfigMap{} 131 nsn := types.NamespacedName{ 132 Namespace: constants.VerrazzanoMultiClusterNamespace, 133 Name: constants.AdminClusterConfigMapName, 134 } 135 err := client.Get(context.TODO(), nsn, &cm) 136 if err != nil { 137 if errors.IsNotFound(err) { 138 return fmt.Errorf("The ConfigMap %s does not exist in namespace %s", constants.AdminClusterConfigMapName, constants.VerrazzanoMultiClusterNamespace) 139 } 140 return fmt.Errorf("Error getting the ConfigMap %s namespace %s. Error: %s", constants.AdminClusterConfigMapName, constants.VerrazzanoMultiClusterNamespace, err.Error()) 141 } 142 _, err = url.ParseRequestURI(cm.Data[constants.ServerDataKey]) 143 if err != nil { 144 return fmt.Errorf("Data with key %q contains invalid url %q in the ConfigMap %s namespace %s", constants.ServerDataKey, cm.Data[constants.ServerDataKey], constants.AdminClusterConfigMapName, constants.VerrazzanoMultiClusterNamespace) 145 } 146 return nil 147 } 148 149 // validateVerrazzanoInstalled enforces that a Verrazzano installation successfully completed 150 func (v VerrazzanoManagedCluster) validateVerrazzanoInstalled(localClient client.Client) (*v1beta1.Verrazzano, error) { 151 // Get the Verrazzano resource 152 verrazzano := v1beta1.VerrazzanoList{} 153 err := localClient.List(context.TODO(), &verrazzano, &client.ListOptions{}) 154 if err != nil { 155 return nil, fmt.Errorf("Verrazzano must be installed: %v", err) 156 } 157 158 // Verify the state is install complete 159 if len(verrazzano.Items) > 0 { 160 for _, cond := range verrazzano.Items[0].Status.Conditions { 161 if cond.Type == v1beta1.CondInstallComplete { 162 return &verrazzano.Items[0], nil 163 } 164 } 165 } 166 167 return nil, fmt.Errorf("the Verrazzano install must successfully complete (run the command %q to view the install status)", "kubectl get verrazzano -A") 168 }