This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 24efaa0f1fd5b9b1489a324b7a512194bd45e041 Author: lburgazzoli <[email protected]> AuthorDate: Wed Nov 27 18:22:19 2019 +0100 Allow to configure kamel CLI with env vars and configuration files #1108 --- go.mod | 2 + go.sum | 11 ++ pkg/cmd/builder.go | 15 ++- pkg/cmd/delete.go | 17 +-- pkg/cmd/describe_integration.go | 3 +- pkg/cmd/describe_kit.go | 7 +- pkg/cmd/describe_platform.go | 7 +- pkg/cmd/get.go | 9 +- pkg/cmd/install.go | 237 +++++++++++++++++++--------------- pkg/cmd/kit_create.go | 74 +++++------ pkg/cmd/kit_delete.go | 27 ++-- pkg/cmd/kit_get.go | 25 ++-- pkg/cmd/log.go | 13 +- pkg/cmd/operator.go | 17 +-- pkg/cmd/rebuild.go | 9 +- pkg/cmd/reset.go | 21 +-- pkg/cmd/root.go | 35 ++++- pkg/cmd/{operator.go => root_test.go} | 27 +--- pkg/cmd/run.go | 102 ++++++++------- pkg/cmd/util.go | 144 +++++++++++++++++++++ 20 files changed, 495 insertions(+), 307 deletions(-) diff --git a/go.mod b/go.mod index 5af83f5..3f5c829 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/coreos/prometheus-operator v0.29.0 github.com/evanphx/json-patch v4.5.0+incompatible github.com/fatih/structs v1.1.0 + github.com/gertd/go-pluralize v0.1.1 github.com/go-logr/logr v0.1.0 github.com/google/go-containerregistry v0.0.0-20190206233756-dbc4da98389f // indirect github.com/google/uuid v1.1.1 @@ -23,6 +24,7 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.3 + github.com/spf13/viper v1.4.0 github.com/stoewer/go-strcase v1.0.2 github.com/stretchr/testify v1.3.0 go.uber.org/multierr v1.1.0 diff --git a/go.sum b/go.sum index c55c3c8..c21e98d 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,7 @@ github.com/Azure/azure-sdk-for-go v21.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -137,6 +138,8 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gertd/go-pluralize v0.1.1 h1:fQhql/WRRwr4TVp+TCw12s2esCacvEVBdkTUUwNqF/Q= +github.com/gertd/go-pluralize v0.1.1/go.mod h1:t5DfHcumb6m0RqyVJDrDLEzL2AGeaiqUXIcDNwLaeAs= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -276,6 +279,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v0.0.0-20160711231752-d8c773c4cba1/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/heketi/heketi v0.0.0-20181109135656-558b29266ce0/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= github.com/heketi/rest v0.0.0-20180404230133-aa6a65207413/go.mod h1:BeS3M108VzVlmAue3lv2WcGuPAX94/KN63MUURzbYSI= @@ -335,6 +339,7 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA= github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= @@ -360,6 +365,7 @@ github.com/miekg/dns v0.0.0-20160614162101-5d001d020961/go.mod h1:W1PPwlIAgtquWB github.com/mindprince/gonvml v0.0.0-20171110221305-fee913ce8fb2/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v0.0.0-20151009155749-1b4ae6fb4e77/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -417,6 +423,7 @@ github.com/operator-framework/operator-sdk v0.12.0 h1:9eAD1L8e6pPCpFCAacBUVf2elo github.com/operator-framework/operator-sdk v0.12.0/go.mod h1:mW8isQxiXlLCVf2E+xqflkQAVLOTbiqjndKdkKIrR0M= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -492,14 +499,17 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v0.0.0-20160816080757-b28a7effac97/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v0.0.0-20160730092037-e31f36ffc91a/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v0.0.0-20160311093646-33c24e77fb80/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -508,6 +518,7 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v0.0.0-20160820190039-7fb2782df3d8/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50/go.mod h1:1pdIZTAHUz+HDKDVZ++5xg/duPlhKAIzw9qy42CWYp4= github.com/stoewer/go-strcase v1.0.2 h1:l3iQ2FPu8+36ars/7syO1dQAkjwMCb1IE3J+Th0ohfE= diff --git a/pkg/cmd/builder.go b/pkg/cmd/builder.go index adb9509..9517e7d 100644 --- a/pkg/cmd/builder.go +++ b/pkg/cmd/builder.go @@ -27,21 +27,22 @@ func newCmdBuilder(rootCmdOptions *RootCmdOptions) *cobra.Command { RootCmdOptions: rootCmdOptions, } cmd := cobra.Command{ - Use: "builder", - Short: "Run the Camel K builder", - Long: `Run the Camel K builder`, - Hidden: true, - Run: impl.run, + Use: "builder", + Short: "Run the Camel K builder", + Long: `Run the Camel K builder`, + Hidden: true, + PreRunE: decode(&impl), + Run: impl.run, } - cmd.Flags().StringVar(&impl.BuildName, "build-name", "", "The name of the build resource") + cmd.Flags().String("build-name", "", "The name of the build resource") return &cmd } type builderCmdOptions struct { *RootCmdOptions - BuildName string + BuildName string `mapstructure:"build-name"` } func (o *builderCmdOptions) run(_ *cobra.Command, _ []string) { diff --git a/pkg/cmd/delete.go b/pkg/cmd/delete.go index 6eeda59..a3d9a55 100644 --- a/pkg/cmd/delete.go +++ b/pkg/cmd/delete.go @@ -36,8 +36,9 @@ func newCmdDelete(rootCmdOptions *RootCmdOptions) *cobra.Command { RootCmdOptions: rootCmdOptions, } cmd := cobra.Command{ - Use: "delete [integration1] [integration2] ...", - Short: "Delete integrations deployed on Kubernetes", + Use: "delete [integration1] [integration2] ...", + Short: "Delete integrations deployed on Kubernetes", + PreRunE: decode(&impl), RunE: func(_ *cobra.Command, args []string) error { if err := impl.validate(args); err != nil { return err @@ -50,21 +51,21 @@ func newCmdDelete(rootCmdOptions *RootCmdOptions) *cobra.Command { }, } - cmd.Flags().BoolVar(&impl.deleteAll, "all", false, "Delete all integrations") + cmd.Flags().Bool("all", false, "Delete all integrations") return &cmd } type deleteCmdOptions struct { *RootCmdOptions - deleteAll bool + DeleteAll bool `mapstructure:"all"` } func (command *deleteCmdOptions) validate(args []string) error { - if command.deleteAll && len(args) > 0 { + if command.DeleteAll && len(args) > 0 { return errors.New("invalid combination: both all flag and named integrations are set") } - if !command.deleteAll && len(args) == 0 { + if !command.DeleteAll && len(args) == 0 { return errors.New("invalid combination: neither all flag nor named integrations are set") } @@ -76,7 +77,7 @@ func (command *deleteCmdOptions) run(args []string) error { if err != nil { return err } - if len(args) != 0 && !command.deleteAll { + if len(args) != 0 && !command.DeleteAll { for _, arg := range args { name := kubernetes.SanitizeName(arg) err := DeleteIntegration(command.Context, c, name, command.Namespace) @@ -90,7 +91,7 @@ func (command *deleteCmdOptions) run(args []string) error { fmt.Println("Integration " + name + " deleted") } } - } else if command.deleteAll { + } else if command.DeleteAll { integrationList := v1alpha1.IntegrationList{ TypeMeta: metav1.TypeMeta{ APIVersion: v1alpha1.SchemeGroupVersion.String(), diff --git a/pkg/cmd/describe_integration.go b/pkg/cmd/describe_integration.go index 117868f..25882f4 100644 --- a/pkg/cmd/describe_integration.go +++ b/pkg/cmd/describe_integration.go @@ -41,6 +41,7 @@ func newDescribeIntegrationCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { Aliases: []string{"it"}, Short: "Describe an Integration", Long: `Describe an Integration.`, + PreRunE: decode(&impl), RunE: func(_ *cobra.Command, args []string) error { if err := impl.validate(args); err != nil { return err @@ -60,7 +61,7 @@ func newDescribeIntegrationCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { type describeIntegrationCommand struct { *RootCmdOptions - showSourceContent bool + showSourceContent bool `mapstructure:"show-source-content"` } func (command *describeIntegrationCommand) validate(args []string) error { diff --git a/pkg/cmd/describe_kit.go b/pkg/cmd/describe_kit.go index c182650..a3ce855 100644 --- a/pkg/cmd/describe_kit.go +++ b/pkg/cmd/describe_kit.go @@ -34,9 +34,10 @@ func newDescribeKitCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ - Use: "kit", - Short: "Describe an Integration Kit", - Long: `Describe an Integration Kit.`, + Use: "kit", + Short: "Describe an Integration Kit", + Long: `Describe an Integration Kit.`, + PreRunE: decode(impl), RunE: func(_ *cobra.Command, args []string) error { if err := impl.validate(args); err != nil { return err diff --git a/pkg/cmd/describe_platform.go b/pkg/cmd/describe_platform.go index 162a7a0..17c639b 100644 --- a/pkg/cmd/describe_platform.go +++ b/pkg/cmd/describe_platform.go @@ -34,9 +34,10 @@ func newDescribePlatformCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ - Use: "platform", - Short: "Describe an Integration Platform", - Long: `Describe an Integration Platform.`, + Use: "platform", + Short: "Describe an Integration Platform", + Long: `Describe an Integration Platform.`, + PreRunE: decode(impl), RunE: func(_ *cobra.Command, args []string) error { if err := impl.validate(args); err != nil { return err diff --git a/pkg/cmd/get.go b/pkg/cmd/get.go index 6774065..d5dea19 100644 --- a/pkg/cmd/get.go +++ b/pkg/cmd/get.go @@ -39,10 +39,11 @@ func newCmdGet(rootCmdOptions *RootCmdOptions) *cobra.Command { RootCmdOptions: rootCmdOptions, } cmd := cobra.Command{ - Use: "get [integration]", - Short: "Get integrations deployed on Kubernetes", - Long: `Get the status of integrations deployed on on Kubernetes.`, - RunE: options.run, + Use: "get [integration]", + Short: "Get integrations deployed on Kubernetes", + Long: `Get the status of integrations deployed on on Kubernetes.`, + PreRunE: decode(&options), + RunE: options.run, } return &cmd diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go index 56f8c33..bc10726 100644 --- a/pkg/cmd/install.go +++ b/pkg/cmd/install.go @@ -38,6 +38,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -49,45 +50,53 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ Use: "install", - Short: "Install Camel K on a Kubernetes cluster", + Short: "Installs Camel K on a Kubernetes cluster", Long: `Installs Camel K on a Kubernetes or OpenShift cluster.`, - PreRunE: impl.validate, - RunE: impl.install, + PreRunE: impl.decode, + RunE: func(cmd *cobra.Command, args []string) error { + if err := impl.validate(cmd, args); err != nil { + return err + } + if err := impl.install(cmd, args); err != nil { + return err + } + return nil + }, } - cmd.Flags().BoolVarP(&impl.wait, "wait", "w", false, "Waits for the platform to be running") - cmd.Flags().BoolVar(&impl.clusterSetupOnly, "cluster-setup", false, "Execute cluster-wide operations only (may require admin rights)") - cmd.Flags().StringVar(&impl.clusterType, "cluster-type", "", "Set explicitly the cluster type to Kubernetes or OpenShift") - cmd.Flags().BoolVar(&impl.skipOperatorSetup, "skip-operator-setup", false, "Do not install the operator in the namespace (in case there's a global one)") - cmd.Flags().BoolVar(&impl.skipClusterSetup, "skip-cluster-setup", false, "Skip the cluster-setup phase") - cmd.Flags().BoolVar(&impl.exampleSetup, "example", false, "Install example integration") - cmd.Flags().BoolVar(&impl.global, "global", false, "Configure the operator to watch all namespaces. No integration platform is created.") - - cmd.Flags().StringVarP(&impl.outputFormat, "output", "o", "", "Output format. One of: json|yaml") - cmd.Flags().StringVar(&impl.registry.Organization, "organization", "", "A organization on the Docker registry that can be used to publish images") - cmd.Flags().StringVar(&impl.registry.Address, "registry", "", "A Docker registry that can be used to publish images") - cmd.Flags().StringVar(&impl.registry.Secret, "registry-secret", "", "A secret used to push/pull images to the Docker registry") - cmd.Flags().BoolVar(&impl.registry.Insecure, "registry-insecure", false, "Configure to configure registry access in insecure mode or not") - cmd.Flags().StringVar(&impl.registryAuth.Server, "registry-auth-server", "", "The docker registry authentication server") - cmd.Flags().StringVar(&impl.registryAuth.Username, "registry-auth-username", "", "The docker registry authentication username") - cmd.Flags().StringVar(&impl.registryAuth.Password, "registry-auth-password", "", "The docker registry authentication password") - cmd.Flags().StringSliceVarP(&impl.properties, "property", "p", nil, "Add a camel property") - cmd.Flags().StringVar(&impl.camelVersion, "camel-version", "", "Set the camel version") - cmd.Flags().StringVar(&impl.runtimeVersion, "runtime-version", "", "Set the camel-k runtime version") - cmd.Flags().StringVar(&impl.baseImage, "base-image", "", "Set the base image used to run integrations") - cmd.Flags().StringVar(&impl.operatorImage, "operator-image", "", "Set the operator image used for the operator deployment") - cmd.Flags().StringSliceVar(&impl.kits, "kit", nil, "Add an integration kit to build at startup") - cmd.Flags().StringVar(&impl.buildStrategy, "build-strategy", "", "Set the build strategy") - cmd.Flags().StringVar(&impl.buildTimeout, "build-timeout", "", "Set how long the build process can last") - cmd.Flags().StringVar(&impl.traitProfile, "trait-profile", "", "The profile to use for traits") - cmd.Flags().BoolVar(&impl.kanikoBuildCache, "kaniko-build-cache", true, "To enable or disable the Kaniko Cache in building phase") - cmd.Flags().StringVar(&impl.httpProxySecret, "http-proxy-secret", "", "Configure the source of the secret holding HTTP proxy server details "+ + cmd.Flags().BoolP("wait", "w", false, "Waits for the platform to be running") + cmd.Flags().Bool("cluster-setup", false, "Execute cluster-wide operations only (may require admin rights)") + cmd.Flags().String("cluster-type", "", "Set explicitly the cluster type to Kubernetes or OpenShift") + cmd.Flags().Bool("skip-operator-setup", false, "Do not install the operator in the namespace (in case there's a global one)") + cmd.Flags().Bool("skip-cluster-setup", false, "Skip the cluster-setup phase") + cmd.Flags().Bool("example", false, "Install example integration") + cmd.Flags().Bool("global", false, "Configure the operator to watch all namespaces. No integration platform is created.") + + cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml") + cmd.Flags().String("organization", "", "A organization on the Docker registry that can be used to publish images") + cmd.Flags().String("registry", "", "A Docker registry that can be used to publish images") + cmd.Flags().String("registry-secret", "", "A secret used to push/pull images to the Docker registry") + cmd.Flags().Bool("registry-insecure", false, "Configure to configure registry access in insecure mode or not") + cmd.Flags().String("registry-auth-server", "", "The docker registry authentication server") + cmd.Flags().String("registry-auth-username", "", "The docker registry authentication username") + cmd.Flags().String("registry-auth-password", "", "The docker registry authentication password") + cmd.Flags().StringArrayP("property", "p", nil, "Add a camel property") + cmd.Flags().String("camel-version", "", "Set the camel version") + cmd.Flags().String("runtime-version", "", "Set the camel-k runtime version") + cmd.Flags().String("base-image", "", "Set the base Image used to run integrations") + cmd.Flags().String("operator-image", "", "Set the operator Image used for the operator deployment") + cmd.Flags().StringArray("kit", nil, "Add an integration kit to build at startup") + cmd.Flags().String("build-strategy", "", "Set the build strategy") + cmd.Flags().String("build-timeout", "", "Set how long the build process can last") + cmd.Flags().String("trait-profile", "", "The profile to use for traits") + cmd.Flags().Bool("kaniko-build-cache", true, "To enable or disable the Kaniko Cache in building phase") + cmd.Flags().String("http-proxy-secret", "", "Configure the source of the secret holding HTTP proxy server details "+ "(HTTP_PROXY|HTTPS_PROXY|NO_PROXY)") // maven settings - cmd.Flags().StringVar(&impl.localRepository, "local-repository", "", "Location of the local maven repository") - cmd.Flags().StringVar(&impl.mavenSettings, "maven-settings", "", "Configure the source of the maven settings (configmap|secret:name[/key])") - cmd.Flags().StringSliceVar(&impl.mavenRepositories, "maven-repository", nil, "Add a maven repository") + cmd.Flags().String("local-repository", "", "Location of the local maven repository") + 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") // completion support configureBashAnnotationForFlag( @@ -103,40 +112,41 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command { type installCmdOptions struct { *RootCmdOptions - wait bool - clusterSetupOnly bool - skipOperatorSetup bool - skipClusterSetup bool - exampleSetup bool - global bool - kanikoBuildCache bool - clusterType string - outputFormat string - camelVersion string - runtimeVersion string - baseImage string - operatorImage string - localRepository string - buildStrategy string - buildTimeout string - mavenRepositories []string - mavenSettings string - properties []string - kits []string - registry v1alpha1.IntegrationPlatformRegistrySpec - registryAuth registry.Auth - traitProfile string - httpProxySecret string + Wait bool `mapstructure:"wait"` + ClusterSetupOnly bool `mapstructure:"cluster-setup"` + SkipOperatorSetup bool `mapstructure:"skip-operator-setup"` + SkipClusterSetup bool `mapstructure:"skip-cluster-setup"` + ExampleSetup bool `mapstructure:"example"` + Global bool `mapstructure:"global"` + KanikoBuildCache bool `mapstructure:"kaniko-build-cache"` + ClusterType string `mapstructure:"cluster-type"` + OutputFormat string `mapstructure:"output"` + CamelVersion string `mapstructure:"camel-version"` + RuntimeVersion string `mapstructure:"runtime-version"` + BaseImage string `mapstructure:"base-image"` + OperatorImage string `mapstructure:"operator-image"` + LocalRepository string `mapstructure:"local-repository"` + BuildStrategy string `mapstructure:"build-strategy"` + BuildTimeout string `mapstructure:"build-timeout"` + MavenRepositories []string `mapstructure:"maven-repositories"` + MavenSettings string `mapstructure:"maven-settings"` + Properties []string `mapstructure:"properties"` + Kits []string `mapstructure:"kits"` + TraitProfile string `mapstructure:"trait-profile"` + HTTPProxySecret string `mapstructure:"http-proxy-secret"` + + registry v1alpha1.IntegrationPlatformRegistrySpec + registryAuth registry.Auth } // nolint: gocyclo -func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { +func (o *installCmdOptions) install(_ *cobra.Command, _ []string) error { var collection *kubernetes.Collection - if o.outputFormat != "" { + if o.OutputFormat != "" { collection = kubernetes.NewCollection() } - if !o.skipClusterSetup { + if !o.SkipClusterSetup { // Let's use a client provider during cluster installation, to eliminate the problem of CRD object caching clientProvider := client.Provider{Get: o.NewCmdClient} @@ -151,7 +161,7 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { } } - if o.clusterSetupOnly { + if o.ClusterSetupOnly { if collection == nil { fmt.Println("Camel K cluster setup completed successfully") } @@ -163,12 +173,12 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { namespace := o.Namespace - if !o.skipOperatorSetup { + if !o.SkipOperatorSetup { cfg := install.OperatorConfiguration{ - CustomImage: o.operatorImage, + CustomImage: o.OperatorImage, Namespace: namespace, - Global: o.global, - ClusterType: o.clusterType, + Global: o.Global, + ClusterType: o.ClusterType, } err = install.OperatorOrCollect(o.Context, c, cfg, collection) if err != nil { @@ -188,7 +198,7 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { } } - platform, err := install.PlatformOrCollect(o.Context, c, o.clusterType, namespace, o.registry, collection) + platform, err := install.PlatformOrCollect(o.Context, c, o.ClusterType, namespace, o.registry, collection) if err != nil { return err } @@ -197,10 +207,10 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { platform.Spec.Build.Registry.Secret = generatedSecretName } - if len(o.properties) > 0 { + if len(o.Properties) > 0 { platform.Spec.Build.Properties = make(map[string]string) - for _, property := range o.properties { + for _, property := range o.Properties { kv := strings.Split(property, "=") if len(kv) == 2 { @@ -208,20 +218,20 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { } } } - if o.localRepository != "" { - platform.Spec.Build.Maven.LocalRepository = o.localRepository + if o.LocalRepository != "" { + platform.Spec.Build.Maven.LocalRepository = o.LocalRepository } - if o.camelVersion != "" { - platform.Spec.Build.CamelVersion = o.camelVersion + if o.CamelVersion != "" { + platform.Spec.Build.CamelVersion = o.CamelVersion } - if o.runtimeVersion != "" { - platform.Spec.Build.RuntimeVersion = o.runtimeVersion + if o.RuntimeVersion != "" { + platform.Spec.Build.RuntimeVersion = o.RuntimeVersion } - if o.baseImage != "" { - platform.Spec.Build.BaseImage = o.baseImage + if o.BaseImage != "" { + platform.Spec.Build.BaseImage = o.BaseImage } - if o.buildStrategy != "" { - switch s := o.buildStrategy; s { + if o.BuildStrategy != "" { + switch s := o.BuildStrategy; s { case v1alpha1.IntegrationPlatformBuildStrategyPod: platform.Spec.Build.BuildStrategy = v1alpha1.IntegrationPlatformBuildStrategyPod case v1alpha1.IntegrationPlatformBuildStrategyRoutine: @@ -230,8 +240,8 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { return fmt.Errorf("unknown build strategy: %s", s) } } - if o.buildTimeout != "" { - d, err := time.ParseDuration(o.buildTimeout) + if o.BuildTimeout != "" { + d, err := time.ParseDuration(o.BuildTimeout) if err != nil { return err } @@ -240,54 +250,52 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { Duration: d, } } - if o.traitProfile != "" { - platform.Spec.Profile = v1alpha1.TraitProfileByName(o.traitProfile) + if o.TraitProfile != "" { + platform.Spec.Profile = v1alpha1.TraitProfileByName(o.TraitProfile) } - if len(o.mavenRepositories) > 0 { - for _, r := range o.mavenRepositories { + if len(o.MavenRepositories) > 0 { + for _, r := range o.MavenRepositories { platform.AddConfiguration("repository", r) } } - if o.mavenSettings != "" { - mavenSettings, err := decodeMavenSettings(o.mavenSettings) + if o.MavenSettings != "" { + mavenSettings, err := decodeMavenSettings(o.MavenSettings) if err != nil { return err } platform.Spec.Build.Maven.Settings = mavenSettings } - if o.httpProxySecret != "" { - platform.Spec.Build.HTTPProxySecret = o.httpProxySecret + if o.HTTPProxySecret != "" { + platform.Spec.Build.HTTPProxySecret = o.HTTPProxySecret } - if o.clusterType != "" { + if o.ClusterType != "" { for _, c := range v1alpha1.AllIntegrationPlatformClusters { - if strings.EqualFold(string(c), o.clusterType) { + if strings.EqualFold(string(c), o.ClusterType) { platform.Spec.Cluster = c } } } - kanikoBuildCacheFlag := cobraCmd.Flags().Lookup("kaniko-build-cache") - - if kanikoBuildCacheFlag.Changed { - platform.Spec.Build.KanikoBuildCache = &o.kanikoBuildCache + if !o.KanikoBuildCache { + platform.Spec.Build.KanikoBuildCache = &o.KanikoBuildCache } - platform.Spec.Resources.Kits = o.kits + platform.Spec.Resources.Kits = o.Kits // Do not create an integration platform in global mode as platforms are expected // to be created in other namespaces - if !o.global { + if !o.Global { err = install.RuntimeObjectOrCollect(o.Context, c, namespace, collection, platform) if err != nil { return err } } - if o.exampleSetup { + if o.ExampleSetup { err = install.ExampleOrCollect(o.Context, c, namespace, collection) if err != nil { return err @@ -295,14 +303,14 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { } if collection == nil { - if o.wait { + if o.Wait { err = o.waitForPlatformReady(platform) if err != nil { return err } } - if o.global { + if o.Global { fmt.Println("Camel K installed in namespace", namespace, "(global mode)") } else { fmt.Println("Camel K installed in namespace", namespace) @@ -319,7 +327,7 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error { func (o *installCmdOptions) printOutput(collection *kubernetes.Collection) error { lst := collection.AsKubernetesList() - switch o.outputFormat { + switch o.OutputFormat { case "yaml": data, err := kubernetes.ToYAML(lst) if err != nil { @@ -333,7 +341,7 @@ func (o *installCmdOptions) printOutput(collection *kubernetes.Collection) error } fmt.Print(string(data)) default: - return errors.New("unknown output format: " + o.outputFormat) + return errors.New("unknown output format: " + o.OutputFormat) } return nil } @@ -360,6 +368,23 @@ func (o *installCmdOptions) waitForPlatformReady(platform *v1alpha1.IntegrationP return watch.HandlePlatformStateChanges(o.Context, platform, handler) } +func (o *installCmdOptions) decode(cmd *cobra.Command, _ []string) error { + path := pathToRoot(cmd) + if err := decodeKey(o, path); err != nil { + return err + } + + o.registry.Address = viper.GetString(path + ".registry") + o.registry.Organization = viper.GetString(path + ".organization") + o.registry.Secret = viper.GetString(path + ".registry-secret") + o.registry.Insecure = viper.GetBool(path + ".registry-insecure") + o.registryAuth.Username = viper.GetString(path + ".registry-auth-username") + o.registryAuth.Password = viper.GetString(path + ".registry-auth-password") + o.registryAuth.Server = viper.GetString(path + ".registry-auth-server") + + return nil +} + func (o *installCmdOptions) validate(_ *cobra.Command, _ []string) error { var result error @@ -369,20 +394,20 @@ func (o *installCmdOptions) validate(_ *cobra.Command, _ []string) error { return err } - for _, kit := range o.kits { + for _, kit := range o.Kits { err := errorIfKitIsNotAvailable(schema, kit) result = multierr.Append(result, err) } - if len(o.mavenRepositories) > 0 && o.mavenSettings != "" { + if len(o.MavenRepositories) > 0 && o.MavenSettings != "" { err := fmt.Errorf("incompatible options combinations: you cannot set both mavenRepository and mavenSettings") result = multierr.Append(result, err) } - if o.traitProfile != "" { - tp := v1alpha1.TraitProfileByName(o.traitProfile) + if o.TraitProfile != "" { + tp := v1alpha1.TraitProfileByName(o.TraitProfile) if tp == v1alpha1.TraitProfile("") { - err := fmt.Errorf("unknown trait profile %s", o.traitProfile) + err := fmt.Errorf("unknown trait profile %s", o.TraitProfile) result = multierr.Append(result, err) } } diff --git a/pkg/cmd/kit_create.go b/pkg/cmd/kit_create.go index f9475ef..3f66c2f 100644 --- a/pkg/cmd/kit_create.go +++ b/pkg/cmd/kit_create.go @@ -22,14 +22,13 @@ import ( "fmt" "strings" + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/apache/camel-k/pkg/trait" - "github.com/apache/camel-k/pkg/util" - - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/spf13/cobra" + k8serrors "k8s.io/apimachinery/pkg/api/errors" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -40,20 +39,21 @@ func newKitCreateCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ - Use: "create <name>", - Short: "Create an Integration Kit", - Long: `Create an Integration Kit.`, - Args: impl.validateArgs, - RunE: impl.run, - } - - cmd.Flags().StringVar(&impl.image, "image", "", "Image used to create the kit") - cmd.Flags().StringSliceVarP(&impl.dependencies, "dependency", "d", nil, "Add a dependency") - cmd.Flags().StringSliceVarP(&impl.properties, "property", "p", nil, "Add a camel property") - cmd.Flags().StringSliceVar(&impl.configmaps, "configmap", nil, "Add a ConfigMap") - cmd.Flags().StringSliceVar(&impl.secrets, "secret", nil, "Add a Secret") - cmd.Flags().StringSliceVar(&impl.repositories, "repository", nil, "Add a maven repository") - cmd.Flags().StringSliceVarP(&impl.traits, "trait", "t", nil, "Configure a trait. E.g. \"-t service.enabled=false\"") + Use: "create <name>", + Short: "Create an Integration Kit", + Long: `Create an Integration Kit.`, + Args: impl.validateArgs, + PreRunE: decode(impl), + RunE: impl.run, + } + + cmd.Flags().String("image", "", "Image used to create the kit") + cmd.Flags().StringArrayP("dependency", "d", nil, "Add a dependency") + cmd.Flags().StringArrayP("property", "p", nil, "Add a camel property") + cmd.Flags().StringArray("configmap", nil, "Add a ConfigMap") + cmd.Flags().StringArray("secret", nil, "Add a Secret") + cmd.Flags().StringArray("repository", nil, "Add a maven repository") + cmd.Flags().StringArrayP("trait", "t", nil, "Configure a trait. E.g. \"-t service.enabled=false\"") // completion support configureKnownCompletions(&cmd) @@ -64,13 +64,13 @@ func newKitCreateCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { type kitCreateCommand struct { *RootCmdOptions - image string - dependencies []string - properties []string - configmaps []string - secrets []string - repositories []string - traits []string + Image string `mapstructure:"image"` + Dependencies []string `mapstructure:"dependencies"` + Properties []string `mapstructure:"properties"` + Configmaps []string `mapstructure:"configmaps"` + Secrets []string `mapstructure:"secrets"` + Repositories []string `mapstructure:"repositories"` + Traits []string `mapstructure:"traits"` } func (command *kitCreateCommand) validateArgs(_ *cobra.Command, args []string) error { @@ -89,7 +89,7 @@ func (command *kitCreateCommand) run(_ *cobra.Command, args []string) error { catalog := trait.NewCatalog(command.Context, c) tp := catalog.ComputeTraitsProperties() - for _, t := range command.traits { + for _, t := range command.Traits { kv := strings.SplitN(t, "=", 2) if !util.StringSliceExists(tp, kv[0]) { @@ -118,25 +118,25 @@ func (command *kitCreateCommand) run(_ *cobra.Command, args []string) error { "camel.apache.org/kit.type": v1alpha1.IntegrationKitTypeUser, } ctx.Spec = v1alpha1.IntegrationKitSpec{ - Dependencies: make([]string, 0, len(command.dependencies)), + Dependencies: make([]string, 0, len(command.Dependencies)), Configuration: make([]v1alpha1.ConfigurationSpec, 0), - Repositories: command.repositories, + Repositories: command.Repositories, } - if command.image != "" { + if command.Image != "" { // - // if the image is set, the kit do not require any build but + // if the Image is set, the kit do not require any build but // is be marked as external as the information about the classpath - // is missing so it cannot be used as base for other kits + // is missing so it cannot be used as base for other Kits // ctx.Labels["camel.apache.org/kit.type"] = v1alpha1.IntegrationKitTypeExternal // - // Set the image to be used by the kit + // Set the Image to be used by the kit // - ctx.Spec.Image = command.image + ctx.Spec.Image = command.Image } - for _, item := range command.dependencies { + for _, item := range command.Dependencies { switch { case strings.HasPrefix(item, "mvn:"): ctx.Spec.Dependencies = append(ctx.Spec.Dependencies, item) @@ -149,25 +149,25 @@ func (command *kitCreateCommand) run(_ *cobra.Command, args []string) error { } } - for _, item := range command.properties { + for _, item := range command.Properties { ctx.Spec.Configuration = append(ctx.Spec.Configuration, v1alpha1.ConfigurationSpec{ Type: "property", Value: item, }) } - for _, item := range command.configmaps { + for _, item := range command.Configmaps { ctx.Spec.Configuration = append(ctx.Spec.Configuration, v1alpha1.ConfigurationSpec{ Type: "configmap", Value: item, }) } - for _, item := range command.secrets { + for _, item := range command.Secrets { ctx.Spec.Configuration = append(ctx.Spec.Configuration, v1alpha1.ConfigurationSpec{ Type: "secret", Value: item, }) } - for _, item := range command.traits { + for _, item := range command.Traits { if err := command.configureTrait(&ctx, item); err != nil { return nil } diff --git a/pkg/cmd/kit_delete.go b/pkg/cmd/kit_delete.go index 0d24172..7902fae 100644 --- a/pkg/cmd/kit_delete.go +++ b/pkg/cmd/kit_delete.go @@ -35,9 +35,10 @@ func newKitDeleteCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ - Use: "delete", - Short: "Delete an Integration Kit", - Long: `Delete an Integration Kit.`, + Use: "delete", + Short: "Delete an Integration Kit", + Long: `Delete an Integration Kit.`, + PreRunE: decode(&impl), RunE: func(_ *cobra.Command, args []string) error { if err := impl.validate(args); err != nil { return err @@ -50,22 +51,22 @@ func newKitDeleteCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { }, } - cmd.Flags().BoolVar(&impl.all, "all", false, "Delete all integration kits") + cmd.Flags().Bool("all", false, "Delete all integration Kits") return &cmd } type kitDeleteCommand struct { *RootCmdOptions - all bool + All bool `mapstructure:"all"` } func (command *kitDeleteCommand) validate(args []string) error { - if command.all && len(args) > 0 { - return errors.New("invalid combination: both all flag and named kits are set") + if command.All && len(args) > 0 { + return errors.New("invalid combination: both all flag and named Kits are set") } - if !command.all && len(args) == 0 { - return errors.New("invalid combination: neither all flag nor named kits are set") + if !command.All && len(args) == 0 { + return errors.New("invalid combination: neither all flag nor named Kits are set") } return nil @@ -79,7 +80,7 @@ func (command *kitDeleteCommand) run(args []string) error { return err } - if command.all { + if command.All { kitList := v1alpha1.NewIntegrationKitList() if err := c.List(command.Context, &kitList, k8sclient.InNamespace(command.Namespace)); err != nil { return err @@ -87,7 +88,7 @@ func (command *kitDeleteCommand) run(args []string) error { names = make([]string, 0, len(kitList.Items)) for _, item := range kitList.Items { - // only include non platform kits + // only include non platform Kits if item.Labels["camel.apache.org/kit.type"] != v1alpha1.IntegrationKitTypePlatform { names = append(names, item.Name) } @@ -129,8 +130,8 @@ func (command *kitDeleteCommand) delete(name string) error { // check that it is not a platform one which is supposed to be "read only" // thus not managed by the end user if ctx.Labels["camel.apache.org/kit.type"] == v1alpha1.IntegrationKitTypePlatform { - // skip platform kits while deleting all kits - if command.all { + // skip platform Kits while deleting all Kits + if command.All { return nil } diff --git a/pkg/cmd/kit_get.go b/pkg/cmd/kit_get.go index 9d7f109..a22af7b 100644 --- a/pkg/cmd/kit_get.go +++ b/pkg/cmd/kit_get.go @@ -34,9 +34,10 @@ func newKitGetCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ - Use: "get", - Short: "Get defined Integration Kit", - Long: `Get defined Integration Kit.`, + Use: "get", + Short: "Get defined Integration Kit", + Long: `Get defined Integration Kit.`, + PreRunE: decode(&impl), RunE: func(cmd *cobra.Command, args []string) error { if err := impl.validate(cmd, args); err != nil { return err @@ -49,18 +50,18 @@ func newKitGetCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { }, } - cmd.Flags().BoolVar(&impl.user, v1alpha1.IntegrationKitTypeUser, true, "Includes user kits") - cmd.Flags().BoolVar(&impl.external, v1alpha1.IntegrationKitTypeExternal, true, "Includes external kits") - cmd.Flags().BoolVar(&impl.platform, v1alpha1.IntegrationKitTypePlatform, true, "Includes platform kits") + cmd.Flags().Bool(v1alpha1.IntegrationKitTypeUser, true, "Includes user Kits") + cmd.Flags().Bool(v1alpha1.IntegrationKitTypeExternal, true, "Includes external Kits") + cmd.Flags().Bool(v1alpha1.IntegrationKitTypePlatform, true, "Includes platform Kits") return &cmd } type kitGetCommand struct { *RootCmdOptions - user bool - external bool - platform bool + User bool `mapstructure:"user"` + External bool `mapstructure:"external"` + Platform bool `mapstructure:"platform"` } func (command *kitGetCommand) validate(cmd *cobra.Command, args []string) error { @@ -81,9 +82,9 @@ func (command *kitGetCommand) run(cmd *cobra.Command) error { fmt.Fprintln(w, "NAME\tPHASE\tTYPE\tIMAGE") for _, ctx := range kitList.Items { t := ctx.Labels["camel.apache.org/kit.type"] - u := command.user && t == v1alpha1.IntegrationKitTypeUser - e := command.external && t == v1alpha1.IntegrationKitTypeExternal - p := command.platform && t == v1alpha1.IntegrationKitTypePlatform + u := command.User && t == v1alpha1.IntegrationKitTypeUser + e := command.External && t == v1alpha1.IntegrationKitTypeExternal + p := command.Platform && t == v1alpha1.IntegrationKitTypePlatform if u || e || p { fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", ctx.Name, string(ctx.Status.Phase), t, ctx.Status.Image) diff --git a/pkg/cmd/log.go b/pkg/cmd/log.go index 151c8aa..852c9c3 100644 --- a/pkg/cmd/log.go +++ b/pkg/cmd/log.go @@ -33,11 +33,12 @@ func newCmdLog(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ - Use: "log integration", - Short: "Print the logs of an integration", - Long: `Print the logs of an integration.`, - Args: options.validate, - RunE: options.run, + Use: "log integration", + Short: "Print the logs of an integration", + Long: `Print the logs of an integration.`, + Args: options.validate, + PreRunE: decode(&options), + RunE: options.run, } // completion support @@ -85,7 +86,7 @@ func (o *logCmdOptions) run(cmd *cobra.Command, args []string) error { return err } - // Let's add a wait point, otherwise the script terminates + // Let's add a Wait point, otherwise the script terminates <-o.Context.Done() return nil diff --git a/pkg/cmd/operator.go b/pkg/cmd/operator.go index a1a70c2..87361fd 100644 --- a/pkg/cmd/operator.go +++ b/pkg/cmd/operator.go @@ -22,25 +22,16 @@ import ( "github.com/spf13/cobra" ) -func newCmdOperator(rootCmdOptions *RootCmdOptions) *cobra.Command { - impl := operatorCmdOptions{ - RootCmdOptions: rootCmdOptions, - } +func newCmdOperator() *cobra.Command { cmd := cobra.Command{ Use: "operator", Short: "Run the Camel K operator", Long: `Run the Camel K operator`, Hidden: true, - Run: impl.run, + Run: func(cmd *cobra.Command, args []string) { + operator.Run() + }, } return &cmd } - -type operatorCmdOptions struct { - *RootCmdOptions -} - -func (o *operatorCmdOptions) run(_ *cobra.Command, _ []string) { - operator.Run() -} diff --git a/pkg/cmd/rebuild.go b/pkg/cmd/rebuild.go index c906b90..d88738a 100644 --- a/pkg/cmd/rebuild.go +++ b/pkg/cmd/rebuild.go @@ -34,10 +34,11 @@ func newCmdRebuild(rootCmdOptions *RootCmdOptions) *cobra.Command { RootCmdOptions: rootCmdOptions, } cmd := cobra.Command{ - Use: "rebuild [integration]", - Short: "Clear the state of integrations to rebuild them", - Long: `Clear the state of one or more integrations causing a rebuild.`, - RunE: options.rebuild, + Use: "rebuild [integration]", + Short: "Clear the state of integrations to rebuild them", + Long: `Clear the state of one or more integrations causing a rebuild.`, + PreRunE: decode(&options), + RunE: options.rebuild, } return &cmd diff --git a/pkg/cmd/reset.go b/pkg/cmd/reset.go index ff9c86d..02bd058 100644 --- a/pkg/cmd/reset.go +++ b/pkg/cmd/reset.go @@ -34,22 +34,23 @@ func newCmdReset(rootCmdOptions *RootCmdOptions) *cobra.Command { RootCmdOptions: rootCmdOptions, } cmd := cobra.Command{ - Use: "reset", - Short: "Reset the Camel K installation", - Long: `Reset the Camel K installation by deleting everything except current platform configuration.`, - Run: options.reset, + Use: "reset", + Short: "Reset the Camel K installation", + Long: `Reset the Camel K installation by deleting everything except current platform configuration.`, + PreRunE: decode(&options), + Run: options.reset, } - cmd.Flags().BoolVar(&options.SkipKits, "skip-kits", false, "Do not delete the integration kits") - cmd.Flags().BoolVar(&options.SkipIntegrations, "skip-integrations", false, "Do not delete the integrations") + cmd.Flags().Bool("skip-kits", false, "Do not delete the integration kits") + cmd.Flags().Bool("skip-integrations", false, "Do not delete the integrations") return &cmd } type resetCmdOptions struct { *RootCmdOptions - SkipKits bool - SkipIntegrations bool + SkipKits bool `mapstructure:"skip-kits"` + SkipIntegrations bool `mapstructure:"skip-integrations"` } func (o *resetCmdOptions) reset(_ *cobra.Command, _ []string) { @@ -73,7 +74,7 @@ func (o *resetCmdOptions) reset(_ *cobra.Command, _ []string) { fmt.Print(err) return } - fmt.Printf("%d integration kits deleted from namespace %s\n", n, o.Namespace) + fmt.Printf("%d integration Kits deleted from namespace %s\n", n, o.Namespace) } if err = o.resetIntegrationPlatform(c); err != nil { @@ -101,7 +102,7 @@ func (o *resetCmdOptions) deleteAllIntegrations(c client.Client) (int, error) { func (o *resetCmdOptions) deleteAllIntegrationKits(c client.Client) (int, error) { list := v1alpha1.NewIntegrationKitList() if err := c.List(o.Context, &list, k8sclient.InNamespace(o.Namespace)); err != nil { - return 0, errors.Wrap(err, fmt.Sprintf("could not retrieve integration kits from namespace %s", o.Namespace)) + return 0, errors.Wrap(err, fmt.Sprintf("could not retrieve integration Kits from namespace %s", o.Namespace)) } for _, i := range list.Items { kit := i diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index c5b9168..bbf7acc 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -20,6 +20,9 @@ package cmd import ( "context" "os" + "strings" + + "github.com/spf13/viper" "github.com/apache/camel-k/pkg/client" "github.com/pkg/errors" @@ -32,10 +35,10 @@ superpowers. // RootCmdOptions -- type RootCmdOptions struct { - Context context.Context - _client client.Client - KubeConfig string - Namespace string + Context context.Context `mapstructure:"-"` + _client client.Client `mapstructure:"-"` + KubeConfig string `mapstructure:"kube-config"` + Namespace string `mapstructure:"namespace"` } // NewKamelCommand -- @@ -65,9 +68,31 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) { cmd.AddCommand(newCmdReset(&options)) cmd.AddCommand(newCmdDescribe(&options)) cmd.AddCommand(newCmdRebuild(&options)) - cmd.AddCommand(newCmdOperator(&options)) + cmd.AddCommand(newCmdOperator()) cmd.AddCommand(newCmdBuilder(&options)) + bindPFlagsHierarchy(&cmd) + + configName := os.Getenv("KAMEL_CONFIG_NAME") + if configName != "" { + configName = "config" + } + + viper.SetConfigName(configName) + viper.AddConfigPath(".kamel") + viper.AddConfigPath("$HOME/.kamel") + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer( + ".", "_", + "-", "_", + )) + + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + return nil, err + } + } + return &cmd, nil } diff --git a/pkg/cmd/operator.go b/pkg/cmd/root_test.go similarity index 59% copy from pkg/cmd/operator.go copy to pkg/cmd/root_test.go index a1a70c2..57f6621 100644 --- a/pkg/cmd/operator.go +++ b/pkg/cmd/root_test.go @@ -17,30 +17,7 @@ limitations under the License. package cmd -import ( - "github.com/apache/camel-k/pkg/cmd/operator" - "github.com/spf13/cobra" -) +import "testing" -func newCmdOperator(rootCmdOptions *RootCmdOptions) *cobra.Command { - impl := operatorCmdOptions{ - RootCmdOptions: rootCmdOptions, - } - cmd := cobra.Command{ - Use: "operator", - Short: "Run the Camel K operator", - Long: `Run the Camel K operator`, - Hidden: true, - Run: impl.run, - } - - return &cmd -} - -type operatorCmdOptions struct { - *RootCmdOptions -} - -func (o *operatorCmdOptions) run(_ *cobra.Command, _ []string) { - operator.Run() +func TestDecodeRoot(t *testing.T) { } diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index 31b411c..4c41081 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -40,8 +40,10 @@ 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" @@ -57,34 +59,34 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command { } cmd := cobra.Command{ - Use: "run [file to run]", - Short: "Run a integration on Kubernetes", - Long: `Deploys and execute a integration pod on Kubernetes.`, - Args: options.validateArgs, - RunE: options.run, - } - - cmd.Flags().StringVar(&options.IntegrationName, "name", "", "The integration name") - cmd.Flags().StringSliceVarP(&options.Dependencies, "dependency", "d", nil, "The integration dependency") - cmd.Flags().BoolVarP(&options.Wait, "wait", "w", false, "Waits for the integration to be running") - cmd.Flags().StringVarP(&options.IntegrationKit, "kit", "k", "", "The kit used to run the integration") - cmd.Flags().StringArrayVarP(&options.Properties, "property", "p", nil, "Add a camel property") - cmd.Flags().StringSliceVar(&options.ConfigMaps, "configmap", nil, "Add a ConfigMap") - cmd.Flags().StringSliceVar(&options.Secrets, "secret", nil, "Add a Secret") - cmd.Flags().StringSliceVar(&options.Repositories, "repository", nil, "Add a maven repository") - cmd.Flags().BoolVar(&options.Logs, "logs", false, "Print integration logs") - cmd.Flags().BoolVar(&options.Sync, "sync", false, "Synchronize the local source file with the cluster, republishing at each change") - cmd.Flags().BoolVar(&options.Dev, "dev", false, "Enable Dev mode (equivalent to \"-w --logs --sync\")") - cmd.Flags().StringVar(&options.Profile, "profile", "", "Trait profile used for deployment") - cmd.Flags().StringSliceVarP(&options.Traits, "trait", "t", nil, "Configure a trait. E.g. \"-t service.enabled=false\"") - cmd.Flags().StringSliceVar(&options.LoggingLevels, "logging-level", nil, "Configure the logging level. "+ - "E.g. \"--logging-level org.apache.camel=DEBUG\"") - cmd.Flags().StringVarP(&options.OutputFormat, "output", "o", "", "Output format. One of: json|yaml") - cmd.Flags().BoolVar(&options.Compression, "compression", false, "Enable store source as a compressed binary blob") - cmd.Flags().StringSliceVar(&options.Resources, "resource", nil, "Add a resource") - cmd.Flags().StringSliceVar(&options.OpenAPIs, "open-api", nil, "Add an OpenAPI v2 spec") - cmd.Flags().StringSliceVarP(&options.Volumes, "volume", "v", nil, "Mount a volume into the integration container. E.g \"-v pvcname:/container/path\"") - cmd.Flags().StringSliceVarP(&options.EnvVars, "env", "e", nil, "Set an environment variable in the integration container. E.g \"-e MY_VAR=my-value\"") + Use: "run [file to run]", + Short: "Run a integration on Kubernetes", + Long: `Deploys and execute a integration pod on Kubernetes.`, + Args: options.validateArgs, + PreRunE: decode(&options), + RunE: options.run, + } + + cmd.Flags().String("name", "", "The integration name") + cmd.Flags().StringArrayP("dependency", "d", nil, "The integration dependency") + cmd.Flags().BoolP("wait", "w", false, "Waits for the integration to be running") + cmd.Flags().StringP("kit", "k", "", "The kit used to run the integration") + cmd.Flags().StringArrayP("property", "p", nil, "Add a camel property") + cmd.Flags().StringArray("configmap", nil, "Add a ConfigMap") + cmd.Flags().StringArray("secret", nil, "Add a Secret") + cmd.Flags().StringArray("maven-repository", nil, "Add a maven repository") + cmd.Flags().Bool("logs", false, "Print integration logs") + cmd.Flags().Bool("sync", false, "Synchronize the local source file with the cluster, republishing at each change") + cmd.Flags().Bool("dev", false, "Enable Dev mode (equivalent to \"-w --logs --sync\")") + cmd.Flags().String("profile", "", "Trait profile used for deployment") + cmd.Flags().StringArrayP("trait", "t", nil, "Configure a trait. E.g. \"-t service.enabled=false\"") + cmd.Flags().StringArray("logging-level", nil, "Configure the logging level. e.g. \"--logging-level org.apache.camel=DEBUG\"") + cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml") + cmd.Flags().Bool("compression", false, "Enable store source as a compressed binary blob") + cmd.Flags().StringArray("resource", nil, "Add a resource") + cmd.Flags().StringArray("open-api", nil, "Add an OpenAPI v2 spec") + 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\"") // completion support configureKnownCompletions(&cmd) @@ -94,26 +96,26 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command { type runCmdOptions struct { *RootCmdOptions - Compression bool - Wait bool - Logs bool - Sync bool - Dev bool - IntegrationKit string - IntegrationName string - Profile string - OutputFormat string - Resources []string - OpenAPIs []string - Dependencies []string - Properties []string - ConfigMaps []string - Secrets []string - Repositories []string - Traits []string - LoggingLevels []string - Volumes []string - EnvVars []string + Compression bool `mapstructure:"compression"` + Wait bool `mapstructure:"wait"` + Logs bool `mapstructure:"logs"` + Sync bool `mapstructure:"sync"` + Dev bool `mapstructure:"dev"` + IntegrationKit string `mapstructure:"kit"` + IntegrationName string `mapstructure:"name"` + Profile string `mapstructure:"profile"` + OutputFormat string `mapstructure:"output"` + Resources []string `mapstructure:"resources"` + OpenAPIs []string `mapstructure:"open-apis"` + Dependencies []string `mapstructure:"dependencies"` + Properties []string `mapstructure:"properties"` + ConfigMaps []string `mapstructure:"configmaps"` + Secrets []string `mapstructure:"secrets"` + Repositories []string `mapstructure:"maven-repositories"` + Traits []string `mapstructure:"traits"` + LoggingLevels []string `mapstructure:"logging-levels"` + Volumes []string `mapstructure:"volumes"` + EnvVars []string `mapstructure:"envs"` } func (o *runCmdOptions) validateArgs(_ *cobra.Command, args []string) error { @@ -232,7 +234,7 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error { } if o.Sync && !o.Logs && !o.Dev { - // Let's add a wait point, otherwise the script terminates + // Let's add a Wait point, otherwise the script terminates <-o.Context.Done() } return nil @@ -241,7 +243,7 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error { func (o *runCmdOptions) waitForIntegrationReady(integration *v1alpha1.Integration) (*v1alpha1.IntegrationPhase, error) { handler := func(i *v1alpha1.Integration) bool { // - // TODO when we add health checks, we should wait until they are passed + // TODO when we add health checks, we should Wait until they are passed // if i.Status.Phase != "" { fmt.Println("integration \""+integration.Name+"\" in phase", i.Status.Phase) diff --git a/pkg/cmd/util.go b/pkg/cmd/util.go index d6cb4ee..a32dcce 100644 --- a/pkg/cmd/util.go +++ b/pkg/cmd/util.go @@ -19,9 +19,21 @@ package cmd import ( "context" + "encoding/csv" + "fmt" + "log" + "reflect" + "strings" + + "github.com/mitchellh/mapstructure" "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/apache/camel-k/pkg/client" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + + p "github.com/gertd/go-pluralize" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -39,3 +51,135 @@ func DeleteIntegration(ctx context.Context, c client.Client, name string, namesp } return c.Delete(ctx, &integration) } + +func bindPFlagsHierarchy(cmd *cobra.Command) error { + for _, c := range cmd.Commands() { + if err := bindPFlags(c); err != nil { + return err + } + + if err := bindPFlagsHierarchy(c); err != nil { + return err + } + } + + return nil +} + +func bindPFlags(cmd *cobra.Command) error { + prefix := pathToRoot(cmd) + pl := p.NewClient() + + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + name := flag.Name + name = strings.ReplaceAll(name, "_", "-") + name = strings.ReplaceAll(name, ".", "-") + + if err := viper.BindPFlag(prefix+"."+name, flag); err != nil { + log.Printf("error binding flag %s with prefix %s to viper: %v", flag.Name, prefix, err) + } + + // this is a little bit of an hack to register plural version of properties + // based on the naming conventions used by the flag type because it is not + // possible to know what is the type of a flag + flagType := strings.ToUpper(flag.Value.Type()) + if strings.Contains(flagType, "SLICE") || strings.Contains(flagType, "ARRAY") { + if err := viper.BindPFlag(prefix+"."+pl.Plural(name), flag); err != nil { + log.Printf("error binding plural flag %s with prefix %s to viper: %v", flag.Name, prefix, err) + } + } + }) + + return nil +} + +func pathToRoot(cmd *cobra.Command) string { + path := cmd.Name() + + for current := cmd.Parent(); current != nil; current = current.Parent() { + name := current.Name() + name = strings.ReplaceAll(name, "_", "-") + name = strings.ReplaceAll(name, ".", "-") + path = name + "." + path + } + + return path +} + +func decodeKey(target interface{}, key string) error { + nodes := strings.Split(key, ".") + settings := viper.AllSettings() + + for _, node := range nodes { + v := settings[node] + + if v == nil { + return nil + } + + if m, ok := v.(map[string]interface{}); ok { + settings = m + } else { + return fmt.Errorf("unable to find node %s", node) + } + } + + c := mapstructure.DecoderConfig{ + Result: target, + WeaklyTypedInput: true, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToIPNetHookFunc(), + mapstructure.StringToTimeDurationHookFunc(), + stringToSliceHookFunc(','), + ), + } + + decoder, err := mapstructure.NewDecoder(&c) + if err != nil { + return err + } + + err = decoder.Decode(settings) + if err != nil { + return err + } + + return nil +} + +func decode(target interface{}) func(*cobra.Command, []string) error { + return func(cmd *cobra.Command, args []string) error { + path := pathToRoot(cmd) + if err := decodeKey(target, path); err != nil { + return err + } + + return nil + } +} + +func stringToSliceHookFunc(comma rune) mapstructure.DecodeHookFunc { + return func( + f reflect.Kind, + t reflect.Kind, + data interface{}) (interface{}, error) { + if f != reflect.String || t != reflect.Slice { + return data, nil + } + + s := data.(string) + s = strings.TrimPrefix(s, "[") + s = strings.TrimSuffix(s, "]") + + if s == "" { + return []string{}, nil + } + + stringReader := strings.NewReader(s) + csvReader := csv.NewReader(stringReader) + csvReader.Comma = comma + csvReader.LazyQuotes = true + + return csvReader.Read() + } +}
