MCPcopy Create free account
hub / github.com/cozystack/cozystack / Handle

Method Handle

internal/fluxshardoperator/webhook.go:56–104  ·  view source on GitHub ↗

Handle stamps the recorded shard assignment onto a newly created HelmRelease. Every miss is permissive: correctness is restored by the placement controller, the webhook only removes the handoff gap.

(ctx context.Context, req admission.Request)

Source from the content-addressed store, hash-verified

54// HelmRelease. Every miss is permissive: correctness is restored by the
55// placement controller, the webhook only removes the handoff gap.
56func (h *ShardWebhook) Handle(ctx context.Context, req admission.Request) admission.Response {
57 if req.Operation != admissionv1.Create {
58 return admission.Allowed("only CREATE is mutated")
59 }
60
61 obj := &metav1.PartialObjectMetadata{}
62 if err := json.Unmarshal(req.Object.Raw, obj); err != nil {
63 return admission.Errored(http.StatusBadRequest, fmt.Errorf("decoding object metadata: %w", err))
64 }
65 // Admission objects may omit metadata.namespace before defaulting.
66 if obj.Namespace == "" {
67 obj.Namespace = req.Namespace
68 }
69
70 tenantNS, ok := TenantNamespaceForHR(obj)
71 if !ok {
72 return admission.Allowed("not a tenant HelmRelease")
73 }
74
75 ns := NamespaceMeta()
76 if err := h.Reader.Get(ctx, types.NamespacedName{Name: tenantNS}, ns); err != nil {
77 if apierrors.IsNotFound(err) {
78 // Rare: the tenant namespace is not created yet (e.g. the parent
79 // tenant HelmRelease arrives before its chart renders the
80 // namespace). The placement controller assigns on first sight.
81 return admission.Allowed("tenant namespace not found, deferring to the placement controller")
82 }
83 log.FromContext(ctx).Error(err, "resolving tenant namespace", "tenant", tenantNS)
84 return admission.Allowed("tenant namespace lookup failed, deferring to the placement controller")
85 }
86
87 shard := ns.Labels[TenantShardLabel]
88 if _, ok := ParseShardIndex(shard); !ok {
89 return admission.Allowed("tenant has no recorded shard assignment yet")
90 }
91
92 if obj.Labels[ShardKeyLabel] == shard {
93 return admission.Allowed("already on the assigned shard")
94 }
95
96 // JSON Patch "add" both creates and overwrites object members.
97 var op jsonpatch.JsonPatchOperation
98 if obj.Labels == nil {
99 op = jsonpatch.NewOperation("add", "/metadata/labels", map[string]string{ShardKeyLabel: shard})
100 } else {
101 op = jsonpatch.NewOperation("add", "/metadata/labels/"+escapeJSONPointer(ShardKeyLabel), shard)
102 }
103 return admission.Patched("stamped shard "+shard+" for tenant "+tenantNS, op)
104}
105
106// escapeJSONPointer escapes a JSON pointer path segment (RFC 6901).
107func escapeJSONPointer(s string) string {

Calls 6

TenantNamespaceForHRFunction · 0.85
NamespaceMetaFunction · 0.85
ParseShardIndexFunction · 0.85
escapeJSONPointerFunction · 0.85
GetMethod · 0.65
ErrorMethod · 0.45