From 32065dc163a662357c656702709792f6fe0e8c6f Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Thu, 21 Nov 2024 14:13:31 -0700 Subject: [PATCH] refactor: name changes and code clean up --- cmd/config.go | 4 +- cmd/fetch.go | 9 +-- cmd/generate.go | 196 +++++++++++++++++++++++++----------------------- cmd/root.go | 13 ++-- cmd/serve.go | 47 +++++------- 5 files changed, 134 insertions(+), 135 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 1de4015..05e183d 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/cobra" - configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/config" "github.com/OpenCHAMI/configurator/pkg/util" ) @@ -23,7 +23,7 @@ var configCmd = &cobra.Command{ fmt.Printf("file or directory exists\n") continue } - configurator.SaveDefaultConfig(path) + config.SaveDefault(path) } }, } diff --git a/cmd/fetch.go b/cmd/fetch.go index c2ba4e5..0bbf309 100644 --- a/cmd/fetch.go +++ b/cmd/fetch.go @@ -25,11 +25,11 @@ var fetchCmd = &cobra.Command{ } // check to see if an access token is available from env - if config.AccessToken == "" { + if conf.AccessToken == "" { // check if OCHAMI_ACCESS_TOKEN env var is set if no access token is provided and use that instead accessToken := os.Getenv("ACCESS_TOKEN") if accessToken != "" { - config.AccessToken = accessToken + conf.AccessToken = accessToken } else { // TODO: try and fetch token first if it is needed if verbose { @@ -46,7 +46,7 @@ var fetchCmd = &cobra.Command{ for _, target := range targets { // make a request for each target - url := fmt.Sprintf("%s:%d/generate?target=%s", remoteHost, remotePort, target) + url := fmt.Sprintf("%s/generate?target=%s", remoteHost, target) res, body, err := util.MakeRequest(url, http.MethodGet, nil, headers) if err != nil { log.Error().Err(err).Msg("failed to make request") @@ -63,8 +63,7 @@ var fetchCmd = &cobra.Command{ } func init() { - fetchCmd.Flags().StringVar(&remoteHost, "host", "", "set the remote configurator host") - fetchCmd.Flags().IntVar(&remotePort, "port", 3334, "set the remote configurator port") + fetchCmd.Flags().StringVar(&remoteHost, "host", "", "set the remote configurator host and port") fetchCmd.Flags().StringSliceVar(&targets, "target", nil, "set the target configs to make") fetchCmd.Flags().StringVarP(&outputPath, "output", "o", "", "set the output path for config targets") fetchCmd.Flags().StringVar(&accessToken, "access-token", "o", "set the output path for config targets") diff --git a/cmd/generate.go b/cmd/generate.go index c373b9b..634a775 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -9,7 +9,7 @@ import ( "os" "path/filepath" - configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/config" "github.com/OpenCHAMI/configurator/pkg/generator" "github.com/OpenCHAMI/configurator/pkg/util" "github.com/rs/zerolog/log" @@ -28,155 +28,165 @@ var generateCmd = &cobra.Command{ Short: "Generate a config file from state management", Run: func(cmd *cobra.Command, args []string) { // make sure that we have a token present before trying to make request - if config.AccessToken == "" { + if conf.AccessToken == "" { // check if OCHAMI_ACCESS_TOKEN env var is set if no access token is provided and use that instead accessToken := os.Getenv("ACCESS_TOKEN") if accessToken != "" { - config.AccessToken = accessToken + conf.AccessToken = accessToken } else { // TODO: try and fetch token first if it is needed if verbose { - fmt.Printf("No token found. Attempting to generate config without one...\n") + fmt.Printf("No token found. Attempting to generate conf without one...\n") } } } // use cert path from cobra if empty - if config.CertPath == "" { - config.CertPath = cacertPath + if conf.CertPath == "" { + conf.CertPath = cacertPath } - // show config as JSON and generators if verbose + // show conf as JSON and generators if verbose if verbose { - b, err := json.MarshalIndent(config, "", " ") + b, err := json.MarshalIndent(conf, "", " ") if err != nil { - fmt.Printf("failed to marshal config: %v\n", err) + fmt.Printf("failed to marshal conf: %v\n", err) } fmt.Printf("%v\n", string(b)) } // run all of the target recursively until completion if provided if len(targets) > 0 { - RunTargets(&config, args, targets...) + RunTargets(&conf, args, targets...) } else { if pluginPath == "" { fmt.Printf("no plugin path specified") return } - // run generator.Generate() with just plugin path and templates provided - generator.Generate(&config, generator.Params{ - PluginPath: pluginPath, - TemplatePaths: templatePaths, - }) + // load the templates to use + templates := map[string]generator.Template{} + for _, path := range templatePaths { + template := generator.Template{} + template.LoadFromFile(path) + if !template.IsEmpty() { + templates[path] = template + } + } + params := generator.Params{ + Templates: templates, + } + + // run generator.Generate() with just plugin path and templates provided + outputBytes, err := generator.Generate(pluginPath, params) + if err != nil { + log.Error().Err(err).Msg("failed to generate files") + } + + // if we have more than one target and output is set, create configs in directory + writeOutput(outputBytes, len(targets), len(outputMap)) } }, } // Generate files by supplying a list of targets as string values. Currently, -// targets are defined statically in a config file. Targets are ran recursively +// targets are defined statically in a conf file. Targets are ran recursively // if more targets are nested in a defined target, but will not run additional // child targets if it is the same as the parent. // // NOTE: This may be changed in the future how this is done. -func RunTargets(config *configurator.Config, args []string, targets ...string) { - // generate config with each supplied target +func RunTargets(conf *config.Config, args []string, targets ...string) { + // generate conf with each supplied target for _, target := range targets { - outputBytes, err := generator.GenerateWithTarget(config, generator.Params{ - Args: args, - PluginPath: pluginPath, - Target: target, - Verbose: verbose, - }) + outputBytes, err := generator.GenerateWithTarget(conf, target) if err != nil { - log.Error().Err(err).Msg("failed to generate config") + log.Error().Err(err).Msg("failed to generate conf") os.Exit(1) } // if we have more than one target and output is set, create configs in directory - var ( - outputMap = generator.ConvertContentsToString(outputBytes) - targetCount = len(targets) - templateCount = len(outputMap) - ) - if outputPath == "" { - // write only to stdout by default - if len(outputMap) == 1 { - for _, contents := range outputMap { - fmt.Printf("%s\n", string(contents)) - } - } else { - for path, contents := range outputMap { - fmt.Printf("-- file: %s, size: %d B\n%s\n", path, len(contents), string(contents)) - } - } - } else if outputPath != "" && targetCount == 1 && templateCount == 1 { - // write just a single file using provided name - for _, contents := range outputBytes { - err := os.WriteFile(outputPath, contents, 0o644) - if err != nil { - log.Error().Err(err).Msg("failed to write config to file") - os.Exit(1) - } - log.Info().Msgf("wrote file to '%s'\n", outputPath) - } - } else if outputPath != "" && targetCount > 1 && useCompression { - // write multiple files to archive, compress, then save to output path - out, err := os.Create(fmt.Sprintf("%s.tar.gz", outputPath)) - if err != nil { - log.Error().Err(err).Msg("failed to write archive") - os.Exit(1) - } - files := make([]string, len(outputBytes)) - i := 0 - for path := range outputBytes { - files[i] = path - i++ - } - err = util.CreateArchive(files, out) - if err != nil { - log.Error().Err(err).Msg("failed to create archive") - os.Exit(1) - } - - } else if outputPath != "" && targetCount > 1 || templateCount > 1 { - // write multiple files in directory using template name - err := os.MkdirAll(filepath.Clean(outputPath), 0o755) - if err != nil { - log.Error().Err(err).Msg("failed to make output directory") - os.Exit(1) - } - for path, contents := range outputBytes { - filename := filepath.Base(path) - cleanPath := fmt.Sprintf("%s/%s", filepath.Clean(outputPath), filename) - err := os.WriteFile(cleanPath, contents, 0o755) - if err != nil { - log.Error().Err(err).Msg("failed to write config to file") - os.Exit(1) - } - log.Info().Msgf("wrote file to '%s'\n", cleanPath) - } - } + writeOutput(outputBytes, len(targets), len(outputMap)) // remove any targets that are the same as current to prevent infinite loop - nextTargets := util.CopyIf(config.Targets[target].RunTargets, func(nextTarget string) bool { + nextTargets := util.CopyIf(conf.Targets[target].RunTargets, func(nextTarget string) bool { return nextTarget != target }) // ...then, run any other targets that the current target has - RunTargets(config, args, nextTargets...) + RunTargets(conf, args, nextTargets...) + } +} + +func writeOutput(outputBytes generator.FileMap, targetCount int, templateCount int) { + outputMap := generator.ConvertContentsToString(outputBytes) + if outputPath == "" { + // write only to stdout by default + if len(outputMap) == 1 { + for _, contents := range outputMap { + fmt.Printf("%s\n", string(contents)) + } + } else { + for path, contents := range outputMap { + fmt.Printf("-- file: %s, size: %d B\n%s\n", path, len(contents), string(contents)) + } + } + } else if outputPath != "" && targetCount == 1 && templateCount == 1 { + // write just a single file using provided name + for _, contents := range outputBytes { + err := os.WriteFile(outputPath, contents, 0o644) + if err != nil { + log.Error().Err(err).Msg("failed to write conf to file") + os.Exit(1) + } + log.Info().Msgf("wrote file to '%s'\n", outputPath) + } + } else if outputPath != "" && targetCount > 1 && useCompression { + // write multiple files to archive, compress, then save to output path + out, err := os.Create(fmt.Sprintf("%s.tar.gz", outputPath)) + if err != nil { + log.Error().Err(err).Msg("failed to write archive") + os.Exit(1) + } + files := make([]string, len(outputBytes)) + i := 0 + for path := range outputBytes { + files[i] = path + i++ + } + err = util.CreateArchive(files, out) + if err != nil { + log.Error().Err(err).Msg("failed to create archive") + os.Exit(1) + } + + } else if outputPath != "" && targetCount > 1 || templateCount > 1 { + // write multiple files in directory using template name + err := os.MkdirAll(filepath.Clean(outputPath), 0o755) + if err != nil { + log.Error().Err(err).Msg("failed to make output directory") + os.Exit(1) + } + for path, contents := range outputBytes { + filename := filepath.Base(path) + cleanPath := fmt.Sprintf("%s/%s", filepath.Clean(outputPath), filename) + err := os.WriteFile(cleanPath, contents, 0o755) + if err != nil { + log.Error().Err(err).Msg("failed to write conf to file") + os.Exit(1) + } + log.Info().Msgf("wrote file to '%s'\n", cleanPath) + } } } func init() { - generateCmd.Flags().StringSliceVar(&targets, "target", []string{}, "set the targets to run pre-defined config") + generateCmd.Flags().StringSliceVar(&targets, "target", []string{}, "set the targets to run pre-defined conf") generateCmd.Flags().StringSliceVar(&templatePaths, "template", []string{}, "set the paths for the Jinja 2 templates to use") generateCmd.Flags().StringVar(&pluginPath, "plugin", "", "set the generator plugin path") - generateCmd.Flags().StringVarP(&outputPath, "output", "o", "", "set the output path for config targets") + generateCmd.Flags().StringVarP(&outputPath, "output", "o", "", "set the output path for conf targets") generateCmd.Flags().IntVar(&tokenFetchRetries, "fetch-retries", 5, "set the number of retries to fetch an access token") generateCmd.Flags().StringVar(&remoteHost, "host", "http://localhost", "set the remote host") - generateCmd.Flags().IntVar(&remotePort, "port", 80, "set the remote port") generateCmd.Flags().BoolVar(&useCompression, "compress", false, "set whether to archive and compress multiple file outputs") // requires either 'target' by itself or 'plugin' and 'templates' together diff --git a/cmd/root.go b/cmd/root.go index ab190da..be9659c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,14 +4,14 @@ import ( "fmt" "os" - configurator "github.com/OpenCHAMI/configurator/pkg" + "github.com/OpenCHAMI/configurator/pkg/config" "github.com/OpenCHAMI/configurator/pkg/util" "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) var ( - config configurator.Config + conf config.Config configPath string cacertPath string verbose bool @@ -19,12 +19,11 @@ var ( outputPath string accessToken string remoteHost string - remotePort int ) var rootCmd = &cobra.Command{ Use: "configurator", - Short: "Tool for building common config files", + Short: "Dynamically generate files defined by generators", Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { cmd.Help() @@ -55,7 +54,7 @@ func initConfig() { fmt.Printf("failed to load config") os.Exit(1) } else if exists { - config = configurator.LoadConfig(configPath) + conf = config.Load(configPath) } else { // show error and exit since a path was specified log.Error().Str("path", configPath).Msg("config file not found") @@ -64,7 +63,7 @@ func initConfig() { } else { // set to the default value and create a new one configPath = "./config.yaml" - config = configurator.NewConfig() + conf = config.New() } // @@ -74,6 +73,6 @@ func initConfig() { // set the JWKS url if we find the CONFIGURATOR_JWKS_URL environment variable jwksUrl := os.Getenv("CONFIGURATOR_JWKS_URL") if jwksUrl != "" { - config.Server.Jwks.Uri = jwksUrl + conf.Server.Jwks.Uri = jwksUrl } } diff --git a/cmd/serve.go b/cmd/serve.go index 647482b..61bfeff 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -10,8 +10,8 @@ import ( "net/http" "os" - "github.com/OpenCHAMI/configurator/pkg/generator" "github.com/OpenCHAMI/configurator/pkg/server" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) @@ -20,66 +20,57 @@ var serveCmd = &cobra.Command{ Short: "Start configurator as a server and listen for requests", Run: func(cmd *cobra.Command, args []string) { // make sure that we have a token present before trying to make request - if config.AccessToken == "" { - // TODO: make request to check if request will need token - - // check if OCHAMI_ACCESS_TOKEN env var is set if no access token is provided and use that instead + if conf.AccessToken == "" { + // check if ACCESS_TOKEN env var is set if no access token is provided and use that instead accessToken := os.Getenv("ACCESS_TOKEN") if accessToken != "" { - config.AccessToken = accessToken + conf.AccessToken = accessToken } else { - // TODO: try and fetch token first if it is needed if verbose { - fmt.Printf("No token found. Attempting to generate config without one...\n") + fmt.Printf("No token found. Continuing without one...\n") } } } - // show config as JSON and generators if verbose + // show conf as JSON and generators if verbose if verbose { - b, err := json.MarshalIndent(config, "", " ") + b, err := json.MarshalIndent(conf, "", "\t") if err != nil { - fmt.Printf("failed to marshal config: %v\n", err) + log.Error().Err(err).Msg("failed to marshal config") } fmt.Printf("%v\n", string(b)) } // set up the routes and start the serve server := server.Server{ - Config: &config, + Config: &conf, Server: &http.Server{ - Addr: fmt.Sprintf("%s:%d", config.Server.Host, config.Server.Port), + Addr: fmt.Sprintf("%s:%d", conf.Server.Host, conf.Server.Port), }, Jwks: server.Jwks{ - Uri: config.Server.Jwks.Uri, - Retries: config.Server.Jwks.Retries, - }, - GeneratorParams: generator.Params{ - Args: args, - // PluginPath: pluginPath, - // Target: target, // NOTE: targets are set via HTTP requests (ex: curl http://configurator:3334/generate?target=dnsmasq) - Verbose: verbose, + Uri: conf.Server.Jwks.Uri, + Retries: conf.Server.Jwks.Retries, }, } // start listening with the server - err := server.Serve(cacertPath) + err := server.Serve() if errors.Is(err, http.ErrServerClosed) { if verbose { - fmt.Printf("Server closed.") + log.Info().Msg("server closed") } } else if err != nil { - fmt.Errorf("failed to start server: %v", err) + log.Error().Err(err).Msg("failed to start server") os.Exit(1) } }, } func init() { - serveCmd.Flags().StringVar(&config.Server.Host, "host", config.Server.Host, "set the server host") - serveCmd.Flags().IntVar(&config.Server.Port, "port", config.Server.Port, "set the server port") + serveCmd.Flags().StringVar(&conf.Server.Host, "host", conf.Server.Host, "set the server host") + serveCmd.Flags().IntVar(&conf.Server.Port, "port", conf.Server.Port, "set the server port") // serveCmd.Flags().StringVar(&pluginPath, "plugin", "", "set the generator plugins directory path") - serveCmd.Flags().StringVar(&config.Server.Jwks.Uri, "jwks-uri", config.Server.Jwks.Uri, "set the JWKS url to fetch public key") - serveCmd.Flags().IntVar(&config.Server.Jwks.Retries, "jwks-fetch-retries", config.Server.Jwks.Retries, "set the JWKS fetch retry count") + serveCmd.Flags().StringVar(&conf.Server.Jwks.Uri, "jwks-uri", conf.Server.Jwks.Uri, "set the JWKS url to fetch public key") + serveCmd.Flags().IntVar(&conf.Server.Jwks.Retries, "jwks-fetch-retries", conf.Server.Jwks.Retries, "set the JWKS fetch retry count") rootCmd.AddCommand(serveCmd) }