This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a commit to branch 1184/configure-default-namespace
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit b1464c0c4a60996d230e8759c2091d4f39ba54d4
Author: Nicolas Filotto <[email protected]>
AuthorDate: Mon Sep 5 10:17:40 2022 +0200

    feat(cli): Add a config command to manage the default settings
---
 pkg/cmd/config.go      | 163 +++++++++++++++++++++++++++++++++++++++++++++++++
 pkg/cmd/config_test.go | 137 +++++++++++++++++++++++++++++++++++++++++
 pkg/cmd/root.go        |  11 +++-
 pkg/cmd/util_config.go |  11 +++-
 4 files changed, 318 insertions(+), 4 deletions(-)

diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go
new file mode 100644
index 000000000..7250900aa
--- /dev/null
+++ b/pkg/cmd/config.go
@@ -0,0 +1,163 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+       "errors"
+       "fmt"
+       "os"
+       "path/filepath"
+
+       "github.com/spf13/cobra"
+       "github.com/spf13/viper"
+       "gopkg.in/yaml.v2"
+)
+
+// ConfigFolder defines the different types of folder containing the 
configuration file
+type ConfigFolder string
+
+const (
+       // The path of the folder containing the configuration file is 
retrieved from the environment
+       // variable KAMEL_CONFIG_PATH
+       ConfigFolderEnvVar ConfigFolder = "env"
+       // The path of the folder containing the configuration file is 
$HOME/.kamel
+       ConfigFolderHome ConfigFolder = "home"
+       // The folder containing the configuration file is .kamel located in 
the working directory
+       ConfigFolderSubDirectory ConfigFolder = "sub"
+       // The folder containing the configuration file is the working directory
+       ConfigFolderWorking ConfigFolder = "working"
+       // The folder containing the configuration file is the directory 
currently used by the kamel script
+       ConfigFolderUsed ConfigFolder = "used"
+)
+
+func newCmdConfig(rootCmdOptions *RootCmdOptions) (*cobra.Command, 
*configCmdOptions) {
+       options := configCmdOptions{}
+       cmd := cobra.Command{
+               Use:     "config",
+               Short:   "Configure the default settings",
+               PreRunE: decode(&options),
+               Args:    options.validateArgs,
+               RunE:    options.run,
+       }
+
+       cmd.Flags().String("folder", "used", "The type of folder containing the 
configuration file to read/write. The supported values are env, home, sub, 
working and used for respectively $KAMEL_CONFIG_PATH, $HOME/.kamel, .kamel, . 
and the folder used by kamel")
+       cmd.Flags().String("default-namespace", "", "The name of the namespace 
to use by default")
+       cmd.Flags().BoolP("list", "l", false, "List all existing settings")
+       return &cmd, &options
+}
+
+type configCmdOptions struct {
+       DefaultNamespace string `mapstructure:"default-namespace"`
+}
+
+func (o *configCmdOptions) validateArgs(cmd *cobra.Command, args []string) 
error {
+       if len(args) > 0 {
+               return errors.New("No arguments are expected")
+       }
+       return nil
+}
+
+func (o *configCmdOptions) run(cmd *cobra.Command, args []string) error {
+       path, err := getConfigLocation(cmd)
+       if err != nil {
+               return err
+       }
+       if cmd.Flags().Lookup("default-namespace").Changed {
+               err = o.saveConfiguration(cmd, path)
+               if err != nil {
+                       return err
+               }
+       }
+       if cmd.Flags().Lookup("list").Changed {
+               err = printConfiguration(cmd, path)
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+// Save the configuration at the given location
+func (o *configCmdOptions) saveConfiguration(cmd *cobra.Command, path string) 
error {
+       cfg, err := LoadConfigurationFrom(path)
+       if err != nil {
+               return err
+       }
+
+       cfg.Update(cmd, pathToRoot(cmd), o, true)
+
+       err = cfg.Save()
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+// Gives the location of the configuration file
+func getConfigLocation(cmd *cobra.Command) (string, error) {
+       var folder ConfigFolder
+       if s, err := cmd.Flags().GetString("folder"); err == nil {
+               folder = ConfigFolder(s)
+       } else {
+               return "", err
+       }
+       var path string
+       switch folder {
+       case ConfigFolderUsed:
+               path = viper.ConfigFileUsed()
+               if path != "" {
+                       return path, nil
+               }
+       case ConfigFolderEnvVar:
+               path = os.Getenv("KAMEL_CONFIG_PATH")
+       case ConfigFolderHome:
+               home, err := os.UserHomeDir()
+               cobra.CheckErr(err)
+               path = filepath.Join(home, ".kamel")
+       case ConfigFolderSubDirectory:
+               path = ".kamel"
+       case ConfigFolderWorking:
+               path = "."
+       default:
+               return "", errors.New(fmt.Sprintf("unsupported type of folder: 
%s", folder))
+       }
+       configName := os.Getenv("KAMEL_CONFIG_NAME")
+       if configName == "" {
+               configName = DefaultConfigName
+       }
+       return filepath.Join(path, fmt.Sprintf("%s.yaml", configName)), nil
+}
+
+// Print the content of the configuration file located at the given path
+func printConfiguration(cmd *cobra.Command, path string) error {
+       cfg, err := LoadConfigurationFrom(path)
+       if err != nil {
+               return err
+       }
+       if len(cfg.content) == 0 {
+               fmt.Fprintln(cmd.OutOrStdout(), fmt.Sprintf("No settings could 
be found at %s", cfg.location))
+       } else {
+               bs, err := yaml.Marshal(cfg.content)
+               if err != nil {
+                       return errors.New(fmt.Sprintf("unable to marshal config 
to YAML: %v", err))
+               }
+               fmt.Fprintln(cmd.OutOrStdout(), fmt.Sprintf("Configuration file 
read from %s", cfg.location))
+               fmt.Fprintln(cmd.OutOrStdout(), string(bs))
+       }
+       return nil
+}
diff --git a/pkg/cmd/config_test.go b/pkg/cmd/config_test.go
new file mode 100644
index 000000000..57d5a981e
--- /dev/null
+++ b/pkg/cmd/config_test.go
@@ -0,0 +1,137 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+       "fmt"
+       "os"
+       "strings"
+       "testing"
+
+       "github.com/apache/camel-k/pkg/util/test"
+       "github.com/spf13/cobra"
+       "github.com/stretchr/testify/assert"
+)
+
+const cmdConfig = "config"
+
+// nolint: unparam
+func initializeConfigCmdOptions(t *testing.T, mock bool) (*configCmdOptions, 
*cobra.Command, RootCmdOptions) {
+       t.Helper()
+
+       options, rootCmd := kamelTestPreAddCommandInit()
+       configCmdOptions := addTestConfigCmd(*options, rootCmd, mock)
+       kamelTestPostAddCommandInit(t, rootCmd)
+
+       return configCmdOptions, rootCmd, *options
+}
+
+func addTestConfigCmd(options RootCmdOptions, rootCmd *cobra.Command, mock 
bool) *configCmdOptions {
+       // add a testing version of config Command
+       configCmd, configOptions := newCmdConfig(&options)
+       if mock {
+               configCmd.RunE = func(c *cobra.Command, args []string) error {
+                       return nil
+               }
+       }
+       configCmd.Args = test.ArbitraryArgs
+       rootCmd.AddCommand(configCmd)
+       return configOptions
+}
+
+func TestConfigNonExistingFlag(t *testing.T) {
+       _, rootCmd, _ := initializeConfigCmdOptions(t, true)
+       _, err := test.ExecuteCommand(rootCmd, cmdConfig, "--nonExistingFlag")
+       assert.NotNil(t, err)
+}
+
+func TestConfigDefaultNamespaceFlag(t *testing.T) {
+       configCmdOptions, rootCmd, _ := initializeConfigCmdOptions(t, true)
+       _, err := test.ExecuteCommand(rootCmd, cmdConfig, 
"--default-namespace", "foo")
+       assert.Nil(t, err)
+       assert.Equal(t, "foo", configCmdOptions.DefaultNamespace)
+}
+
+func TestConfigListFlag(t *testing.T) {
+       _, rootCmd, _ := initializeConfigCmdOptions(t, false)
+       output, err := test.ExecuteCommand(rootCmd, cmdConfig, "--list")
+       assert.Nil(t, err)
+       assert.True(t, strings.Contains(output, "No settings"), "The output is 
unexpected: "+output)
+}
+
+func TestConfigFolderFlagToUsed(t *testing.T) {
+       _, rootCmd, _ := initializeConfigCmdOptions(t, false)
+       output, err := test.ExecuteCommand(rootCmd, cmdConfig, "--list", 
"--folder", "used")
+       assert.Nil(t, err)
+       assert.True(t, strings.Contains(output, fmt.Sprintf(" %s", 
DefaultConfigLocation)), "The output is unexpected: "+output)
+}
+
+func TestConfigFolderFlagToSub(t *testing.T) {
+       _, rootCmd, _ := initializeConfigCmdOptions(t, false)
+       output, err := test.ExecuteCommand(rootCmd, cmdConfig, "--list", 
"--folder", "sub")
+       assert.Nil(t, err)
+       assert.True(t, strings.Contains(output, fmt.Sprintf(" .kamel/%s", 
DefaultConfigLocation)), "The output is unexpected: "+output)
+}
+
+func TestConfigFolderFlagToHome(t *testing.T) {
+       _, rootCmd, _ := initializeConfigCmdOptions(t, false)
+       output, err := test.ExecuteCommand(rootCmd, cmdConfig, "--list", 
"--folder", "home")
+       assert.Nil(t, err)
+       assert.True(t, strings.Contains(output, fmt.Sprintf("/.kamel/%s", 
DefaultConfigLocation)), "The output is unexpected: "+output)
+}
+
+func TestConfigFolderFlagToEnv(t *testing.T) {
+       os.Setenv("KAMEL_CONFIG_PATH", "/foo/bar")
+       t.Cleanup(func() { os.Unsetenv("KAMEL_CONFIG_PATH") })
+       _, rootCmd, _ := initializeConfigCmdOptions(t, false)
+       output, err := test.ExecuteCommand(rootCmd, cmdConfig, "--list", 
"--folder", "env")
+       assert.Nil(t, err)
+       assert.True(t, strings.Contains(output, fmt.Sprintf("/foo/bar/%s", 
DefaultConfigLocation)), "The output is unexpected: "+output)
+}
+
+func TestConfigFolderFlagToEnvWithConfigName(t *testing.T) {
+       os.Setenv("KAMEL_CONFIG_NAME", "config")
+       os.Setenv("KAMEL_CONFIG_PATH", "/foo/bar")
+       t.Cleanup(func() {
+               os.Unsetenv("KAMEL_CONFIG_NAME")
+               os.Unsetenv("KAMEL_CONFIG_PATH")
+       })
+       _, rootCmd, _ := initializeConfigCmdOptions(t, false)
+       output, err := test.ExecuteCommand(rootCmd, cmdConfig, "--list", 
"--folder", "env")
+       assert.Nil(t, err)
+       assert.True(t, strings.Contains(output, "/foo/bar/config.yaml"), "The 
output is unexpected: "+output)
+}
+
+func TestConfigDefaultNamespace(t *testing.T) {
+       _, err := os.Stat(DefaultConfigLocation)
+       assert.True(t, os.IsNotExist(err), "No file at 
"+DefaultConfigLocation+" was expected")
+       _, rootCmd, _ := initializeConfigCmdOptions(t, false)
+       t.Cleanup(func() { os.Remove(DefaultConfigLocation) })
+       _, err = test.ExecuteCommand(rootCmd, cmdConfig, "--default-namespace", 
"foo")
+       assert.Nil(t, err)
+       _, err = os.Stat(DefaultConfigLocation)
+       assert.Nil(t, err, "A file at "+DefaultConfigLocation+" was expected")
+       output, err := test.ExecuteCommand(rootCmd, cmdConfig, "--list")
+       assert.Nil(t, err)
+       assert.True(t, strings.Contains(output, "foo"), "The output is 
unexpected: "+output)
+       _, rootCmd, _ = initializeInstallCmdOptions(t)
+       _, err = test.ExecuteCommand(rootCmd, cmdInstall)
+       assert.Nil(t, err)
+       // Check default namespace is set
+       assert.Equal(t, "foo", rootCmd.Flag("namespace").Value.String())
+}
diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go
index b75afd321..f72128903 100644
--- a/pkg/cmd/root.go
+++ b/pkg/cmd/root.go
@@ -152,6 +152,7 @@ func addKamelSubcommands(cmd *cobra.Command, options 
*RootCmdOptions) {
        cmd.AddCommand(cmdOnly(newCmdBind(options)))
        cmd.AddCommand(cmdOnly(newCmdPromote(options)))
        cmd.AddCommand(newCmdKamelet(options))
+       cmd.AddCommand(cmdOnly(newCmdConfig(options)))
 }
 
 func addHelpSubCommands(cmd *cobra.Command, options *RootCmdOptions) error {
@@ -182,9 +183,13 @@ func (command *RootCmdOptions) preRun(cmd *cobra.Command, 
_ []string) error {
                        return errors.Wrap(err, "cannot get command client")
                }
                if command.Namespace == "" {
-                       current, err := 
c.GetCurrentNamespace(command.KubeConfig)
-                       if err != nil {
-                               return errors.Wrap(err, "cannot get current 
namespace")
+                       current := 
viper.GetString("kamel.config.default-namespace")
+                       if current == "" {
+                               defaultNS, err := 
c.GetCurrentNamespace(command.KubeConfig)
+                               if err != nil {
+                                       return errors.Wrap(err, "cannot get 
current namespace")
+                               }
+                               current = defaultNS
                        }
                        err = cmd.Flag("namespace").Value.Set(current)
                        if err != nil {
diff --git a/pkg/cmd/util_config.go b/pkg/cmd/util_config.go
index 68d47eb7a..3ebb313e2 100644
--- a/pkg/cmd/util_config.go
+++ b/pkg/cmd/util_config.go
@@ -55,8 +55,17 @@ type Config struct {
 
 // LoadConfiguration loads a kamel configuration file.
 func LoadConfiguration() (*Config, error) {
+       return loadConfiguration(viper.ConfigFileUsed())
+}
+
+// LoadConfiguration loads a kamel configuration file from a specific location.
+func LoadConfigurationFrom(location string) (*Config, error) {
+       return loadConfiguration(location)
+}
+
+func loadConfiguration(location string) (*Config, error) {
        config := Config{
-               location: viper.ConfigFileUsed(),
+               location: location,
                content:  make(map[string]interface{}),
        }
 

Reply via email to