Repository: apache/camel-k
Pull request #1142 (2019-12-13 18:56:38)

Added unit tests for the prometheus trait #255


Patch:
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index fff2f0abc..a930cceb8 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -5,7 +5,8 @@
 package v1alpha1
 
 import (
-	v1 "k8s.io/api/core/v1"
+	corev1 "k8s.io/api/core/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	runtime "k8s.io/apimachinery/pkg/runtime"
 )
 
@@ -723,7 +724,11 @@ func (in *IntegrationPlatformBuildSpec) DeepCopyInto(out *IntegrationPlatformBui
 		}
 	}
 	out.Registry = in.Registry
-	out.Timeout = in.Timeout
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	in.Maven.DeepCopyInto(&out.Maven)
 	if in.KanikoBuildCache != nil {
 		in, out := &in.KanikoBuildCache, &out.KanikoBuildCache
@@ -864,6 +869,7 @@ func (in *IntegrationPlatformSpec) DeepCopy() *IntegrationPlatformSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *IntegrationPlatformStatus) DeepCopyInto(out *IntegrationPlatformStatus) {
 	*out = *in
+	in.IntegrationPlatformSpec.DeepCopyInto(&out.IntegrationPlatformSpec)
 	if in.Conditions != nil {
 		in, out := &in.Conditions, &out.Conditions
 		*out = make([]IntegrationPlatformCondition, len(*in))
@@ -994,7 +1000,11 @@ func (in *IntegrationStatus) DeepCopy() *IntegrationStatus {
 func (in *MavenSpec) DeepCopyInto(out *MavenSpec) {
 	*out = *in
 	in.Settings.DeepCopyInto(&out.Settings)
-	out.Timeout = in.Timeout
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	return
 }
 
@@ -1107,12 +1117,12 @@ func (in *ValueSource) DeepCopyInto(out *ValueSource) {
 	*out = *in
 	if in.ConfigMapKeyRef != nil {
 		in, out := &in.ConfigMapKeyRef, &out.ConfigMapKeyRef
-		*out = new(v1.ConfigMapKeySelector)
+		*out = new(corev1.ConfigMapKeySelector)
 		(*in).DeepCopyInto(*out)
 	}
 	if in.SecretKeyRef != nil {
 		in, out := &in.SecretKeyRef, &out.SecretKeyRef
-		*out = new(v1.SecretKeySelector)
+		*out = new(corev1.SecretKeySelector)
 		(*in).DeepCopyInto(*out)
 	}
 	return
diff --git a/pkg/cmd/completion_bash.go b/pkg/cmd/completion_bash.go
index 0cdc16c14..9048ea38c 100644
--- a/pkg/cmd/completion_bash.go
+++ b/pkg/cmd/completion_bash.go
@@ -20,7 +20,6 @@ package cmd
 import (
 	"context"
 	"fmt"
-	"os"
 	"strings"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -184,7 +183,7 @@ func newCmdCompletionBash(root *cobra.Command) *cobra.Command {
 		Short: "Generates bash completion scripts",
 		Long:  bashCompletionCmdLongDescription,
 		Run: func(_ *cobra.Command, _ []string) {
-			err := root.GenBashCompletion(os.Stdout)
+			err := root.GenBashCompletion(root.OutOrStdout())
 			if err != nil {
 				fmt.Print(err.Error())
 			}
diff --git a/pkg/cmd/completion_zsh.go b/pkg/cmd/completion_zsh.go
index 976bcc10c..e9d9680e6 100644
--- a/pkg/cmd/completion_zsh.go
+++ b/pkg/cmd/completion_zsh.go
@@ -19,7 +19,6 @@ package cmd
 
 import (
 	"fmt"
-	"os"
 
 	"github.com/spf13/cobra"
 )
@@ -50,7 +49,7 @@ func newCmdCompletionZsh(root *cobra.Command) *cobra.Command {
 		Short: "Generates zsh completion scripts",
 		Long:  zshCompletionCmdLongDescription,
 		Run: func(_ *cobra.Command, _ []string) {
-			err := root.GenZshCompletion(os.Stdout)
+			err := root.GenZshCompletion(root.OutOrStdout())
 			if err != nil {
 				fmt.Print(err.Error())
 			}
diff --git a/pkg/cmd/get.go b/pkg/cmd/get.go
index c76f66e9e..677406508 100644
--- a/pkg/cmd/get.go
+++ b/pkg/cmd/get.go
@@ -19,7 +19,6 @@ package cmd
 
 import (
 	"fmt"
-	"os"
 	"text/tabwriter"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -49,7 +48,7 @@ func newCmdGet(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	return &cmd
 }
 
-func (o *getCmdOptions) run(_ *cobra.Command, args []string) error {
+func (o *getCmdOptions) run(cmd *cobra.Command, args []string) error {
 	c, err := o.GetCmdClient()
 	if err != nil {
 		return err
@@ -78,7 +77,7 @@ func (o *getCmdOptions) run(_ *cobra.Command, args []string) error {
 		return err
 	}
 
-	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
+	w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 8, 1, '\t', 0)
 	fmt.Fprintln(w, "NAME\tPHASE\tKIT")
 	for _, integration := range integrationList.Items {
 		fmt.Fprintf(w, "%s\t%s\t%s\n", integration.Name, string(integration.Status.Phase), integration.Status.Kit)
diff --git a/pkg/cmd/kit_get.go b/pkg/cmd/kit_get.go
index fb72884fe..9d7f10972 100644
--- a/pkg/cmd/kit_get.go
+++ b/pkg/cmd/kit_get.go
@@ -19,7 +19,6 @@ package cmd
 
 import (
 	"fmt"
-	"os"
 	"text/tabwriter"
 
 	"github.com/spf13/cobra"
@@ -42,7 +41,7 @@ func newKitGetCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 			if err := impl.validate(cmd, args); err != nil {
 				return err
 			}
-			if err := impl.run(); err != nil {
+			if err := impl.run(cmd); err != nil {
 				fmt.Println(err.Error())
 			}
 
@@ -68,7 +67,7 @@ func (command *kitGetCommand) validate(cmd *cobra.Command, args []string) error
 	return nil
 }
 
-func (command *kitGetCommand) run() error {
+func (command *kitGetCommand) run(cmd *cobra.Command) error {
 	kitList := v1alpha1.NewIntegrationKitList()
 	c, err := command.GetCmdClient()
 	if err != nil {
@@ -78,7 +77,7 @@ func (command *kitGetCommand) run() error {
 		return err
 	}
 
-	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
+	w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 8, 1, '\t', 0)
 	fmt.Fprintln(w, "NAME\tPHASE\tTYPE\tIMAGE")
 	for _, ctx := range kitList.Items {
 		t := ctx.Labels["camel.apache.org/kit.type"]
diff --git a/pkg/cmd/log.go b/pkg/cmd/log.go
index 709f0e002..151c8aa29 100644
--- a/pkg/cmd/log.go
+++ b/pkg/cmd/log.go
@@ -58,7 +58,7 @@ func (o *logCmdOptions) validate(_ *cobra.Command, args []string) error {
 	return nil
 }
 
-func (o *logCmdOptions) run(_ *cobra.Command, args []string) error {
+func (o *logCmdOptions) run(cmd *cobra.Command, args []string) error {
 	c, err := o.GetCmdClient()
 	if err != nil {
 		return err
@@ -81,7 +81,7 @@ func (o *logCmdOptions) run(_ *cobra.Command, args []string) error {
 	if err := c.Get(o.Context, key, &integration); err != nil {
 		return err
 	}
-	if err := k8slog.Print(o.Context, c, &integration); err != nil {
+	if err := k8slog.Print(o.Context, c, &integration, cmd.OutOrStdout()); err != nil {
 		return err
 	}
 
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 672c3bcd0..31b411c01 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -125,7 +125,7 @@ func (o *runCmdOptions) validateArgs(_ *cobra.Command, args []string) error {
 	}
 
 	for _, fileName := range args {
-		if !strings.HasPrefix(fileName, "http://") && !strings.HasPrefix(fileName, "https://") {
+		if !isRemoteHTTPFile(fileName) {
 			if _, err := os.Stat(fileName); err != nil && os.IsNotExist(err) {
 				return errors.Wrap(err, "file "+fileName+" does not exist")
 			} else if err != nil {
@@ -156,7 +156,7 @@ func (o *runCmdOptions) validateArgs(_ *cobra.Command, args []string) error {
 	return nil
 }
 
-func (o *runCmdOptions) run(_ *cobra.Command, args []string) error {
+func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error {
 	c, err := o.GetCmdClient()
 	if err != nil {
 		return err
@@ -225,7 +225,7 @@ func (o *runCmdOptions) run(_ *cobra.Command, args []string) error {
 		}
 	}
 	if o.Logs || o.Dev {
-		err = k8slog.Print(o.Context, c, integration)
+		err = k8slog.Print(o.Context, c, integration, cmd.OutOrStdout())
 		if err != nil {
 			return err
 		}
@@ -265,23 +265,27 @@ func (o *runCmdOptions) waitForIntegrationReady(integration *v1alpha1.Integratio
 
 func (o *runCmdOptions) syncIntegration(c client.Client, sources []string) error {
 	for _, s := range sources {
-		changes, err := sync.File(o.Context, s)
-		if err != nil {
-			return err
-		}
-		go func() {
-			for {
-				select {
-				case <-o.Context.Done():
-					return
-				case <-changes:
-					_, err := o.updateIntegrationCode(c, sources)
-					if err != nil {
-						fmt.Println("Unable to sync integration: ", err.Error())
+		if !isRemoteHTTPFile(s) {
+			changes, err := sync.File(o.Context, s)
+			if err != nil {
+				return err
+			}
+			go func() {
+				for {
+					select {
+					case <-o.Context.Done():
+						return
+					case <-changes:
+						_, err := o.updateIntegrationCode(c, sources)
+						if err != nil {
+							fmt.Println("Unable to sync integration: ", err.Error())
+						}
 					}
 				}
-			}
-		}()
+			}()
+		} else {
+			fmt.Printf("WARNING: the following URL will not be watched for changes: %s\n", s)
+		}
 	}
 
 	return nil
@@ -457,7 +461,7 @@ func (*runCmdOptions) loadData(fileName string, compress bool) (string, error) {
 	var content []byte
 	var err error
 
-	if !strings.HasPrefix(fileName, "http://") && !strings.HasPrefix(fileName, "https://") {
+	if !isRemoteHTTPFile(fileName) {
 		content, err = ioutil.ReadFile(fileName)
 		if err != nil {
 			return "", err
@@ -523,3 +527,7 @@ func (*runCmdOptions) configureTrait(integration *v1alpha1.Integration, config s
 	integration.Spec.Traits[traitID] = spec
 	return nil
 }
+
+func isRemoteHTTPFile(fileName string) bool {
+	return strings.HasPrefix(fileName, "http://") || strings.HasPrefix(fileName, "https://")
+}
diff --git a/pkg/platform/defaults.go b/pkg/platform/defaults.go
index 640e385ee..94f2e63aa 100644
--- a/pkg/platform/defaults.go
+++ b/pkg/platform/defaults.go
@@ -97,10 +97,10 @@ func setPlatformDefaults(ctx context.Context, c client.Client, p *v1alpha1.Integ
 		p.Status.Profile = DetermineBestProfile(ctx, c, p)
 	}
 	if p.Status.Build.CamelVersion == "" {
-		p.Status.Build.CamelVersion = defaults.CamelVersionConstraint
+		p.Status.Build.CamelVersion = defaults.DefaultCamelVersion
 	}
 	if p.Status.Build.RuntimeVersion == "" {
-		p.Status.Build.RuntimeVersion = defaults.RuntimeVersionConstraint
+		p.Status.Build.RuntimeVersion = defaults.DefaultRuntimeVersion
 	}
 	if p.Status.Build.BaseImage == "" {
 		p.Status.Build.BaseImage = defaults.BaseImage
diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go
index 72a5be703..97d7395ba 100644
--- a/pkg/trait/quarkus.go
+++ b/pkg/trait/quarkus.go
@@ -205,7 +205,7 @@ func (t *quarkusTrait) determineQuarkusVersion(e *Environment) string {
 		e.Platform.Status.Build.RuntimeProvider.Quarkus.QuarkusVersion != "" {
 		return e.Platform.Status.Build.RuntimeProvider.Quarkus.QuarkusVersion
 	}
-	return defaults.QuarkusVersionConstraint
+	return defaults.DefaultQuarkusVersion
 }
 
 func (t *quarkusTrait) determineCamelQuarkusVersion(e *Environment) string {
@@ -224,5 +224,5 @@ func (t *quarkusTrait) determineCamelQuarkusVersion(e *Environment) string {
 		e.Platform.Status.Build.RuntimeProvider.Quarkus.CamelQuarkusVersion != "" {
 		return e.Platform.Status.Build.RuntimeProvider.Quarkus.CamelQuarkusVersion
 	}
-	return defaults.CamelQuarkusVersionConstraint
+	return defaults.DefaultCamelQuarkusVersion
 }
diff --git a/pkg/util/camel/catalog.go b/pkg/util/camel/catalog.go
index 12289e26b..f6c268e6d 100644
--- a/pkg/util/camel/catalog.go
+++ b/pkg/util/camel/catalog.go
@@ -43,7 +43,7 @@ func DefaultCatalog() (*RuntimeCatalog, error) {
 // QuarkusCatalog --
 func QuarkusCatalog() (*RuntimeCatalog, error) {
 	return catalogForRuntimeProvider(v1alpha1.QuarkusRuntimeProvider{
-		CamelQuarkusVersion: defaults.CamelQuarkusVersionConstraint,
+		CamelQuarkusVersion: defaults.DefaultCamelQuarkusVersion,
 		QuarkusVersion:      defaults.DefaultQuarkusVersion,
 	})
 }
diff --git a/pkg/util/defaults/defaults.go b/pkg/util/defaults/defaults.go
index d9ee9eb46..5afb15ed2 100644
--- a/pkg/util/defaults/defaults.go
+++ b/pkg/util/defaults/defaults.go
@@ -14,6 +14,7 @@ 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 defaults
 
 // ***********************
@@ -27,27 +28,15 @@ const (
 	// DefaultCamelVersion --
 	DefaultCamelVersion = "3.0.0-RC3"
 
-	// CamelVersionConstraint --
-	CamelVersionConstraint = ">=3.0.0-RC3"
-
 	// DefaultRuntimeVersion --
 	DefaultRuntimeVersion = "1.0.7"
 
-	// RuntimeVersionConstraint --
-	RuntimeVersionConstraint = ">=1.0.7"
-
 	// DefaultCamelQuarkusVersion --
 	DefaultCamelQuarkusVersion = "0.3.1"
 
-	// CamelQuarkusVersionConstraint --
-	CamelQuarkusVersionConstraint = ">=0.3.1"
-
 	// DefaultQuarkusVersion --
 	DefaultQuarkusVersion = "0.27.0"
 
-	// QuarkusVersionConstraint --
-	QuarkusVersionConstraint = ">=0.27.0"
-
 	// KanikoVersion --
 	KanikoVersion = "0.9.0"
 
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index d825a7864..9ba325bc6 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -19,6 +19,7 @@ package kubernetes
 
 import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	monitoringv1 "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
 	routev1 "github.com/openshift/api/route/v1"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -348,3 +349,21 @@ func (c *Collection) Remove(selector func(runtime.Object) bool) runtime.Object {
 	}
 	return nil
 }
+
+func (c *Collection) VisitServiceMonitor(visitor func(*monitoringv1.ServiceMonitor)) {
+	c.Visit(func(res runtime.Object) {
+		if conv, ok := res.(*monitoringv1.ServiceMonitor); ok {
+			visitor(conv)
+		}
+	})
+}
+
+func (c *Collection) GetServiceMonitor(filter func(*monitoringv1.ServiceMonitor) bool) *monitoringv1.ServiceMonitor {
+	var retValue *monitoringv1.ServiceMonitor
+	c.VisitServiceMonitor(func(serviceMonitor *monitoringv1.ServiceMonitor) {
+		if filter(serviceMonitor) {
+			retValue = serviceMonitor
+		}
+	})
+	return retValue
+}
diff --git a/pkg/util/kubernetes/log/util.go b/pkg/util/kubernetes/log/util.go
index 56f8cc158..1120d4a41 100644
--- a/pkg/util/kubernetes/log/util.go
+++ b/pkg/util/kubernetes/log/util.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"os"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 
@@ -30,11 +29,11 @@ import (
 )
 
 // Print prints integrations logs to the stdout
-func Print(ctx context.Context, client kubernetes.Interface, integration *v1alpha1.Integration) error {
+func Print(ctx context.Context, client kubernetes.Interface, integration *v1alpha1.Integration, out io.Writer) error {
 	scraper := NewSelectorScraper(client, integration.Namespace, integration.Name, "camel.apache.org/integration="+integration.Name)
 	reader := scraper.Start(ctx)
 
-	if _, err := io.Copy(os.Stdout, ioutil.NopCloser(reader)); err != nil {
+	if _, err := io.Copy(out, ioutil.NopCloser(reader)); err != nil {
 		fmt.Println(err.Error())
 	}
 
diff --git a/script/Makefile b/script/Makefile
index 7de91f828..f1c4e008d 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -17,13 +17,9 @@ VERSIONFILE := pkg/util/defaults/defaults.go
 VERSION := 1.0.0-M5-SNAPSHOT
 LAST_RELEASED_VERSION := 1.0.0-M3
 RUNTIME_VERSION := 1.0.7
-RUNTIME_VERSION_CONSTRAINT := >=1.0.7
 CAMEL_VERSION := 3.0.0-RC3
-CAMEL_VERSION_CONSTRAINT := >=3.0.0-RC3
 CAMEL_QUARKUS_VERSION := 0.3.1
-CAMEL_QUARKUS_VERSION_CONSTRAINT := >=0.3.1
 QUARKUS_VERSION := 0.27.0
-QUARKUS_VERSION_CONSTRAINT := >=0.27.0
 KANIKO_VERSION := 0.9.0
 BASE_IMAGE := fabric8/s2i-java:3.0-java8
 LOCAL_REPOSITORY := /tmp/artifacts/m2
@@ -72,6 +68,7 @@ codegen:
 	@echo "/*" > $(VERSIONFILE)
 	@echo "$$LICENSE_HEADER" >> $(VERSIONFILE)
 	@echo "*/" >> $(VERSIONFILE)
+	@echo "" >> $(VERSIONFILE)
 	@echo "package defaults" >> $(VERSIONFILE)
 	@echo "" >> $(VERSIONFILE)
 	@echo "// ***********************" >> $(VERSIONFILE)
@@ -85,27 +82,15 @@ codegen:
 	@echo "  // DefaultCamelVersion -- " >> $(VERSIONFILE)
 	@echo "  DefaultCamelVersion = \"$(CAMEL_VERSION)\"" >> $(VERSIONFILE)
 	@echo "" >> $(VERSIONFILE)
-	@echo "  // CamelVersionConstraint -- " >> $(VERSIONFILE)
-	@echo "  CamelVersionConstraint = \"$(CAMEL_VERSION_CONSTRAINT)\"" >> $(VERSIONFILE)
-	@echo "" >> $(VERSIONFILE)
 	@echo "  // DefaultRuntimeVersion -- " >> $(VERSIONFILE)
 	@echo "  DefaultRuntimeVersion = \"$(RUNTIME_VERSION)\"" >> $(VERSIONFILE)
 	@echo "" >> $(VERSIONFILE)
-	@echo "  // RuntimeVersionConstraint -- " >> $(VERSIONFILE)
-	@echo "  RuntimeVersionConstraint = \"$(RUNTIME_VERSION_CONSTRAINT)\"" >> $(VERSIONFILE)
-	@echo "" >> $(VERSIONFILE)
 	@echo "  // DefaultCamelQuarkusVersion -- " >> $(VERSIONFILE)
 	@echo "  DefaultCamelQuarkusVersion = \"$(CAMEL_QUARKUS_VERSION)\"" >> $(VERSIONFILE)
 	@echo "" >> $(VERSIONFILE)
-	@echo "  // CamelQuarkusVersionConstraint -- " >> $(VERSIONFILE)
-	@echo "  CamelQuarkusVersionConstraint = \"$(CAMEL_QUARKUS_VERSION_CONSTRAINT)\"" >> $(VERSIONFILE)
-	@echo "" >> $(VERSIONFILE)
 	@echo "  // DefaultQuarkusVersion -- " >> $(VERSIONFILE)
 	@echo "  DefaultQuarkusVersion = \"$(QUARKUS_VERSION)\"" >> $(VERSIONFILE)
 	@echo "" >> $(VERSIONFILE)
-	@echo "  // QuarkusVersionConstraint -- " >> $(VERSIONFILE)
-	@echo "  QuarkusVersionConstraint = \"$(QUARKUS_VERSION_CONSTRAINT)\"" >> $(VERSIONFILE)
-	@echo "" >> $(VERSIONFILE)
 	@echo "  // KanikoVersion -- " >> $(VERSIONFILE)
 	@echo "  KanikoVersion = \"$(KANIKO_VERSION)\"" >> $(VERSIONFILE)
 	@echo "" >> $(VERSIONFILE)


Test patch:
diff --git a/e2e/dev_mode_test.go b/e2e/dev_mode_test.go
new file mode 100644
index 000000000..4e5cdda1c
--- /dev/null
+++ b/e2e/dev_mode_test.go
@@ -0,0 +1,79 @@
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+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 e2e
+
+import (
+	"context"
+	"io"
+	"testing"
+	"time"
+
+	"github.com/apache/camel-k/e2e/util"
+	. "github.com/onsi/gomega"
+)
+
+func TestRunDevMode(t *testing.T) {
+	withNewTestNamespace(t, func(ns string) {
+		Expect(kamel("install", "-n", ns).Execute()).Should(BeNil())
+
+		t.Run("run yaml dev mode", func(t *testing.T) {
+			ctx, cancel := context.WithCancel(testContext)
+			defer cancel()
+			piper, pipew := io.Pipe()
+			defer pipew.Close()
+			defer piper.Close()
+
+			file := util.MakeTempCopy(t, "files/yaml.yaml")
+
+			kamelRun := kamelWithContext(ctx, "run", "-n", ns, file, "--dev")
+			kamelRun.SetOut(pipew)
+
+			logScanner := util.NewLogScanner(ctx, piper, "Magicstring!", "Magicjordan!")
+
+			go kamelRun.Execute()
+
+			Eventually(logScanner.IsFound("Magicstring!"), 5*time.Minute).Should(BeTrue())
+			Expect(logScanner.IsFound("Magicjordan!")()).To(BeFalse())
+
+			util.ReplaceInFile(t, file, "string!", "jordan!")
+			Eventually(logScanner.IsFound("Magicjordan!"), 5*time.Minute).Should(BeTrue())
+		})
+
+		t.Run("run yaml remote dev mode", func(t *testing.T) {
+			ctx, cancel := context.WithCancel(testContext)
+			defer cancel()
+			piper, pipew := io.Pipe()
+			defer pipew.Close()
+			defer piper.Close()
+
+			remoteFile := "https://github.com/apache/camel-k/raw/e80eb5353cbccf47c89a9f0a1c68ffbe3d0f1521/e2e/files/yaml.yaml"
+			kamelRun := kamelWithContext(ctx, "run", "-n", ns, remoteFile, "--dev")
+			kamelRun.SetOut(pipew)
+
+			logScanner := util.NewLogScanner(ctx, piper, "Magicstring!")
+
+			go kamelRun.Execute()
+
+			Eventually(logScanner.IsFound("Magicstring!"), 5*time.Minute).Should(BeTrue())
+		})
+	})
+}
diff --git a/e2e/test_support.go b/e2e/test_support.go
index e79aa3cc5..863618917 100644
--- a/e2e/test_support.go
+++ b/e2e/test_support.go
@@ -96,6 +96,10 @@ func newTestClient() (client.Client, error) {
 }
 
 func kamel(args ...string) *cobra.Command {
+	return kamelWithContext(testContext, args...)
+}
+
+func kamelWithContext(ctx context.Context, args ...string) *cobra.Command {
 	var c *cobra.Command
 	var err error
 
@@ -121,7 +125,7 @@ func kamel(args ...string) *cobra.Command {
 			},
 		}
 	} else {
-		c, err = cmd.NewKamelCommand(testContext)
+		c, err = cmd.NewKamelCommand(ctx)
 	}
 	if err != nil {
 		panic(err)
diff --git a/e2e/util/log_scanner.go b/e2e/util/log_scanner.go
new file mode 100644
index 000000000..34a148f53
--- /dev/null
+++ b/e2e/util/log_scanner.go
@@ -0,0 +1,71 @@
+/*
+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 util
+
+import (
+	"bufio"
+	"context"
+	"fmt"
+	"io"
+	"strings"
+)
+
+// LogScanner can attach to a stream and check if a value is printed
+type LogScanner struct {
+	in     io.Reader
+	ctx    context.Context
+	values map[string]bool
+}
+
+// NewLogScanner --
+func NewLogScanner(ctx context.Context, in io.Reader, values ...string) *LogScanner {
+	scanner := LogScanner{
+		ctx:    ctx,
+		in:     in,
+		values: make(map[string]bool),
+	}
+	for _, v := range values {
+		scanner.values[v] = false
+	}
+	go scanner.startScan()
+	return &scanner
+}
+
+func (s *LogScanner) startScan() {
+	scanner := bufio.NewScanner(s.in)
+	for scanner.Scan() {
+		if s.ctx.Err() != nil {
+			return
+		}
+		text := scanner.Text()
+		fmt.Println(text)
+		for k := range s.values {
+			if strings.Contains(text, k) {
+				fmt.Printf("LogScanner - Found match for: %s\n", k)
+				s.values[k] = true
+			}
+		}
+	}
+}
+
+// IsFound returns if the string has been found in the logs
+func (s *LogScanner) IsFound(value string) func() bool {
+	return func() bool {
+		return s.values[value]
+	}
+}
diff --git a/e2e/util/temp_file.go b/e2e/util/temp_file.go
new file mode 100644
index 000000000..6c9805a84
--- /dev/null
+++ b/e2e/util/temp_file.go
@@ -0,0 +1,62 @@
+/*
+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 util
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"strings"
+	"testing"
+)
+
+// MakeTempCopy --
+func MakeTempCopy(t *testing.T, fileName string) string {
+	_, simpleName := path.Split(fileName)
+	var err error
+	var tmpDir string
+	if tmpDir, err = ioutil.TempDir("", "camel-k-"); err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+	tmpFileName := path.Join(tmpDir, simpleName)
+	var content []byte
+	if content, err = ioutil.ReadFile(fileName); err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+	if err = ioutil.WriteFile(tmpFileName, content, os.FileMode(0777)); err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+	return tmpFileName
+}
+
+// ReplaceInFile replace strings in a file with new values
+func ReplaceInFile(t *testing.T, fileName string, old, new string) {
+	content, err := ioutil.ReadFile(fileName)
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+	res := strings.ReplaceAll(string(content), old, new)
+	if err = ioutil.WriteFile(fileName, []byte(res), os.FileMode(0777)); err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+}
diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go
index a9f2c0060..d2ec6c3ac 100644
--- a/pkg/trait/builder_test.go
+++ b/pkg/trait/builder_test.go
@@ -148,7 +148,7 @@ func createBuilderTestEnv(cluster v1alpha1.IntegrationPlatformCluster, strategy
 				Build: v1alpha1.IntegrationPlatformBuildSpec{
 					PublishStrategy: strategy,
 					Registry:        v1alpha1.IntegrationPlatformRegistrySpec{Address: "registry"},
-					CamelVersion:    defaults.CamelVersionConstraint,
+					CamelVersion:    defaults.DefaultCamelVersion,
 				},
 			},
 		},
diff --git a/pkg/trait/prometheus_test.go b/pkg/trait/prometheus_test.go
new file mode 100644
index 000000000..78addd4af
--- /dev/null
+++ b/pkg/trait/prometheus_test.go
@@ -0,0 +1,215 @@
+/*
+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 trait
+
+import (
+	"context"
+	"testing"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/test"
+
+	"github.com/stretchr/testify/assert"
+
+	monitoringv1 "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
+
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestConfigurePrometheusTraitInRightPhaseDoesSucceed(t *testing.T) {
+	trait, environment := createNominalPrometheusTest()
+
+	configured, err := trait.Configure(environment)
+
+	assert.Nil(t, err)
+	assert.True(t, configured)
+}
+
+func TestConfigurePrometheusTraitInWrongPhaseDoesNotSucceed(t *testing.T) {
+	trait, environment := createNominalPrometheusTest()
+	environment.Integration.Status.Phase = v1alpha1.IntegrationPhaseResolvingKit
+
+	configured, err := trait.Configure(environment)
+
+	assert.Nil(t, err)
+	assert.False(t, configured)
+}
+
+func TestApplyNominalPrometheusTraitDoesSucceed(t *testing.T) {
+	trait, environment := createNominalPrometheusTest()
+
+	err := trait.Apply(environment)
+
+	assert.Nil(t, err)
+
+	container := environment.Resources.GetContainerByName(defaultContainerName)
+	assert.NotNil(t, container)
+	test.EnvVarHasValue(t, container.Env, "AB_PROMETHEUS_PORT", "9779")
+	ports := container.Ports
+	assert.Len(t, ports, 1)
+	assert.Equal(t, "prometheus", ports[0].Name)
+	assert.Equal(t, int32(9779), ports[0].ContainerPort)
+	assert.Equal(t, corev1.ProtocolTCP, ports[0].Protocol)
+
+	service := environment.Resources.GetService(func(service *corev1.Service) bool {
+		return service.Name == "integration-name"
+	})
+	assert.NotNil(t, service)
+	assert.Len(t, service.Spec.Ports, 1)
+
+	serviceMonitor := environment.Resources.GetServiceMonitor(func(service *monitoringv1.ServiceMonitor) bool {
+		return service.Name == "integration-name"
+	})
+	assert.NotNil(t, serviceMonitor)
+
+	assert.Len(t, environment.Integration.Status.Conditions, 1)
+	condition := environment.Integration.Status.Conditions[0]
+	assert.Equal(t, v1alpha1.IntegrationConditionPrometheusAvailable, condition.Type)
+	assert.Equal(t, corev1.ConditionTrue, condition.Status)
+}
+
+func TestApplyPrometheusTraitWithoutContainerDoesNotSucceed(t *testing.T) {
+	trait, environment := createNominalPrometheusTest()
+	environment.Resources = kubernetes.NewCollection()
+
+	err := trait.Apply(environment)
+
+	assert.Nil(t, err)
+
+	assert.Len(t, environment.Integration.Status.Conditions, 1)
+	condition := environment.Integration.Status.Conditions[0]
+	assert.Equal(t, v1alpha1.IntegrationConditionPrometheusAvailable, condition.Type)
+	assert.Equal(t, corev1.ConditionFalse, condition.Status)
+}
+
+func TestApplyPrometheusTraitWithServiceDoesNotSucceed(t *testing.T) {
+	trait, environment := createNominalPrometheusTest()
+	environment.Resources = kubernetes.NewCollection(
+		&appsv1.Deployment{
+			Spec: appsv1.DeploymentSpec{
+				Template: corev1.PodTemplateSpec{
+					Spec: corev1.PodSpec{
+						Containers: []corev1.Container{
+							{
+								Name: defaultContainerName,
+							},
+						},
+					},
+				},
+			},
+		},
+		&corev1.Service{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "Service",
+				APIVersion: "v1",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "service-name",
+				Namespace: "namespace",
+				Labels: map[string]string{
+					"camel.apache.org/integration":  "integration-name",
+					"camel.apache.org/service.type": v1alpha1.ServiceTypeUser,
+				},
+			},
+			Spec: corev1.ServiceSpec{
+				Ports: []corev1.ServicePort{},
+				Selector: map[string]string{
+					"camel.apache.org/integration": "integration-name",
+				},
+			},
+		})
+
+	err := trait.Apply(environment)
+
+	assert.Nil(t, err)
+
+	assert.Len(t, environment.Integration.Status.Conditions, 1)
+	condition := environment.Integration.Status.Conditions[0]
+	assert.Equal(t, v1alpha1.IntegrationConditionServiceNotAvailableReason, condition.Reason)
+	assert.Equal(t, corev1.ConditionFalse, condition.Status)
+}
+
+func TestApplyDisabledPrometheusTraitShouldDeactivateJavaAgent(t *testing.T) {
+	trait, environment := createNominalPrometheusTest()
+	trait.Enabled = new(bool)
+
+	err := trait.Apply(environment)
+
+	assert.Nil(t, err)
+
+	container := environment.Resources.GetContainerByName(defaultContainerName)
+	assert.NotNil(t, container)
+	test.EnvVarHasValue(t, container.Env, "AB_PROMETHEUS_OFF", "true")
+}
+
+func TestPrometheusTraitGetServiceMonitor(t *testing.T) {
+	trait, environment := createNominalPrometheusTest()
+
+	serviceMonitor, err := trait.getServiceMonitorFor(environment)
+
+	assert.Nil(t, err)
+
+	assert.NotNil(t, serviceMonitor)
+	assert.Equal(t, "ServiceMonitor", serviceMonitor.Kind)
+	assert.Equal(t, "monitoring.coreos.com/v1", serviceMonitor.APIVersion)
+	assert.Equal(t, "integration-name", serviceMonitor.Name)
+	assert.Equal(t, "integration-namespace", serviceMonitor.Namespace)
+	assert.Equal(t, "integration-name", serviceMonitor.Labels["camel.apache.org/integration"])
+	assert.Equal(t, "integration-name", serviceMonitor.Spec.Selector.MatchLabels["camel.apache.org/integration"])
+	assert.Len(t, serviceMonitor.Spec.Endpoints, 1)
+	assert.Equal(t, "prometheus", serviceMonitor.Spec.Endpoints[0].Port)
+}
+
+func createNominalPrometheusTest() (*prometheusTrait, *Environment) {
+	trait := newPrometheusTrait()
+	enabled := true
+	trait.Enabled = &enabled
+
+	environment := &Environment{
+		Catalog: NewCatalog(context.TODO(), nil),
+		Integration: &v1alpha1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "integration-namespace",
+				Name:      "integration-name",
+			},
+			Status: v1alpha1.IntegrationStatus{
+				Phase: v1alpha1.IntegrationPhaseDeploying,
+			},
+		},
+		Resources: kubernetes.NewCollection(
+			&appsv1.Deployment{
+				Spec: appsv1.DeploymentSpec{
+					Template: corev1.PodTemplateSpec{
+						Spec: corev1.PodSpec{
+							Containers: []corev1.Container{
+								{
+									Name: defaultContainerName,
+								},
+							},
+						},
+					},
+				},
+			},
+		),
+	}
+
+	return trait, environment
+}