github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_cattle_agent.go (about) 1 // Copyright (c) 2022, 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 mcagent 5 6 import ( 7 "bytes" 8 "crypto/sha256" 9 "fmt" 10 "strings" 11 12 "github.com/verrazzano/verrazzano/pkg/constants" 13 "github.com/verrazzano/verrazzano/pkg/k8s/resource" 14 "github.com/verrazzano/verrazzano/pkg/k8sutil" 15 16 "github.com/Jeffail/gabs/v2" 17 "go.uber.org/zap" 18 corev1 "k8s.io/api/core/v1" 19 "k8s.io/apimachinery/pkg/runtime/schema" 20 "k8s.io/apimachinery/pkg/types" 21 "k8s.io/apimachinery/pkg/util/yaml" 22 "sigs.k8s.io/controller-runtime/pkg/client" 23 ) 24 25 const cattleAgent = "cattle-cluster-agent" 26 27 // syncCattleClusterAgent syncs the Rancher cattle-cluster-agent deployment 28 // and the cattle-credentials secret from the admin cluster to the managed cluster 29 // if they have changed in the registration-manifest 30 func (s *Syncer) syncCattleClusterAgent(currentCattleAgentHash string, kubeconfigPath string) (string, error) { 31 manifestSecret := corev1.Secret{} 32 err := s.AdminClient.Get(s.Context, client.ObjectKey{ 33 Namespace: constants.VerrazzanoMultiClusterNamespace, 34 Name: getManifestSecretName(s.ManagedClusterName), 35 }, &manifestSecret) 36 37 if err != nil { 38 return currentCattleAgentHash, fmt.Errorf("failed to fetch manifest secret for %s cluster: %v", s.ManagedClusterName, err) 39 } 40 s.Log.Debugf(fmt.Sprintf("Found manifest secret for %s cluster: %s", s.ManagedClusterName, manifestSecret.Name)) 41 42 manifestData := manifestSecret.Data["yaml"] 43 yamlSections := bytes.Split(manifestData, []byte("---\n")) 44 45 cattleAgentResource, cattleCredentialResource := checkForCattleResources(yamlSections) 46 if cattleAgentResource == nil || cattleCredentialResource == nil { 47 s.Log.Debugf("The registration manifest doesn't contain the required resources. Will try to update the cattle-cluster-agent in the next iteration") 48 return currentCattleAgentHash, nil 49 } 50 51 newCattleAgentHash := createHash(cattleAgentResource) 52 53 // We have a previous hash to compare to 54 if len(currentCattleAgentHash) > 0 { 55 // If they are the same, do nothing 56 if currentCattleAgentHash == newCattleAgentHash { 57 return currentCattleAgentHash, nil 58 } 59 } 60 61 // No previous hash or the hash has changed 62 // Sync the cattle-agent and update the hash for next iterations 63 s.Log.Info("No previous cattle hash found or cattle hash has changed. Updating the cattle-cluster-agent") 64 err = updateCattleResources(cattleAgentResource, cattleCredentialResource, s.Log, kubeconfigPath) 65 if err != nil { 66 return currentCattleAgentHash, fmt.Errorf("failed to update the cattle-cluster-agent on %s cluster: %v", s.ManagedClusterName, err) 67 } 68 s.Log.Infof("Successfully synched cattle-cluster-agent") 69 70 return newCattleAgentHash, nil 71 } 72 73 // checkForCattleResources iterates through the list of resources in the manifest yaml 74 // and returns the cattle-cluster-agent deployment and cattle-credentials secret if found 75 func checkForCattleResources(yamlData [][]byte) (*gabs.Container, *gabs.Container) { 76 var cattleAgentResource, cattleCredentialResource *gabs.Container 77 for _, eachResource := range yamlData { 78 json, _ := yaml.ToJSON(eachResource) 79 container, _ := gabs.ParseJSON(json) 80 81 name := strings.Trim(container.Path("metadata.name").String(), "\"") 82 namespace := strings.Trim(container.Path("metadata.namespace").String(), "\"") 83 kind := strings.Trim(container.Path("kind").String(), "\"") 84 85 if name == cattleAgent && namespace == constants.RancherSystemNamespace && kind == "Deployment" { 86 cattleAgentResource = container 87 } else if strings.Contains(name, "cattle-credentials-") && namespace == constants.RancherSystemNamespace && kind == "Secret" { 88 cattleCredentialResource = container 89 } 90 } 91 92 return cattleAgentResource, cattleCredentialResource 93 } 94 95 // updateCattleResources patches the cattle-cluster-agent and creates the cattle-credentials secret 96 func updateCattleResources(cattleAgentResource *gabs.Container, cattleCredentialResource *gabs.Container, log *zap.SugaredLogger, kubeconfigPath string) error { 97 98 config, err := k8sutil.BuildKubeConfig(kubeconfigPath) 99 if err != nil { 100 log.Errorf("failed to create incluster config: %v", err) 101 return err 102 } 103 log.Debugf("Built kubeconfig: %s, now updating resources", config.Host) 104 105 patch := cattleAgentResource.Bytes() 106 gvr := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} 107 err = resource.PatchResourceFromBytes(gvr, types.StrategicMergePatchType, constants.RancherSystemNamespace, cattleAgent, patch, config) 108 if err != nil { 109 log.Errorf("failed to patch cattle-cluster-agent: %v", err) 110 return err 111 } 112 113 err = resource.CreateOrUpdateResourceFromBytesUsingConfig(cattleCredentialResource.Bytes(), config) 114 if err != nil { 115 log.Errorf("failed to create new cattle-credential: %v", err) 116 return err 117 } 118 log.Debugf("Successfully patched cattle-cluster-agent and created a new cattle-credential secret") 119 120 return nil 121 } 122 123 // createHash returns a hash of the cattle-cluster-agent deployment 124 func createHash(cattleAgent *gabs.Container) string { 125 data := cattleAgent.Path("spec.template.spec.containers.0").Bytes() 126 sha := sha256.New() 127 sha.Write(data) 128 129 return string(sha.Sum(nil)) 130 } 131 132 // getManifestSecretName returns the manifest secret name for a managed cluster on the admin cluster 133 func getManifestSecretName(clusterName string) string { 134 manifestSecretSuffix := "-manifest" 135 return generateManagedResourceName(clusterName) + manifestSecretSuffix 136 }