Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Simplify the controller registration step via tooling #3459

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package generatecontroller

import (
"context"
"errors"
"fmt"
"strings"

Expand Down Expand Up @@ -96,5 +97,10 @@ func RunController(ctx context.Context, o *GenerateControllerOptions) error {
ProtoResource: o.ProtoName,
ProtoVersion: version,
}
return scaffold.Scaffold(serviceName, o.ProtoName, cArgs)
err1 := scaffold.GenerateController(serviceName, o.ProtoName, cArgs)
err2 := scaffold.RegisterController(serviceName, o.ProtoName)
if err1 != nil || err2 != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think you can just always return errors.Join(err1, err2)

return errors.Join(err1, err2)
}
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ func RunGenerateBasicReconciler(ctx context.Context, o *GenerateBasicReconcilerO
OutputMapperDirectory: o.OutputMapperDirectory,
}
if err := generatemapper.RunGenerateMapper(ctx, mapperOps); err != nil {
return fmt.Errorf("generate types: %w", err)
return fmt.Errorf("generate mapper: %w", err)
}
controllerOps := &generatecontroller.GenerateControllerOptions{
GenerateOptions: o.GenerateOptions,
Kind: o.Kind,
ProtoName: o.ProtoName,
}
if err := generatecontroller.RunController(ctx, controllerOps); err != nil {
return fmt.Errorf("generate types: %w", err)
return fmt.Errorf("generate controller: %w", err)
}
return nil
}
80 changes: 60 additions & 20 deletions dev/tools/controllerbuilder/scaffold/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ import (
"bytes"
"errors"
"fmt"
"io/fs"
"go/format"
"go/parser"
"go/token"
"os"
"path/filepath"
"runtime/debug"
"strings"
"text/template"

ccTemplate "github.com/GoogleCloudPlatform/k8s-config-connector/dev/tools/controllerbuilder/template/controller"
"github.com/fatih/color"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/imports"
)

Expand All @@ -38,14 +42,47 @@ var funcMap = template.FuncMap{
"ToLower": strings.ToLower,
}

func Scaffold(service, kind string, cArgs *ccTemplate.ControllerArgs) error {
if err := generateController(service, kind, cArgs); err != nil {
func RegisterController(service, kind string) error {
// Read register file
directControllerPkgPath, err := buildDirectControllerPath()
if err != nil {
return nil
}
registerFilePath := filepath.Join(directControllerPkgPath, "register", "register.go")
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, registerFilePath, nil, parser.ParseComments)
if err != nil {
return err
}

// Get main model name
bi, ok := debug.ReadBuildInfo()
if !ok {
return fmt.Errorf("could not read build info")
}
modelPath := strings.TrimSuffix(bi.Main.Path, currRelPath)

importPath := filepath.Join(modelPath, directControllerRelPath, service)
added := astutil.AddNamedImport(fset, f, "_", importPath)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool!

if !added {
fmt.Printf("skip registering controller %s\n", service)
return nil
}

out := &bytes.Buffer{}
err = format.Node(out, fset, f)
if err != nil {
return fmt.Errorf("error formatting code: %w", err)
}

if err := FormatImports(registerFilePath, out.Bytes()); err != nil {
return err
}
color.HiGreen("New controller %s has been registered.\n", kind)
return nil
}

func generateController(service, kind string, cArgs *ccTemplate.ControllerArgs) error {
func GenerateController(service, kind string, cArgs *ccTemplate.ControllerArgs) error {
tmpl, err := template.New(cArgs.Kind).Funcs(funcMap).Parse(ccTemplate.ControllerTemplate)
if err != nil {
return fmt.Errorf("parse controller template: %w", err)
Expand All @@ -60,6 +97,12 @@ func generateController(service, kind string, cArgs *ccTemplate.ControllerArgs)
if err != nil {
return err
}
if _, err := os.Stat(controllerFilePath); err == nil {
fmt.Printf("file %s already exists, skipping\n", controllerFilePath)
return nil
} else if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("unexpected controller file: %w", err)
}

// Write the generated controller.go to pkg/controller/direct/<service>/<resource>_controller.go
if err := WriteToFile(controllerFilePath, controllerOutput.Bytes()); err != nil {
Expand All @@ -69,11 +112,11 @@ func generateController(service, kind string, cArgs *ccTemplate.ControllerArgs)
if err := FormatImports(controllerFilePath, controllerOutput.Bytes()); err != nil {
return err
}
color.HiGreen("New controller %s has been generated. \nEnjoy it!\n", kind)
color.HiGreen("New controller %s has been generated.", kind)
return nil
}

func buildResourcePath(service, filename string) (string, error) {
func buildDirectControllerPath() (string, error) {
pwd, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("get current working directory: %w", err)
Expand All @@ -83,24 +126,21 @@ func buildResourcePath(service, filename string) (string, error) {
return "", fmt.Errorf("get absolute path %s: %w", pwd, err)
}
seg := strings.Split(abs, currRelPath)
controllerDir := filepath.Join(seg[0], directControllerRelPath, service)
return filepath.Join(seg[0], directControllerRelPath), nil
}

func buildControllerPath(service, protoResource string) (string, error) {
filename := strings.ToLower(protoResource) + "_controller.go"
directControllerPkgPath, err := buildDirectControllerPath()
if err != nil {
return "", nil
}
controllerDir := filepath.Join(directControllerPkgPath, service)
err = os.MkdirAll(controllerDir, os.ModePerm)
if err != nil {
return "", fmt.Errorf("create controller directory %s: %w", controllerDir, err)
}
resourceFilePath := filepath.Join(controllerDir, filename)
if _, err = os.Stat(resourceFilePath); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return "", fmt.Errorf("could not stat path %s: %w", resourceFilePath, err)
}
// otherwise create the file
return resourceFilePath, nil
}
return "", fmt.Errorf("file %s already exist", resourceFilePath)
}

func buildControllerPath(service, protoResource string) (string, error) {
return buildResourcePath(service, strings.ToLower(protoResource)+"_controller.go")
return filepath.Join(controllerDir, filename), nil
}

func FormatImports(path string, out []byte) error {
Expand Down
8 changes: 1 addition & 7 deletions docs/develop-resources/deep-dives/4-add-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ if there is no previous reference method, You may need to add a new` Resolve<Ref

Check to make sure your validation is complete.


## 4.3 Register your controller

To wire your controller in the Config Connector operator, you need to [register the controller](https://github.com/GoogleCloudPlatform/k8s-config-connector/blob/master/pkg/controller/direct/register/register.go)


## 4.4 Verify your controller
## 4.3 Verify your controller

To turn on the SciFi controller to reconcile resources:

Expand Down
Loading