entangle/controllers/webhooks/pod.go

167 lines
4.7 KiB
Go
Raw Normal View History

2022-09-01 21:30:25 +00:00
package webhooks
import (
"context"
"encoding/json"
"fmt"
"net/http"
2022-11-14 22:52:21 +00:00
"strings"
2022-09-01 21:30:25 +00:00
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/snorwin/k8s-generic-webhook/pkg/webhook"
)
type Webhook struct {
webhook.MutatingWebhook
client.Client
clientSet *kubernetes.Clientset
Scheme *runtime.Scheme
SidecarImage, LogLevel string
2022-09-01 21:30:25 +00:00
}
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
2022-12-27 12:59:14 +00:00
//+kubebuilder:webhook:verbs=create;update,path=/mutate-entangle-kairos-x-io-v1alpha1-entanglement,mutating=true,failurePolicy=fail,sideEffects=None,groups=core,resources=pods,versions=v1,name=mentanglement.kb.io,admissionReviewVersions={v1,v1alpha1}
2022-09-01 21:30:25 +00:00
var (
2022-09-19 10:55:35 +00:00
EntanglementNameLabel = "entanglement.kairos.io/name"
EntanglementServiceLabel = "entanglement.kairos.io/service"
EntanglementDirectionLabel = "entanglement.kairos.io/direction"
EntanglementNetHost = "entanglement.kairos.io/nethost"
2022-09-19 10:55:35 +00:00
EntanglementPortLabel = "entanglement.kairos.io/target_port"
EntanglementHostLabel = "entanglement.kairos.io/host"
2022-11-14 22:52:21 +00:00
EnvPrefix = "entanglement.kairos.io/env."
2022-09-01 21:30:25 +00:00
)
func (w *Webhook) SetupWebhookWithManager(mgr manager.Manager) error {
clientset, err := kubernetes.NewForConfig(mgr.GetConfig())
if err != nil {
return err
}
w.clientSet = clientset
return webhook.NewGenericWebhookManagedBy(mgr).
For(&corev1.Pod{}).
2022-09-19 10:55:35 +00:00
WithMutatePath("/mutate-entangle-kairos-x-io-v1alpha1-entanglement").
2022-09-01 21:30:25 +00:00
Complete(w)
}
func (w *Webhook) Mutate(ctx context.Context, request admission.Request, object runtime.Object) admission.Response {
_ = log.FromContext(ctx)
pod := object.(*corev1.Pod)
2022-11-11 08:03:25 +00:00
// Let user use both label and annotations
info := make(map[string]string)
2022-11-11 08:03:25 +00:00
// Annotations take precedence
for k, v := range pod.Labels {
info[k] = v
}
// Annotations take precedence
for k, v := range pod.Annotations {
info[k] = v
2022-11-11 08:03:25 +00:00
}
entanglementName, exists := info[EntanglementNameLabel]
2022-09-01 21:30:25 +00:00
if !exists {
return admission.Allowed("")
}
2022-11-14 22:52:21 +00:00
envs := []corev1.EnvVar{
{
Name: "EDGEVPNTOKEN",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Key: "network_token",
LocalObjectReference: corev1.LocalObjectReference{
Name: entanglementName,
},
},
},
}}
for k, v := range info {
if strings.HasPrefix(k, EnvPrefix) {
env := strings.ReplaceAll(k, EnvPrefix, "")
envs = append(envs, corev1.EnvVar{Name: env, Value: v})
}
}
2022-11-11 08:03:25 +00:00
entanglementPort, exists := info[EntanglementPortLabel]
2022-09-01 21:30:25 +00:00
if !exists {
return admission.Allowed("")
}
cmd := "service-connect"
2022-11-11 08:03:25 +00:00
entanglementDirection, exists := info[EntanglementDirectionLabel]
2022-09-01 21:30:25 +00:00
if exists && entanglementDirection == "entangle" {
cmd = "service-add"
}
host := "127.0.0.1"
2022-11-11 08:03:25 +00:00
entanglementHost, exists := info[EntanglementHostLabel]
2022-09-01 21:30:25 +00:00
if exists && entanglementHost != "" {
host = entanglementHost
}
2022-11-11 08:03:25 +00:00
entanglementService, exists := info[EntanglementServiceLabel]
2022-09-01 21:30:25 +00:00
if !exists {
return admission.Allowed("")
}
podCopy := pod.DeepCopy()
hostNetwork, exists := info[EntanglementNetHost]
// By default it injects hostnetwork, however if set to false it does enforces it to false
if exists && hostNetwork == "false" {
podCopy.Spec.HostNetwork = false
} else {
podCopy.Spec.HostNetwork = true
}
2022-09-01 21:30:25 +00:00
secret, err := w.clientSet.CoreV1().Secrets(request.Namespace).Get(context.Background(), entanglementName, v1.GetOptions{})
if err != nil || secret == nil {
return admission.Denied("entanglement secret not found: " + entanglementName + err.Error())
}
privileged := false
for _, p := range podCopy.Spec.Containers {
if p.Name == "entanglement" {
return admission.Allowed("already entangled")
}
}
servingContainer := corev1.Container{
ImagePullPolicy: corev1.PullAlways,
Command: []string{"/usr/bin/edgevpn"},
Args: []string{cmd, entanglementService, fmt.Sprintf("%s:%s", host, entanglementPort), "--log-level", w.LogLevel},
2022-11-14 22:52:21 +00:00
Env: envs,
2022-09-01 21:30:25 +00:00
SecurityContext: &corev1.SecurityContext{Privileged: &privileged},
Name: "entanglement",
Image: w.SidecarImage,
}
podCopy.Spec.Containers = append(podCopy.Spec.Containers, servingContainer)
return patchFromPod(request, podCopy)
}
func patchFromPod(req admission.Request, pod *corev1.Pod) admission.Response {
marshaledPod, err := json.Marshal(pod)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
}