feat(zli): add new subcommand to remove a zli config (#1619)

https://github.com/project-zot/zot/issues/1594
- use indent '  ' in the config json

https://github.com/project-zot/zot/issues/1593
- add subcommand `zli config remove`

Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
This commit is contained in:
Andrei Aaron 2023-07-13 19:34:01 +03:00 committed by GitHub
parent d3f27b4ba6
commit e3ac50b2a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 157 additions and 9 deletions

View File

@ -97,16 +97,18 @@ func NewConfigCommand() *cobra.Command {
configCmd.Flags().BoolVar(&isReset, "reset", false, "Reset a variable value")
configCmd.SetUsageTemplate(configCmd.UsageTemplate() + supportedOptions)
configCmd.AddCommand(NewConfigAddCommand())
configCmd.AddCommand(NewConfigRemoveCommand())
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),
Use: "add <config-name> <url>",
Example: " zli config add main https://zot-foo.com:8080",
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 {
@ -124,9 +126,42 @@ func NewConfigAddCommand() *cobra.Command {
},
}
// Prevent parent template from overwriting default template
configAddCmd.SetUsageTemplate(configAddCmd.UsageTemplate())
return configAddCmd
}
func NewConfigRemoveCommand() *cobra.Command {
configRemoveCmd := &cobra.Command{
Use: "remove <config-name>",
Example: " zli config remove main",
Short: "Remove configuration for a zot registry",
Long: "Remove configuration for a zot registry",
Args: cobra.ExactArgs(oneArg),
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 = removeConfig(configPath, args[0])
if err != nil {
return err
}
return nil
},
}
// Prevent parent template from overwriting default template
configRemoveCmd.SetUsageTemplate(configRemoveCmd.UsageTemplate())
return configRemoveCmd
}
func getConfigMapFromFile(filePath string) ([]interface{}, error) {
file, err := os.OpenFile(filePath, os.O_RDONLY|os.O_CREATE, defaultConfigPerms)
if err != nil {
@ -164,7 +199,7 @@ func saveConfigMapToFile(filePath string, configMap []interface{}) error {
listMap := make(map[string]interface{})
listMap["configs"] = configMap
marshalled, err := json.Marshal(&listMap)
marshalled, err := json.MarshalIndent(&listMap, "", " ")
if err != nil {
return err
}
@ -235,6 +270,38 @@ func addConfig(configPath, configName, url string) error {
return nil
}
func removeConfig(configPath, configName string) error {
configs, err := getConfigMapFromFile(configPath)
if err != nil {
return err
}
for i, val := range configs {
configMap, ok := val.(map[string]interface{})
if !ok {
return zerr.ErrBadConfig
}
name := configMap[nameKey]
if name != configName {
continue
}
// Remove config from the config list before saving
newConfigs := configs[:i]
newConfigs = append(newConfigs, configs[i+1:]...)
err = saveConfigMapToFile(configPath, newConfigs)
if err != nil {
return err
}
return nil
}
return zerr.ErrConfigNotFound
}
func addDefaultConfigs(config map[string]interface{}) {
if _, ok := config[showspinnerConfig]; !ok {
config[showspinnerConfig] = true
@ -411,9 +478,10 @@ func configNameExists(configs []interface{}, configName string) bool {
const (
examples = ` zli config add main https://zot-foo.com:8080
zli config --list
zli config main url
zli config main --list
zli config --list`
zli config remove main`
supportedOptions = `
Useful variables:

View File

@ -7,6 +7,7 @@ import (
"bytes"
"log"
"os"
"regexp"
"strings"
"testing"
@ -162,6 +163,85 @@ func TestConfigCmdMain(t *testing.T) {
So(err, ShouldEqual, zotErrors.ErrInvalidURL)
})
Convey("Test remove config entry successfully", t, func() {
args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath)
cmd := NewConfigCommand()
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
actual, err := os.ReadFile(configPath)
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
actualString := space.ReplaceAllString(string(actual), " ")
So(actualString, ShouldEqual, `{ "configs": [] }`)
})
Convey("Test remove missing config entry", t, func() {
args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"configs":[]`)
defer os.Remove(configPath)
cmd := NewConfigCommand()
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "does not exist")
})
Convey("Test remove bad config file content", t, func() {
args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"asdf":[]`)
defer os.Remove(configPath)
cmd := NewConfigCommand()
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "config json is empty")
})
Convey("Test remove bad config file entry", t, func() {
args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"configs":[asdad]`)
defer os.Remove(configPath)
cmd := NewConfigCommand()
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid config")
})
Convey("Test remove config bad permissions", t, func() {
args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer func() {
_ = os.Chmod(configPath, 0o600)
os.Remove(configPath)
}()
err := os.Chmod(configPath, 0o400) // Read-only, so we fail only on updating the file, not reading
So(err, ShouldBeNil)
cmd := NewConfigCommand()
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "permission denied")
})
Convey("Test fetch all config", t, func() {
args := []string{"--list"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
@ -294,7 +374,7 @@ func TestConfigCmdMain(t *testing.T) {
}
actualStr := string(actual)
So(actualStr, ShouldContainSubstring, "https://test-url.com")
So(actualStr, ShouldContainSubstring, `"showspinner":false`)
So(actualStr, ShouldContainSubstring, `"showspinner": false`)
So(buff.String(), ShouldEqual, "")
Convey("To an empty file", func() {
@ -330,7 +410,7 @@ func TestConfigCmdMain(t *testing.T) {
}
actualStr := string(actual)
So(actualStr, ShouldContainSubstring, `https://new-url.com`)
So(actualStr, ShouldContainSubstring, `"showspinner":false`)
So(actualStr, ShouldContainSubstring, `"showspinner": false`)
So(actualStr, ShouldNotContainSubstring, `https://test-url.com`)
So(buff.String(), ShouldEqual, "")
})
@ -353,7 +433,7 @@ func TestConfigCmdMain(t *testing.T) {
}
actualStr := string(actual)
So(actualStr, ShouldNotContainSubstring, "showspinner")
So(actualStr, ShouldContainSubstring, `"url":"https://test-url.com"`)
So(actualStr, ShouldContainSubstring, `"url": "https://test-url.com"`)
So(buff.String(), ShouldEqual, "")
})