mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
d325c8b5f4
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>
433 lines
8.5 KiB
Go
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"
|
|
)
|