Repository: apache/camel-k
Pull request #2351 (2021-06-03 08:42:54)

feat(cli): add sub command kamel kamelet for get and delete #2312
This adds a feature-rich `kamel kamelet` sub command with `get` and `delete` supports.

```
$ ./kamel kamelet get -h
Get defined Kamelet.

Usage:
  kamel kamelet get [flags]

Flags:
      --action         Show only action Kamelets
      --bundled        Includes bundled Kamelets (default true)
      --group string   Filters Kamelets by group
  -h, --help           help for get
      --read-only      Includes read-only Kamelets (default true)
      --sink           Show only sink Kamelets
      --source         Show only source Kamelets
...
```

```
$ ./kamel kamelet delete -h
Delete a Kamelet.

Usage:
  kamel kamelet delete <name> [flags]

Flags:
      --all    Delete all Kamelets
  -h, --help   help for delete
...
```

`kamel kamelet get` output would look like this:
```
$ ./kamel kamelet get
NAME				PHASE	TYPE	GROUP			BUNDLED	READ ONLY	TITLE
aws-ddb-streams-source		Ready	source	AWS DynamoDB Streams	true	true		AWS DynamoDB Streams Source
aws-kinesis-firehose-sink	Ready	sink	AWS Kinesis Firehose	true	true		AWS Kinesis Firehose Sink
aws-kinesis-sink		Ready	sink	AWS Kinesis		true	true		AWS Kinesis Sink
aws-kinesis-source		Ready	source	AWS Kinesis		true	true		AWS Kinesis Source
aws-lambda-sink			Ready	sink	AWS Lambda		true	true		AWS Lambda Sink
aws-s3-sink			Ready	sink	AWS S3			true	true		AWS S3 Sink
aws-s3-source			Ready	source	AWS S3			true	true		AWS S3 Source
aws-sns-fifo-sink		Ready	sink	AWS SNS			true	true		AWS SNS FIFO Sink
aws-sns-sink			Ready	sink	AWS SNS			true	true		AWS SNS Sink
aws-sqs-batch-sink		Ready	sink	AWS SQS			true	true		AWS SQS Batch Sink
aws-sqs-fifo-sink		Ready	sink	AWS SQS			true	true		AWS SQS FIFO Sink
aws-sqs-sink			Ready	sink	AWS SQS			true	true		AWS SQS Sink
aws-sqs-source			Ready	source	AWS SQS			true	true		AWS SQS Source
...
```


Patch:
diff --git a/docs/modules/ROOT/pages/cli/modeline.adoc b/docs/modules/ROOT/pages/cli/modeline.adoc
index 243c795bc..f4dd43691 100644
--- a/docs/modules/ROOT/pages/cli/modeline.adoc
+++ b/docs/modules/ROOT/pages/cli/modeline.adoc
@@ -67,7 +67,10 @@ The following is a partial list of useful options:
 |Option | Description
 
 |build-property
-|Add a build time property or properties file (syntax, my-key=my-value \| file:/path/to/my-conf.properties)"
+|Add a build time property or properties file (syntax: _[my-key=my-value\|file:/path/to/my-conf.properties]_
+
+|config
+|Add runtime configuration from a Configmap, a Secret or a file (syntax: _[configmap\|secret\|file]:name)_
 
 |dependency
 |An external library that should be included, e.g. for Maven dependencies `dependency=mvn:org.my:app:1.0`
@@ -88,7 +91,7 @@ The following is a partial list of useful options:
 |Trait profile used for deployment
 
 |property
-|Add a runtime property or properties file (syntax, my-key=my-value \| file:/path/to/my-conf.properties)
+|Add a runtime property or properties file (syntax: _[my-key=my-value\|file:/path/to/my-conf.properties]_)
 
 |resource
 |Add a resource
diff --git a/examples/modeline/modeline-config-file-route.groovy b/examples/modeline/modeline-config-file-route.groovy
new file mode 100644
index 000000000..850f7ada1
--- /dev/null
+++ b/examples/modeline/modeline-config-file-route.groovy
@@ -0,0 +1,30 @@
+// camel-k: language=groovy
+/*
+ * 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.
+ */
+
+//
+// To run this integrations use:
+// kamel run modeline-config-file-route.groovy --dev
+//
+
+// camel-k: config=file:resources-data.txt
+
+from('timer:resources')
+    .routeId('resources')
+    .setBody()
+        .simple("resource:classpath:resources-data.txt")
+    .log('resource file content is: ${body}')
diff --git a/examples/modeline/modeline-secret-route.groovy b/examples/modeline/modeline-secret-route.groovy
new file mode 100644
index 000000000..0107b289f
--- /dev/null
+++ b/examples/modeline/modeline-secret-route.groovy
@@ -0,0 +1,32 @@
+// camel-k: language=groovy
+/*
+ * 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.
+ */
+
+//
+// To run this integrations use:
+//
+// kubectl create secret generic my-sec --from-literal=my-secret-key="very top secret"
+// kamel run secret-route.groovy --dev
+//
+
+// camel-k: config=secret:my-sec
+
+from('timer:secret')
+    .routeId('secret')
+    .setBody()
+        .simple("resource:classpath:my-secret-key")
+    .log('secret content is: ${body}')
diff --git a/examples/modeline/resources-data.txt b/examples/modeline/resources-data.txt
new file mode 100644
index 000000000..dff79473e
--- /dev/null
+++ b/examples/modeline/resources-data.txt
@@ -0,0 +1,1 @@
+the file body
\ No newline at end of file
diff --git a/examples/user-config/config-file-route.groovy b/examples/user-config/config-file-route.groovy
new file mode 100644
index 000000000..53b347f6d
--- /dev/null
+++ b/examples/user-config/config-file-route.groovy
@@ -0,0 +1,28 @@
+// camel-k: language=groovy
+/*
+ * 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.
+ */
+
+//
+// To run this integrations use:
+// kamel run --config file:resources-data.txt resources-route.groovy --dev
+//
+
+from('timer:resources')
+    .routeId('resources')
+    .setBody()
+        .simple("resource:classpath:resources-data.txt")
+    .log('resource file content is: ${body}')
diff --git a/examples/user-config/configmap-route.groovy b/examples/user-config/configmap-route.groovy
index 5a61c56b0..948cb1504 100644
--- a/examples/user-config/configmap-route.groovy
+++ b/examples/user-config/configmap-route.groovy
@@ -20,7 +20,7 @@
 // To run this integrations use:
 //
 // kubectl create configmap my-cm --from-literal=my-configmap-key="configmap content"
-// kamel run --configmap my-cm configmap-route.groovy --dev
+// kamel run --config configmap:my-cm configmap-route.groovy --dev
 //
 
 from('timer:configmap')
diff --git a/examples/user-config/resources-route.groovy b/examples/user-config/resources-route.groovy
index 6f572853c..108584056 100644
--- a/examples/user-config/resources-route.groovy
+++ b/examples/user-config/resources-route.groovy
@@ -21,8 +21,5 @@
 // kamel run --resource resources-data.txt resources-route.groovy --dev
 //
 
-from('timer:resources')
-    .routeId('resources')
-    .setBody()
-        .simple("resource:classpath:resources-data.txt")
+from('file:/etc/camel/resources/?fileName=resources-data.txt&noop=true&idempotent=false')
     .log('resource file content is: ${body}')
diff --git a/examples/user-config/secret-route.groovy b/examples/user-config/secret-route.groovy
index 49966aed6..7027b8442 100644
--- a/examples/user-config/secret-route.groovy
+++ b/examples/user-config/secret-route.groovy
@@ -20,7 +20,7 @@
 // To run this integrations use:
 //
 // kubectl create secret generic my-sec --from-literal=my-secret-key="very top secret"
-// kamel run --secret my-sec secret-route.groovy --dev
+// kamel run --config secret:my-sec secret-route.groovy --dev
 //
 
 from('timer:secret')
diff --git a/pkg/apis/camel/v1alpha1/kamelet_types.go b/pkg/apis/camel/v1alpha1/kamelet_types.go
index fd10387f5..5a6def744 100644
--- a/pkg/apis/camel/v1alpha1/kamelet_types.go
+++ b/pkg/apis/camel/v1alpha1/kamelet_types.go
@@ -25,7 +25,15 @@ import (
 )
 
 const (
-	AnnotationIcon = "camel.apache.org/kamelet.icon"
+	AnnotationIcon       = "camel.apache.org/kamelet.icon"
+	KameletBundledLabel  = "camel.apache.org/kamelet.bundled"
+	KameletReadOnlyLabel = "camel.apache.org/kamelet.readonly"
+	KameletTypeLabel     = "camel.apache.org/kamelet.type"
+	KameletGroupLabel    = "camel.apache.org/kamelet.group"
+
+	KameletTypeSink   = "sink"
+	KameletTypeSource = "source"
+	KameletTypeAction = "action"
 )
 
 var (
diff --git a/pkg/cmd/kamelet.go b/pkg/cmd/kamelet.go
new file mode 100644
index 000000000..89a09062c
--- /dev/null
+++ b/pkg/cmd/kamelet.go
@@ -0,0 +1,35 @@
+/*
+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 (
+	"github.com/spf13/cobra"
+)
+
+func newCmdKamelet(rootCmdOptions *RootCmdOptions) *cobra.Command {
+	cmd := cobra.Command{
+		Use:   "kamelet",
+		Short: "Configure a Kamelet",
+		Long:  `Configure a Kamelet.`,
+	}
+
+	cmd.AddCommand(cmdOnly(newKameletGetCmd(rootCmdOptions)))
+	cmd.AddCommand(cmdOnly(newKameletDeleteCmd(rootCmdOptions)))
+
+	return &cmd
+}
diff --git a/pkg/cmd/kamelet_delete.go b/pkg/cmd/kamelet_delete.go
new file mode 100644
index 000000000..7f6ed67f6
--- /dev/null
+++ b/pkg/cmd/kamelet_delete.go
@@ -0,0 +1,146 @@
+/*
+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"
+
+	"github.com/spf13/cobra"
+	k8errors "k8s.io/apimachinery/pkg/api/errors"
+
+	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
+
+func newKameletDeleteCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kameletDeleteCommandOptions) {
+	options := kameletDeleteCommandOptions{
+		RootCmdOptions: rootCmdOptions,
+	}
+
+	cmd := cobra.Command{
+		Use:     "delete <name>",
+		Short:   "Delete a Kamelet",
+		Long:    `Delete a Kamelet.`,
+		PreRunE: decode(&options),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if err := options.validate(args); err != nil {
+				return err
+			}
+			if err := options.run(args); err != nil {
+				fmt.Println(err.Error())
+			}
+
+			return nil
+		},
+	}
+
+	cmd.Flags().Bool("all", false, "Delete all Kamelets")
+
+	return &cmd, &options
+}
+
+type kameletDeleteCommandOptions struct {
+	*RootCmdOptions
+	All bool `mapstructure:"all"`
+}
+
+func (command *kameletDeleteCommandOptions) validate(args []string) error {
+	if command.All && len(args) > 0 {
+		return errors.New("invalid combination: both all flag and named kamelets are set")
+	}
+	if !command.All && len(args) == 0 {
+		return errors.New("invalid combination: neither all flag nor named kamelets are set")
+	}
+
+	return nil
+}
+
+func (command *kameletDeleteCommandOptions) run(args []string) error {
+	names := args
+
+	c, err := command.GetCmdClient()
+	if err != nil {
+		return err
+	}
+
+	if command.All {
+		klList := v1alpha1.NewKameletList()
+		if err := c.List(command.Context, &klList, k8sclient.InNamespace(command.Namespace)); err != nil {
+			return err
+		}
+		names = make([]string, 0, len(klList.Items))
+		for _, kl := range klList.Items {
+			// only include non-bundled, non-readonly kamelets
+			if kl.Labels[v1alpha1.KameletBundledLabel] != "true" && kl.Labels[v1alpha1.KameletReadOnlyLabel] != "true" {
+				names = append(names, kl.Name)
+			}
+		}
+	}
+
+	for _, name := range names {
+		if err := command.delete(name); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (command *kameletDeleteCommandOptions) delete(name string) error {
+	c, err := command.GetCmdClient()
+	if err != nil {
+		return err
+	}
+
+	kl := v1alpha1.NewKamelet(command.Namespace, name)
+	key := k8sclient.ObjectKey{
+		Namespace: command.Namespace,
+		Name:      name,
+	}
+	err = c.Get(command.Context, key, &kl)
+	if err != nil {
+		if k8errors.IsNotFound(err) {
+			return fmt.Errorf("no kamelet found with name \"%s\"", name)
+		} else {
+			return err
+		}
+	}
+
+	// check that it is not a bundled nor read-only one which is supposed to belong to platform
+	// thus not managed by the end user
+	if kl.Labels[v1alpha1.KameletBundledLabel] == "true" || kl.Labels[v1alpha1.KameletReadOnlyLabel] == "true" {
+		// skip platform Kamelets while deleting all Kamelets
+		if command.All {
+			return nil
+		}
+		return fmt.Errorf("kamelet \"%s\" is not editable", name)
+	}
+
+	err = c.Delete(command.Context, &kl)
+	if err != nil {
+		if k8errors.IsNotFound(err) {
+			return fmt.Errorf("no kamelet found with name \"%s\"", name)
+		} else {
+			return fmt.Errorf("error deleting kamelet \"%s\", %s", name, err)
+		}
+	}
+	fmt.Printf("kamelet \"%s\" has been deleted\n", name)
+	return nil
+}
diff --git a/pkg/cmd/kamelet_get.go b/pkg/cmd/kamelet_get.go
new file mode 100644
index 000000000..b9e5cd40f
--- /dev/null
+++ b/pkg/cmd/kamelet_get.go
@@ -0,0 +1,139 @@
+/*
+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"
+	"strings"
+	"text/tabwriter"
+
+	"github.com/spf13/cobra"
+
+	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
+
+func newKameletGetCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kameletGetCommandOptions) {
+	options := kameletGetCommandOptions{
+		RootCmdOptions: rootCmdOptions,
+	}
+
+	cmd := cobra.Command{
+		Use:     "get",
+		Short:   "Get defined Kamelet",
+		Long:    `Get defined Kamelet.`,
+		PreRunE: decode(&options),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if err := options.validate(cmd, args); err != nil {
+				return err
+			}
+			if err := options.run(cmd); err != nil {
+				fmt.Println(err.Error())
+			}
+
+			return nil
+		},
+	}
+
+	cmd.Flags().Bool("sink", false, "Show only sink Kamelets")
+	cmd.Flags().Bool("source", false, "Show only source Kamelets")
+	cmd.Flags().Bool("action", false, "Show only action Kamelets")
+	cmd.Flags().String("group", "", "Filters Kamelets by group")
+	cmd.Flags().Bool("bundled", true, "Includes bundled Kamelets")
+	cmd.Flags().Bool("read-only", true, "Includes read-only Kamelets")
+
+	return &cmd, &options
+}
+
+type kameletGetCommandOptions struct {
+	*RootCmdOptions
+	Sink     bool   `mapstructure:"sink"`
+	Source   bool   `mapstructure:"source"`
+	Action   bool   `mapstructure:"action"`
+	Group    string `mapstructure:"group"`
+	Bundled  bool   `mapstructure:"bundled"`
+	ReadOnly bool   `mapstructure:"read-only"`
+}
+
+func (command *kameletGetCommandOptions) validate(cmd *cobra.Command, args []string) error {
+	count := 0
+	for _, b := range []bool{command.Sink, command.Source, command.Action} {
+		if b {
+			count++
+		}
+	}
+
+	if count > 1 {
+		return errors.New("invalid combination: flags --sink, --source, and --action are mutually exclusive")
+	}
+	return nil
+}
+
+func (command *kameletGetCommandOptions) run(cmd *cobra.Command) error {
+	c, err := command.GetCmdClient()
+	if err != nil {
+		return err
+	}
+
+	klList := v1alpha1.NewKameletList()
+	if err := c.List(command.Context, &klList, k8sclient.InNamespace(command.Namespace)); err != nil {
+		return err
+	}
+
+	w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 8, 1, '\t', 0)
+	fmt.Fprintln(w, "NAME\tPHASE\tTYPE\tGROUP\tBUNDLED\tREAD ONLY\tTITLE")
+	for _, kl := range klList.Items {
+		klType := kl.Labels[v1alpha1.KameletTypeLabel]
+		group := kl.Annotations[v1alpha1.KameletGroupLabel]
+		bundled := kl.Labels[v1alpha1.KameletBundledLabel]
+		readOnly := kl.Labels[v1alpha1.KameletReadOnlyLabel]
+
+		if command.Sink && klType != v1alpha1.KameletTypeSink {
+			continue
+		}
+		if command.Source && klType != v1alpha1.KameletTypeSource {
+			continue
+		}
+		if command.Action && klType != v1alpha1.KameletTypeAction {
+			continue
+		}
+		if command.Group != "" && !strings.EqualFold(command.Group, group) {
+			continue
+		}
+		if !command.Bundled && bundled == "true" {
+			continue
+		}
+		if !command.ReadOnly && readOnly == "true" {
+			continue
+		}
+
+		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
+			kl.Name,
+			string(kl.Status.Phase),
+			klType,
+			group,
+			bundled,
+			readOnly,
+			kl.Spec.Definition.Title)
+	}
+	w.Flush()
+
+	return nil
+}
diff --git a/pkg/cmd/kit_delete.go b/pkg/cmd/kit_delete.go
index 53dec6121..0a0428db1 100644
--- a/pkg/cmd/kit_delete.go
+++ b/pkg/cmd/kit_delete.go
@@ -35,7 +35,7 @@ func newKitDeleteCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kitDelete
 	}
 
 	cmd := cobra.Command{
-		Use:     "delete",
+		Use:     "delete <name>",
 		Short:   "Delete an Integration Kit",
 		Long:    `Delete an Integration Kit.`,
 		PreRunE: decode(&options),
diff --git a/pkg/cmd/modeline.go b/pkg/cmd/modeline.go
index d6fe533a8..0f540f85a 100644
--- a/pkg/cmd/modeline.go
+++ b/pkg/cmd/modeline.go
@@ -53,10 +53,17 @@ var (
 	// file options must be considered relative to the source files they belong to
 	fileOptions = map[string]bool{
 		"resource":      true,
-		"config":        true,
+		"kube-config":   true,
 		"open-api":      true,
 		"property-file": true,
 	}
+
+	// file format options are those options that admit multiple values, not only files (ie, key=value|configmap|secret|file syntax)
+	fileFormatOptions = map[string]bool{
+		"config":         true,
+		"property":       true,
+		"build-property": true,
+	}
 )
 
 // NewKamelWithModelineCommand ---
@@ -227,12 +234,34 @@ func extractModelineOptionsFromSource(resolvedSource Source) ([]modeline.Option,
 				o.Value = full
 				ops[i] = o
 			}
+		} else if fileFormatOptions[o.Name] && resolvedSource.Local {
+			baseDir := filepath.Dir(resolvedSource.Origin)
+			refPath := getRefPathOrProperty(o.Value)
+			if !filepath.IsAbs(refPath) {
+				full := getFullPathOrProperty(o.Value, path.Join(baseDir, refPath))
+				o.Value = full
+				ops[i] = o
+			}
 		}
 	}
 
 	return ops, nil
 }
 
+func getRefPathOrProperty(pathOrProperty string) string {
+	if strings.HasPrefix(pathOrProperty, "file:") {
+		return strings.Replace(pathOrProperty, "file:", "", 1)
+	}
+	return pathOrProperty
+}
+
+func getFullPathOrProperty(pathOrProperty string, fullPath string) string {
+	if strings.HasPrefix(pathOrProperty, "file:") {
+		return fmt.Sprintf("file:%s", fullPath)
+	}
+	return pathOrProperty
+}
+
 func expandModelineEnvVarOptions(ops []modeline.Option) ([]modeline.Option, error) {
 	// List of additional command line options with expanded values for variables
 	// marked as immediate environment variables.
diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go
index 06a775b00..b98228692 100644
--- a/pkg/cmd/root.go
+++ b/pkg/cmd/root.go
@@ -84,7 +84,7 @@ func kamelPreAddCommandInit(options *RootCmdOptions) *cobra.Command {
 		SilenceUsage:           true,
 	}
 
-	cmd.PersistentFlags().StringVar(&options.KubeConfig, "config", os.Getenv("KUBECONFIG"), "Path to the config file to use for CLI requests")
+	cmd.PersistentFlags().StringVar(&options.KubeConfig, "kube-config", os.Getenv("KUBECONFIG"), "Path to the kube config file to use for CLI requests")
 	cmd.PersistentFlags().StringVarP(&options.Namespace, "namespace", "n", "", "Namespace to use for all operations")
 
 	return &cmd
@@ -148,6 +148,7 @@ func addKamelSubcommands(cmd *cobra.Command, options *RootCmdOptions) {
 	cmd.AddCommand(cmdOnly(newCmdDump(options)))
 	cmd.AddCommand(newCmdLocal(options))
 	cmd.AddCommand(cmdOnly(newCmdBind(options)))
+	cmd.AddCommand(newCmdKamelet(options))
 }
 
 func addHelpSubCommands(cmd *cobra.Command, options *RootCmdOptions) error {
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 78129a468..b097cf188 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -78,10 +78,11 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions)
 	cmd.Flags().StringArrayP("dependency", "d", nil, "A dependency that should be included, e.g., \"-d camel-mail\" for a Camel component, or \"-d mvn:org.my:app:1.0\" for a Maven dependency")
 	cmd.Flags().BoolP("wait", "w", false, "Wait 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 runtime property or properties file (syntax, my-key=my-value | file:/path/to/my-conf.properties)")
-	cmd.Flags().StringArray("build-property", nil, "Add a build time property or properties file (syntax, my-key=my-value | file:/path/to/my-conf.properties)")
-	cmd.Flags().StringArray("configmap", nil, "Add a ConfigMap")
-	cmd.Flags().StringArray("secret", nil, "Add a Secret")
+	cmd.Flags().StringArrayP("property", "p", nil, "Add a runtime property or properties file (syntax: [my-key=my-value|file:/path/to/my-conf.properties])")
+	cmd.Flags().StringArray("build-property", nil, "Add a build time property or properties file (syntax: [my-key=my-value|file:/path/to/my-conf.properties])")
+	cmd.Flags().StringArray("config", nil, "Add runtime configuration from a Configmap, a Secret or a file (syntax: [configmap|secret|file]:name)")
+	cmd.Flags().StringArray("configmap", nil, "[Deprecated] Add a ConfigMap")
+	cmd.Flags().StringArray("secret", nil, "[Deprecated] 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")
@@ -129,6 +130,7 @@ type runCmdOptions struct {
 	Dependencies    []string `mapstructure:"dependencies" yaml:",omitempty"`
 	Properties      []string `mapstructure:"properties" yaml:",omitempty"`
 	BuildProperties []string `mapstructure:"build-properties" yaml:",omitempty"`
+	Configs         []string `mapstructure:"configs" yaml:",omitempty"`
 	ConfigMaps      []string `mapstructure:"configmaps" yaml:",omitempty"`
 	Secrets         []string `mapstructure:"secrets" yaml:",omitempty"`
 	Repositories    []string `mapstructure:"maven-repositories" yaml:",omitempty"`
@@ -227,8 +229,15 @@ func (o *runCmdOptions) validate() error {
 
 	// Deprecation warning
 	if o.PropertyFiles != nil {
-		fmt.Println("Warn: --property-files has been deprecated. You should use --property file:/path/to/conf.properties instead.")
+		fmt.Println("Warn: --property-file has been deprecated. You should use --property file:/path/to/conf.properties instead.")
 	}
+	if o.ConfigMaps != nil {
+		fmt.Println("Warn: --configmap has been deprecated. You should use --config configmap:my-configmap instead.")
+	}
+	if o.Secrets != nil {
+		fmt.Println("Warn: --secret has been deprecated. You should use --config secret:my-secret instead.")
+	}
+
 	err := validatePropertyFiles(o.PropertyFiles)
 	if err != nil {
 		return err
@@ -549,19 +558,7 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
 	}
 
 	for _, resource := range o.OpenAPIs {
-		data, _, compressed, err := loadTextContent(resource, o.Compression)
-		if err != nil {
-			return nil, err
-		}
-
-		integration.Spec.AddResources(v1.ResourceSpec{
-			DataSpec: v1.DataSpec{
-				Name:        path.Base(resource),
-				Content:     data,
-				Compression: compressed,
-			},
-			Type: v1.ResourceTypeOpenAPI,
-		})
+		addResource(resource, &integration.Spec, o.Compression, v1.ResourceTypeOpenAPI)
 	}
 
 	for _, item := range o.Dependencies {
@@ -598,6 +595,15 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
 	for _, item := range o.LoggingLevels {
 		integration.Spec.AddConfiguration("property", "logging.level."+item)
 	}
+	for _, item := range o.Configs {
+		if config, parseErr := ParseConfigOption(item); parseErr == nil {
+			if applyConfigOptionErr := ApplyConfigOption(config, &integration.Spec, c, namespace, o.Compression); applyConfigOptionErr != nil {
+				return nil, applyConfigOptionErr
+			}
+		} else {
+			return nil, parseErr
+		}
+	}
 	for _, item := range o.ConfigMaps {
 		integration.Spec.AddConfiguration("configmap", item)
 	}
@@ -681,6 +687,23 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
 	return &integration, nil
 }
 
+func addResource(resourceLocation string, integrationSpec *v1.IntegrationSpec, enableCompression bool, resourceType v1.ResourceType) error {
+	if data, _, compressed, err := loadTextContent(resourceLocation, enableCompression); err == nil {
+		integrationSpec.AddResources(v1.ResourceSpec{
+			DataSpec: v1.DataSpec{
+				Name:        path.Base(resourceLocation),
+				Content:     data,
+				Compression: compressed,
+			},
+			Type: resourceType,
+		})
+	} else {
+		return err
+	}
+
+	return nil
+}
+
 // The function parse the value and if it is a file (file:/path/), it will parse as property file
 // otherwise return a single property built from the item passed as `key=value`
 func extractProperties(value string) (*properties.Properties, error) {
diff --git a/pkg/cmd/run_help.go b/pkg/cmd/run_help.go
new file mode 100644
index 000000000..2f78b7972
--- /dev/null
+++ b/pkg/cmd/run_help.go
@@ -0,0 +1,118 @@
+/*
+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 (
+	"context"
+	"fmt"
+	"path"
+	"regexp"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/client"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+)
+
+// RunConfigOption represents a config option
+type RunConfigOption struct {
+	ConfigType configOptionType
+	Value      string
+}
+
+type configOptionType string
+
+const (
+	// ConfigOptionTypeConfigmap --
+	ConfigOptionTypeConfigmap configOptionType = "configmap"
+	// ConfigOptionTypeSecret --
+	ConfigOptionTypeSecret configOptionType = "secret"
+	// ConfigOptionTypeFile --
+	ConfigOptionTypeFile configOptionType = "file"
+)
+
+var validConfigRegexp = regexp.MustCompile(`^(configmap|secret|file)\:([\w\.\-\_\:\/]+)$`)
+
+func newRunConfigOption(configType configOptionType, value string) *RunConfigOption {
+	return &RunConfigOption{
+		ConfigType: configType,
+		Value:      value,
+	}
+}
+
+// ParseConfigOption will parse and return a runConfigOption
+func ParseConfigOption(item string) (*RunConfigOption, error) {
+	if !validConfigRegexp.MatchString(item) {
+		return nil, fmt.Errorf("could not match configuration %s, must match %v regular expression", item, validConfigRegexp)
+	}
+	// Parse the regexp groups
+	groups := validConfigRegexp.FindStringSubmatch(item)
+	var cot configOptionType
+	switch groups[1] {
+	case "configmap":
+		cot = ConfigOptionTypeConfigmap
+	case "secret":
+		cot = ConfigOptionTypeSecret
+	case "file":
+		cot = ConfigOptionTypeFile
+	default:
+		// Should never reach this
+		return nil, fmt.Errorf("invalid config option type %s", groups[1])
+	}
+	return newRunConfigOption(cot, groups[2]), nil
+}
+
+// ApplyConfigOption will set the proper option behavior to the IntegrationSpec
+func ApplyConfigOption(config *RunConfigOption, integrationSpec *v1.IntegrationSpec, c client.Client, namespace string, enableCompression bool) error {
+	switch config.ConfigType {
+	case ConfigOptionTypeConfigmap:
+		cm := kubernetes.LookupConfigmap(context.Background(), c, namespace, config.Value)
+		if cm == nil {
+			fmt.Printf("Warn: %s Configmap not found in %s namespace, make sure to provide it before the Integration can run\n",
+				config.Value, namespace)
+		} else if cm.BinaryData != nil {
+			return fmt.Errorf("you cannot provide a binary config, use a text file instead")
+		}
+		integrationSpec.AddConfiguration(string(config.ConfigType), config.Value)
+	case ConfigOptionTypeSecret:
+		secret := kubernetes.LookupSecret(context.Background(), c, namespace, config.Value)
+		if secret == nil {
+			fmt.Printf("Warn: %s Secret not found in %s namespace, make sure to provide it before the Integration can run\n",
+				config.Value, namespace)
+		}
+		integrationSpec.AddConfiguration(string(config.ConfigType), config.Value)
+	case ConfigOptionTypeFile:
+		// Don't allow a binary non compressed resource
+		rawData, contentType, err := loadRawContent(config.Value)
+		if err != nil {
+			return err
+		}
+		if !enableCompression && isBinary(contentType) {
+			return fmt.Errorf("you cannot provide a binary config, use a text file or check --resource flag instead")
+		}
+		resourceSpec, err := binaryOrTextResource(path.Base(config.Value), rawData, contentType, enableCompression)
+		if err != nil {
+			return err
+		}
+		integrationSpec.AddResources(resourceSpec)
+	default:
+		// Should never reach this
+		return fmt.Errorf("invalid config option type %s", config.ConfigType)
+	}
+
+	return nil
+}
diff --git a/pkg/cmd/util_content.go b/pkg/cmd/util_content.go
index c02fda2f9..62cda96c4 100644
--- a/pkg/cmd/util_content.go
+++ b/pkg/cmd/util_content.go
@@ -51,7 +51,7 @@ func loadRawContent(source string) ([]byte, string, error) {
 		case "https":
 			content, err = loadContentHTTP(u)
 		default:
-			return nil, "", fmt.Errorf("unsupported scheme %s", u.Scheme)
+			return nil, "", fmt.Errorf("missing file or unsupported scheme %s", u.Scheme)
 		}
 	}
 
diff --git a/pkg/install/kamelets.go b/pkg/install/kamelets.go
index 6d92b9db6..939c6a90b 100644
--- a/pkg/install/kamelets.go
+++ b/pkg/install/kamelets.go
@@ -37,8 +37,6 @@ import (
 
 const kameletDirEnv = "KAMELET_CATALOG_DIR"
 const defaultKameletDir = "/kamelets/"
-const kameletBundledLabel = "camel.apache.org/kamelet.bundled"
-const kameletReadOnlyLabel = "camel.apache.org/kamelet.readonly"
 
 // KameletCatalog installs the bundled KameletCatalog into one namespace
 func KameletCatalog(ctx context.Context, c client.Client, namespace string) error {
@@ -93,8 +91,8 @@ func KameletCatalog(ctx context.Context, c client.Client, namespace string) erro
 				if k.GetLabels() == nil {
 					k.SetLabels(make(map[string]string))
 				}
-				k.GetLabels()[kameletBundledLabel] = "true"
-				k.GetLabels()[kameletReadOnlyLabel] = "true"
+				k.GetLabels()[v1alpha1.KameletBundledLabel] = "true"
+				k.GetLabels()[v1alpha1.KameletReadOnlyLabel] = "true"
 
 				err := ObjectOrCollect(ctx, c, namespace, nil, true, k)
 
diff --git a/pkg/util/kubernetes/lookup.go b/pkg/util/kubernetes/lookup.go
new file mode 100644
index 000000000..92b63def8
--- /dev/null
+++ b/pkg/util/kubernetes/lookup.go
@@ -0,0 +1,76 @@
+/*
+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 kubernetes
+
+import (
+	"context"
+
+	"github.com/apache/camel-k/pkg/client"
+	corev1 "k8s.io/api/core/v1"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// LookupConfigmap will look for any k8s Configmap with a given name in a given namespace
+func LookupConfigmap(ctx context.Context, c client.Client, ns string, name string) *corev1.ConfigMap {
+	cm := corev1.ConfigMap{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "ConfigMap",
+			APIVersion: corev1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: ns,
+			Name:      name,
+		},
+	}
+	key := ctrl.ObjectKey{
+		Namespace: ns,
+		Name:      name,
+	}
+	if err := c.Get(ctx, key, &cm); err != nil && k8serrors.IsNotFound(err) {
+		return nil
+	} else if err != nil {
+		return nil
+	}
+	return &cm
+}
+
+// LookupSecret will look for any k8s Secret with a given name in a given namespace
+func LookupSecret(ctx context.Context, c client.Client, ns string, name string) *corev1.Secret {
+	secret := corev1.Secret{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Secret",
+			APIVersion: corev1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: ns,
+			Name:      name,
+		},
+	}
+	key := ctrl.ObjectKey{
+		Namespace: ns,
+		Name:      name,
+	}
+	if err := c.Get(ctx, key, &secret); err != nil && k8serrors.IsNotFound(err) {
+		return nil
+	} else if err != nil {
+		return nil
+	}
+	return &secret
+}


Test patch:
diff --git a/e2e/common/cli/offline_commands_test.go b/e2e/common/cli/offline_commands_test.go
index 25028bfb7..834fc4104 100644
--- a/e2e/common/cli/offline_commands_test.go
+++ b/e2e/common/cli/offline_commands_test.go
@@ -31,11 +31,11 @@ import (
 )
 
 func TestKamelVersionWorksOffline(t *testing.T) {
-	assert.Nil(t, Kamel("version", "--config", "non-existent-kubeconfig-file").Execute())
+	assert.Nil(t, Kamel("version", "--kube-config", "non-existent-kubeconfig-file").Execute())
 }
 
 func TestKamelHelpTraitWorksOffline(t *testing.T) {
-	traitCmd := Kamel("help", "trait", "--all", "--config", "non-existent-kubeconfig-file")
+	traitCmd := Kamel("help", "trait", "--all", "--kube-config", "non-existent-kubeconfig-file")
 	traitCmd.SetOut(ioutil.Discard)
 	assert.Nil(t, traitCmd.Execute())
 }
@@ -47,9 +47,9 @@ func TestKamelHelpOptionWorksOffline(t *testing.T) {
 }
 
 func TestKamelCompletionWorksOffline(t *testing.T) {
-	bashCmd := Kamel("completion", "bash", "--config", "non-existent-kubeconfig-file")
+	bashCmd := Kamel("completion", "bash", "--kube-config", "non-existent-kubeconfig-file")
 	bashCmd.SetOut(ioutil.Discard)
-	zshCmd := Kamel("completion", "zsh", "--config", "non-existent-kubeconfig-file")
+	zshCmd := Kamel("completion", "zsh", "--kube-config", "non-existent-kubeconfig-file")
 	zshCmd.SetOut(ioutil.Discard)
 	assert.Nil(t, bashCmd.Execute())
 	assert.Nil(t, zshCmd.Execute())
diff --git a/e2e/common/config/config_test.go b/e2e/common/config/config_test.go
index 8512a468f..2cb79abec 100644
--- a/e2e/common/config/config_test.go
+++ b/e2e/common/config/config_test.go
@@ -65,7 +65,7 @@ func TestRunConfigExamples(t *testing.T) {
 			cmData["my-configmap-key"] = "my-configmap-content"
 			NewPlainTextConfigmap(ns, "my-cm", cmData)
 
-			Expect(Kamel("run", "-n", ns, "./files/configmap-route.groovy", "--configmap", "my-cm").Execute()).To(Succeed())
+			Expect(Kamel("run", "-n", ns, "./files/configmap-route.groovy", "--config", "configmap:my-cm").Execute()).To(Succeed())
 			Eventually(IntegrationPodPhase(ns, "configmap-route"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
 			Eventually(IntegrationCondition(ns, "configmap-route", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
 			Eventually(IntegrationLogs(ns, "configmap-route"), TestTimeoutShort).Should(ContainSubstring(cmData["my-configmap-key"]))
@@ -80,13 +80,23 @@ func TestRunConfigExamples(t *testing.T) {
 			secData["my-secret-key"] = "very top secret"
 			NewPlainTextSecret(ns, "my-sec", secData)
 
-			Expect(Kamel("run", "-n", ns, "./files/secret-route.groovy", "--secret", "my-sec").Execute()).To(Succeed())
+			Expect(Kamel("run", "-n", ns, "./files/secret-route.groovy", "--config", "secret:my-sec").Execute()).To(Succeed())
 			Eventually(IntegrationPodPhase(ns, "secret-route"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
 			Eventually(IntegrationCondition(ns, "secret-route", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
 			Eventually(IntegrationLogs(ns, "secret-route"), TestTimeoutShort).Should(ContainSubstring(secData["my-secret-key"]))
 			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
 		})
 
+		// File Configuration
+
+		t.Run("Plain text configuration file", func(t *testing.T) {
+			Expect(Kamel("run", "-n", ns, "./files/config-file-route.groovy", "--config", "file:./files/resources-data.txt").Execute()).To(Succeed())
+			Eventually(IntegrationPodPhase(ns, "config-file-route"), TestTimeoutMedium).Should(Equal(v1.PodRunning))
+			Eventually(IntegrationCondition(ns, "config-file-route", camelv1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(v1.ConditionTrue))
+			Eventually(IntegrationLogs(ns, "config-file-route"), TestTimeoutShort).Should(ContainSubstring("the file body"))
+			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
+		})
+
 		// Resources
 
 		t.Run("Plain text resource file", func(t *testing.T) {
diff --git a/e2e/common/config/files/config-file-route.groovy b/e2e/common/config/files/config-file-route.groovy
new file mode 100644
index 000000000..53b347f6d
--- /dev/null
+++ b/e2e/common/config/files/config-file-route.groovy
@@ -0,0 +1,28 @@
+// camel-k: language=groovy
+/*
+ * 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.
+ */
+
+//
+// To run this integrations use:
+// kamel run --config file:resources-data.txt resources-route.groovy --dev
+//
+
+from('timer:resources')
+    .routeId('resources')
+    .setBody()
+        .simple("resource:classpath:resources-data.txt")
+    .log('resource file content is: ${body}')
diff --git a/e2e/common/config/files/configmap-route.groovy b/e2e/common/config/files/configmap-route.groovy
index 5a61c56b0..948cb1504 100644
--- a/e2e/common/config/files/configmap-route.groovy
+++ b/e2e/common/config/files/configmap-route.groovy
@@ -20,7 +20,7 @@
 // To run this integrations use:
 //
 // kubectl create configmap my-cm --from-literal=my-configmap-key="configmap content"
-// kamel run --configmap my-cm configmap-route.groovy --dev
+// kamel run --config configmap:my-cm configmap-route.groovy --dev
 //
 
 from('timer:configmap')
diff --git a/e2e/common/config/files/property-file-route.groovy b/e2e/common/config/files/property-file-route.groovy
index 13a09fce8..6ce683811 100644
--- a/e2e/common/config/files/property-file-route.groovy
+++ b/e2e/common/config/files/property-file-route.groovy
@@ -19,7 +19,7 @@
 //
 // To run this integrations use:
 //
-// kamel run --property-file my.properties property-file-route.groovy --dev
+// kamel run --property file:my.properties property-file-route.groovy --dev
 //
 
 from('timer:property-file')
diff --git a/e2e/common/config/files/resources-route.groovy b/e2e/common/config/files/resources-route.groovy
index 6f572853c..108584056 100644
--- a/e2e/common/config/files/resources-route.groovy
+++ b/e2e/common/config/files/resources-route.groovy
@@ -21,8 +21,5 @@
 // kamel run --resource resources-data.txt resources-route.groovy --dev
 //
 
-from('timer:resources')
-    .routeId('resources')
-    .setBody()
-        .simple("resource:classpath:resources-data.txt")
+from('file:/etc/camel/resources/?fileName=resources-data.txt&noop=true&idempotent=false')
     .log('resource file content is: ${body}')
diff --git a/e2e/common/config/files/secret-route.groovy b/e2e/common/config/files/secret-route.groovy
index 49966aed6..7027b8442 100644
--- a/e2e/common/config/files/secret-route.groovy
+++ b/e2e/common/config/files/secret-route.groovy
@@ -20,7 +20,7 @@
 // To run this integrations use:
 //
 // kubectl create secret generic my-sec --from-literal=my-secret-key="very top secret"
-// kamel run --secret my-sec secret-route.groovy --dev
+// kamel run --config secret:my-sec secret-route.groovy --dev
 //
 
 from('timer:secret')
diff --git a/pkg/cmd/modeline_test.go b/pkg/cmd/modeline_test.go
index 912e741b2..659293973 100644
--- a/pkg/cmd/modeline_test.go
+++ b/pkg/cmd/modeline_test.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
 	"context"
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path"
@@ -142,14 +143,14 @@ func TestModelineRunPropertyFiles(t *testing.T) {
 	propFile := `
 		a=b
 	`
-	propFileName := path.Join(dir, "application.properties")
+	propFileName := path.Join(subDir, "application.properties")
 	err = ioutil.WriteFile(propFileName, []byte(propFile), 0777)
 	assert.NoError(t, err)
 
 	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
 	assert.NoError(t, err)
 	assert.NotNil(t, cmd)
-	assert.Equal(t, []string{"run", fileName, "--property=file:application.properties"}, flags)
+	assert.Equal(t, []string{"run", fileName, fmt.Sprintf("--property=file:%s", propFileName)}, flags)
 }
 
 func TestModelineRunBuildProperty(t *testing.T) {
@@ -193,14 +194,87 @@ func TestModelineRunBuildPropertyFiles(t *testing.T) {
 	propFile := `
 		a=b
 	`
-	propFileName := path.Join(dir, "application.properties")
+	propFileName := path.Join(subDir, "application.properties")
 	err = ioutil.WriteFile(propFileName, []byte(propFile), 0777)
 	assert.NoError(t, err)
 
 	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
 	assert.NoError(t, err)
 	assert.NotNil(t, cmd)
-	assert.Equal(t, []string{"run", fileName, "--build-property=file:application.properties"}, flags)
+	assert.Equal(t, []string{"run", fileName, fmt.Sprintf("--build-property=file:%s", propFileName)}, flags)
+}
+
+func TestModelineRunConfigConfigmap(t *testing.T) {
+	dir, err := ioutil.TempDir("", "camel-k-test-")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	subDir := path.Join(dir, "sub")
+	err = os.Mkdir(subDir, 0777)
+	assert.NoError(t, err)
+
+	file := `
+		// camel-k: config=configmap:my-cm
+	`
+	fileName := path.Join(subDir, "simple.groovy")
+	err = ioutil.WriteFile(fileName, []byte(file), 0777)
+	assert.NoError(t, err)
+
+	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
+	assert.NoError(t, err)
+	assert.NotNil(t, cmd)
+	assert.Equal(t, []string{"run", fileName, "--config=configmap:my-cm"}, flags)
+}
+
+func TestModelineRunConfigSecret(t *testing.T) {
+	dir, err := ioutil.TempDir("", "camel-k-test-")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	subDir := path.Join(dir, "sub")
+	err = os.Mkdir(subDir, 0777)
+	assert.NoError(t, err)
+
+	file := `
+		// camel-k: config=secret:my-secret
+	`
+	fileName := path.Join(subDir, "simple.groovy")
+	err = ioutil.WriteFile(fileName, []byte(file), 0777)
+	assert.NoError(t, err)
+
+	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
+	assert.NoError(t, err)
+	assert.NotNil(t, cmd)
+	assert.Equal(t, []string{"run", fileName, "--config=secret:my-secret"}, flags)
+}
+
+func TestModelineRunConfigFile(t *testing.T) {
+	dir, err := ioutil.TempDir("", "camel-k-test-")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	subDir := path.Join(dir, "sub")
+	err = os.Mkdir(subDir, 0777)
+	assert.NoError(t, err)
+
+	file := `
+		// camel-k: config=file:application.properties
+	`
+	fileName := path.Join(subDir, "simple.groovy")
+	err = ioutil.WriteFile(fileName, []byte(file), 0777)
+	assert.NoError(t, err)
+
+	propFile := `
+		a=b
+	`
+	propFileName := path.Join(subDir, "application.properties")
+	err = ioutil.WriteFile(propFileName, []byte(propFile), 0777)
+	assert.NoError(t, err)
+
+	cmd, flags, err := NewKamelWithModelineCommand(context.TODO(), []string{"kamel", "run", fileName})
+	assert.NoError(t, err)
+	assert.NotNil(t, cmd)
+	assert.Equal(t, []string{"run", fileName, fmt.Sprintf("--config=file:%s", propFileName)}, flags)
 }
 
 func TestModelineInspectSimple(t *testing.T) {
diff --git a/pkg/cmd/run_help_test.go b/pkg/cmd/run_help_test.go
new file mode 100644
index 000000000..39d67f0ab
--- /dev/null
+++ b/pkg/cmd/run_help_test.go
@@ -0,0 +1,46 @@
+/*
+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 (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParseConfigOption(t *testing.T) {
+	validConfigMap := "configmap:my-config_map"
+	validSecret := "secret:my-secret"
+	validFile := "file:/tmp/my-file.txt"
+	notValid := "someprotocol:wrong"
+
+	configmap, err := ParseConfigOption(validConfigMap)
+	assert.Nil(t, err)
+	assert.Equal(t, ConfigOptionTypeConfigmap, configmap.ConfigType)
+	assert.Equal(t, "my-config_map", configmap.Value)
+	secret, err := ParseConfigOption(validSecret)
+	assert.Nil(t, err)
+	assert.Equal(t, ConfigOptionTypeSecret, secret.ConfigType)
+	assert.Equal(t, "my-secret", secret.Value)
+	file, err := ParseConfigOption(validFile)
+	assert.Nil(t, err)
+	assert.Equal(t, ConfigOptionTypeFile, file.ConfigType)
+	assert.Equal(t, "/tmp/my-file.txt", file.Value)
+	_, err = ParseConfigOption(notValid)
+	assert.NotNil(t, err)
+}