knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/codegen/cmd/injection-gen/generators/reconciler/reconciler_state.go (about) 1 /* 2 Copyright 2020 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package reconciler 18 19 import ( 20 "io" 21 22 "k8s.io/gengo/v2/generator" 23 "k8s.io/gengo/v2/namer" 24 "k8s.io/gengo/v2/types" 25 "k8s.io/klog/v2" 26 ) 27 28 // reconcilerStateGenerator produces a reconciler state object to manage reconciliation runs. 29 type reconcilerStateGenerator struct { 30 generator.GoGenerator 31 outputPackage string 32 imports namer.ImportTracker 33 typeToGenerate *types.Type 34 } 35 36 var _ generator.Generator = (*reconcilerStateGenerator)(nil) 37 38 func (g *reconcilerStateGenerator) Filter(c *generator.Context, t *types.Type) bool { 39 // Only process the type for this generator. 40 return t == g.typeToGenerate 41 } 42 43 func (g *reconcilerStateGenerator) Namers(c *generator.Context) namer.NameSystems { 44 return namer.NameSystems{ 45 "raw": namer.NewRawNamer(g.outputPackage, g.imports), 46 } 47 } 48 49 func (g *reconcilerStateGenerator) Imports(c *generator.Context) (imports []string) { 50 imports = append(imports, g.imports.ImportLines()...) 51 return imports 52 } 53 54 func (g *reconcilerStateGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 55 sw := generator.NewSnippetWriter(w, c, "{{", "}}") 56 57 klog.V(5).Info("processing type ", t) 58 59 m := map[string]interface{}{ 60 "type": t, 61 // methods 62 "cacheSplitMetaNamespaceKey": c.Universe.Function(types.Name{ 63 Package: "k8s.io/client-go/tools/cache", 64 Name: "SplitMetaNamespaceKey", 65 }), 66 "fmtErrorf": c.Universe.Package("fmt").Function("Errorf"), 67 "reconcilerLeaderAware": c.Universe.Type(types.Name{ 68 Package: "knative.dev/pkg/reconciler", 69 Name: "LeaderAware", 70 }), 71 "typesNamespacedName": c.Universe.Type(types.Name{ 72 Package: "k8s.io/apimachinery/pkg/types", 73 Name: "NamespacedName", 74 }), 75 "doReconcileKind": c.Universe.Type(types.Name{ 76 Package: "knative.dev/pkg/reconciler", 77 Name: "DoReconcileKind", 78 }), 79 "doObserveKind": c.Universe.Type(types.Name{ 80 Package: "knative.dev/pkg/reconciler", 81 Name: "DoObserveKind", 82 }), 83 "doFinalizeKind": c.Universe.Type(types.Name{ 84 Package: "knative.dev/pkg/reconciler", 85 Name: "DoFinalizeKind", 86 }), 87 } 88 89 sw.Do(reconcilerStateType, m) 90 sw.Do(reconcilerStateMethods, m) 91 92 return sw.Error() 93 } 94 95 var reconcilerStateType = ` 96 // state is used to track the state of a reconciler in a single run. 97 type state struct { 98 // key is the original reconciliation key from the queue. 99 key string 100 // namespace is the namespace split from the reconciliation key. 101 namespace string 102 // name is the name split from the reconciliation key. 103 name string 104 // reconciler is the reconciler. 105 reconciler Interface 106 // roi is the read only interface cast of the reconciler. 107 roi ReadOnlyInterface 108 // isROI (Read Only Interface) the reconciler only observes reconciliation. 109 isROI bool 110 // isLeader the instance of the reconciler is the elected leader. 111 isLeader bool 112 } 113 114 func newState(key string, r *reconcilerImpl) (*state, error) { 115 // Convert the namespace/name string into a distinct namespace and name. 116 namespace, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key) 117 if err != nil { 118 return nil, {{.fmtErrorf|raw}}("invalid resource key: %s", key) 119 } 120 121 roi, isROI := r.reconciler.(ReadOnlyInterface) 122 123 isLeader := r.IsLeaderFor({{.typesNamespacedName|raw}}{ 124 Namespace: namespace, 125 Name: name, 126 }) 127 128 return &state{ 129 key: key, 130 namespace: namespace, 131 name: name, 132 reconciler: r.reconciler, 133 roi: roi, 134 isROI: isROI, 135 isLeader: isLeader, 136 }, nil 137 } 138 ` 139 140 var reconcilerStateMethods = ` 141 // isNotLeaderNorObserver checks to see if this reconciler with the current 142 // state is enabled to do any work or not. 143 // isNotLeaderNorObserver returns true when there is no work possible for the 144 // reconciler. 145 func (s *state) isNotLeaderNorObserver() bool { 146 if !s.isLeader && !s.isROI { 147 // If we are not the leader, and we don't implement the ReadOnly 148 // interface, then take a fast-path out. 149 return true 150 } 151 return false 152 } 153 154 func (s *state) reconcileMethodFor(o *{{.type|raw}}) (string, doReconcile) { 155 if o.GetDeletionTimestamp().IsZero() { 156 if s.isLeader { 157 return {{.doReconcileKind|raw}}, s.reconciler.ReconcileKind 158 } else if s.isROI { 159 return {{.doObserveKind|raw}}, s.roi.ObserveKind 160 } 161 } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { 162 return {{.doFinalizeKind|raw}}, fin.FinalizeKind 163 } 164 return "unknown", nil 165 } 166 `