github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/internal/agent/hooks/flux.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/json"
     9  	"fmt"
    10  
    11  	"github.com/Racer159/jackal/src/config"
    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  // SecretRef contains the name used to reference a git repository secret.
    23  type SecretRef struct {
    24  	Name string `json:"name"`
    25  }
    26  
    27  // GenericGitRepo contains the URL of a git repo and the secret that corresponds to it for use with Flux.
    28  type GenericGitRepo struct {
    29  	Spec struct {
    30  		URL       string    `json:"url"`
    31  		SecretRef SecretRef `json:"secretRef,omitempty"`
    32  	} `json:"spec"`
    33  }
    34  
    35  // NewGitRepositoryMutationHook creates a new instance of the git repo mutation hook.
    36  func NewGitRepositoryMutationHook() operations.Hook {
    37  	message.Debug("hooks.NewGitRepositoryMutationHook()")
    38  	return operations.Hook{
    39  		Create: mutateGitRepo,
    40  		Update: mutateGitRepo,
    41  	}
    42  }
    43  
    44  // mutateGitRepoCreate mutates the git repository url to point to the repository URL defined in the JackalState.
    45  func mutateGitRepo(r *v1.AdmissionRequest) (result *operations.Result, err error) {
    46  
    47  	var (
    48  		jackalState *types.JackalState
    49  		patches     []operations.PatchOperation
    50  		isPatched   bool
    51  
    52  		isCreate = r.Operation == v1.Create
    53  		isUpdate = r.Operation == v1.Update
    54  	)
    55  
    56  	// Form the jackalState.GitServer.Address from the jackalState
    57  	if jackalState, err = state.GetJackalStateFromAgentPod(); err != nil {
    58  		return nil, fmt.Errorf(lang.AgentErrGetState, err)
    59  	}
    60  
    61  	message.Debugf("Using the url of (%s) to mutate the flux repository", jackalState.GitServer.Address)
    62  
    63  	// parse to simple struct to read the git url
    64  	src := &GenericGitRepo{}
    65  	if err = json.Unmarshal(r.Object.Raw, &src); err != nil {
    66  		return nil, fmt.Errorf(lang.ErrUnmarshal, err)
    67  	}
    68  	patchedURL := src.Spec.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 than 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.Spec.URL)
    75  		if err != nil {
    76  			return nil, fmt.Errorf(lang.AgentErrHostnameMatch, err)
    77  		}
    78  	}
    79  
    80  	// Mutate the git URL 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 git url, using the original url we have: %s", patchedURL)
    86  		}
    87  		patchedURL = transformedURL.String()
    88  		message.Debugf("original git URL of (%s) got mutated to (%s)", src.Spec.URL, patchedURL)
    89  	}
    90  
    91  	// Patch updates of the repo spec
    92  	patches = populatePatchOperations(patchedURL, src.Spec.SecretRef.Name)
    93  
    94  	return &operations.Result{
    95  		Allowed:  true,
    96  		PatchOps: patches,
    97  	}, nil
    98  }
    99  
   100  // Patch updates of the repo spec.
   101  func populatePatchOperations(repoURL string, secretName string) []operations.PatchOperation {
   102  	var patches []operations.PatchOperation
   103  	patches = append(patches, operations.ReplacePatchOperation("/spec/url", repoURL))
   104  
   105  	// If a prior secret exists, replace it
   106  	if secretName != "" {
   107  		patches = append(patches, operations.ReplacePatchOperation("/spec/secretRef/name", config.JackalGitServerSecretName))
   108  	} else {
   109  		// Otherwise, add the new secret
   110  		patches = append(patches, operations.AddPatchOperation("/spec/secretRef", SecretRef{Name: config.JackalGitServerSecretName}))
   111  	}
   112  
   113  	return patches
   114  }