github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/internal/agent/hooks/pods.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2021-Present The Jackal Authors
     3  
     4  // Package hooks provides HTTP handlers for the mutating webhook.
     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  	v1 "k8s.io/api/admission/v1"
    18  
    19  	corev1 "k8s.io/api/core/v1"
    20  )
    21  
    22  // NewPodMutationHook creates a new instance of pods mutation hook.
    23  func NewPodMutationHook() operations.Hook {
    24  	message.Debug("hooks.NewMutationHook()")
    25  	return operations.Hook{
    26  		Create: mutatePod,
    27  		Update: mutatePod,
    28  	}
    29  }
    30  
    31  func parsePod(object []byte) (*corev1.Pod, error) {
    32  	message.Debugf("pods.parsePod(%s)", string(object))
    33  	var pod corev1.Pod
    34  	if err := json.Unmarshal(object, &pod); err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	return &pod, nil
    39  }
    40  
    41  func mutatePod(r *v1.AdmissionRequest) (*operations.Result, error) {
    42  	message.Debugf("hooks.mutatePod()(*v1.AdmissionRequest) - %#v , %s/%s: %#v", r.Kind, r.Namespace, r.Name, r.Operation)
    43  
    44  	var patchOperations []operations.PatchOperation
    45  	pod, err := parsePod(r.Object.Raw)
    46  	if err != nil {
    47  		return &operations.Result{Msg: err.Error()}, nil
    48  	}
    49  
    50  	if pod.Labels != nil && pod.Labels["jackal-agent"] == "patched" {
    51  		// We've already played with this pod, just keep swimming 🐟
    52  		return &operations.Result{
    53  			Allowed:  true,
    54  			PatchOps: patchOperations,
    55  		}, nil
    56  	}
    57  
    58  	// Add the jackal secret to the podspec
    59  	jackalSecret := []corev1.LocalObjectReference{{Name: config.JackalImagePullSecretName}}
    60  	patchOperations = append(patchOperations, operations.ReplacePatchOperation("/spec/imagePullSecrets", jackalSecret))
    61  
    62  	jackalState, err := state.GetJackalStateFromAgentPod()
    63  	if err != nil {
    64  		return nil, fmt.Errorf(lang.AgentErrGetState, err)
    65  	}
    66  	containerRegistryURL := jackalState.RegistryInfo.Address
    67  
    68  	// update the image host for each init container
    69  	for idx, container := range pod.Spec.InitContainers {
    70  		path := fmt.Sprintf("/spec/initContainers/%d/image", idx)
    71  		replacement, err := transform.ImageTransformHost(containerRegistryURL, container.Image)
    72  		if err != nil {
    73  			message.Warnf(lang.AgentErrImageSwap, container.Image)
    74  			continue // Continue, because we might as well attempt to mutate the other containers for this pod
    75  		}
    76  		patchOperations = append(patchOperations, operations.ReplacePatchOperation(path, replacement))
    77  	}
    78  
    79  	// update the image host for each ephemeral container
    80  	for idx, container := range pod.Spec.EphemeralContainers {
    81  		path := fmt.Sprintf("/spec/ephemeralContainers/%d/image", idx)
    82  		replacement, err := transform.ImageTransformHost(containerRegistryURL, container.Image)
    83  		if err != nil {
    84  			message.Warnf(lang.AgentErrImageSwap, container.Image)
    85  			continue // Continue, because we might as well attempt to mutate the other containers for this pod
    86  		}
    87  		patchOperations = append(patchOperations, operations.ReplacePatchOperation(path, replacement))
    88  	}
    89  
    90  	// update the image host for each normal container
    91  	for idx, container := range pod.Spec.Containers {
    92  		path := fmt.Sprintf("/spec/containers/%d/image", idx)
    93  		replacement, err := transform.ImageTransformHost(containerRegistryURL, container.Image)
    94  		if err != nil {
    95  			message.Warnf(lang.AgentErrImageSwap, container.Image)
    96  			continue // Continue, because we might as well attempt to mutate the other containers for this pod
    97  		}
    98  		patchOperations = append(patchOperations, operations.ReplacePatchOperation(path, replacement))
    99  	}
   100  
   101  	// Add a label noting the jackal mutation
   102  	patchOperations = append(patchOperations, operations.ReplacePatchOperation("/metadata/labels/jackal-agent", "patched"))
   103  
   104  	return &operations.Result{
   105  		Allowed:  true,
   106  		PatchOps: patchOperations,
   107  	}, nil
   108  }