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

acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/master by this push:
     new 3c82acc  Fix #1181: add --save option to install and run
     new acab986  Merge pull request #1217 from nicolaferraro/save-config
3c82acc is described below

commit 3c82accb9192678f0afd26378992a4ba9aee3221
Author: Nicola Ferraro <ni.ferr...@gmail.com>
AuthorDate: Tue Jan 21 12:51:32 2020 +0100

    Fix #1181: add --save option to install and run
---
 go.sum                    |   2 +
 pkg/cmd/install.go        |  10 ++++
 pkg/cmd/root.go           |   5 +-
 pkg/cmd/run.go            |  54 ++++++++++++++----
 pkg/cmd/util.go           |  31 ++++++++++
 pkg/util/config/config.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 231 insertions(+), 12 deletions(-)

diff --git a/go.sum b/go.sum
index d9b9953..65c6ad4 100644
--- a/go.sum
+++ b/go.sum
@@ -965,6 +965,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod 
h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod 
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 
h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
+gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod 
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools v2.1.0+incompatible/go.mod 
h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools v2.2.0+incompatible/go.mod 
h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools/gotestsum v0.3.5/go.mod 
h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index 17ccad2..6bd3f7a 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -98,6 +98,9 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) 
(*cobra.Command, *installCmdO
        cmd.Flags().String("maven-settings", "", "Configure the source of the 
maven settings (configmap|secret:name[/key])")
        cmd.Flags().StringArray("maven-repository", nil, "Add a maven 
repository")
 
+       // save
+       cmd.Flags().Bool("save", false, "Save the install parameters into the 
default kamel configuration file (kamel-config.yaml)")
+
        // completion support
        configureBashAnnotationForFlag(
                &cmd,
@@ -119,6 +122,7 @@ type installCmdOptions struct {
        ExampleSetup      bool     `mapstructure:"example"`
        Global            bool     `mapstructure:"global"`
        KanikoBuildCache  bool     `mapstructure:"kaniko-build-cache"`
+       Save              bool     `mapstructure:"save"`
        ClusterType       string   `mapstructure:"cluster-type"`
        OutputFormat      string   `mapstructure:"output"`
        CamelVersion      string   `mapstructure:"camel-version"`
@@ -323,6 +327,12 @@ func (o *installCmdOptions) install(cobraCmd 
*cobra.Command, _ []string) error {
                return o.printOutput(collection)
        }
 
+       if o.Save {
+               if err := saveDefaultConfig(cobraCmd, "kamel.install", 
"kamel.install"); err != nil {
+                       return err
+               }
+       }
+
        return nil
 }
 
diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go
index 699d3cd..a232039 100644
--- a/pkg/cmd/root.go
+++ b/pkg/cmd/root.go
@@ -78,11 +78,12 @@ func kamelPostAddCommandInit(cmd *cobra.Command) error {
        }
 
        configName := os.Getenv("KAMEL_CONFIG_NAME")
-       if configName != "" {
-               configName = "config"
+       if configName == "" {
+               configName = "kamel-config"
        }
 
        viper.SetConfigName(configName)
+       viper.AddConfigPath(".")
        viper.AddConfigPath(".kamel")
        viper.AddConfigPath("$HOME/.kamel")
        viper.AutomaticEnv()
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 3df33fa..d8ab15c 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -40,10 +40,8 @@ import (
        k8slog "github.com/apache/camel-k/pkg/util/kubernetes/log"
        "github.com/apache/camel-k/pkg/util/sync"
        "github.com/apache/camel-k/pkg/util/watch"
-
        "github.com/pkg/errors"
        "github.com/spf13/cobra"
-
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -63,7 +61,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) 
(*cobra.Command, *runCmdOptions)
                Short:   "Run a integration on Kubernetes",
                Long:    `Deploys and execute a integration pod on Kubernetes.`,
                Args:    options.validateArgs,
-               PreRunE: decode(&options),
+               PreRunE: options.decode,
                RunE:    options.run,
        }
 
@@ -88,6 +86,8 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) 
(*cobra.Command, *runCmdOptions)
        cmd.Flags().StringArrayP("volume", "v", nil, "Mount a volume into the 
integration container. E.g \"-v pvcname:/container/path\"")
        cmd.Flags().StringArrayP("env", "e", nil, "Set an environment variable 
in the integration container. E.g \"-e MY_VAR=my-value\"")
 
+       cmd.Flags().Bool("save", false, "Save the run parameters into the 
default kamel configuration file (kamel-config.yaml)")
+
        // completion support
        configureKnownCompletions(&cmd)
 
@@ -101,6 +101,7 @@ type runCmdOptions struct {
        Logs            bool     `mapstructure:"logs"`
        Sync            bool     `mapstructure:"sync"`
        Dev             bool     `mapstructure:"dev"`
+       Save            bool     `mapstructure:"save"`
        IntegrationKit  string   `mapstructure:"kit"`
        IntegrationName string   `mapstructure:"name"`
        Profile         string   `mapstructure:"profile"`
@@ -118,6 +119,23 @@ type runCmdOptions struct {
        EnvVars         []string `mapstructure:"envs"`
 }
 
+func (o *runCmdOptions) decode(cmd *cobra.Command, args []string) error {
+       pathToRoot := pathToRoot(cmd)
+       if err := decodeKey(o, pathToRoot); err != nil {
+               return err
+       }
+
+       name := o.GetIntegrationName(args)
+       if name != "" {
+               secondaryPath := pathToRoot + ".integration." + name
+               if err := decodeKey(o, secondaryPath); err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
 func (o *runCmdOptions) validateArgs(_ *cobra.Command, args []string) error {
        if len(args) < 1 {
                return errors.New("run expects at least 1 argument, received 0")
@@ -241,6 +259,17 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args 
[]string) error {
                // Let's add a Wait point, otherwise the script terminates
                <-o.Context.Done()
        }
+
+       if o.Save {
+               name := o.GetIntegrationName(args)
+               if name != "" {
+                       key := fmt.Sprintf("kamel.run.integration.%s", name)
+                       if err := saveDefaultConfig(cmd, "kamel.run", key); err 
!= nil {
+                               return err
+                       }
+               }
+       }
+
        return nil
 }
 
@@ -304,13 +333,7 @@ func (o *runCmdOptions) createIntegration(c client.Client, 
sources []string) (*v
 func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources 
[]string) (*v1.Integration, error) {
        namespace := o.Namespace
 
-       name := ""
-       if o.IntegrationName != "" {
-               name = o.IntegrationName
-               name = kubernetes.SanitizeName(name)
-       } else if len(sources) == 1 {
-               name = kubernetes.SanitizeName(sources[0])
-       }
+       name := o.GetIntegrationName(sources)
 
        if name == "" {
                return nil, errors.New("unable to determine integration name")
@@ -462,6 +485,17 @@ func (o *runCmdOptions) updateIntegrationCode(c 
client.Client, sources []string)
        return &integration, nil
 }
 
+func (o *runCmdOptions) GetIntegrationName(sources []string) string {
+       name := ""
+       if o.IntegrationName != "" {
+               name = o.IntegrationName
+               name = kubernetes.SanitizeName(name)
+       } else if len(sources) == 1 {
+               name = kubernetes.SanitizeName(sources[0])
+       }
+       return name
+}
+
 func (*runCmdOptions) loadData(fileName string, compress bool) (string, error) 
{
        var content []byte
        var err error
diff --git a/pkg/cmd/util.go b/pkg/cmd/util.go
index 7bb4bd0..bb38cdf 100644
--- a/pkg/cmd/util.go
+++ b/pkg/cmd/util.go
@@ -25,6 +25,7 @@ import (
        "reflect"
        "strings"
 
+       "github.com/apache/camel-k/pkg/util/config"
        "github.com/mitchellh/mapstructure"
 
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
@@ -158,6 +159,36 @@ func decode(target interface{}) func(*cobra.Command, 
[]string) error {
        }
 }
 
+func saveDefaultConfig(cmd *cobra.Command, from string, to string) error {
+       settings := viper.AllSettings()
+       cfg, err := config.LoadDefault()
+       if err != nil {
+               return err
+       }
+       cfg.Delete(to)
+       cfg.Set(settings, from, to, func(s string) bool {
+               if s == "save" {
+                       return false
+               }
+               pl := p.NewClient()
+               f := cmd.Flag(s)
+               if f == nil {
+                       // may be a plural flag, let's lookup the singular 
version to check if changed
+                       cmd.Flags().VisitAll(func(flag *pflag.Flag) {
+                               if pl.Plural(flag.Name) == s {
+                                       f = flag
+                               }
+                       })
+               }
+
+               return f != nil && f.Changed
+       })
+       if err := cfg.WriteDefault(); err != nil {
+               return err
+       }
+       return nil
+}
+
 func stringToSliceHookFunc(comma rune) mapstructure.DecodeHookFunc {
        return func(
                f reflect.Kind,
diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go
new file mode 100644
index 0000000..b6eb5af
--- /dev/null
+++ b/pkg/util/config/config.go
@@ -0,0 +1,141 @@
+/*
+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 config
+
+import (
+       "io/ioutil"
+       "os"
+       "strings"
+
+       p "github.com/gertd/go-pluralize"
+       yaml "gopkg.in/yaml.v2"
+)
+
+const (
+       // DefaultConfigLocation is the main place where the kamel config is 
stored
+       DefaultConfigLocation = "./kamel-config.yaml"
+)
+
+// KamelConfig is a helper class to manipulate kamel configuration files
+type KamelConfig struct {
+       config map[string]interface{}
+}
+
+// LoadDefault loads the kamel configuration from the default location
+func LoadDefault() (*KamelConfig, error) {
+       return LoadConfig(DefaultConfigLocation)
+}
+
+// LoadConfig loads a kamel configuration file
+func LoadConfig(file string) (*KamelConfig, error) {
+       config := make(map[string]interface{})
+       data, err := ioutil.ReadFile(file)
+       if err != nil && os.IsNotExist(err) {
+               return &KamelConfig{config: config}, nil
+       } else if err != nil {
+               return nil, err
+       }
+       if err = yaml.Unmarshal(data, &config); err != nil {
+               return nil, err
+       }
+
+       return &KamelConfig{config: config}, nil
+}
+
+// Set allows to replace a subtree with a given config
+func (c *KamelConfig) Set(values map[string]interface{}, from, to string, 
filter func(string) bool) {
+       source := navigate(values, from, false)
+       destination := navigate(c.config, to, true)
+       pl := p.NewClient()
+       for k, v := range source {
+               if filter(k) {
+                       plural := pl.Plural(k)
+                       key := k
+                       if source[plural] != nil {
+                               // prefer plural names if available
+                               key = plural
+                       }
+                       destination[key] = v
+               }
+       }
+}
+
+// Delete allows to remove a substree from the kamel config
+func (c *KamelConfig) Delete(path string) {
+       leaf := navigate(c.config, path, false)
+       for k := range leaf {
+               delete(leaf, k)
+       }
+}
+
+// WriteDefault writes the configuration in the default location
+func (c *KamelConfig) WriteDefault() error {
+       return c.Write(DefaultConfigLocation)
+}
+
+// Write writes a kamel configuration to a file
+func (c *KamelConfig) Write(file string) error {
+       data, err := yaml.Marshal(c.config)
+       if err != nil {
+               return err
+       }
+       return ioutil.WriteFile(file, data, 0777)
+}
+
+func navigate(values map[string]interface{}, prefix string, create bool) 
map[string]interface{} {
+       nodes := strings.Split(prefix, ".")
+
+       for _, node := range nodes {
+               v := values[node]
+
+               if v == nil {
+                       if create {
+                               v = make(map[string]interface{})
+                               values[node] = v
+                       } else {
+                               return nil
+                       }
+               }
+
+               if m, ok := v.(map[string]interface{}); ok {
+                       values = m
+               } else if mg, ok := v.(map[interface{}]interface{}); ok {
+                       converted := convert(mg)
+                       values[node] = converted
+                       values = converted
+               } else {
+                       if create {
+                               child := make(map[string]interface{})
+                               values[node] = child
+                               return child
+                       }
+                       return nil
+               }
+       }
+       return values
+}
+
+func convert(m map[interface{}]interface{}) map[string]interface{} {
+       res := make(map[string]interface{})
+       for k, v := range m {
+               if ks, ok := k.(string); ok {
+                       res[ks] = v
+               }
+       }
+       return res
+}

Reply via email to