func()

in pkg/webhook/webhook.go [117:207]


func (m *podMutator) Handle(ctx context.Context, req admission.Request) (response admission.Response) {
	timeStart := time.Now()
	defer func() {
		ReportRequest(ctx, req.Namespace, time.Since(timeStart))
	}()

	pod := &corev1.Pod{}
	err := m.decoder.Decode(req, pod)
	if err != nil {
		return admission.Errored(http.StatusBadRequest, err)
	}

	podName := pod.GetName()
	if podName == "" {
		podName = pod.GetGenerateName() + " (prefix)"
	}
	// for daemonset/deployment pods the namespace field is not set in objectMeta
	// explicitly set the namespace to request namespace
	pod.Namespace = req.Namespace
	serviceAccountName := pod.Spec.ServiceAccountName
	// When you create a pod, if you do not specify a service account, it is automatically
	// assigned the default service account in the same namespace.
	// xref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server
	if serviceAccountName == "" {
		serviceAccountName = "default"
	}

	logger := mlog.New().WithName("handler").WithValues("pod", podName, "namespace", pod.Namespace, "service-account", serviceAccountName)
	// get service account associated with the pod
	serviceAccount := &corev1.ServiceAccount{}
	if err = m.client.Get(ctx, types.NamespacedName{Name: serviceAccountName, Namespace: pod.Namespace}, serviceAccount); err != nil {
		if !apierrors.IsNotFound(err) {
			logger.Error("failed to get service account", err)
			return admission.Errored(http.StatusBadRequest, err)
		}
		// bypass cache and get from the API server as it's not found in cache
		err = m.reader.Get(ctx, types.NamespacedName{Name: serviceAccountName, Namespace: pod.Namespace}, serviceAccount)
		if err != nil {
			logger.Error("failed to get service account", err)
			return admission.Errored(http.StatusBadRequest, err)
		}
	}

	if shouldInjectProxySidecar(pod) {
		// if the pod has hostNetwork set to true, we cannot inject the proxy sidecar
		// as it'll end up modifying the network stack of the host and affecting other pods
		if pod.Spec.HostNetwork {
			err := errors.New("hostNetwork is set to true, cannot inject proxy sidecar")
			logger.Error("failed to inject proxy sidecar", err)
			return admission.Errored(http.StatusBadRequest, err)
		}

		proxyPort, err := getProxyPort(pod)
		if err != nil {
			logger.Error("failed to get proxy port", err)
			return admission.Errored(http.StatusBadRequest, err)
		}

		pod.Spec.InitContainers = m.injectProxyInitContainer(pod.Spec.InitContainers, proxyPort)
		if m.useNativeSidecar {
			pod.Spec.InitContainers = m.injectProxySidecarContainer(pod.Spec.InitContainers, proxyPort, ptr.To(corev1.ContainerRestartPolicyAlways))
		} else {
			pod.Spec.Containers = m.injectProxySidecarContainer(pod.Spec.Containers, proxyPort, nil)
		}
	}

	// get service account token expiration
	serviceAccountTokenExpiration, err := getServiceAccountTokenExpiration(pod, serviceAccount)
	if err != nil {
		logger.Error("failed to get service account token expiration", err)
		return admission.Errored(http.StatusBadRequest, err)
	}
	// get the clientID
	clientID := getClientID(serviceAccount)
	// get the tenantID
	tenantID := getTenantID(serviceAccount, m.config)
	// get containers to skip
	skipContainers := getSkipContainers(pod)
	pod.Spec.InitContainers = m.mutateContainers(pod.Spec.InitContainers, clientID, tenantID, skipContainers)
	pod.Spec.Containers = m.mutateContainers(pod.Spec.Containers, clientID, tenantID, skipContainers)

	// add the projected service account token volume to the pod if not exists
	addProjectedServiceAccountTokenVolume(pod, serviceAccountTokenExpiration, m.audience)

	marshaledPod, err := json.Marshal(pod)
	if err != nil {
		logger.Error("failed to marshal pod object", err)
		return admission.Errored(http.StatusInternalServerError, err)
	}
	return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
}