github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_k8ssecret.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 mcagent 5 6 import ( 7 "fmt" 8 "reflect" 9 "strings" 10 11 "github.com/verrazzano/verrazzano/application-operator/constants" 12 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 13 apierrors "k8s.io/apimachinery/pkg/api/errors" 14 15 vzstring "github.com/verrazzano/verrazzano/pkg/string" 16 17 clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1" 18 corev1 "k8s.io/api/core/v1" 19 "k8s.io/apimachinery/pkg/types" 20 "sigs.k8s.io/controller-runtime/pkg/client" 21 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 22 ) 23 24 // Synchronize Secret objects to the local cluster 25 func (s *Syncer) syncSecretObjects(namespace string) error { 26 // Get all the MultiClusterApplicationConfiguration objects from the admin cluster 27 allAdminMCAppConfigs := clustersv1alpha1.MultiClusterApplicationConfigurationList{} 28 listOptions := &client.ListOptions{Namespace: namespace} 29 err := s.AdminClient.List(s.Context, &allAdminMCAppConfigs, listOptions) 30 // When placements are changed a forbidden error can be returned. In this case, 31 // we want to fall through and delete orphaned resources. 32 if err != nil && !apierrors.IsNotFound(err) && !apierrors.IsForbidden(err) { 33 return err 34 } 35 36 // Write each of the secrets that are targeted for the local cluster 37 for _, mcAppConfig := range allAdminMCAppConfigs.Items { 38 if s.isThisCluster(mcAppConfig.Spec.Placement) { 39 for _, adminSecret := range mcAppConfig.Spec.Secrets { 40 secret := corev1.Secret{} 41 namespacedName := types.NamespacedName{Name: adminSecret, Namespace: namespace} 42 err := s.AdminClient.Get(s.Context, namespacedName, &secret) 43 if err != nil { 44 return err 45 } 46 _, err = s.createOrUpdateSecret(secret, mcAppConfig.Name) 47 if err != nil { 48 s.Log.Errorw(fmt.Sprintf("Failed syncing object: %v", err), 49 "Secret", 50 types.NamespacedName{Namespace: secret.Namespace, Name: secret.Name}) 51 } 52 } 53 } 54 } 55 56 // Cleanup orphaned or no longer placed Secret resources. 57 // Get the list of Secret resources on the local cluster and compare to the list received from the admin cluster. 58 // The admin cluster is the source of truth. 59 allLocalSecrets := corev1.SecretList{} 60 listOptions = &client.ListOptions{Namespace: namespace} 61 err = s.LocalClient.List(s.Context, &allLocalSecrets, listOptions) 62 if err != nil { 63 s.Log.Errorf("Failed to list Secrets on local cluster: %v", err) 64 return nil 65 } 66 for i, secret := range allLocalSecrets.Items { 67 appConfigs, found := secret.Labels[mcAppConfigsLabel] 68 // Only look at the secrets we have synced 69 if !found { 70 continue 71 } 72 // Delete Secret object if it is no longer placed on this local cluster 73 if !s.k8sSecretPlacedOnCluster(secret, &allAdminMCAppConfigs) { 74 err := s.LocalClient.Delete(s.Context, &allLocalSecrets.Items[i]) 75 if err != nil { 76 s.Log.Errorf("Failed to delete Secret with name %s and namespace %s: %v", secret.Name, secret.Namespace, err) 77 } 78 } else { 79 // Update the secrets label if the secret was shared across app configs and one of the app configs 80 // was deleted. 81 secretAppConfigs := strings.Split(appConfigs, ",") 82 var actualAppConfigs []string 83 for _, mcAppConfig := range allAdminMCAppConfigs.Items { 84 for _, cluster := range mcAppConfig.Spec.Placement.Clusters { 85 if cluster.Name == s.ManagedClusterName { 86 for _, appConfigSecret := range mcAppConfig.Spec.Secrets { 87 // Save the name of the MultiClusterApplicationConfiguration if we have a secret match 88 if appConfigSecret == secret.Name { 89 actualAppConfigs = append(actualAppConfigs, mcAppConfig.Name) 90 } 91 } 92 } 93 } 94 } 95 if !reflect.DeepEqual(secretAppConfigs, actualAppConfigs) { 96 secret.Labels[mcAppConfigsLabel] = strings.Join(actualAppConfigs, ",") 97 err := s.LocalClient.Update(s.Context, &allLocalSecrets.Items[i]) 98 if err != nil { 99 s.Log.Errorf("Failed to update Secret with name %s and namespace %s: %v", secret.Name, secret.Namespace, err) 100 } 101 } 102 } 103 } 104 105 return nil 106 } 107 108 // Create or update a Secret 109 func (s *Syncer) createOrUpdateSecret(secret corev1.Secret, mcAppConfigName string) (controllerutil.OperationResult, error) { 110 var secretNew corev1.Secret 111 secretNew.Namespace = secret.Namespace 112 secretNew.Name = secret.Name 113 // Create or update on the local cluster 114 return controllerutil.CreateOrUpdate(s.Context, s.LocalClient, &secretNew, func() error { 115 mutateSecret(s.ManagedClusterName, mcAppConfigName, secret, &secretNew) 116 return nil 117 }) 118 } 119 120 // mutateSecret mutates the Secret to reflect the contents of the parent Secret 121 func mutateSecret(managedClusterName string, mcAppConfigName string, secret corev1.Secret, secretNew *corev1.Secret) { 122 if secretNew.Labels == nil { 123 secretNew.Labels = make(map[string]string) 124 if secret.Labels != nil { 125 secretNew.Labels = secret.Labels 126 } 127 } 128 129 secretNew.Labels[mcAppConfigsLabel] = vzstring.AppendToCommaSeparatedString(secretNew.Labels[mcAppConfigsLabel], mcAppConfigName) 130 secretNew.Labels[managedClusterLabel] = managedClusterName 131 // Mark the secret synced from Admin cluster with verrazzano-managed=true, to distinguish 132 // those directly created by user on managed cluster 133 secretNew.Labels[vzconst.VerrazzanoManagedLabelKey] = constants.LabelVerrazzanoManagedDefault 134 secretNew.Annotations = secret.Annotations 135 secretNew.Type = secret.Type 136 secretNew.Immutable = secret.Immutable 137 secretNew.Data = secret.Data 138 secretNew.StringData = secret.StringData 139 } 140 141 // k8sSecretPlacedOnCluster returns boolean indicating if the secret is placed on the local cluster 142 func (s *Syncer) k8sSecretPlacedOnCluster(secret corev1.Secret, allAdminMCAppConfigs *clustersv1alpha1.MultiClusterApplicationConfigurationList) bool { 143 for _, mcAppConfig := range allAdminMCAppConfigs.Items { 144 // Both a matching application configuration label and a matching cluster label be found for the 145 // secret to be placed on the local cluster. 146 if vzstring.CommaSeparatedStringContains(secret.Labels[mcAppConfigsLabel], mcAppConfig.Name) { 147 for _, cluster := range mcAppConfig.Spec.Placement.Clusters { 148 if cluster.Name == secret.Labels[managedClusterLabel] { 149 return true 150 } 151 } 152 } 153 } 154 155 return false 156 }