mirror of
https://github.com/project-zot/zot.git
synced 2024-12-23 22:27:35 -05:00
ada21ed842
Files were added to be built whether an extension is on or off. New build tags were added for each extension, while minimal and extended disappeared. added custom binary naming depending on extensions used and changed references from binary to binary-extended added automated blackbox tests for sync, search, scrub, metrics added contributor guidelines Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro>
433 lines
8.5 KiB
Go
433 lines
8.5 KiB
Go
//go:build search || ui_base
|
|
// +build search ui_base
|
|
|
|
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"
|
|
)
|