0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00
zot/pkg/cli/config_cmd.go
Alex Stan d325c8b5f4 Fix problems signaled by new linter version v1.45.2
PR (linter: upgrade linter version #405) triggered lint job which failed
with many errors generated by various linters. Configurations were added to
golangcilint.yaml and several refactorings were made in order to improve the
results of the linter.

maintidx linter disabled

Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro>
2022-04-27 09:55:44 -07:00

433 lines
8.5 KiB
Go

//go:build extended
// +build extended
package cli
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"text/tabwriter"
jsoniter "github.com/json-iterator/go"
"github.com/spf13/cobra"
zerr "zotregistry.io/zot/errors"
)
const (
defaultConfigPerms = 0o644
defaultFilePerms = 0o600
)
func NewConfigCommand() *cobra.Command {
var isListing bool
var isReset bool
configCmd := &cobra.Command{
Use: "config <config-name> [variable] [value]",
Example: examples,
Short: "Configure zot registry parameters for CLI",
Long: `Configure zot registry parameters for CLI`,
Args: cobra.ArbitraryArgs,
RunE: func(cmd *cobra.Command, args []string) error {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
configPath := path.Join(home + "/.zot")
switch len(args) {
case noArgs:
if isListing { // zot config -l
res, err := getConfigNames(configPath)
if err != nil {
return err
}
fmt.Fprint(cmd.OutOrStdout(), res)
return nil
}
return zerr.ErrInvalidArgs
case oneArg:
// zot config <name> -l
if isListing {
res, err := getAllConfig(configPath, args[0])
if err != nil {
return err
}
fmt.Fprint(cmd.OutOrStdout(), res)
return nil
}
return zerr.ErrInvalidArgs
case twoArgs:
if isReset { // zot config <name> <key> --reset
return resetConfigValue(configPath, args[0], args[1])
}
// zot config <name> <key>
res, err := getConfigValue(configPath, args[0], args[1])
if err != nil {
return err
}
fmt.Fprintln(cmd.OutOrStdout(), res)
case threeArgs:
// zot config <name> <key> <value>
if err := setConfigValue(configPath, args[0], args[1], args[2]); err != nil {
return err
}
default:
return zerr.ErrInvalidArgs
}
return nil
},
}
configCmd.Flags().BoolVarP(&isListing, "list", "l", false, "List configurations")
configCmd.Flags().BoolVar(&isReset, "reset", false, "Reset a variable value")
configCmd.SetUsageTemplate(configCmd.UsageTemplate() + supportedOptions)
configCmd.AddCommand(NewConfigAddCommand())
return configCmd
}
func NewConfigAddCommand() *cobra.Command {
configAddCmd := &cobra.Command{
Use: "add <config-name> <url>",
Short: "Add configuration for a zot registry",
Long: "Add configuration for a zot registry",
Args: cobra.ExactArgs(twoArgs),
RunE: func(cmd *cobra.Command, args []string) error {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
configPath := path.Join(home + "/.zot")
// zot config add <config-name> <url>
err = addConfig(configPath, args[0], args[1])
if err != nil {
return err
}
return nil
},
}
return configAddCmd
}
func getConfigMapFromFile(filePath string) ([]interface{}, error) {
file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, defaultConfigPerms)
if err != nil {
return nil, err
}
file.Close()
data, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}
var jsonMap map[string]interface{}
json := jsoniter.ConfigCompatibleWithStandardLibrary
_ = json.Unmarshal(data, &jsonMap)
if jsonMap["configs"] == nil {
return nil, zerr.ErrEmptyJSON
}
configs, ok := jsonMap["configs"].([]interface{})
if !ok {
return nil, zerr.ErrCliBadConfig
}
return configs, nil
}
func saveConfigMapToFile(filePath string, configMap []interface{}) error {
json := jsoniter.ConfigCompatibleWithStandardLibrary
listMap := make(map[string]interface{})
listMap["configs"] = configMap
marshalled, err := json.Marshal(&listMap)
if err != nil {
return err
}
if err := ioutil.WriteFile(filePath, marshalled, defaultFilePerms); err != nil {
return err
}
return nil
}
func getConfigNames(configPath string) (string, error) {
configs, err := getConfigMapFromFile(configPath)
if err != nil {
if errors.Is(err, zerr.ErrEmptyJSON) {
return "", nil
}
return "", err
}
var builder strings.Builder
writer := tabwriter.NewWriter(&builder, 0, 8, 1, '\t', tabwriter.AlignRight) //nolint:gomnd
for _, val := range configs {
configMap, ok := val.(map[string]interface{})
if !ok {
return "", zerr.ErrBadConfig
}
fmt.Fprintf(writer, "%s\t%s\n", configMap[nameKey], configMap["url"])
}
err = writer.Flush()
if err != nil {
return "", err
}
return builder.String(), nil
}
func addConfig(configPath, configName, url string) error {
configs, err := getConfigMapFromFile(configPath)
if err != nil && !errors.Is(err, zerr.ErrEmptyJSON) {
return err
}
if !isURL(url) {
return zerr.ErrInvalidURL
}
if configNameExists(configs, configName) {
return zerr.ErrDuplicateConfigName
}
configMap := make(map[string]interface{})
configMap["url"] = url
configMap[nameKey] = configName
addDefaultConfigs(configMap)
configs = append(configs, configMap)
err = saveConfigMapToFile(configPath, configs)
if err != nil {
return err
}
return nil
}
func addDefaultConfigs(config map[string]interface{}) {
if _, ok := config[showspinnerConfig]; !ok {
config[showspinnerConfig] = true
}
if _, ok := config[verifyTLSConfig]; !ok {
config[verifyTLSConfig] = true
}
}
func getConfigValue(configPath, configName, key string) (string, error) {
configs, err := getConfigMapFromFile(configPath)
if err != nil {
if errors.Is(err, zerr.ErrEmptyJSON) {
return "", zerr.ErrConfigNotFound
}
return "", err
}
for _, val := range configs {
configMap, ok := val.(map[string]interface{})
if !ok {
return "", zerr.ErrBadConfig
}
addDefaultConfigs(configMap)
name := configMap[nameKey]
if name == configName {
if configMap[key] == nil {
return "", nil
}
return fmt.Sprintf("%v", configMap[key]), nil
}
}
return "", zerr.ErrConfigNotFound
}
func resetConfigValue(configPath, configName, key string) error {
if key == "url" || key == nameKey {
return zerr.ErrCannotResetConfigKey
}
configs, err := getConfigMapFromFile(configPath)
if err != nil {
if errors.Is(err, zerr.ErrEmptyJSON) {
return zerr.ErrConfigNotFound
}
return err
}
for _, val := range configs {
configMap, ok := val.(map[string]interface{})
if !ok {
return zerr.ErrBadConfig
}
addDefaultConfigs(configMap)
name := configMap[nameKey]
if name == configName {
delete(configMap, key)
err = saveConfigMapToFile(configPath, configs)
if err != nil {
return err
}
return nil
}
}
return zerr.ErrConfigNotFound
}
func setConfigValue(configPath, configName, key, value string) error {
if key == nameKey {
return zerr.ErrIllegalConfigKey
}
configs, err := getConfigMapFromFile(configPath)
if err != nil {
if errors.Is(err, zerr.ErrEmptyJSON) {
return zerr.ErrConfigNotFound
}
return err
}
for _, val := range configs {
configMap, ok := val.(map[string]interface{})
if !ok {
return zerr.ErrBadConfig
}
addDefaultConfigs(configMap)
name := configMap[nameKey]
if name == configName {
boolVal, err := strconv.ParseBool(value)
if err == nil {
configMap[key] = boolVal
} else {
configMap[key] = value
}
err = saveConfigMapToFile(configPath, configs)
if err != nil {
return err
}
return nil
}
}
return zerr.ErrConfigNotFound
}
func getAllConfig(configPath, configName string) (string, error) {
configs, err := getConfigMapFromFile(configPath)
if err != nil {
if errors.Is(err, zerr.ErrEmptyJSON) {
return "", nil
}
return "", err
}
var builder strings.Builder
for _, value := range configs {
configMap, ok := value.(map[string]interface{})
if !ok {
return "", zerr.ErrBadConfig
}
addDefaultConfigs(configMap)
name := configMap[nameKey]
if name == configName {
for key, val := range configMap {
if key == nameKey {
continue
}
fmt.Fprintf(&builder, "%s = %v\n", key, val)
}
return builder.String(), nil
}
}
return "", zerr.ErrConfigNotFound
}
func configNameExists(configs []interface{}, configName string) bool {
for _, val := range configs {
configMap, ok := val.(map[string]interface{})
if !ok {
return false
}
if configMap[nameKey] == configName {
return true
}
}
return false
}
const (
examples = ` zli config add main https://zot-foo.com:8080
zli config main url
zli config main --list
zli config --list`
supportedOptions = `
Useful variables:
url zot server URL
showspinner show spinner while loading data [true/false]
verify-tls enable TLS certificate verification of the server [default: true]`
nameKey = "_name"
noArgs = 0
oneArg = 1
twoArgs = 2
threeArgs = 3
showspinnerConfig = "showspinner"
verifyTLSConfig = "verify-tls"
)