Repository: apache/camel-k
Pull request #2352 (2021-06-03 09:00:58)

Related to #2165: account for annotations when checking if integratio…
…n is changed

<!-- Description -->




<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
NONE
```



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d90b94b7..329f7e36b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,8 +10,10 @@
 - maven-repository flag is not honored in the run command  [\#2327](https://github.com/apache/camel-k/issues/2327)
 - Timeout when debugging the operator locally [\#2315](https://github.com/apache/camel-k/issues/2315)
 - Creating an IntegrationPlatform with the route Trait Crashes the Operator [\#2313](https://github.com/apache/camel-k/issues/2313)
+- Kamel Kamelets subcommand [\#2312](https://github.com/apache/camel-k/issues/2312)
 - Integration container build errors are absent from the log messages [\#2306](https://github.com/apache/camel-k/issues/2306)
 - Kamel delete should consider KameletBindings [\#2305](https://github.com/apache/camel-k/issues/2305)
+- Log parsing errors [\#2299](https://github.com/apache/camel-k/issues/2299)
 - Structured logs: remaining bits [\#2286](https://github.com/apache/camel-k/issues/2286)
 - \[Docs\] Broken links on autogenerated API [\#2280](https://github.com/apache/camel-k/issues/2280)
 - Normalize Spectrum build logs [\#2276](https://github.com/apache/camel-k/issues/2276)
@@ -41,11 +43,14 @@
 
 **Merged pull requests:**
 
+- feat\(cli\): add sub command kamel kamelet for get and delete \#2312 [\#2351](https://github.com/apache/camel-k/pull/2351) ([tadayosi](https://github.com/tadayosi))
+- fix\(cli\): stop printing \<nil\> at the end of kamel describe output [\#2350](https://github.com/apache/camel-k/pull/2350) ([tadayosi](https://github.com/tadayosi))
 - chore\(build\): Cancellable Maven operations [\#2348](https://github.com/apache/camel-k/pull/2348) ([astefanutti](https://github.com/astefanutti))
 - feat\(cli\): support k8s api shortnames for kit & platform with kamel describe [\#2346](https://github.com/apache/camel-k/pull/2346) ([tadayosi](https://github.com/tadayosi))
 - feat\(cli\): initial support for describing Kamelets with kamel describe \#2312 [\#2345](https://github.com/apache/camel-k/pull/2345) ([tadayosi](https://github.com/tadayosi))
 - chore\(api\): Encapsulate Maven properties [\#2344](https://github.com/apache/camel-k/pull/2344) ([astefanutti](https://github.com/astefanutti))
 - Add link to the github repo [\#2340](https://github.com/apache/camel-k/pull/2340) ([davidlanouette](https://github.com/davidlanouette))
+- feat\(cmd/run\): --config option flag [\#2339](https://github.com/apache/camel-k/pull/2339) ([squakez](https://github.com/squakez))
 - Fix \#2165: allow configuring traits via annotations [\#2338](https://github.com/apache/camel-k/pull/2338) ([nicolaferraro](https://github.com/nicolaferraro))
 - kamelets: add condition to report when an integration is stuck waiting for them [\#2337](https://github.com/apache/camel-k/pull/2337) ([lburgazzoli](https://github.com/lburgazzoli))
 - doc\(dev\): add instruction for deploying custom images to remote clusters [\#2336](https://github.com/apache/camel-k/pull/2336) ([tadayosi](https://github.com/tadayosi))
diff --git a/deploy/traits.yaml b/deploy/traits.yaml
index 59f6e1e0c..9e9944e22 100755
--- a/deploy/traits.yaml
+++ b/deploy/traits.yaml
@@ -221,6 +221,10 @@ traits:
       or a passive consumer (e.g. `direct` is a passive consumer).It's required that
       all periodic consumers have the same period and it can be expressed as cron
       schedule (e.g. `1m` can be expressed as `0/1 * * * *`,while `35m` or `50s` cannot).
+  - name: starting-deadline-seconds
+    type: int64
+    description: Optional deadline in seconds for starting the job if it misses scheduledtime
+      for any reason.  Missed jobs executions will be counted as failed ones.
 - name: dependencies
   platform: true
   profiles:
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/docs/modules/traits/pages/cron.adoc b/docs/modules/traits/pages/cron.adoc
index bebde3eed..7e67e5140 100755
--- a/docs/modules/traits/pages/cron.adoc
+++ b/docs/modules/traits/pages/cron.adoc
@@ -71,6 +71,11 @@ either starting from a periodic consumer (only `cron`, `timer` and `quartz` are
 It's required that all periodic consumers have the same period and it can be expressed as cron schedule (e.g. `1m` can be expressed as `0/1 * * * *`,
 while `35m` or `50s` cannot).
 
+| cron.starting-deadline-seconds
+| int64
+| Optional deadline in seconds for starting the job if it misses scheduled
+time for any reason.  Missed jobs executions will be counted as failed ones.
+
 |===
 
 // End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/docs/modules/traits/pages/toleration.adoc b/docs/modules/traits/pages/toleration.adoc
index d3b3e88f6..d2df24682 100755
--- a/docs/modules/traits/pages/toleration.adoc
+++ b/docs/modules/traits/pages/toleration.adoc
@@ -59,4 +59,4 @@ $ kamel run -t toleration.taints="node.kubernetes.io/network-unavailable:NoExecu
 * To tolerate the integration pod(s) to be scheduled on a node with a disk of SSD type:
 +
 [source,console]
-$ kamel run -t toleration.taints="disktype=ssd:PreferNoSchedule" ...
\ No newline at end of file
+$ kamel run -t toleration.taints="disktype=ssd:PreferNoSchedule" ...
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/trait/cron.go b/pkg/trait/cron.go
index e87612884..49541824a 100644
--- a/pkg/trait/cron.go
+++ b/pkg/trait/cron.go
@@ -76,6 +76,9 @@ type cronTrait struct {
 	// It's required that all periodic consumers have the same period and it can be expressed as cron schedule (e.g. `1m` can be expressed as `0/1 * * * *`,
 	// while `35m` or `50s` cannot).
 	Auto *bool `property:"auto" json:"auto,omitempty"`
+	// Optional deadline in seconds for starting the job if it misses scheduled
+	// time for any reason.  Missed jobs executions will be counted as failed ones.
+	StartingDeadlineSeconds *int64 `property:"starting-deadline-seconds" json:"startingDeadlineSeconds,omitempty"`
 }
 
 var _ ControllerStrategySelector = &cronTrait{}
@@ -283,8 +286,9 @@ func (t *cronTrait) getCronJobFor(e *Environment) *v1beta1.CronJob {
 			Annotations: e.Integration.Annotations,
 		},
 		Spec: v1beta1.CronJobSpec{
-			Schedule:          t.Schedule,
-			ConcurrencyPolicy: v1beta1.ConcurrencyPolicy(t.ConcurrencyPolicy),
+			Schedule:                t.Schedule,
+			ConcurrencyPolicy:       v1beta1.ConcurrencyPolicy(t.ConcurrencyPolicy),
+			StartingDeadlineSeconds: t.StartingDeadlineSeconds,
 			JobTemplate: v1beta1.JobTemplateSpec{
 				Spec: batchv1.JobSpec{
 					Template: corev1.PodTemplateSpec{
diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go
index c63259c42..e3baad4d8 100644
--- a/pkg/util/digest/digest.go
+++ b/pkg/util/digest/digest.go
@@ -29,6 +29,7 @@ import (
 	"path"
 	"sort"
 	"strconv"
+	"strings"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 	"github.com/apache/camel-k/pkg/util"
@@ -120,6 +121,13 @@ func ComputeForIntegration(integration *v1.Integration) (string, error) {
 			return "", err
 		}
 	}
+	// Integration traits as annotations
+	for _, k := range sortedTraitAnnotationsKeys(integration) {
+		v := integration.Annotations[k]
+		if _, err := hash.Write([]byte(fmt.Sprintf("%s=%v,", k, v))); err != nil {
+			return "", err
+		}
+	}
 
 	// Add a letter at the beginning and use URL safe encoding
 	digest := "v" + base64.RawURLEncoding.EncodeToString(hash.Sum(nil))
@@ -241,6 +249,17 @@ func sortedTraitSpecMapKeys(m map[string]v1.TraitSpec) []string {
 	return res
 }
 
+func sortedTraitAnnotationsKeys(it *v1.Integration) []string {
+	res := make([]string, 0, len(it.Annotations))
+	for k := range it.Annotations {
+		if strings.HasPrefix(k, v1.TraitAnnotationPrefix) {
+			res = append(res, k)
+		}
+	}
+	sort.Strings(res)
+	return res
+}
+
 func ComputeSHA1(elem ...string) (string, error) {
 	file := path.Join(elem...)
 
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)
+}
diff --git a/pkg/util/digest/digest_test.go b/pkg/util/digest/digest_test.go
new file mode 100644
index 000000000..096146f1c
--- /dev/null
+++ b/pkg/util/digest/digest_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 digest
+
+import (
+	"testing"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestDigestUsesAnnotations(t *testing.T) {
+	it := v1.Integration{}
+	digest1, err := ComputeForIntegration(&it)
+	assert.NoError(t, err)
+
+	it.Annotations = map[string]string{
+		"another.annotation": "hello",
+	}
+	digest2, err := ComputeForIntegration(&it)
+	assert.NoError(t, err)
+	assert.Equal(t, digest1, digest2)
+
+	it.Annotations = map[string]string{
+		"another.annotation":                   "hello",
+		"trait.camel.apache.org/cron.fallback": "true",
+	}
+	digest3, err := ComputeForIntegration(&it)
+	assert.NoError(t, err)
+	assert.NotEqual(t, digest1, digest3)
+}