diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9f5ffbc..a985a73 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -18,7 +18,7 @@ PSU_KEY=value ... ``` -With configurations (if applicable): +With settings (if applicable): ```yaml key: value diff --git a/CHANGELOG.md b/CHANGELOG.md index b4e02a5..24ae9e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A Custom User-Agent header is sent on requests to the Portainer server to identify the client. - Supported platforms and architectures linux 32/64 bit, darwin 32/64 bit, windows 32/64 bit, and arm7 32/64 bit. - `completion` command to print Bash completion script. -- `config set` command to set configuration options. -- `config get` command to get configuration options. -- `config list|ls` command to print configuration options. +- `setting set` command to set configuration options. +- `setting get` command to get configuration options. +- `setting list|ls` command to print configuration options. - `--format` flag to select output format from "table", "json" or a custom Go template. Defaults to "table". - `container access` command to set access control for containers. - `--admins` flag to limit access to administrators. @@ -70,7 +70,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--public` flag to give access to all users. - `-h, --help` flags on each command to print its help. - `-A, --auth-token` global flag to set Portainer auth token. -- `--config` global flag to set the path to a configuration file. Supported file formats are JSON, TOML, YAML, HCL, envfile and Java properties config files. Defaults to "$HOME/.psu.yaml". +- `--settings` global flag to set the path to a configuration file. Supported file formats are JSON, TOML, YAML, HCL, envfile and Java properties config files. Defaults to "$HOME/.psu.yaml". - `-h, --help` global flag to print global help. - `--log-format` global flag to set log format from "text" and "json". Defaults to "text". - `-v, --log-level` global flag to set log level from "panic", "faltal", "error", "warning", "info", "debug" and "trace". Defaults to "info". diff --git a/Dockerfile b/Dockerfile index c1e31f0..0ade75e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,6 @@ FROM alpine:3.10 ENV PSU_AUTH_TOKEN="" -ENV PSU_CONFIG="" -ENV PSU_CONFIG_LIST_FORMAT="" ENV PSU_CONTAINER_ACCESS_ADMINS="" ENV PSU_CONTAINER_ACCESS_ENDPOINT="" ENV PSU_CONTAINER_ACCESS_PRIVATE="" @@ -30,6 +28,8 @@ ENV PSU_SERVICE_ACCESS_ADMINS="" ENV PSU_SERVICE_ACCESS_ENDPOINT="" ENV PSU_SERVICE_ACCESS_PRIVATE="" ENV PSU_SERVICE_ACCESS_PUBLIC="" +ENV PSU_SETTING_LIST_FORMAT="" +ENV PSU_SETTINGS_FILE="" ENV PSU_STACK_ACCESS_ADMINS="" ENV PSU_STACK_ACCESS_ENDPOINT="" ENV PSU_STACK_ACCESS_PRIVATE="" diff --git a/cmd/config.go b/cmd/config.go deleted file mode 100644 index 4aa57fe..0000000 --- a/cmd/config.go +++ /dev/null @@ -1,15 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -// configCmd represents the config command -var configCmd = &cobra.Command{ - Use: "config", - Short: "Manage configs", -} - -func init() { - rootCmd.AddCommand(configCmd) -} diff --git a/cmd/configGet.go b/cmd/configGet.go deleted file mode 100644 index 572d99b..0000000 --- a/cmd/configGet.go +++ /dev/null @@ -1,44 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/greenled/portainer-stack-utils/common" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -// configGetCmd represents the config get command -var configGetCmd = &cobra.Command{ - Use: "get ", - Short: "Get config", - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - keyExists := common.CheckConfigKeyExists(args[0]) - if !keyExists { - logrus.WithFields(logrus.Fields{ - "key": args[0], - "suggestions": "try looking up the available configuration keys: psu config ls", - }).Fatal("unknown configuration key") - } - - // Get config - value, configGettingErr := getConfig(args[0]) - common.CheckError(configGettingErr) - fmt.Println(value) - }, -} - -func init() { - configCmd.AddCommand(configGetCmd) -} - -func getConfig(key string) (value interface{}, err error) { - newViper, err := common.LoadCofig() - if err != nil { - return - } - value = newViper.Get(key) - - return -} diff --git a/cmd/configSet.go b/cmd/configSet.go deleted file mode 100644 index f9b83cc..0000000 --- a/cmd/configSet.go +++ /dev/null @@ -1,55 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/sirupsen/logrus" - - "github.com/greenled/portainer-stack-utils/common" - - "github.com/spf13/cobra" -) - -// configSetCmd represents the config set command -var configSetCmd = &cobra.Command{ - Use: "set ", - Short: "Set config", - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - keyExists := common.CheckConfigKeyExists(args[0]) - if !keyExists { - logrus.WithFields(logrus.Fields{ - "key": args[0], - "suggestions": "try looking up the available configuration keys: psu config ls", - }).Fatal("unknown configuration key") - } - - // Set config - configSettingErr := setConfig(args[0], args[1]) - common.CheckError(configSettingErr) - }, -} - -func init() { - configCmd.AddCommand(configSetCmd) -} - -func setConfig(key string, value string) (err error) { - newViper, err := common.LoadCofig() - if err != nil { - return - } - - newViper.Set(key, value) - - // Make sure the config file exists - _, err = os.Create(newViper.ConfigFileUsed()) - if err != nil { - return - } - - // Write te config file - err = newViper.WriteConfig() - - return -} diff --git a/cmd/login.go b/cmd/login.go index 4f157fb..d7b0cfe 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -35,8 +35,8 @@ var loginCmd = &cobra.Command{ } // Save auth token - configSettingErr := setConfig("auth-token", authToken) - common.CheckError(configSettingErr) + settingErr := setSetting("auth-token", authToken) + common.CheckError(settingErr) }, } diff --git a/cmd/root.go b/cmd/root.go index a8e0e09..a9df027 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,7 +14,7 @@ import ( "github.com/spf13/viper" ) -var cfgFile string +var settingsFile string // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ @@ -33,12 +33,12 @@ func Execute() { } func init() { - cobra.OnInitialize(initConfig, initLogger) + cobra.OnInitialize(initSettings, initLogger) rootCmd.SetVersionTemplate("{{ version }}\n") cobra.AddTemplateFunc("version", version.BuildVersionString) - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Config file. (default \"$HOME/.psu.yaml)\"") + rootCmd.PersistentFlags().StringVar(&settingsFile, "settings-file", "", "Settings file. (default \"$HOME/.psu.yaml)\"") rootCmd.PersistentFlags().StringP("log-level", "v", "info", "Log level. One of trace, debug, info, warning, error, fatal or panic.") rootCmd.PersistentFlags().String("log-format", "text", "Log format. One of text or json.") rootCmd.PersistentFlags().BoolP("insecure", "i", false, "Skip Portainer SSL certificate verification.") @@ -47,7 +47,7 @@ func init() { rootCmd.PersistentFlags().StringP("password", "p", "", "Portainer password.") rootCmd.PersistentFlags().StringP("auth-token", "A", "", "Portainer auth token.") rootCmd.PersistentFlags().DurationP("timeout", "t", 0, "Waiting time before aborting (like 100ms, 30s, 1h20m).") - viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config")) + viper.BindPFlag("settings-file", rootCmd.PersistentFlags().Lookup("settings-file")) viper.BindPFlag("log-level", rootCmd.PersistentFlags().Lookup("log-level")) viper.BindPFlag("log-format", rootCmd.PersistentFlags().Lookup("log-format")) viper.BindPFlag("insecure", rootCmd.PersistentFlags().Lookup("insecure")) @@ -58,11 +58,11 @@ func init() { viper.BindPFlag("auth-token", rootCmd.PersistentFlags().Lookup("auth-token")) } -// initConfig reads in config file and ENV variables if set. -func initConfig() { - if cfgFile != "" { - // Use config file from the flag. - viper.SetConfigFile(cfgFile) +// initSettings reads in setting file and ENV variables if set. +func initSettings() { + if settingsFile != "" { + // Use setting file from the flag. + viper.SetConfigFile(settingsFile) } else { // Find home directory. home, err := homedir.Dir() @@ -71,7 +71,7 @@ func initConfig() { os.Exit(1) } - // Search config in home directory with name ".psu" (without extension). + // Search settings file in home directory with name ".psu" (without extension). viper.AddConfigPath(home) viper.SetConfigName(".psu") } @@ -82,7 +82,7 @@ func initConfig() { replacer := strings.NewReplacer("-", "_", ".", "_") viper.SetEnvKeyReplacer(replacer) - // If a config file is found, read it in. + // If a setting file is found, read it in. viper.ReadInConfig() } diff --git a/cmd/setting.go b/cmd/setting.go new file mode 100644 index 0000000..f01f382 --- /dev/null +++ b/cmd/setting.go @@ -0,0 +1,15 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +// settingCmd represents the config command +var settingCmd = &cobra.Command{ + Use: "setting", + Short: "Manage settings", +} + +func init() { + rootCmd.AddCommand(settingCmd) +} diff --git a/cmd/settingGet.go b/cmd/settingGet.go new file mode 100644 index 0000000..25fcf04 --- /dev/null +++ b/cmd/settingGet.go @@ -0,0 +1,44 @@ +package cmd + +import ( + "fmt" + + "github.com/greenled/portainer-stack-utils/common" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +// settingGetCmd represents the setting get command +var settingGetCmd = &cobra.Command{ + Use: "get ", + Short: "Get setting", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + keyExists := common.CheckSettingKeyExists(args[0]) + if !keyExists { + logrus.WithFields(logrus.Fields{ + "key": args[0], + "suggestions": "try looking up the available setting keys: psu setting ls", + }).Fatal("unknown setting key") + } + + // Get setting + value, err := getSetting(args[0]) + common.CheckError(err) + fmt.Println(value) + }, +} + +func init() { + settingCmd.AddCommand(settingGetCmd) +} + +func getSetting(key string) (value interface{}, err error) { + newViper, err := common.LoadSettings() + if err != nil { + return + } + value = newViper.Get(key) + + return +} diff --git a/cmd/configList.go b/cmd/settingList.go similarity index 50% rename from cmd/configList.go rename to cmd/settingList.go index 2942758..cb0ad94 100644 --- a/cmd/configList.go +++ b/cmd/settingList.go @@ -15,56 +15,56 @@ import ( "github.com/spf13/cobra" ) -// configListCmd represents the list command -var configListCmd = &cobra.Command{ +// settingListCmd represents the setting list command +var settingListCmd = &cobra.Command{ Use: "list", - Short: "List configs", + Short: "List settings", Aliases: []string{"ls"}, - Example: ` Print configs in a table format: - psu config ls + Example: ` Print settings in a table format: + psu setting ls - Print available config keys: - psu config ls --format "{{ .Key }}" + Print available setting keys: + psu setting ls --format "{{ .Key }}" - Print configs in a yaml|properties format: - psu config ls --format "{{ .Key }}:{{ if .CurrentValue }} {{ .CurrentValue }}{{ end }}" + Print settings in a yaml|properties format: + psu setting ls --format "{{ .Key }}:{{ if .CurrentValue }} {{ .CurrentValue }}{{ end }}" Print available environment variables: - psu config ls --format "{{ .EnvironmentVariable }}" + psu setting ls --format "{{ .EnvironmentVariable }}" - Print configs in a dotenv format: - psu config ls --format "{{ .EnvironmentVariable }}={{ if .CurrentValue }}{{ .CurrentValue }}{{ end }}"`, + Print settings in a dotenv format: + psu setting ls --format "{{ .EnvironmentVariable }}={{ if .CurrentValue }}{{ .CurrentValue }}{{ end }}"`, Run: func(cmd *cobra.Command, args []string) { - // Get alphabetically ordered list of config keys + // Get alphabetically ordered list of setting keys keys := viper.AllKeys() sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) - // Create config objects - var configs []config + // Create setting objects + var settings []setting for _, key := range keys { envvar := strings.Replace(key, "-", "_", -1) envvar = strings.Replace(envvar, ".", "_", -1) envvar = strings.ToUpper(envvar) envvar = "PSU_" + envvar - configs = append(configs, config{ + settings = append(settings, setting{ Key: key, EnvironmentVariable: envvar, CurrentValue: viper.Get(key), }) } - switch viper.GetString("config.list.format") { + switch viper.GetString("setting.list.format") { case "table": - // Print configs in a table format + // Print settings in a table format writer, err := common.NewTabWriter([]string{ "KEY", "ENV VAR", "CURRENT VALUE", }) common.CheckError(err) - for _, c := range configs { + for _, c := range settings { _, err := fmt.Fprintln(writer, fmt.Sprintf( "%s\t%s\t%v", c.Key, @@ -76,15 +76,15 @@ var configListCmd = &cobra.Command{ flushErr := writer.Flush() common.CheckError(flushErr) case "json": - // Print configs in a json format - statusJSONBytes, err := json.Marshal(configs) + // Print settings in a json format + statusJSONBytes, err := json.Marshal(settings) common.CheckError(err) fmt.Println(string(statusJSONBytes)) default: - // Print configs in a custom format - template, templateParsingErr := template.New("configTpl").Parse(viper.GetString("config.list.format")) + // Print settings in a custom format + template, templateParsingErr := template.New("settingTpl").Parse(viper.GetString("setting.list.format")) common.CheckError(templateParsingErr) - for _, c := range configs { + for _, c := range settings { templateExecutionErr := template.Execute(os.Stdout, c) common.CheckError(templateExecutionErr) fmt.Println() @@ -94,15 +94,15 @@ var configListCmd = &cobra.Command{ } func init() { - configCmd.AddCommand(configListCmd) + settingCmd.AddCommand(settingListCmd) - configListCmd.Flags().String("format", "table", `Output format. Can be "table", "json" or a Go template.`) - viper.BindPFlag("config.list.format", configListCmd.Flags().Lookup("format")) + settingListCmd.Flags().String("format", "table", `Output format. Can be "table", "json" or a Go template.`) + viper.BindPFlag("setting.list.format", settingListCmd.Flags().Lookup("format")) - configListCmd.SetUsageTemplate(configListCmd.UsageTemplate() + common.GetFormatHelp(config{})) + settingListCmd.SetUsageTemplate(settingListCmd.UsageTemplate() + common.GetFormatHelp(setting{})) } -type config struct { +type setting struct { Key string EnvironmentVariable string CurrentValue interface{} diff --git a/cmd/settingSet.go b/cmd/settingSet.go new file mode 100644 index 0000000..4588e1a --- /dev/null +++ b/cmd/settingSet.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "os" + + "github.com/sirupsen/logrus" + + "github.com/greenled/portainer-stack-utils/common" + + "github.com/spf13/cobra" +) + +// settingSetCmd represents the setting set command +var settingSetCmd = &cobra.Command{ + Use: "set ", + Short: "Set setting", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + keyExists := common.CheckSettingKeyExists(args[0]) + if !keyExists { + logrus.WithFields(logrus.Fields{ + "key": args[0], + "suggestions": "try looking up the available setting keys: psu setting ls", + }).Fatal("unknown setting key") + } + + // Set setting + err := setSetting(args[0], args[1]) + common.CheckError(err) + }, +} + +func init() { + settingCmd.AddCommand(settingSetCmd) +} + +func setSetting(key string, value string) (err error) { + newViper, err := common.LoadSettings() + if err != nil { + return + } + + newViper.Set(key, value) + + // Make sure the setting file exists + _, err = os.Create(newViper.ConfigFileUsed()) + if err != nil { + return + } + + // Write te setting file + err = newViper.WriteConfig() + + return +} diff --git a/common/configs.go b/common/configs.go deleted file mode 100644 index 0c18842..0000000 --- a/common/configs.go +++ /dev/null @@ -1,47 +0,0 @@ -package common - -import ( - "fmt" - "os" - - "github.com/mitchellh/go-homedir" - "github.com/spf13/viper" -) - -// LoadCofig loads the configuration file currently used by viper into a new viper instance -func LoadCofig() (v *viper.Viper, err error) { - // Set config file name - var configFile string - if viper.ConfigFileUsed() != "" { - // Use config file from viper - configFile = viper.ConfigFileUsed() - } else { - // Find home directory - var home string - home, err = homedir.Dir() - if err != nil { - return - } - - // Use $HOME/.psu.yaml - configFile = fmt.Sprintf("%s%s.psu.yaml", home, string(os.PathSeparator)) - } - v = viper.New() - v.SetConfigFile(configFile) - - // Read config from file - err = v.ReadInConfig() - - return -} - -// CheckConfigKeyExists checks a given configuration key exists in the default viper -func CheckConfigKeyExists(key string) (keyExists bool) { - for _, k := range viper.AllKeys() { - if k == key { - keyExists = true - break - } - } - return -} diff --git a/common/settings.go b/common/settings.go new file mode 100644 index 0000000..0bd69ff --- /dev/null +++ b/common/settings.go @@ -0,0 +1,47 @@ +package common + +import ( + "fmt" + "os" + + "github.com/mitchellh/go-homedir" + "github.com/spf13/viper" +) + +// LoadSettings loads the settings file currently used by viper into a new viper instance +func LoadSettings() (v *viper.Viper, err error) { + // Set settings file name + var settingsFile string + if viper.ConfigFileUsed() != "" { + // Use settings file from viper + settingsFile = viper.ConfigFileUsed() + } else { + // Find home directory + var home string + home, err = homedir.Dir() + if err != nil { + return + } + + // Use $HOME/.psu.yaml + settingsFile = fmt.Sprintf("%s%s.psu.yaml", home, string(os.PathSeparator)) + } + v = viper.New() + v.SetConfigFile(settingsFile) + + // Read settings from file + err = v.ReadInConfig() + + return +} + +// CheckSettingKeyExists checks a given setting key exists in the default viper +func CheckSettingKeyExists(key string) (keyExists bool) { + for _, k := range viper.AllKeys() { + if k == key { + keyExists = true + break + } + } + return +}