github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/multicluster_validation.go (about) 1 // Copyright (c) 2021, 2022, 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 webhooks 5 6 import ( 7 "context" 8 "fmt" 9 10 clusters "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1" 11 "github.com/verrazzano/verrazzano/application-operator/constants" 12 clusterutil "github.com/verrazzano/verrazzano/application-operator/controllers/clusters" 13 core "k8s.io/api/core/v1" 14 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 15 "k8s.io/apimachinery/pkg/runtime/schema" 16 "sigs.k8s.io/controller-runtime/pkg/client" 17 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 18 ) 19 20 func validateMultiClusterResource(c client.Client, r clusterutil.MultiClusterResource) error { 21 p := r.GetPlacement() 22 if len(p.Clusters) == 0 { 23 return fmt.Errorf("One or more target clusters must be provided") 24 } 25 if !isLocalClusterManagedCluster(c) { 26 if err := validateTargetClustersExist(c, p); err != nil { 27 return err 28 } 29 } 30 return nil 31 } 32 33 // isLocalClusterManagedCluster determines if the local cluster is a registered managed cluster. 34 func isLocalClusterManagedCluster(c client.Client) bool { 35 s := core.Secret{} 36 k := client.ObjectKey{Name: constants.MCRegistrationSecret, Namespace: constants.VerrazzanoSystemNamespace} 37 err := c.Get(context.TODO(), k, &s) 38 return err == nil 39 } 40 41 // validateTargetClustersExist determines if all of the target clusters of the project have 42 // corresponding managed cluster resources. The results are only valid when this 43 // is executed against an admin cluster. 44 func validateTargetClustersExist(c client.Client, p clusters.Placement) error { 45 for _, cluster := range p.Clusters { 46 targetClusterName := cluster.Name 47 // If the target cluster name is local then assume it is valid. 48 if targetClusterName != constants.DefaultClusterName { 49 key := client.ObjectKey{Name: targetClusterName, Namespace: constants.VerrazzanoMultiClusterNamespace} 50 // Need to use unstructured here to avoid a dependency on the platform operator 51 vmc := unstructured.Unstructured{} 52 vmc.SetGroupVersionKind(schema.GroupVersionKind{ 53 Group: "clusters.verrazzano.io", 54 Version: "v1alpha1", 55 Kind: "VerrazzanoManagedCluster", 56 }) 57 vmc.SetNamespace(constants.VerrazzanoMultiClusterNamespace) 58 vmc.SetName(targetClusterName) 59 err := c.Get(context.TODO(), key, &vmc) 60 if err != nil { 61 return fmt.Errorf("target managed cluster %s is not registered: %v", cluster.Name, err) 62 } 63 } 64 } 65 return nil 66 } 67 68 // translateErrorToResponse translates an error to an admission.Response 69 func translateErrorToResponse(err error) admission.Response { 70 if err == nil { 71 return admission.Allowed("") 72 } 73 return admission.Denied(err.Error()) 74 }