knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/codegen/cmd/injection-gen/generators/reconciler/targets.go (about)

     1  /*
     2  Copyright 2024 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  	"path/filepath"
    21  	"strings"
    22  
    23  	clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
    24  	"k8s.io/gengo/v2/generator"
    25  	"k8s.io/gengo/v2/types"
    26  	"knative.dev/pkg/codegen/cmd/injection-gen/args"
    27  	"knative.dev/pkg/codegen/cmd/injection-gen/tags"
    28  )
    29  
    30  func Targets(
    31  	args *args.Args,
    32  	groupPkgName string,
    33  	gv clientgentypes.GroupVersion,
    34  	groupGoName string,
    35  	typesToGenerate []*types.Type,
    36  ) []generator.Target {
    37  	basePackage := args.GetOutputPackagePath()
    38  	packagePath := filepath.Join(basePackage, "reconciler", groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
    39  	packageDir := filepath.Join(args.GetOutputDir(), "reconciler", groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
    40  
    41  	clientPackagePath := filepath.Join(basePackage, "client")
    42  
    43  	vers := make([]generator.Target, 0, 4*len(typesToGenerate))
    44  
    45  	for _, t := range typesToGenerate {
    46  		extracted := extractCommentTags(t)
    47  		reconcilerClasses, hasReconcilerClass := extractReconcilerClassesTag(extracted)
    48  		nonNamespaced := isNonNamespaced(extracted)
    49  		isKRShaped := isKRShaped(extracted)
    50  		stubs := stubs(extracted)
    51  
    52  		packagePath := filepath.Join(packagePath, strings.ToLower(t.Name.Name))
    53  		packageDir := filepath.Join(packageDir, strings.ToLower(t.Name.Name))
    54  
    55  		informerPackagePath := filepath.Join(basePackage, "informers", groupPkgName, strings.ToLower(gv.Version.NonEmpty()), strings.ToLower(t.Name.Name))
    56  		listerPackagePath := filepath.Join(args.ListersPackage, groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
    57  
    58  		// Controller
    59  		vers = append(vers, &generator.SimpleTarget{
    60  			PkgName:       strings.ToLower(t.Name.Name),
    61  			PkgPath:       packagePath,
    62  			PkgDir:        packageDir,
    63  			HeaderComment: args.Boilerplate,
    64  			GeneratorsFunc: func(c *generator.Context) []generator.Generator {
    65  				// Impl
    66  				return []generator.Generator{&reconcilerControllerGenerator{
    67  					GoGenerator: generator.GoGenerator{
    68  						OutputFilename: "controller.go",
    69  					},
    70  					typeToGenerate:      t,
    71  					outputPackage:       packagePath,
    72  					imports:             generator.NewImportTracker(),
    73  					groupName:           gv.Group.String(),
    74  					clientPkg:           clientPackagePath,
    75  					informerPackagePath: informerPackagePath,
    76  					schemePkg:           filepath.Join(args.VersionedClientSetPackage, "scheme"),
    77  					reconcilerClasses:   reconcilerClasses,
    78  					hasReconcilerClass:  hasReconcilerClass,
    79  					hasStatus:           hasStatus(t),
    80  				}}
    81  			},
    82  			FilterFunc: func(c *generator.Context, t *types.Type) bool {
    83  				tags := tags.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
    84  				return tags.NeedsReconciler(t, args)
    85  			},
    86  		})
    87  
    88  		if stubs {
    89  			// Controller Stub
    90  			vers = append(vers, &generator.SimpleTarget{
    91  				PkgName:       strings.ToLower(t.Name.Name),
    92  				PkgPath:       filepath.Join(packagePath, "stub"),
    93  				PkgDir:        filepath.Join(packageDir, "stub"),
    94  				HeaderComment: args.Boilerplate,
    95  				GeneratorsFunc: func(c *generator.Context) []generator.Generator {
    96  					// Impl
    97  					return []generator.Generator{&reconcilerControllerStubGenerator{
    98  						GoGenerator: generator.GoGenerator{
    99  							OutputFilename: "controller.go",
   100  						},
   101  						typeToGenerate:      t,
   102  						reconcilerPkg:       packagePath,
   103  						outputPackage:       filepath.Join(packagePath, "stub"),
   104  						imports:             generator.NewImportTracker(),
   105  						informerPackagePath: informerPackagePath,
   106  						reconcilerClasses:   reconcilerClasses,
   107  						hasReconcilerClass:  hasReconcilerClass,
   108  					}}
   109  				},
   110  				FilterFunc: func(c *generator.Context, t *types.Type) bool {
   111  					tags := tags.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
   112  					return tags.NeedsReconciler(t, args)
   113  				},
   114  			})
   115  		}
   116  
   117  		// Reconciler
   118  		vers = append(vers, &generator.SimpleTarget{
   119  			PkgName:       strings.ToLower(t.Name.Name),
   120  			PkgPath:       packagePath,
   121  			PkgDir:        packageDir,
   122  			HeaderComment: args.Boilerplate,
   123  			GeneratorsFunc: func(c *generator.Context) []generator.Generator {
   124  				// Impl
   125  				return []generator.Generator{&reconcilerReconcilerGenerator{
   126  					GoGenerator: generator.GoGenerator{
   127  						OutputFilename: "reconciler.go",
   128  					},
   129  					typeToGenerate:     t,
   130  					outputPackage:      packagePath,
   131  					imports:            generator.NewImportTracker(),
   132  					clientsetPkg:       args.VersionedClientSetPackage,
   133  					listerName:         t.Name.Name + "Lister",
   134  					listerPkg:          listerPackagePath,
   135  					groupGoName:        groupGoName,
   136  					groupVersion:       gv,
   137  					reconcilerClasses:  reconcilerClasses,
   138  					hasReconcilerClass: hasReconcilerClass,
   139  					nonNamespaced:      nonNamespaced,
   140  					isKRShaped:         isKRShaped,
   141  					hasStatus:          hasStatus(t),
   142  				}}
   143  			},
   144  			FilterFunc: func(c *generator.Context, t *types.Type) bool {
   145  				tags := tags.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
   146  				return tags.NeedsReconciler(t, args)
   147  			},
   148  		})
   149  
   150  		if stubs {
   151  			// Reconciler Stub
   152  			vers = append(vers, &generator.SimpleTarget{
   153  				PkgName:       strings.ToLower(t.Name.Name),
   154  				PkgPath:       filepath.Join(packagePath, "stub"),
   155  				PkgDir:        filepath.Join(packageDir, "stub"),
   156  				HeaderComment: args.Boilerplate,
   157  				GeneratorsFunc: func(c *generator.Context) []generator.Generator {
   158  					// Impl
   159  					return []generator.Generator{&reconcilerReconcilerStubGenerator{
   160  						GoGenerator: generator.GoGenerator{
   161  							OutputFilename: "reconciler.go",
   162  						},
   163  						typeToGenerate: t,
   164  						reconcilerPkg:  packagePath,
   165  						outputPackage:  filepath.Join(packagePath, "stub"),
   166  						imports:        generator.NewImportTracker(),
   167  					}}
   168  				},
   169  				FilterFunc: func(c *generator.Context, t *types.Type) bool {
   170  					tags := tags.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
   171  					return tags.NeedsReconciler(t, args)
   172  				},
   173  			})
   174  		}
   175  
   176  		// Reconciler State
   177  		vers = append(vers, &generator.SimpleTarget{
   178  			PkgName:       strings.ToLower(t.Name.Name),
   179  			PkgPath:       packagePath,
   180  			PkgDir:        packageDir,
   181  			HeaderComment: args.Boilerplate,
   182  			GeneratorsFunc: func(c *generator.Context) []generator.Generator {
   183  				// state
   184  				return []generator.Generator{&reconcilerStateGenerator{
   185  					GoGenerator: generator.GoGenerator{
   186  						OutputFilename: "state.go",
   187  					},
   188  					typeToGenerate: t,
   189  					outputPackage:  packagePath,
   190  					imports:        generator.NewImportTracker(),
   191  				}}
   192  			},
   193  			FilterFunc: func(c *generator.Context, t *types.Type) bool {
   194  				tags := tags.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
   195  				return tags.NeedsReconciler(t, args)
   196  			},
   197  		})
   198  	}
   199  	return vers
   200  }
   201  
   202  func extractCommentTags(t *types.Type) tags.CommentTags {
   203  	comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
   204  	return tags.ExtractCommentTags("+", comments)
   205  }
   206  
   207  func extractReconcilerClassesTag(tags tags.CommentTags) ([]string, bool) {
   208  	vals, ok := tags["genreconciler"]
   209  	if !ok {
   210  		return nil, false
   211  	}
   212  	classnames, has := vals["class"]
   213  	if has && len(classnames) == 0 {
   214  		return nil, false
   215  	}
   216  	return classnames, has
   217  }
   218  
   219  func isKRShaped(tags tags.CommentTags) bool {
   220  	vals, has := tags["genreconciler"]
   221  	if !has {
   222  		return false
   223  	}
   224  	stringVals, has := vals["krshapedlogic"]
   225  	if !has || len(vals) == 0 {
   226  		return true // Default is true
   227  	}
   228  	return stringVals[0] != "false"
   229  }
   230  
   231  func isNonNamespaced(tags tags.CommentTags) bool {
   232  	vals, has := tags["genclient"]
   233  	if !has {
   234  		return false
   235  	}
   236  	_, has = vals["nonNamespaced"]
   237  	return has
   238  }
   239  
   240  func stubs(tags tags.CommentTags) bool {
   241  	vals, has := tags["genreconciler"]
   242  	if !has {
   243  		return false
   244  	}
   245  	_, has = vals["stubs"]
   246  	return has
   247  }
   248  
   249  func hasStatus(t *types.Type) bool {
   250  	for _, member := range t.Members {
   251  		if member.Name == "Status" {
   252  			return true
   253  		}
   254  	}
   255  	return false
   256  }