@@ -3,13 +3,12 @@ package admission
33import (
44 "context"
55 "encoding/json"
6- "fmt"
7- "strings"
86
97 "github.com/projecteru2/core/log"
108 admissionv1 "k8s.io/api/admission/v1"
119 corev1 "k8s.io/api/core/v1"
1210
11+ commonadmission "github.com/cocoonstack/cocoon-common/k8s/admission"
1312 "github.com/cocoonstack/cocoon-common/meta"
1413 "github.com/cocoonstack/cocoon-webhook/affinity"
1514 "github.com/cocoonstack/cocoon-webhook/metrics"
@@ -26,33 +25,33 @@ func (s *Server) mutatePod(ctx context.Context, review *admissionv1.AdmissionRev
2625
2726 if req .Kind .Kind != "Pod" {
2827 metrics .RecordAdmission (metrics .HandlerMutate , metrics .DecisionAllow )
29- return allowResponse ()
28+ return commonadmission . Allow ()
3029 }
3130
3231 var pod corev1.Pod
3332 if err := json .Unmarshal (req .Object .Raw , & pod ); err != nil {
3433 logger .Warnf (ctx , "decode pod %s/%s: %v" , req .Namespace , req .Name , err )
3534 metrics .RecordAdmission (metrics .HandlerMutate , metrics .DecisionError )
36- return allowResponse ()
35+ return commonadmission . Allow ()
3736 }
3837
3938 if ! meta .HasCocoonToleration (pod .Spec .Tolerations ) {
4039 metrics .RecordAdmission (metrics .HandlerMutate , metrics .DecisionAllow )
41- return allowResponse ()
40+ return commonadmission . Allow ()
4241 }
4342
4443 if meta .IsOwnedByCocoonSet (pod .OwnerReferences ) {
4544 // CocoonSet-managed pods come pre-annotated by the operator.
4645 metrics .RecordAdmission (metrics .HandlerMutate , metrics .DecisionAllow )
47- return allowResponse ()
46+ return commonadmission . Allow ()
4847 }
4948
5049 if pod .Spec .NodeName != "" {
5150 metrics .RecordAdmission (metrics .HandlerMutate , metrics .DecisionAllow )
52- return allowResponse ()
51+ return commonadmission . Allow ()
5352 }
5453
55- pool := podNodePool (& pod )
54+ pool := meta . PodNodePool (& pod )
5655 name := podDisplayName (& pod , req )
5756 res , err := s .store .Reserve (ctx , affinity.ReserveRequest {
5857 Pool : pool ,
@@ -65,15 +64,15 @@ func (s *Server) mutatePod(ctx context.Context, review *admissionv1.AdmissionRev
6564 // unreachable: log loudly and let the pod through unmutated.
6665 logger .Errorf (ctx , err , "reserve affinity for pod %s/%s" , req .Namespace , name )
6766 metrics .RecordAdmission (metrics .HandlerMutate , metrics .DecisionAffinityFailed )
68- return allowResponse ()
67+ return commonadmission . Allow ()
6968 }
7069 metrics .RecordReservation (pool )
7170
7271 patch , err := buildMutatePatch (& pod , res )
7372 if err != nil {
7473 logger .Errorf (ctx , err , "build mutate patch for pod %s/%s" , req .Namespace , name )
7574 metrics .RecordAdmission (metrics .HandlerMutate , metrics .DecisionError )
76- return allowResponse ()
75+ return commonadmission . Allow ()
7776 }
7877
7978 logger .Infof (ctx , "mutate %s/%s: vm=%s node=%s" , req .Namespace , name , res .VMName , res .Node )
@@ -101,63 +100,28 @@ func podDisplayName(pod *corev1.Pod, req *admissionv1.AdmissionRequest) string {
101100 return pod .GenerateName + "<unnamed>"
102101}
103102
104- // podNodePool returns the cocoon pool the pod requests. Resolution
105- // order: nodeSelector[cocoonstack.io/pool] -> labels[cocoonstack.io/pool]
106- // -> annotations[cocoonstack.io/pool] -> default.
107- func podNodePool (pod * corev1.Pod ) string {
108- if v := pod .Spec .NodeSelector [meta .LabelNodePool ]; v != "" {
109- return v
110- }
111- if v := pod .Labels [meta .LabelNodePool ]; v != "" {
112- return v
113- }
114- if v := pod .Annotations [meta .LabelNodePool ]; v != "" {
115- return v
116- }
117- return meta .DefaultNodePool
118- }
119-
120- // jsonPatchOp is a single RFC 6902 patch operation.
121- type jsonPatchOp struct {
122- Op string `json:"op"`
123- Path string `json:"path"`
124- Value any `json:"value,omitempty"`
125- }
126-
127103// buildMutatePatch produces an RFC 6902 JSON patch that writes the
128104// VM name annotation and (when present) pins spec.nodeName.
129105func buildMutatePatch (pod * corev1.Pod , res affinity.Reservation ) ([]byte , error ) {
130- var ops []jsonPatchOp
106+ var ops []commonadmission. JSONPatchOp
131107 if pod .Annotations == nil {
132- ops = append (ops , jsonPatchOp {
108+ ops = append (ops , commonadmission. JSONPatchOp {
133109 Op : "add" ,
134110 Path : "/metadata/annotations" ,
135111 Value : map [string ]string {},
136112 })
137113 }
138- ops = append (ops , jsonPatchOp {
114+ ops = append (ops , commonadmission. JSONPatchOp {
139115 Op : "add" ,
140- Path : "/metadata/annotations/" + escapeJSONPointer (meta .AnnotationVMName ),
116+ Path : "/metadata/annotations/" + commonadmission . EscapeJSONPointer (meta .AnnotationVMName ),
141117 Value : res .VMName ,
142118 })
143119 if res .Node != "" {
144- ops = append (ops , jsonPatchOp {
120+ ops = append (ops , commonadmission. JSONPatchOp {
145121 Op : "add" ,
146122 Path : "/spec/nodeName" ,
147123 Value : res .Node ,
148124 })
149125 }
150- out , err := json .Marshal (ops )
151- if err != nil {
152- return nil , fmt .Errorf ("marshal patch: %w" , err )
153- }
154- return out , nil
155- }
156-
157- // escapeJSONPointer escapes the two characters that are reserved in
158- // RFC 6901 JSON Pointer paths.
159- func escapeJSONPointer (s string ) string {
160- s = strings .ReplaceAll (s , "~" , "~0" )
161- s = strings .ReplaceAll (s , "/" , "~1" )
162- return s
126+ return commonadmission .MarshalPatch (ops )
163127}
0 commit comments