github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/internal/agent/hooks/argocd-repository.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package hooks contains the mutation hooks for the Jackal agent. 5 package hooks 6 7 import ( 8 "encoding/base64" 9 "encoding/json" 10 "fmt" 11 12 "github.com/Racer159/jackal/src/config/lang" 13 "github.com/Racer159/jackal/src/internal/agent/operations" 14 "github.com/Racer159/jackal/src/internal/agent/state" 15 "github.com/Racer159/jackal/src/pkg/message" 16 "github.com/Racer159/jackal/src/pkg/transform" 17 "github.com/Racer159/jackal/src/types" 18 "github.com/defenseunicorns/pkg/helpers" 19 v1 "k8s.io/api/admission/v1" 20 ) 21 22 // ArgoRepository represents a subset of the Argo Repository object needed for Jackal Git URL mutations 23 type ArgoRepository struct { 24 Data struct { 25 URL string `json:"url"` 26 } 27 } 28 29 // NewRepositoryMutationHook creates a new instance of the ArgoCD Repository mutation hook. 30 func NewRepositoryMutationHook() operations.Hook { 31 message.Debug("hooks.NewRepositoryMutationHook()") 32 return operations.Hook{ 33 Create: mutateRepository, 34 Update: mutateRepository, 35 } 36 } 37 38 // mutateRepository mutates the git repository URL to point to the repository URL defined in the JackalState. 39 func mutateRepository(r *v1.AdmissionRequest) (result *operations.Result, err error) { 40 41 var ( 42 jackalState *types.JackalState 43 patches []operations.PatchOperation 44 isPatched bool 45 46 isCreate = r.Operation == v1.Create 47 isUpdate = r.Operation == v1.Update 48 ) 49 50 // Form the jackalState.GitServer.Address from the jackalState 51 if jackalState, err = state.GetJackalStateFromAgentPod(); err != nil { 52 return nil, fmt.Errorf(lang.AgentErrGetState, err) 53 } 54 55 message.Debugf("Using the url of (%s) to mutate the ArgoCD Repository Secret", jackalState.GitServer.Address) 56 57 // parse to simple struct to read the git url 58 src := &ArgoRepository{} 59 60 if err = json.Unmarshal(r.Object.Raw, &src); err != nil { 61 return nil, fmt.Errorf(lang.ErrUnmarshal, err) 62 } 63 decodedURL, err := base64.StdEncoding.DecodeString(src.Data.URL) 64 if err != nil { 65 message.Fatalf("Error decoding URL from Repository Secret %s", src.Data.URL) 66 } 67 src.Data.URL = string(decodedURL) 68 patchedURL := src.Data.URL 69 70 // Check if this is an update operation and the hostname is different from what we have in the jackalState 71 // NOTE: We mutate on updates IF AND ONLY IF the hostname in the request is different from the hostname in the jackalState 72 // NOTE: We are checking if the hostname is different before because we do not want to potentially mutate a URL that has already been mutated. 73 if isUpdate { 74 isPatched, err = helpers.DoHostnamesMatch(jackalState.GitServer.Address, src.Data.URL) 75 if err != nil { 76 return nil, fmt.Errorf(lang.AgentErrHostnameMatch, err) 77 } 78 } 79 80 // Mutate the repoURL if necessary 81 if isCreate || (isUpdate && !isPatched) { 82 // Mutate the git URL so that the hostname matches the hostname in the Jackal state 83 transformedURL, err := transform.GitURL(jackalState.GitServer.Address, patchedURL, jackalState.GitServer.PushUsername) 84 if err != nil { 85 message.Warnf("Unable to transform the url, using the original url we have: %s", patchedURL) 86 } 87 patchedURL = transformedURL.String() 88 message.Debugf("original url of (%s) got mutated to (%s)", src.Data.URL, patchedURL) 89 } 90 91 // Patch updates of the repo spec 92 patches = populateArgoRepositoryPatchOperations(patchedURL, jackalState.GitServer.PullPassword) 93 94 return &operations.Result{ 95 Allowed: true, 96 PatchOps: patches, 97 }, nil 98 } 99 100 // Patch updates of the Argo Repository Secret. 101 func populateArgoRepositoryPatchOperations(repoURL string, jackalGitPullPassword string) []operations.PatchOperation { 102 var patches []operations.PatchOperation 103 patches = append(patches, operations.ReplacePatchOperation("/data/url", base64.StdEncoding.EncodeToString([]byte(repoURL)))) 104 patches = append(patches, operations.ReplacePatchOperation("/data/username", base64.StdEncoding.EncodeToString([]byte(types.JackalGitReadUser)))) 105 patches = append(patches, operations.ReplacePatchOperation("/data/password", base64.StdEncoding.EncodeToString([]byte(jackalGitPullPassword)))) 106 107 return patches 108 }