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  `