knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/codegen/cmd/injection-gen/generators/reconciler/reconciler_controller.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 // reconcilerControllerGenerator produces a file for setting up the reconciler 29 // with injection. 30 type reconcilerControllerGenerator struct { 31 generator.GoGenerator 32 outputPackage string 33 imports namer.ImportTracker 34 typeToGenerate *types.Type 35 36 groupName string 37 clientPkg string 38 schemePkg string 39 informerPackagePath string 40 41 reconcilerClasses []string 42 hasReconcilerClass bool 43 hasStatus bool 44 } 45 46 var _ generator.Generator = (*reconcilerControllerGenerator)(nil) 47 48 func (g *reconcilerControllerGenerator) Filter(c *generator.Context, t *types.Type) bool { 49 // Only process the type for this generator. 50 return t == g.typeToGenerate 51 } 52 53 func (g *reconcilerControllerGenerator) Namers(c *generator.Context) namer.NameSystems { 54 return namer.NameSystems{ 55 "raw": namer.NewRawNamer(g.outputPackage, g.imports), 56 } 57 } 58 59 func (g *reconcilerControllerGenerator) Imports(c *generator.Context) (imports []string) { 60 imports = append(imports, g.imports.ImportLines()...) 61 return imports 62 } 63 64 func (g *reconcilerControllerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 65 sw := generator.NewSnippetWriter(w, c, "{{", "}}") 66 67 klog.V(5).Info("processing type ", t) 68 69 m := map[string]interface{}{ 70 "type": t, 71 "group": g.groupName, 72 "classes": g.reconcilerClasses, 73 "hasClass": g.hasReconcilerClass, 74 "hasStatus": g.hasStatus, 75 "controllerImpl": c.Universe.Type(types.Name{ 76 Package: "knative.dev/pkg/controller", 77 Name: "Impl", 78 }), 79 "controllerReconciler": c.Universe.Type(types.Name{ 80 Package: "knative.dev/pkg/controller", 81 Name: "Reconciler", 82 }), 83 "controllerNewContext": c.Universe.Function(types.Name{ 84 Package: "knative.dev/pkg/controller", 85 Name: "NewContext", 86 }), 87 "loggingFromContext": c.Universe.Function(types.Name{ 88 Package: "knative.dev/pkg/logging", 89 Name: "FromContext", 90 }), 91 "ptrString": c.Universe.Function(types.Name{ 92 Package: "knative.dev/pkg/ptr", 93 Name: "String", 94 }), 95 "corev1EventSource": c.Universe.Function(types.Name{ 96 Package: "k8s.io/api/core/v1", 97 Name: "EventSource", 98 }), 99 "clientGet": c.Universe.Function(types.Name{ 100 Package: g.clientPkg, 101 Name: "Get", 102 }), 103 "informerGet": c.Universe.Function(types.Name{ 104 Package: g.informerPackagePath, 105 Name: "Get", 106 }), 107 "schemeScheme": c.Universe.Function(types.Name{ 108 Package: "k8s.io/client-go/kubernetes/scheme", 109 Name: "Scheme", 110 }), 111 "schemeAddToScheme": c.Universe.Function(types.Name{ 112 Package: g.schemePkg, 113 Name: "AddToScheme", 114 }), 115 "kubeclientGet": c.Universe.Function(types.Name{ 116 Package: "knative.dev/pkg/client/injection/kube/client", 117 Name: "Get", 118 }), 119 "typedcorev1EventSinkImpl": c.Universe.Function(types.Name{ 120 Package: "k8s.io/client-go/kubernetes/typed/core/v1", 121 Name: "EventSinkImpl", 122 }), 123 "recordNewBroadcaster": c.Universe.Function(types.Name{ 124 Package: "k8s.io/client-go/tools/record", 125 Name: "NewBroadcaster", 126 }), 127 "watchInterface": c.Universe.Type(types.Name{ 128 Package: "k8s.io/apimachinery/pkg/watch", 129 Name: "Interface", 130 }), 131 "controllerGetEventRecorder": c.Universe.Function(types.Name{ 132 Package: "knative.dev/pkg/controller", 133 Name: "GetEventRecorder", 134 }), 135 "controllerOptions": c.Universe.Type(types.Name{ 136 Package: "knative.dev/pkg/controller", 137 Name: "ControllerOptions", 138 }), 139 "controllerOptionsFn": c.Universe.Type(types.Name{ 140 Package: "knative.dev/pkg/controller", 141 Name: "OptionsFn", 142 }), 143 "contextContext": c.Universe.Type(types.Name{ 144 Package: "context", 145 Name: "Context", 146 }), 147 "reconcilerLeaderAwareFuncs": c.Universe.Type(types.Name{ 148 Package: "knative.dev/pkg/reconciler", 149 Name: "LeaderAwareFuncs", 150 }), 151 "reconcilerBucket": c.Universe.Type(types.Name{ 152 Package: "knative.dev/pkg/reconciler", 153 Name: "Bucket", 154 }), 155 "typesNamespacedName": c.Universe.Type(types.Name{ 156 Package: "k8s.io/apimachinery/pkg/types", 157 Name: "NamespacedName", 158 }), 159 "labelsEverything": c.Universe.Function(types.Name{ 160 Package: "k8s.io/apimachinery/pkg/labels", 161 Name: "Everything", 162 }), 163 "stringsReplaceAll": c.Universe.Function(types.Name{ 164 Package: "strings", 165 Name: "ReplaceAll", 166 }), 167 "reflectTypeOf": c.Universe.Function(types.Name{ 168 Package: "reflect", 169 Name: "TypeOf", 170 }), 171 "fmtSprintf": c.Universe.Function(types.Name{ 172 Package: "fmt", 173 Name: "Sprintf", 174 }), 175 "logkeyControllerType": c.Universe.Constant(types.Name{ 176 Package: "knative.dev/pkg/logging/logkey", 177 Name: "ControllerType", 178 }), 179 "logkeyControllerKind": c.Universe.Constant(types.Name{ 180 Package: "knative.dev/pkg/logging/logkey", 181 Name: "Kind", 182 }), 183 "zapString": c.Universe.Function(types.Name{ 184 Package: "go.uber.org/zap", 185 Name: "String", 186 }), 187 } 188 189 sw.Do(reconcilerControllerNewImpl, m) 190 191 return sw.Error() 192 } 193 194 var reconcilerControllerNewImpl = ` 195 const ( 196 defaultControllerAgentName = "{{.type|lowercaseSingular}}-controller" 197 defaultFinalizerName = "{{.type|allLowercasePlural}}.{{.group}}" 198 {{if .hasClass }} 199 // ClassAnnotationKey points to the annotation for the class of this resource. 200 {{if gt (.classes | len) 1 }}// Deprecated: Use ClassAnnotationKeys given multiple keys exist 201 {{end -}} 202 ClassAnnotationKey = "{{ index .classes 0 }}" 203 {{end}} 204 ) 205 206 {{if gt (.classes | len) 1 }} 207 var ( 208 // ClassAnnotationKeys points to the annotation for the class of this resource. 209 ClassAnnotationKeys = []string{ 210 {{range $class := .classes}}"{{$class}}", 211 {{end}} 212 } 213 ) 214 {{end}} 215 216 // NewImpl returns a {{.controllerImpl|raw}} that handles queuing and feeding work from 217 // the queue through an implementation of {{.controllerReconciler|raw}}, delegating to 218 // the provided Interface and optional Finalizer methods. OptionsFn is used to return 219 // {{.controllerOptions|raw}} to be used by the internal reconciler. 220 func NewImpl(ctx {{.contextContext|raw}}, r Interface{{if .hasClass}}, classValue string{{end}}, optionsFns ...{{.controllerOptionsFn|raw}}) *{{.controllerImpl|raw}} { 221 logger := {{.loggingFromContext|raw}}(ctx) 222 223 // Check the options function input. It should be 0 or 1. 224 if len(optionsFns) > 1 { 225 logger.Fatal("Up to one options function is supported, found: ", len(optionsFns)) 226 } 227 228 {{.type|lowercaseSingular}}Informer := {{.informerGet|raw}}(ctx) 229 230 lister := {{.type|lowercaseSingular}}Informer.Lister() 231 232 var promoteFilterFunc func(obj interface{}) bool 233 var promoteFunc = func(bkt {{.reconcilerBucket|raw}}) {} 234 235 rec := &reconcilerImpl{ 236 LeaderAwareFuncs: {{.reconcilerLeaderAwareFuncs|raw}}{ 237 PromoteFunc: func(bkt {{.reconcilerBucket|raw}}, enq func({{.reconcilerBucket|raw}}, {{.typesNamespacedName|raw}})) error { 238 239 // Signal promotion event 240 promoteFunc(bkt) 241 242 all, err := lister.List({{.labelsEverything|raw}}()) 243 if err != nil { 244 return err 245 } 246 for _, elt := range all { 247 if promoteFilterFunc != nil { 248 if ok := promoteFilterFunc(elt); !ok { 249 continue 250 } 251 } 252 enq(bkt, {{.typesNamespacedName|raw}}{ 253 Namespace: elt.GetNamespace(), 254 Name: elt.GetName(), 255 }) 256 } 257 return nil 258 }, 259 }, 260 Client: {{.clientGet|raw}}(ctx), 261 Lister: lister, 262 reconciler: r, 263 finalizerName: defaultFinalizerName, 264 {{if .hasClass}}classValue: classValue,{{end}} 265 } 266 267 ctrType := {{.reflectTypeOf|raw}}(r).Elem() 268 ctrTypeName := {{.fmtSprintf|raw}}("%s.%s", ctrType.PkgPath(), ctrType.Name()) 269 ctrTypeName = {{.stringsReplaceAll|raw}}(ctrTypeName, "/", ".") 270 271 logger = logger.With( 272 {{.zapString|raw}}({{.logkeyControllerType|raw}}, ctrTypeName), 273 {{.zapString|raw}}({{.logkeyControllerKind|raw}}, "{{ printf "%s.%s" .group .type.Name.Name }}"), 274 ) 275 276 277 impl := {{.controllerNewContext|raw}}(ctx, rec, {{ .controllerOptions|raw }}{WorkQueueName: ctrTypeName, Logger: logger}) 278 agentName := defaultControllerAgentName 279 280 // Pass impl to the options. Save any optional results. 281 for _, fn := range optionsFns { 282 opts := fn(impl) 283 if opts.ConfigStore != nil { 284 rec.configStore = opts.ConfigStore 285 } 286 if opts.FinalizerName != "" { 287 rec.finalizerName = opts.FinalizerName 288 } 289 if opts.AgentName != "" { 290 agentName = opts.AgentName 291 } 292 {{- if .hasStatus}} 293 if opts.SkipStatusUpdates { 294 rec.skipStatusUpdates = true 295 } 296 {{- end}} 297 if opts.DemoteFunc != nil { 298 rec.DemoteFunc = opts.DemoteFunc 299 } 300 if opts.PromoteFilterFunc != nil { 301 promoteFilterFunc = opts.PromoteFilterFunc 302 } 303 if opts.PromoteFunc != nil { 304 promoteFunc = opts.PromoteFunc 305 } 306 if opts.UseServerSideApplyForFinalizers { 307 if opts.FinalizerFieldManager == "" { 308 logger.Fatal("FinalizerFieldManager must be provided when UseServerSideApplyForFinalizers is enabled") 309 } 310 rec.useServerSideApplyForFinalizers = true 311 rec.finalizerFieldManager = opts.FinalizerFieldManager 312 rec.forceApplyFinalizers = opts.ForceApplyFinalizers 313 } 314 } 315 316 rec.Recorder = createRecorder(ctx, agentName) 317 318 return impl 319 } 320 321 func createRecorder(ctx context.Context, agentName string) record.EventRecorder { 322 logger := {{.loggingFromContext|raw}}(ctx) 323 324 recorder := {{.controllerGetEventRecorder|raw}}(ctx) 325 if recorder == nil { 326 // Create event broadcaster 327 logger.Debug("Creating event broadcaster") 328 eventBroadcaster := {{.recordNewBroadcaster|raw}}() 329 watches := []{{.watchInterface|raw}}{ 330 eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), 331 eventBroadcaster.StartRecordingToSink( 332 &{{.typedcorev1EventSinkImpl|raw}}{Interface: {{.kubeclientGet|raw}}(ctx).CoreV1().Events("")}), 333 } 334 recorder = eventBroadcaster.NewRecorder({{.schemeScheme|raw}}, {{.corev1EventSource|raw}}{Component: agentName}) 335 go func() { 336 <-ctx.Done() 337 for _, w := range watches { 338 w.Stop() 339 } 340 }() 341 } 342 343 return recorder 344 } 345 346 func init() { 347 {{.schemeAddToScheme|raw}}({{.schemeScheme|raw}}) 348 } 349 `