Repository: apache/camel-k (go)

Issue:
Integration naming issues with numbers in them
If you create integrations with the same name but ending in a number the number is ignored and identical integrations are created linked to the *same* deployed Pod.

For example:

~/camelk/kamel run Sample.java --name=looknoeap1

Results in a deployment occurring with the name 'looknoeap'. If I then do:

~/camelk/kamel run Sample.java --name=looknoeap2

I get - integration "looknoeap" updated

For some reason the naming convention is dropping numbers at the end - it also ignores '-', i.e. --name=looknoeap-2 resolves to 'looknoeap' as well.

However, if I then do:

~/camelk/kamel run Sample.java --name=looknoeap-two

It creates an integration called 'looknoeap-two.

Pull request description:
fix: Integration naming issues with numbers in them
fixes #777

Patch:
diff --git a/.travis.yml b/.travis.yml
index 5ad38eda8..a6fda071e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,7 +21,10 @@ sudo: required
 language: go
 
 go:
-  - "1.11.4"
+  - 1.12.x
+
+install:
+  - go mod vendor
 
 env:
   global:
@@ -33,5 +36,11 @@ env:
 services:
   - docker
 
+before_script:
+  - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b . v1.17.1
+  - ./golangci-lint config path
+  - ./golangci-lint --version
+
 script:
+  - ./golangci-lint run --verbose --config .golangci.yml || travis_terminate 1;
   - ./script/travis_build.sh
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 374aba579..dee78b0fb 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -138,8 +138,12 @@ func (o *runCmdOptions) validateArgs(_ *cobra.Command, args []string) error {
 				return errors.Wrap(err, "error while accessing file "+fileName)
 			}
 		} else {
-			/* #nosec */
+			// nolint: gosec
 			resp, err := http.Get(fileName)
+			if resp != nil && resp.Body != nil {
+				resp.Body.Close()
+			}
+
 			if err != nil {
 				return errors.Wrap(err, "The URL provided is not reachable")
 			} else if resp.StatusCode != 200 {
diff --git a/pkg/controller/integration/initialize.go b/pkg/controller/integration/initialize.go
index 3e5f921ff..b8cbac716 100644
--- a/pkg/controller/integration/initialize.go
+++ b/pkg/controller/integration/initialize.go
@@ -55,7 +55,7 @@ func (action *initializeAction) Handle(ctx context.Context, integration *v1alpha
 	}
 
 	// The integration platform needs to be ready before starting to create integrations
-	if err != nil || pl.Status.Phase != v1alpha1.IntegrationPlatformPhaseReady {
+	if pl.Status.Phase != v1alpha1.IntegrationPlatformPhaseReady {
 		action.L.Info("Waiting for the integration platform to be initialized")
 
 		if integration.Status.Phase != v1alpha1.IntegrationPhaseWaitingForPlatform {
diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go
index b02a450f5..8fc4b5a26 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -104,7 +104,9 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
 				// during image build
 				return oldBuild.Status.Phase != newBuild.Status.Phase
 			},
-		})
+		},
+	)
+
 	if err != nil {
 		return err
 	}
@@ -142,6 +144,10 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
 		}),
 	})
 
+	if err != nil {
+		return err
+	}
+
 	// Watch for IntegrationPlatform phase transitioning to ready and enqueue
 	// requests for any integrations that are in phase waiting for platform
 	err = c.Watch(&source.Kind{Type: &v1alpha1.IntegrationPlatform{}}, &handler.EnqueueRequestsFromMapFunc{
@@ -173,6 +179,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
 			return requests
 		}),
 	})
+
 	if err != nil {
 		return err
 	}
diff --git a/pkg/controller/integrationkit/build.go b/pkg/controller/integrationkit/build.go
index 4ea566431..97b7cafa4 100644
--- a/pkg/controller/integrationkit/build.go
+++ b/pkg/controller/integrationkit/build.go
@@ -21,10 +21,10 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/builder"
 	"github.com/apache/camel-k/pkg/trait"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
diff --git a/pkg/controller/integrationplatform/initialize.go b/pkg/controller/integrationplatform/initialize.go
index 2fc150b71..7f52c5ef7 100644
--- a/pkg/controller/integrationplatform/initialize.go
+++ b/pkg/controller/integrationplatform/initialize.go
@@ -111,58 +111,7 @@ func (action *initializeAction) Handle(ctx context.Context, ip *v1alpha1.Integra
 		action.L.Info("No registry specified for publishing images")
 	}
 
-	if target.Spec.Profile == "" {
-		target.Spec.Profile = platform.GetProfile(target)
-	}
-	if target.Spec.Build.CamelVersion == "" {
-		target.Spec.Build.CamelVersion = defaults.CamelVersionConstraint
-	}
-	if target.Spec.Build.RuntimeVersion == "" {
-		target.Spec.Build.RuntimeVersion = defaults.RuntimeVersion
-	}
-	if target.Spec.Build.BaseImage == "" {
-		target.Spec.Build.BaseImage = defaults.BaseImage
-	}
-	if target.Spec.Build.LocalRepository == "" {
-		target.Spec.Build.LocalRepository = defaults.LocalRepository
-	}
-	if target.Spec.Build.PersistentVolumeClaim == "" {
-		target.Spec.Build.PersistentVolumeClaim = target.Name
-	}
-
-	if target.Spec.Build.Timeout.Duration != 0 {
-		d := target.Spec.Build.Timeout.Duration.Truncate(time.Second)
-
-		if target.Spec.Build.Timeout.Duration != d {
-			action.L.Infof("Build timeout minimum unit is sec (configured: %s, truncated: %s)", target.Spec.Build.Timeout.Duration, d)
-		}
-
-		target.Spec.Build.Timeout.Duration = d
-	}
-	if target.Spec.Build.Timeout.Duration == 0 {
-		target.Spec.Build.Timeout.Duration = 5 * time.Minute
-	}
-
-	if target.Spec.Build.Maven.Timeout.Duration != 0 {
-		d := target.Spec.Build.Maven.Timeout.Duration.Truncate(time.Second)
-
-		if target.Spec.Build.Maven.Timeout.Duration != d {
-			action.L.Infof("Maven timeout minimum unit is sec (configured: %s, truncated: %s)", target.Spec.Build.Maven.Timeout.Duration, d)
-		}
-
-		target.Spec.Build.Maven.Timeout.Duration = d
-	}
-	if target.Spec.Build.Maven.Timeout.Duration == 0 {
-		n := target.Spec.Build.Timeout.Duration.Seconds() * 0.75
-		target.Spec.Build.Maven.Timeout.Duration = (time.Duration(n) * time.Second).Truncate(time.Second)
-	}
-
-	action.L.Infof("CamelVersion set to %s", target.Spec.Build.CamelVersion)
-	action.L.Infof("RuntimeVersion set to %s", target.Spec.Build.RuntimeVersion)
-	action.L.Infof("BaseImage set to %s", target.Spec.Build.BaseImage)
-	action.L.Infof("LocalRepository set to %s", target.Spec.Build.LocalRepository)
-	action.L.Infof("Timeout set to %s", target.Spec.Build.Timeout)
-	action.L.Infof("Maven Timeout set to %s", target.Spec.Build.Maven.Timeout.Duration)
+	action.setDefaults(target)
 
 	if target.Spec.Build.Maven.Timeout.Duration != 0 {
 		action.L.Infof("Maven Timeout set to %s", target.Spec.Build.Maven.Timeout.Duration)
@@ -221,6 +170,61 @@ func (action *initializeAction) isDuplicate(ctx context.Context, thisPlatform *v
 	return false, nil
 }
 
+func (action *initializeAction) setDefaults(target *v1alpha1.IntegrationPlatform) {
+	if target.Spec.Profile == "" {
+		target.Spec.Profile = platform.GetProfile(target)
+	}
+	if target.Spec.Build.CamelVersion == "" {
+		target.Spec.Build.CamelVersion = defaults.CamelVersionConstraint
+	}
+	if target.Spec.Build.RuntimeVersion == "" {
+		target.Spec.Build.RuntimeVersion = defaults.RuntimeVersion
+	}
+	if target.Spec.Build.BaseImage == "" {
+		target.Spec.Build.BaseImage = defaults.BaseImage
+	}
+	if target.Spec.Build.LocalRepository == "" {
+		target.Spec.Build.LocalRepository = defaults.LocalRepository
+	}
+	if target.Spec.Build.PersistentVolumeClaim == "" {
+		target.Spec.Build.PersistentVolumeClaim = target.Name
+	}
+
+	if target.Spec.Build.Timeout.Duration != 0 {
+		d := target.Spec.Build.Timeout.Duration.Truncate(time.Second)
+
+		if target.Spec.Build.Timeout.Duration != d {
+			action.L.Infof("Build timeout minimum unit is sec (configured: %s, truncated: %s)", target.Spec.Build.Timeout.Duration, d)
+		}
+
+		target.Spec.Build.Timeout.Duration = d
+	}
+	if target.Spec.Build.Timeout.Duration == 0 {
+		target.Spec.Build.Timeout.Duration = 5 * time.Minute
+	}
+
+	if target.Spec.Build.Maven.Timeout.Duration != 0 {
+		d := target.Spec.Build.Maven.Timeout.Duration.Truncate(time.Second)
+
+		if target.Spec.Build.Maven.Timeout.Duration != d {
+			action.L.Infof("Maven timeout minimum unit is sec (configured: %s, truncated: %s)", target.Spec.Build.Maven.Timeout.Duration, d)
+		}
+
+		target.Spec.Build.Maven.Timeout.Duration = d
+	}
+	if target.Spec.Build.Maven.Timeout.Duration == 0 {
+		n := target.Spec.Build.Timeout.Duration.Seconds() * 0.75
+		target.Spec.Build.Maven.Timeout.Duration = (time.Duration(n) * time.Second).Truncate(time.Second)
+	}
+
+	action.L.Infof("CamelVersion set to %s", target.Spec.Build.CamelVersion)
+	action.L.Infof("RuntimeVersion set to %s", target.Spec.Build.RuntimeVersion)
+	action.L.Infof("BaseImage set to %s", target.Spec.Build.BaseImage)
+	action.L.Infof("LocalRepository set to %s", target.Spec.Build.LocalRepository)
+	action.L.Infof("Timeout set to %s", target.Spec.Build.Timeout)
+	action.L.Infof("Maven Timeout set to %s", target.Spec.Build.Maven.Timeout.Duration)
+}
+
 func createPersistentVolumeClaim(ctx context.Context, client client.Client, platform *v1alpha1.IntegrationPlatform) error {
 	volumeSize, err := resource.ParseQuantity("1Gi")
 	if err != nil {
diff --git a/pkg/util/kubernetes/log/pod_scraper.go b/pkg/util/kubernetes/log/pod_scraper.go
index 094e5bc28..162e26cc7 100644
--- a/pkg/util/kubernetes/log/pod_scraper.go
+++ b/pkg/util/kubernetes/log/pod_scraper.go
@@ -89,21 +89,20 @@ func (s *PodScraper) doScrape(ctx context.Context, out *bufio.Writer, clientClos
 	}
 
 	reader := bufio.NewReader(byteReader)
-	err = nil
-	for err == nil {
-		str, err := reader.ReadString('\n')
+	for {
+		data, err := reader.ReadBytes('\n')
+		if err == io.EOF {
+			return
+		}
 		if err != nil {
 			break
 		}
-		_, err = out.WriteString(str)
+		_, err = out.Write(data)
 		if err != nil {
 			break
 		}
 		out.Flush()
 	}
-	if err == io.EOF {
-		return
-	}
 
 	s.handleAndRestart(ctx, err, 5*time.Second, out, clientCloser)
 }
diff --git a/pkg/util/kubernetes/sanitize.go b/pkg/util/kubernetes/sanitize.go
index fd6bef78c..1ed80261a 100644
--- a/pkg/util/kubernetes/sanitize.go
+++ b/pkg/util/kubernetes/sanitize.go
@@ -48,5 +48,5 @@ func SanitizeLabel(name string) string {
 }
 
 func isDisallowedStartEndChar(rune rune) bool {
-	return !unicode.IsLetter(rune)
+	return !unicode.IsLetter(rune) && !unicode.IsNumber(rune)
 }
diff --git a/pkg/util/maven/maven.go b/pkg/util/maven/maven.go
index 757df372c..8fbe80f8a 100644
--- a/pkg/util/maven/maven.go
+++ b/pkg/util/maven/maven.go
@@ -89,12 +89,14 @@ func Run(ctx Context) error {
 		mvnCmd = c
 	}
 
-	args := append(ctx.AdditionalArguments, "--batch-mode")
+	args := make([]string, 0)
+	args = append(args, ctx.AdditionalArguments...)
+	args = append(args, "--batch-mode")
 
 	if ctx.LocalRepository == "" {
-		args = append(ctx.AdditionalArguments, "-Dcamel.noop=true")
+		args = append(args, "-Dcamel.noop=true")
 	} else if _, err := os.Stat(ctx.LocalRepository); err == nil {
-		args = append(ctx.AdditionalArguments, "-Dmaven.repo.local="+ctx.LocalRepository)
+		args = append(args, "-Dmaven.repo.local="+ctx.LocalRepository)
 	}
 
 	settingsPath := path.Join(ctx.Path, "settings.xml")


Test patch:
diff --git a/pkg/controller/integrationplatform/initialize_test.go b/pkg/controller/integrationplatform/initialize_test.go
index 8260f3a1a..2a1e4b049 100644
--- a/pkg/controller/integrationplatform/initialize_test.go
+++ b/pkg/controller/integrationplatform/initialize_test.go
@@ -22,19 +22,22 @@ import (
 	"testing"
 	"time"
 
+	"github.com/rs/xid"
+
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/util/log"
 	"github.com/apache/camel-k/pkg/util/test"
+
 	"github.com/stretchr/testify/assert"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 func TestTimeouts_Default(t *testing.T) {
 	ip := v1alpha1.IntegrationPlatform{}
 	ip.Namespace = "ns"
-	ip.Name = "camel-k"
+	ip.Name = xid.New().String()
 	ip.Spec.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
 
 	c, err := test.NewFakeClient(&ip)
@@ -67,7 +70,7 @@ func TestTimeouts_Default(t *testing.T) {
 func TestTimeouts_MavenComputedFromBuild(t *testing.T) {
 	ip := v1alpha1.IntegrationPlatform{}
 	ip.Namespace = "ns"
-	ip.Name = "camel-k"
+	ip.Name = xid.New().String()
 	ip.Spec.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
 
 	timeout, err := time.ParseDuration("1m1ms")
@@ -107,7 +110,7 @@ func TestTimeouts_MavenComputedFromBuild(t *testing.T) {
 func TestTimeouts_Truncated(t *testing.T) {
 	ip := v1alpha1.IntegrationPlatform{}
 	ip.Namespace = "ns"
-	ip.Name = "camel-k"
+	ip.Name = xid.New().String()
 	ip.Spec.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
 
 	bt, err := time.ParseDuration("5m1ms")
@@ -144,6 +147,6 @@ func TestTimeouts_Truncated(t *testing.T) {
 	err = c.Get(context.TODO(), key, &answer)
 	assert.Nil(t, err)
 
-	assert.Equal(t, time.Duration(2*time.Minute), answer.Spec.Build.Maven.Timeout.Duration)
-	assert.Equal(t, time.Duration(5*time.Minute), answer.Spec.Build.Timeout.Duration)
+	assert.Equal(t, 2*time.Minute, answer.Spec.Build.Maven.Timeout.Duration)
+	assert.Equal(t, 5*time.Minute, answer.Spec.Build.Timeout.Duration)
 }
diff --git a/pkg/util/kubernetes/sanitize_test.go b/pkg/util/kubernetes/sanitize_test.go
index 839bf0517..31b422e20 100644
--- a/pkg/util/kubernetes/sanitize_test.go
+++ b/pkg/util/kubernetes/sanitize_test.go
@@ -33,6 +33,9 @@ func TestSanitizeName(t *testing.T) {
 		{"input": "http://foo.bar.com/cheese/wine/beer/abc.java", "expect": "abc"},
 		{"input": "http://foo.bar.com/cheese", "expect": "cheese"},
 		{"input": "http://foo.bar.com", "expect": "foo"},
+		{"input": "-foo-bar-", "expect": "foo-bar"},
+		{"input": "1foo-bar2", "expect": "1foo-bar2"},
+		{"input": "foo-bar-1", "expect": "foo-bar-1"},
 	}
 
 	for _, c := range cases {