Refactored generator and config
This commit is contained in:
parent
2a2185ded0
commit
926062124a
4 changed files with 108 additions and 103 deletions
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/OpenCHAMI/configurator/internal/generator"
|
"github.com/OpenCHAMI/configurator/internal/generator"
|
||||||
|
"github.com/OpenCHAMI/configurator/internal/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -70,29 +71,42 @@ var generateCmd = &cobra.Command{
|
||||||
Target: target,
|
Target: target,
|
||||||
Verbose: verbose,
|
Verbose: verbose,
|
||||||
}
|
}
|
||||||
output, err := generator.Generate(&config, params)
|
outputBytes, err := generator.Generate(&config, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to generate config: %v\n", err)
|
fmt.Printf("failed to generate config: %v\n", err)
|
||||||
os.Exit(1)
|
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 == "" {
|
if outputPath == "" {
|
||||||
// write only to stdout
|
// write only to stdout by default
|
||||||
fmt.Printf("%s\n", string(output))
|
for _, contents := range outputMap {
|
||||||
} else if outputPath != "" && targetCount == 1 {
|
fmt.Printf("%s\n", string(contents))
|
||||||
|
}
|
||||||
|
} else if outputPath != "" && targetCount == 1 && len(outputMap) == 1 {
|
||||||
// write just a single file using template name
|
// write just a single file using template name
|
||||||
err := os.WriteFile(outputPath, output, 0o644)
|
for _, contents := range outputBytes {
|
||||||
if err != nil {
|
// FIXME: fix output paths to not overwrite each other with multiple templates
|
||||||
fmt.Printf("failed to write config to file: %v", err)
|
err := os.WriteFile(outputPath, contents, 0o644)
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
fmt.Printf("failed to write config to file: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if outputPath != "" && targetCount > 1 {
|
} else if outputPath != "" && targetCount > 1 {
|
||||||
// write multiple files in directory using template name
|
// write multiple files in directory using template name
|
||||||
err := os.WriteFile(fmt.Sprintf("%s/%s.%s", filepath.Clean(outputPath), target, ".conf"), output, 0o644)
|
for _, contents := range outputBytes {
|
||||||
if err != nil {
|
// FIXME: fix output paths to not overwrite each other with multiple templates
|
||||||
fmt.Printf("failed to write config to file: %v", err)
|
err := os.WriteFile(fmt.Sprintf("%s/%s.%s", filepath.Clean(outputPath), target, ".conf"), contents, 0o644)
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
fmt.Printf("failed to write config to file: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ import (
|
||||||
|
|
||||||
type Options struct{}
|
type Options struct{}
|
||||||
|
|
||||||
|
type Target struct {
|
||||||
|
Templates []string `yaml:"templates,omitempty"`
|
||||||
|
FilePaths []string `yaml:"files,omitempty"`
|
||||||
|
RunTargets []string `yaml:"targets,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Jwks struct {
|
type Jwks struct {
|
||||||
Uri string `yaml:"uri"`
|
Uri string `yaml:"uri"`
|
||||||
Retries int `yaml:"retries"`
|
Retries int `yaml:"retries"`
|
||||||
|
|
@ -22,13 +28,13 @@ type Server struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Version string `yaml:"version"`
|
Version string `yaml:"version"`
|
||||||
Server Server `yaml:"server"`
|
Server Server `yaml:"server"`
|
||||||
SmdClient SmdClient `yaml:"smd"`
|
SmdClient SmdClient `yaml:"smd"`
|
||||||
AccessToken string `yaml:"access-token"`
|
AccessToken string `yaml:"access-token"`
|
||||||
TemplatePaths map[string]string `yaml:"templates"`
|
Targets map[string]Target `yaml:"targets"`
|
||||||
PluginDirs []string `yaml:"plugins"`
|
PluginDirs []string `yaml:"plugins"`
|
||||||
Options Options `yaml:"options"`
|
Options Options `yaml:"options"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig() Config {
|
func NewConfig() Config {
|
||||||
|
|
@ -38,13 +44,21 @@ func NewConfig() Config {
|
||||||
Host: "http://127.0.0.1",
|
Host: "http://127.0.0.1",
|
||||||
Port: 27779,
|
Port: 27779,
|
||||||
},
|
},
|
||||||
TemplatePaths: map[string]string{
|
Targets: map[string]Target{
|
||||||
"dnsmasq": "templates/dnsmasq.jinja",
|
"dnsmasq": Target{
|
||||||
"syslog": "templates/syslog.jinja",
|
Templates: []string{},
|
||||||
"ansible": "templates/ansible.jinja",
|
},
|
||||||
"powerman": "templates/powerman.jinja",
|
"conman": Target{
|
||||||
"conman": "templates/conman.jinja",
|
Templates: []string{},
|
||||||
|
},
|
||||||
|
"warewulf": Target{
|
||||||
|
Templates: []string{
|
||||||
|
"templates/warewulf/defaults/node.jinja",
|
||||||
|
"templates/warewulf/defaults/provision.jinja",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
PluginDirs: []string{},
|
PluginDirs: []string{},
|
||||||
Server: Server{
|
Server: Server{
|
||||||
Host: "127.0.0.1",
|
Host: "127.0.0.1",
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"plugin"
|
"plugin"
|
||||||
|
|
||||||
configurator "github.com/OpenCHAMI/configurator/internal"
|
configurator "github.com/OpenCHAMI/configurator/internal"
|
||||||
|
|
@ -15,10 +16,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mappings = map[string]any
|
type Mappings = map[string]any
|
||||||
|
type Files = map[string][]byte
|
||||||
type Generator interface {
|
type Generator interface {
|
||||||
GetName() string
|
GetName() string
|
||||||
GetGroups() []string
|
Generate(config *configurator.Config, opts ...util.Option) (Files, error)
|
||||||
Generate(config *configurator.Config, opts ...util.Option) ([]byte, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Params struct {
|
type Params struct {
|
||||||
|
|
@ -34,14 +35,16 @@ func LoadPlugin(path string) (Generator, error) {
|
||||||
return nil, fmt.Errorf("failed to load plugin: %v", err)
|
return nil, fmt.Errorf("failed to load plugin: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load the "Generator" symbol from plugin
|
||||||
symbol, err := p.Lookup("Generator")
|
symbol, err := p.Lookup("Generator")
|
||||||
if err != nil {
|
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)
|
gen, ok := symbol.(Generator)
|
||||||
if !ok {
|
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
|
return gen, nil
|
||||||
}
|
}
|
||||||
|
|
@ -90,10 +93,10 @@ func LoadPlugins(dirpath string, opts ...util.Option) (map[string]Generator, err
|
||||||
return gens, nil
|
return gens, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithTemplate(_template string) util.Option {
|
func WithTarget(target string) util.Option {
|
||||||
return func(p util.Params) {
|
return func(p util.Params) {
|
||||||
if p != nil {
|
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")
|
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 {
|
func GetParams(opts ...util.Option) util.Params {
|
||||||
params := util.Params{}
|
params := util.Params{}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
|
@ -131,25 +138,51 @@ func GetParams(opts ...util.Option) util.Params {
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApplyTemplate(path string, mappings map[string]any) ([]byte, error) {
|
func ApplyTemplates(mappings map[string]any, paths ...string) (Files, error) {
|
||||||
data := exec.NewContext(mappings)
|
var (
|
||||||
|
data = exec.NewContext(mappings)
|
||||||
|
outputs = Files{}
|
||||||
|
)
|
||||||
|
|
||||||
// load jinja template from file
|
for _, path := range paths {
|
||||||
t, err := gonja.FromFile(path)
|
// load jinja template from file
|
||||||
if err != nil {
|
t, err := gonja.FromFile(path)
|
||||||
return nil, fmt.Errorf("failed to read template from file: %v", err)
|
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
|
return outputs, nil
|
||||||
b := bytes.Buffer{}
|
|
||||||
if err = t.Execute(&b, data); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to execute: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.Bytes(), 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
|
// load generator plugins to generate configs or to print
|
||||||
var (
|
var (
|
||||||
generators = make(map[string]Generator)
|
generators = make(map[string]Generator)
|
||||||
|
|
@ -194,7 +227,7 @@ func Generate(config *configurator.Config, params Params) ([]byte, error) {
|
||||||
}
|
}
|
||||||
return gen.Generate(
|
return gen.Generate(
|
||||||
config,
|
config,
|
||||||
WithTemplate(gen.GetName()),
|
WithTarget(gen.GetName()),
|
||||||
WithClient(client),
|
WithClient(client),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue