131 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
package controllers
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"net/http"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/actions-runner-controller/actions-runner-controller/github"
 | 
						|
	"github.com/go-logr/logr"
 | 
						|
	"gomodules.xyz/jsonpatch/v2"
 | 
						|
	admissionv1 "k8s.io/api/admission/v1"
 | 
						|
	corev1 "k8s.io/api/core/v1"
 | 
						|
	"k8s.io/client-go/tools/record"
 | 
						|
	ctrl "sigs.k8s.io/controller-runtime"
 | 
						|
	"sigs.k8s.io/controller-runtime/pkg/client"
 | 
						|
	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	AnnotationKeyTokenExpirationDate = "actions-runner-controller/token-expires-at"
 | 
						|
)
 | 
						|
 | 
						|
// +kubebuilder:webhook:path=/mutate-runner-set-pod,mutating=true,failurePolicy=ignore,groups="",resources=pods,verbs=create,versions=v1,name=mutate-runner-pod.webhook.actions.summerwind.dev,sideEffects=None,admissionReviewVersions=v1beta1
 | 
						|
 | 
						|
type PodRunnerTokenInjector struct {
 | 
						|
	client.Client
 | 
						|
 | 
						|
	Name         string
 | 
						|
	Log          logr.Logger
 | 
						|
	Recorder     record.EventRecorder
 | 
						|
	GitHubClient *github.Client
 | 
						|
	decoder      *admission.Decoder
 | 
						|
}
 | 
						|
 | 
						|
func (t *PodRunnerTokenInjector) Handle(ctx context.Context, req admission.Request) admission.Response {
 | 
						|
	var pod corev1.Pod
 | 
						|
	err := t.decoder.Decode(req, &pod)
 | 
						|
	if err != nil {
 | 
						|
		t.Log.Error(err, "Failed to decode request object")
 | 
						|
		return admission.Errored(http.StatusBadRequest, err)
 | 
						|
	}
 | 
						|
 | 
						|
	if pod.Annotations == nil {
 | 
						|
		pod.Annotations = map[string]string{}
 | 
						|
	}
 | 
						|
 | 
						|
	var runnerContainer *corev1.Container
 | 
						|
 | 
						|
	for i := range pod.Spec.Containers {
 | 
						|
		c := pod.Spec.Containers[i]
 | 
						|
 | 
						|
		if c.Name == "runner" {
 | 
						|
			runnerContainer = &c
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if runnerContainer == nil {
 | 
						|
		return newEmptyResponse()
 | 
						|
	}
 | 
						|
 | 
						|
	enterprise, okEnterprise := getEnv(runnerContainer, EnvVarEnterprise)
 | 
						|
	repo, okRepo := getEnv(runnerContainer, EnvVarRepo)
 | 
						|
	org, okOrg := getEnv(runnerContainer, EnvVarOrg)
 | 
						|
	if !okRepo || !okOrg || !okEnterprise {
 | 
						|
		return newEmptyResponse()
 | 
						|
	}
 | 
						|
 | 
						|
	rt, err := t.GitHubClient.GetRegistrationToken(context.Background(), enterprise, org, repo, pod.Name)
 | 
						|
	if err != nil {
 | 
						|
		t.Log.Error(err, "Failed to get new registration token")
 | 
						|
		return admission.Errored(http.StatusInternalServerError, err)
 | 
						|
	}
 | 
						|
 | 
						|
	ts := rt.GetExpiresAt().Format(time.RFC3339)
 | 
						|
 | 
						|
	updated := mutatePod(&pod, *rt.Token)
 | 
						|
 | 
						|
	updated.Annotations[AnnotationKeyTokenExpirationDate] = ts
 | 
						|
 | 
						|
	forceRunnerPodRestartPolicyNever(updated)
 | 
						|
 | 
						|
	buf, err := json.Marshal(updated)
 | 
						|
	if err != nil {
 | 
						|
		t.Log.Error(err, "Failed to encode new object")
 | 
						|
		return admission.Errored(http.StatusInternalServerError, err)
 | 
						|
	}
 | 
						|
 | 
						|
	res := admission.PatchResponseFromRaw(req.Object.Raw, buf)
 | 
						|
	return res
 | 
						|
}
 | 
						|
 | 
						|
func getEnv(container *corev1.Container, key string) (string, bool) {
 | 
						|
	for _, env := range container.Env {
 | 
						|
		if env.Name == key {
 | 
						|
			return env.Value, true
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return "", false
 | 
						|
}
 | 
						|
 | 
						|
func (t *PodRunnerTokenInjector) InjectDecoder(d *admission.Decoder) error {
 | 
						|
	t.decoder = d
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func newEmptyResponse() admission.Response {
 | 
						|
	pt := admissionv1.PatchTypeJSONPatch
 | 
						|
	return admission.Response{
 | 
						|
		Patches: []jsonpatch.Operation{},
 | 
						|
		AdmissionResponse: admissionv1.AdmissionResponse{
 | 
						|
			Allowed:   true,
 | 
						|
			PatchType: &pt,
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *PodRunnerTokenInjector) SetupWithManager(mgr ctrl.Manager) error {
 | 
						|
	name := "pod-runner-token-injector"
 | 
						|
	if r.Name != "" {
 | 
						|
		name = r.Name
 | 
						|
	}
 | 
						|
 | 
						|
	r.Recorder = mgr.GetEventRecorderFor(name)
 | 
						|
 | 
						|
	mgr.GetWebhookServer().Register("/mutate-runner-set-pod", &admission.Webhook{Handler: r})
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |