src/finalizer.bend

bend-kube-runtime@0.2.0.0 · 84 lines · 10 definitions

1import Base
2import bend-kube-runtime@0.2.0.0/src/json.bend as J
3import bend-kube-runtime@0.2.0.0/src/core.bend as C
4
5# Finalizer
6# =========
7#
8# kube-runtime's finalizer helper, decided purely. An object a controller
9# cleans up after carries the controller's finalizer:
10#
11# - live, without it: add it (and reconcile on the change that follows);
12# - live, with it: apply;
13# - deleting, with it: clean up, then remove it so deletion proceeds;
14# - deleting, without it: nothing left to do.
15#
16# Every patch is a JSON patch that first tests what it read, so it fails
17# (and the reconcile retries) instead of clobbering another controller's
18# finalizer, as kube-runtime's does.
19
20type FState is Data:
21  FApply{}
22  FAdd{patch: J.Json}
23  FCleanup{patch: J.Json}
24  FDone{}
25
26def Fin.op(op: String, path: String, v: J.Json) -> J.Json:
27  J.Json.obj([J.KV{"op", J.JStr{op}}, J.KV{"path", J.JStr{path}}, J.KV{"value", v}])
28
29def Fin.strs(xs: List<&2, String>) -> List<&2, J.Json>:
30  match xs:
31    case Nil{}:
32      Nil{}
33    case Con{x, t}:
34      J.JStr{x} <> Fin.strs(t)
35
36def Fin.add_patch.go(fs: List<&2, String>, name: String, empty: Bool) -> J.Json:
37  match empty:
38    case True{}:
39      J.Json.arr([Fin.op("test", "/metadata/finalizers", J.JNull{}),
40        Fin.op("add", "/metadata/finalizers", J.Json.arr([J.JStr{name}]))])
41    case False{}:
42      J.Json.arr([Fin.op("test", "/metadata/finalizers", J.Json.arr(Fin.strs(fs))),
43        Fin.op("add", "/metadata/finalizers/-", J.JStr{name})])
44
45# The patch that adds name to finalizers fs.
46def Fin.add_patch(+fs: List<&2, String>, name: String) -> J.Json:
47  Fin.add_patch.go(fs, name, List.is_empty(&2, String, fs))
48
49# The patch that removes the finalizer at index i, if it is still name.
50def Fin.remove_patch(+i: U32, name: String) -> J.Json:
51  J.Json.arr([Fin.op("test", "/metadata/finalizers/" ++ U32.show(i), J.JStr{name}),
52    J.Json.obj([J.KV{"op", J.JStr{"remove"}}, J.KV{"path", J.JStr{"/metadata/finalizers/" ++ U32.show(i)}}])])
53
54def Fin.index.if(+i: U32, rest: Maybe<&2, U32>, hit: Bool) -> Maybe<&2, U32>:
55  match hit:
56    case True{}:
57      Some{i}
58    case False{}:
59      rest
60
61# Where name sits among the finalizers, counting from i.
62def Fin.index(fs: List<&2, String>, +name: String, +i: U32) -> Maybe<&2, U32>:
63  match fs:
64    case Nil{}:
65      None{}
66    case Con{f, t}:
67      Fin.index.if(i, Fin.index(t, name, (i + 1 : U32)), String.eq(f, name))
68
69def Fin.state.go(+fs: List<&2, String>, +name: String, at: Maybe<&2, U32>, deleting: Bool) ->
70  FState:
71  match at deleting:
72    case Some{i} False{}:
73      FApply{}
74    case Some{i} True{}:
75      FCleanup{Fin.remove_patch(i, name)}
76    case None{} False{}:
77      FAdd{Fin.add_patch(fs, name)}
78    case None{} True{}:
79      FDone{}
80
81# What the object needs, for the finalizer called name.
82def Fin.state(+o: J.Json, +name: String) -> FState:
83  Fin.state.go(C.Obj.finalizers(o), name, Fin.index(C.Obj.finalizers(o), name, 0),
84    C.Obj.is_deleting(o))