Skip to content

Commit

Permalink
Merge pull request GoogleCloudPlatform#2843 from yuwenma/external-ref…
Browse files Browse the repository at this point in the history
…-template

feat: improve the  template for external (no external-ref, use real-id)
  • Loading branch information
google-oss-prow[bot] authored Oct 4, 2024
2 parents 748718d + bcd0e5c commit bac1bde
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 224 deletions.
24 changes: 12 additions & 12 deletions apis/bigqueryconnection/v1alpha1/connection_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

// New builds a BigQueryConnectionConnectionRef from the ConfigConnector BigQueryConnectionConnection object.
func New(ctx context.Context, reader client.Reader, obj *BigQueryConnectionConnection) (*BigQueryConnectionConnectionRef, error) {
// NewBigQueryConnectionConnectionRef builds a BigQueryConnectionConnectionRef from the ConfigConnector BigQueryConnectionConnection object.
func NewBigQueryConnectionConnectionRef(ctx context.Context, reader client.Reader, obj *BigQueryConnectionConnection) (*BigQueryConnectionConnectionRef, error) {
id := &BigQueryConnectionConnectionRef{}

projectRef, err := refsv1beta1.ResolveProject(ctx, reader, obj, obj.Spec.ProjectRef)
Expand Down Expand Up @@ -120,37 +120,37 @@ func (r *BigQueryConnectionConnectionRef) Parent() (string, error) {
return "", fmt.Errorf("BigQueryConnectionConnectionRef not normalized to External form or not created from `New()`")
}

// NormalizeToExternalForm provision the "External" value.
// NormalizedExternal provision the "External" value.
// If the "External" comes from the ConfigConnector object, it has to acquire or reconcile with the GCP resource already.
func (r *BigQueryConnectionConnectionRef) NormalizeToExternalForm(ctx context.Context, reader client.Reader) error {
func (r *BigQueryConnectionConnectionRef) NormalizedExternal(ctx context.Context, reader client.Reader, othernamespace string) (string, error) {
if r.External != "" && r.Name != "" {
return fmt.Errorf("cannot specify both name and external on %s reference", BigQueryConnectionConnectionGVK.Kind)
return "", fmt.Errorf("cannot specify both name and external on %s reference", BigQueryConnectionConnectionGVK.Kind)
}
if r.External != "" {
r.External = strings.TrimPrefix(r.External, "/")
tokens := strings.Split(r.External, "/")
if len(tokens) != 6 || tokens[0] != "projects" || tokens[2] != "locations" || tokens[4] != "connections" {
return fmt.Errorf("format of BigQueryConnectionConnection external=%q was not known (use projects/<projectId>/locations/<location>/connections/<connectionID>)", r.External)
return "", fmt.Errorf("format of BigQueryConnectionConnection external=%q was not known (use projects/<projectId>/locations/<location>/connections/<connectionID>)", r.External)
}
return nil
return r.External, nil
}
key := types.NamespacedName{Name: r.Name, Namespace: r.Namespace}
u := &unstructured.Unstructured{}
u.SetGroupVersionKind(BigQueryConnectionConnectionGVK)
if err := reader.Get(ctx, key, u); err != nil {
if apierrors.IsNotFound(err) {
return k8s.NewReferenceNotFoundError(u.GroupVersionKind(), key)
return "", k8s.NewReferenceNotFoundError(u.GroupVersionKind(), key)
}
return fmt.Errorf("reading referenced %s %s: %w", BigQueryConnectionConnectionGVK, key, err)
return "", fmt.Errorf("reading referenced %s %s: %w", BigQueryConnectionConnectionGVK, key, err)
}
// Get external from status.externalRef. This is the most trustworthy place.
actualExternalRef, _, err := unstructured.NestedString(u.Object, "status", "externalRef")
if err != nil {
return fmt.Errorf("reading status.externalRef: %w", err)
return "", fmt.Errorf("reading status.externalRef: %w", err)
}
if actualExternalRef == "" {
return fmt.Errorf("BigQueryConnectionConnection is not ready yet.")
return "", fmt.Errorf("BigQueryConnectionConnection is not ready yet.")
}
r.External = actualExternalRef
return nil
return r.External, nil
}
4 changes: 2 additions & 2 deletions apis/refs/v1beta1/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (
)

type ExternalNormalizer interface {
// NormalizeToExternalForm expects the implemented struct has a "External" field, and this function
// NormalizedExternal expects the implemented struct has a "External" field, and this function
// assigns a value to the "External" field if it is empty.
// In general, it retrieves the corresponding ConfigConnector object from the cluster, using
// the `status.externalRef` or other field as the "External" value
NormalizeToExternalForm(context.Context, client.Reader) error
NormalizedExternal(ctx context.Context, reader client.Reader, otherNamespace string) (string, error)
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,19 @@ func RunGenerateCRD(ctx context.Context, o *GenerateCRDOptions) error {

kind := o.ResourceKindName
if !scaffolder.TypeFileNotExist(kind) {
fmt.Printf("file %s already exists, skipping\n", scaffolder.GetTypeFile(kind))
fmt.Printf("file %s already exists, skipping\n", scaffolder.PathToTypeFile(kind))
} else {
err := scaffolder.AddTypeFile(kind, o.ResourceProtoName)
if err != nil {
return fmt.Errorf("add type file %s: %w", scaffolder.GetTypeFile(kind), err)
return fmt.Errorf("add type file %s: %w", scaffolder.PathToTypeFile(kind), err)
}
}
if scaffolder.RefsFileExist(kind, o.ResourceProtoName) {
fmt.Printf("file %s already exists, skipping\n", scaffolder.PathToRefsFile(kind, o.ResourceProtoName))
} else {
err := scaffolder.AddRefsFile(kind, o.ResourceProtoName)
if err != nil {
return fmt.Errorf("add refs file %s: %w", scaffolder.PathToRefsFile(kind, o.ResourceProtoName), err)
}
}
return nil
Expand Down
53 changes: 49 additions & 4 deletions dev/tools/controllerbuilder/scaffold/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,74 @@ type APIScaffolder struct {
PackageProtoTag string
}

func (a *APIScaffolder) RefsFileExist(kind, resourceProtoName string) bool {
refsFilePath := a.PathToRefsFile(kind, resourceProtoName)
_, err := os.Stat(refsFilePath)
if err == nil {
return true
}
return !errors.Is(err, os.ErrNotExist)
}

func (a *APIScaffolder) PathToRefsFile(kind, resourceProtoName string) string {
fileName := strings.ToLower(resourceProtoName) + "_reference.go"
return filepath.Join(a.BaseDir, a.GoPackage, fileName)
}

func (a *APIScaffolder) AddRefsFile(kind, resourceProtoName string) error {
refsFilePath := a.PathToRefsFile(kind, resourceProtoName)
cArgs := &apis.APIArgs{
Group: a.Group,
Version: a.Version,
Kind: kind,
PackageProtoTag: a.PackageProtoTag,
KindProtoTag: a.PackageProtoTag + "." + resourceProtoName,
ProtoResource: resourceProtoName,
}
return scaffoldRefsFile(refsFilePath, cArgs)
}

func scaffoldRefsFile(path string, cArgs *apis.APIArgs) error {
tmpl, err := template.New(cArgs.Kind).Funcs(funcMap).Parse(apis.RefsHeaderTemplate)
if err != nil {
return fmt.Errorf("parse %s_reference.go template: %w", strings.ToLower(cArgs.Kind), err)
}
// Apply the APIArgs args to the template
out := &bytes.Buffer{}
if err := tmpl.Execute(out, cArgs); err != nil {
return err
}
// Write the generated <kind>_types.go
if err := WriteToFile(path, out.Bytes()); err != nil {
return err
}
color.HiGreen("New reference file added %s\nPlease EDIT it!\n", path)
return nil
}

func (a *APIScaffolder) TypeFileNotExist(kind string) bool {
typeFilePath := a.GetTypeFile(kind)
typeFilePath := a.PathToTypeFile(kind)
_, err := os.Stat(typeFilePath)
if err == nil {
return false
}
return errors.Is(err, os.ErrNotExist)
}

func (a *APIScaffolder) GetTypeFile(kind string) string {
func (a *APIScaffolder) PathToTypeFile(kind string) string {
fileName := strings.ToLower(kind) + "_types.go"
return filepath.Join(a.BaseDir, a.GoPackage, fileName)
}

func (a *APIScaffolder) AddTypeFile(kind, proto string) error {
typeFilePath := a.GetTypeFile(kind)
typeFilePath := a.PathToTypeFile(kind)
cArgs := &apis.APIArgs{
Group: a.Group,
Version: a.Version,
Kind: kind,
PackageProtoTag: a.PackageProtoTag,
KindProtoTag: a.PackageProtoTag + "." + proto,
GcpResource: proto,
ProtoResource: proto,
}
return scaffoldTypeFile(typeFilePath, cArgs)
}
Expand Down
44 changes: 4 additions & 40 deletions dev/tools/controllerbuilder/scaffold/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,8 @@ var funcMap = template.FuncMap{
}

func Scaffold(service, kind string, cArgs *ccTemplate.ControllerArgs) error {
var errs []error
if err := generateController(service, kind, cArgs); err != nil {
errs = append(errs, err)
}
if err := generateControllerHelpers(service, kind, cArgs); err != nil {
errs = append(errs, err)
}
if len(errs) != 0 {
var finalError []string
for _, err := range errs {
finalError = append(finalError, err.Error())
}
return fmt.Errorf("multiple errors occurred:\n%s", strings.Join(finalError, "\n"))
return err
}
return nil
}
Expand Down Expand Up @@ -84,32 +73,7 @@ func generateController(service, kind string, cArgs *ccTemplate.ControllerArgs)
return nil
}

func generateControllerHelpers(service, kind string, cArgs *ccTemplate.ControllerArgs) error {
// Generate externalresourece.go used for the controller
externalResourcetmpl, err := template.New(cArgs.Kind).Funcs(funcMap).Parse(ccTemplate.ExternalResourceTemplate)
if err != nil {
return fmt.Errorf("parse external resource template: %w", err)
}
externalResourceOutput := &bytes.Buffer{}
if err := externalResourcetmpl.Execute(externalResourceOutput, cArgs); err != nil {
return err
}
externalResourceFilePath, err := buildExternalResourcePath(service, kind)
if err != nil {
return err
}
// Write the generated <resource>_externalresource.go to pkg/controller/direct/<service>/<resource>_externalresource.go
if err := WriteToFile(externalResourceFilePath, externalResourceOutput.Bytes()); err != nil {
return err
}
if err := FormatImports(externalResourceFilePath, externalResourceOutput.Bytes()); err != nil {
return err
}
color.HiGreen("New helpers for controller %s has been generated.", kind)
return nil
}

func buildResourcePath(service, kind, filename string) (string, error) {
func buildResourcePath(service, filename string) (string, error) {
pwd, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("get current working directory: %w", err)
Expand Down Expand Up @@ -137,12 +101,12 @@ func buildResourcePath(service, kind, filename string) (string, error) {

func buildControllerPath(service, kind string) (string, error) {
kind = strings.ToLower(kind)
return buildResourcePath(service, kind, kind + "_controller.go")
return buildResourcePath(service, kind, kind+"_controller.go")
}

func buildExternalResourcePath(service, kind string) (string, error) {
kind = strings.ToLower(kind)
return buildResourcePath(service, kind, kind + "_externalresource.go")
return buildResourcePath(service, kind, kind+"_externalresource.go")
}

func FormatImports(path string, out []byte) error {
Expand Down
Loading

0 comments on commit bac1bde

Please sign in to comment.