diff --git a/pkg/config/provider.go b/pkg/config/provider.go index 73e7c941..1384167e 100644 --- a/pkg/config/provider.go +++ b/pkg/config/provider.go @@ -90,7 +90,10 @@ type Provider struct { SkipList []string // MainTemplate is the template string to be used to render the - // provider subpackage main program. + // provider subpackage main program. If this is set, the generated provider + // is broken up into subpackage families partitioned across the API groups. + // A monolithic provider is also generated to + // ensure backwards-compatibility. MainTemplate string // skippedResourceNames is a list of Terraform resource names diff --git a/pkg/pipeline/run.go b/pkg/pipeline/run.go index 0d8f5c72..e4d2ac4b 100644 --- a/pkg/pipeline/run.go +++ b/pkg/pipeline/run.go @@ -22,6 +22,14 @@ type terraformedInput struct { ParametersTypeName string } +const ( + // TODO: we should be careful that there may also exist short groups with + // these names. We can consider making these configurable by the provider + // maintainer. + configPackageName = "config" + monolithPackageName = "monolith" +) + // Run runs the Upjet code generation pipelines. func Run(pc *config.Provider, rootDir string) { // nolint:gocyclo // Note(turkenh): nolint reasoning - this is the main function of the code @@ -58,12 +66,9 @@ func Run(pc *config.Provider, rootDir string) { // nolint:gocyclo // Add ProviderConfig controller package to the list of controller packages. controllerPkgMap := make(map[string][]string) for _, p := range pc.BasePackages.Controller { - tokens := strings.Split(p, "/") - group := tokens[len(tokens)-1] - if group == "providerconfig" { - group = pc.ShortName - } - controllerPkgMap[group] = append(controllerPkgMap[group], filepath.Join(pc.ModulePath, p)) + path := filepath.Join(pc.ModulePath, p) + controllerPkgMap[configPackageName] = append(controllerPkgMap[configPackageName], path) + controllerPkgMap[monolithPackageName] = append(controllerPkgMap[monolithPackageName], path) } count := 0 for group, versions := range resourcesGroups { @@ -94,6 +99,7 @@ func Run(pc *config.Provider, rootDir string) { // nolint:gocyclo } sGroup := strings.Split(group, ".")[0] controllerPkgMap[sGroup] = append(controllerPkgMap[sGroup], ctrlPkgPath) + controllerPkgMap[monolithPackageName] = append(controllerPkgMap[monolithPackageName], ctrlPkgPath) if err := exampleGen.Generate(group, version, resources[name]); err != nil { panic(errors.Wrapf(err, "cannot generate example manifest for resource %s", name)) } diff --git a/pkg/pipeline/setup.go b/pkg/pipeline/setup.go index 96d179b3..55ef131d 100644 --- a/pkg/pipeline/setup.go +++ b/pkg/pipeline/setup.go @@ -6,6 +6,7 @@ package pipeline import ( "fmt" + "log" "os" "path/filepath" "sort" @@ -35,35 +36,45 @@ type SetupGenerator struct { ModulePath string } -// Generate writes the setup file with the content produced using given -// list of version packages. - +// Generate writes the setup file and the corresponding provider main file +// using the given list of version packages. func (sg *SetupGenerator) Generate(versionPkgMap map[string][]string, mainTemplate string) error { - t, err := template.New("main").Parse(mainTemplate) - if err != nil { - return errors.Wrap(err, "failed to parse provider main program template") + var t *template.Template + if len(mainTemplate) != 0 { + tmpl, err := template.New("main").Parse(mainTemplate) + if err != nil { + return errors.Wrap(err, "failed to parse the provider main program template") + } + t = tmpl + } + if t == nil { + return errors.Wrap(sg.generate("", versionPkgMap[monolithPackageName]), "failed to generate the controller setup file") } for g, versionPkgList := range versionPkgMap { if err := sg.generate(g, versionPkgList); err != nil { - return errors.Wrapf(err, "failed to generate controller setup file for group: %s", g) + return errors.Wrapf(err, "failed to generate the controller setup file for group: %s", g) } - if err := writeMainProgram(sg.ProviderPath, g, t); err != nil { + if err := generateProviderMain(sg.ProviderPath, g, t); err != nil { return errors.Wrapf(err, "failed to write main program for group: %s", g) } } return nil } -func writeMainProgram(providerPath, group string, t *template.Template) error { +func generateProviderMain(providerPath, group string, t *template.Template) error { f := filepath.Join(providerPath, group) - if err := os.MkdirAll(f, 0755); err != nil { + if err := os.MkdirAll(f, 0750); err != nil { return errors.Wrapf(err, "failed to mkdir provider main program path: %s", f) } - m, err := os.OpenFile(filepath.Join(f, "main.go"), os.O_WRONLY|os.O_CREATE, 0755) + m, err := os.OpenFile(filepath.Join(filepath.Clean(f), "zz_main.go"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return errors.Wrap(err, "failed to open provider main program file") } - defer m.Close() + defer func() { + if err := m.Close(); err != nil { + log.Fatalf("Failed to close the templated main %q: %s", f, err.Error()) + } + }() if err := t.Execute(m, map[string]any{ "Group": group, }); err != nil { @@ -82,10 +93,19 @@ func (sg *SetupGenerator) generate(group string, versionPkgList []string) error for i, pkgPath := range versionPkgList { aliases[i] = setupFile.Imports.UsePackage(pkgPath) } + g := "" + if len(group) != 0 { + g = "_" + group + } vars := map[string]any{ "Aliases": aliases, - "Group": group, + "Group": g, + } + filePath := "" + if len(group) == 0 { + filePath = filepath.Join(sg.LocalDirectoryPath, "zz_setup.go") + } else { + filePath = filepath.Join(sg.LocalDirectoryPath, fmt.Sprintf("zz_%s_setup.go", group)) } - filePath := filepath.Join(sg.LocalDirectoryPath, fmt.Sprintf("zz_%s_setup.go", group)) return errors.Wrap(setupFile.Write(filePath, vars, os.ModePerm), "cannot write setup file") } diff --git a/pkg/pipeline/templates/setup.go.tmpl b/pkg/pipeline/templates/setup.go.tmpl index 4a49ebc4..72b9f5a4 100644 --- a/pkg/pipeline/templates/setup.go.tmpl +++ b/pkg/pipeline/templates/setup.go.tmpl @@ -12,9 +12,9 @@ import ( {{ .Imports }} ) -// Setup_{{ .Group }} creates all controllers with the supplied logger and adds them to +// Setup{{ .Group }} creates all controllers with the supplied logger and adds them to // the supplied manager. -func Setup_{{ .Group }}(mgr ctrl.Manager, o controller.Options) error { +func Setup{{ .Group }}(mgr ctrl.Manager, o controller.Options) error { for _, setup := range []func(ctrl.Manager, controller.Options) error{ {{- range $alias := .Aliases }} {{ $alias }}Setup,