From 926062124a7211eb761559187b3c833dbfcdf1b0 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 26 Jun 2024 10:14:40 -0600 Subject: [PATCH] Refactored generator and config --- cmd/generate.go | 40 ++++++++++++------ internal/config.go | 40 ++++++++++++------ internal/config.yaml | 56 ------------------------ internal/generator/generator.go | 75 ++++++++++++++++++++++++--------- 4 files changed, 108 insertions(+), 103 deletions(-) delete mode 100644 internal/config.yaml diff --git a/cmd/generate.go b/cmd/generate.go index ef9a666..cdc49e7 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -10,6 +10,7 @@ import ( "path/filepath" "github.com/OpenCHAMI/configurator/internal/generator" + "github.com/OpenCHAMI/configurator/internal/util" "github.com/spf13/cobra" ) @@ -70,29 +71,42 @@ var generateCmd = &cobra.Command{ Target: target, Verbose: verbose, } - output, err := generator.Generate(&config, params) + outputBytes, err := generator.Generate(&config, params) if err != nil { fmt.Printf("failed to generate config: %v\n", err) os.Exit(1) } - // write config output if no specific targetPath is set + outputMap := util.ConvertMapOutput(outputBytes) + // b, err := json.Marshal(outputMap) + // if err != nil { + // fmt.Printf("failed to marshal output: %v\n", err) + // os.Exit(1) + // } if outputPath == "" { - // write only to stdout - fmt.Printf("%s\n", string(output)) - } else if outputPath != "" && targetCount == 1 { + // write only to stdout by default + for _, contents := range outputMap { + fmt.Printf("%s\n", string(contents)) + } + } else if outputPath != "" && targetCount == 1 && len(outputMap) == 1 { // write just a single file using template name - err := os.WriteFile(outputPath, output, 0o644) - if err != nil { - fmt.Printf("failed to write config to file: %v", err) - os.Exit(1) + for _, contents := range outputBytes { + // FIXME: fix output paths to not overwrite each other with multiple templates + err := os.WriteFile(outputPath, contents, 0o644) + if err != nil { + fmt.Printf("failed to write config to file: %v", err) + os.Exit(1) + } } } else if outputPath != "" && targetCount > 1 { // write multiple files in directory using template name - err := os.WriteFile(fmt.Sprintf("%s/%s.%s", filepath.Clean(outputPath), target, ".conf"), output, 0o644) - if err != nil { - fmt.Printf("failed to write config to file: %v", err) - os.Exit(1) + for _, contents := range outputBytes { + // FIXME: fix output paths to not overwrite each other with multiple templates + err := os.WriteFile(fmt.Sprintf("%s/%s.%s", filepath.Clean(outputPath), target, ".conf"), contents, 0o644) + if err != nil { + fmt.Printf("failed to write config to file: %v", err) + os.Exit(1) + } } } } diff --git a/internal/config.go b/internal/config.go index f8cdaa4..5a7b653 100644 --- a/internal/config.go +++ b/internal/config.go @@ -10,6 +10,12 @@ import ( type Options struct{} +type Target struct { + Templates []string `yaml:"templates,omitempty"` + FilePaths []string `yaml:"files,omitempty"` + RunTargets []string `yaml:"targets,omitempty"` +} + type Jwks struct { Uri string `yaml:"uri"` Retries int `yaml:"retries"` @@ -22,13 +28,13 @@ type Server struct { } type Config struct { - Version string `yaml:"version"` - Server Server `yaml:"server"` - SmdClient SmdClient `yaml:"smd"` - AccessToken string `yaml:"access-token"` - TemplatePaths map[string]string `yaml:"templates"` - PluginDirs []string `yaml:"plugins"` - Options Options `yaml:"options"` + Version string `yaml:"version"` + Server Server `yaml:"server"` + SmdClient SmdClient `yaml:"smd"` + AccessToken string `yaml:"access-token"` + Targets map[string]Target `yaml:"targets"` + PluginDirs []string `yaml:"plugins"` + Options Options `yaml:"options"` } func NewConfig() Config { @@ -38,13 +44,21 @@ func NewConfig() Config { Host: "http://127.0.0.1", Port: 27779, }, - TemplatePaths: map[string]string{ - "dnsmasq": "templates/dnsmasq.jinja", - "syslog": "templates/syslog.jinja", - "ansible": "templates/ansible.jinja", - "powerman": "templates/powerman.jinja", - "conman": "templates/conman.jinja", + Targets: map[string]Target{ + "dnsmasq": Target{ + Templates: []string{}, + }, + "conman": Target{ + Templates: []string{}, + }, + "warewulf": Target{ + Templates: []string{ + "templates/warewulf/defaults/node.jinja", + "templates/warewulf/defaults/provision.jinja", + }, + }, }, + PluginDirs: []string{}, Server: Server{ Host: "127.0.0.1", diff --git a/internal/config.yaml b/internal/config.yaml deleted file mode 100644 index 48308d3..0000000 --- a/internal/config.yaml +++ /dev/null @@ -1,56 +0,0 @@ -version: "0.0.1" -server: - host: "127.0.0.1" - port: 3333 - callback: "/oidc/callback" - -providers: - facebook: "http://facebook.com" - forgejo: "http://git.towk.local:3000" - gitlab: "https://gitlab.newmexicoconsortium.org" - github: "https://github.com" - -authentication: - clients: - - id: "7527e7b4-c96a-4df0-8fc5-00fde18bb65d" - secret: "gto_cc5uvpb5lsdczkwnbarvwmbpv5kcjwg7nhbc75zt65yrfh2ldenq" - name: "forgejo" - issuer: "http://git.towk.local:3000" - scope: - - "openid" - - "profile" - - "read" - - "email" - redirect-uris: - - "http://127.0.0.1:3333/oidc/callback" - - id: "7c0fab1153674a258a705976fcb9468350df3addd91de4ec622fc9ed24bfbcdd" - secret: "a9a8bc55b0cd99236756093adc00ab17855fa507ce106b8038e7f9390ef2ad99" - name: "gitlab" - issuer: "http://gitlab.newmexicoconsortium.org" - scope: - - "openid" - - "profile" - - "email" - redirect-uris: - - "http://127.0.0.1:3333/oidc/callback" - flows: - authorization-code: - state: "" - client-credentials: - -authorization: - urls: - #identities: http://127.0.0.1:4434/admin/identities - trusted-issuers: http://127.0.0.1:4445/admin/trust/grants/jwt-bearer/issuers - login: http://127.0.0.1:4433/self-service/login/api - clients: http://127.0.0.1:4445/admin/clients - authorize: http://127.0.0.1:4444/oauth2/auth - register: http://127.0.0.1:4444/oauth2/register - token: http://127.0.0.1:4444/oauth2/token - - -options: - decode-id-token: true - decode-access-token: true - run-once: true - open-browser: false diff --git a/internal/generator/generator.go b/internal/generator/generator.go index 427a475..a12acdf 100644 --- a/internal/generator/generator.go +++ b/internal/generator/generator.go @@ -5,6 +5,7 @@ import ( "fmt" "maps" "os" + "path/filepath" "plugin" configurator "github.com/OpenCHAMI/configurator/internal" @@ -15,10 +16,10 @@ import ( ) type Mappings = map[string]any +type Files = map[string][]byte type Generator interface { GetName() string - GetGroups() []string - Generate(config *configurator.Config, opts ...util.Option) ([]byte, error) + Generate(config *configurator.Config, opts ...util.Option) (Files, error) } type Params struct { @@ -34,14 +35,16 @@ func LoadPlugin(path string) (Generator, error) { return nil, fmt.Errorf("failed to load plugin: %v", err) } + // load the "Generator" symbol from plugin symbol, err := p.Lookup("Generator") if err != nil { - return nil, fmt.Errorf("failed to look up symbol: %v", err) + return nil, fmt.Errorf("failed to look up symbol at path '%s': %v", path, err) } + // assert that the plugin loaded has a valid generator gen, ok := symbol.(Generator) if !ok { - return nil, fmt.Errorf("failed to load the correct symbol type") + return nil, fmt.Errorf("failed to load the correct symbol type at path '%s'", path) } return gen, nil } @@ -90,10 +93,10 @@ func LoadPlugins(dirpath string, opts ...util.Option) (map[string]Generator, err return gens, nil } -func WithTemplate(_template string) util.Option { +func WithTarget(target string) util.Option { return func(p util.Params) { if p != nil { - p["template"] = _template + p["target"] = target } } } @@ -123,6 +126,10 @@ func GetClient(params util.Params) *configurator.SmdClient { return util.Get[configurator.SmdClient](params, "client") } +func GetTarget(config *configurator.Config, key string) configurator.Target { + return config.Targets[key] +} + func GetParams(opts ...util.Option) util.Params { params := util.Params{} for _, opt := range opts { @@ -131,25 +138,51 @@ func GetParams(opts ...util.Option) util.Params { return params } -func ApplyTemplate(path string, mappings map[string]any) ([]byte, error) { - data := exec.NewContext(mappings) +func ApplyTemplates(mappings map[string]any, paths ...string) (Files, error) { + var ( + data = exec.NewContext(mappings) + outputs = Files{} + ) - // load jinja template from file - t, err := gonja.FromFile(path) - if err != nil { - return nil, fmt.Errorf("failed to read template from file: %v", err) + for _, path := range paths { + // load jinja template from file + t, err := gonja.FromFile(path) + if err != nil { + return nil, fmt.Errorf("failed to read template from file: %v", err) + } + + // execute/render jinja template + b := bytes.Buffer{} + if err = t.Execute(&b, data); err != nil { + return nil, fmt.Errorf("failed to execute: %v", err) + } + outputs[path] = b.Bytes() } - // execute/render jinja template - b := bytes.Buffer{} - if err = t.Execute(&b, data); err != nil { - return nil, fmt.Errorf("failed to execute: %v", err) - } - - return b.Bytes(), nil + return outputs, nil } -func Generate(config *configurator.Config, params Params) ([]byte, error) { +func LoadFiles(paths ...string) (Files, error) { + var outputs = Files{} + for _, path := range paths { + expandedPaths, err := filepath.Glob(path) + if err != nil { + return nil, fmt.Errorf("failed to expand path: %v", err) + } + for _, expandedPath := range expandedPaths { + b, err := os.ReadFile(expandedPath) + if err != nil { + return nil, fmt.Errorf("failed to read file: %v", err) + } + + outputs[path] = b + } + } + + return outputs, nil +} + +func Generate(config *configurator.Config, params Params) (Files, error) { // load generator plugins to generate configs or to print var ( generators = make(map[string]Generator) @@ -194,7 +227,7 @@ func Generate(config *configurator.Config, params Params) ([]byte, error) { } return gen.Generate( config, - WithTemplate(gen.GetName()), + WithTarget(gen.GetName()), WithClient(client), ) }