Repository: apache/camel-k
Pull request #3276 (2022-05-16 11:10:05)

Backport 1.9.x
<!-- Description -->

cc @oscerd , hope this is the right branch, thanks !

<!--
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/.github/actions/kamel-build-bundle/build-bundle-image.sh b/.github/actions/kamel-build-bundle/build-bundle-image.sh
index ff5aef7b1..575ada06d 100755
--- a/.github/actions/kamel-build-bundle/build-bundle-image.sh
+++ b/.github/actions/kamel-build-bundle/build-bundle-image.sh
@@ -108,7 +108,7 @@ export CUSTOM_IMAGE=${IMAGE_NAME}
 
 export PREV_XY_CHANNEL="stable-$(make get-last-released-version | grep -Po '\d.\d')"
 echo "PREV_XY_CHANNEL=${PREV_XY_CHANNEL}" >> $GITHUB_ENV
-export NEW_XY_CHANNEL=stable-$(make get-version | grep -Po "\d.\d")
+export NEW_XY_CHANNEL=stable-dev-$(make get-version | grep -Po "\d.\d")
 echo "NEW_XY_CHANNEL=${NEW_XY_CHANNEL}" >> $GITHUB_ENV
 
 make bundle-build \
diff --git a/.github/actions/kamel-build-bundle/build-index-image.sh b/.github/actions/kamel-build-bundle/build-index-image.sh
index bfad918f0..8857d1531 100755
--- a/.github/actions/kamel-build-bundle/build-index-image.sh
+++ b/.github/actions/kamel-build-bundle/build-index-image.sh
@@ -211,7 +211,7 @@ cat << EOF >> ${CATALOG_DIR}/camel-k.yaml
 ---
 schema: olm.channel
 package: camel-k
-name: stable-$(make get-version | grep -Po "\d.\d")
+name: stable-dev-$(make get-version | grep -Po "\d.\d")
 entries:
   - name: camel-k.v$(make get-version | grep -Po "\d.\d.\d")
     replaces: $(make get-last-released-img-name).v$(make get-last-released-version | grep -Po "\d.\d.\d")
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 247d9e1a5..7d015ba1f 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -39,6 +39,7 @@ import (
 	"path/filepath"
 	"reflect"
 	"regexp"
+	runtimeos "runtime"
 	"strings"
 	"syscall"
 
@@ -788,12 +789,7 @@ func resolvePodTemplate(ctx context.Context, cmd *cobra.Command, templateSrc str
 
 func uploadFileOrDirectory(platform *v1.IntegrationPlatform, item string, integrationName string, cmd *cobra.Command, integration *v1.Integration) error {
 	path := strings.TrimPrefix(item, "file://")
-	localPath := path
-	targetPath := ""
-	if i := strings.Index(path, ":"); i > 0 {
-		targetPath = path[i+1:]
-		localPath = path[:i]
-	}
+	localPath, targetPath := getPaths(path, runtimeos.GOOS, filepath.IsAbs(path))
 	options := getSpectrumOptions(platform, cmd)
 	dirName, err := getDirName(localPath)
 	if err != nil {
@@ -839,6 +835,23 @@ func uploadFileOrDirectory(platform *v1.IntegrationPlatform, item string, integr
 	})
 }
 
+func getPaths(path string, os string, isAbs bool) (localPath string, targetPath string) {
+	localPath = path
+	targetPath = ""
+	parts := strings.Split(path, ":")
+	if len(parts) > 1 {
+		if os != "windows" || !isAbs {
+			localPath = parts[0]
+			targetPath = parts[1]
+		} else if isAbs && len(parts) == 3 {
+			// special case on Windows for absolute paths e.g C:\foo\bar\test.csv:remote/path
+			localPath = fmt.Sprintf("%s:%s", parts[0], parts[1])
+			targetPath = parts[2]
+		}
+	}
+	return localPath, targetPath
+}
+
 func getMountPath(targetPath string, dirName string, path string) (string, error) {
 	// if the target path is a file then use that as the exact mount path
 	if filepath.Ext(targetPath) != "" {
@@ -952,6 +965,15 @@ func extractGav(src *zip.File, localPath string) (maven.Dependency, bool) {
 func uploadAsMavenArtifact(dependency maven.Dependency, path string, platform *v1.IntegrationPlatform, ns string, options spectrum.Options) error {
 	artifactHTTPPath := getArtifactHTTPPath(dependency, platform, ns)
 	options.Target = fmt.Sprintf("%s/%s:%s", platform.Spec.Build.Registry.Address, artifactHTTPPath, dependency.Version)
+	if runtimeos.GOOS == "windows" {
+		// workaround for https://github.com/container-tools/spectrum/issues/8
+		// work with relative paths instead
+		rel, err := getRelativeToWorkingDirectory(path)
+		if err != nil {
+			return err
+		}
+		path = rel
+	}
 	_, err := spectrum.Build(options, fmt.Sprintf("%s:.", path))
 	if err != nil {
 		return err
@@ -960,6 +982,23 @@ func uploadAsMavenArtifact(dependency maven.Dependency, path string, platform *v
 	return uploadChecksumFiles(path, options, platform, artifactHTTPPath, dependency)
 }
 
+// Deprecated: workaround for https://github.com/container-tools/spectrum/issues/8
+func getRelativeToWorkingDirectory(path string) (string, error) {
+	wd, err := os.Getwd()
+	if err != nil {
+		return "", err
+	}
+	abs, err := filepath.Abs(path)
+	if err != nil {
+		return "", err
+	}
+	path, err = filepath.Rel(wd, abs)
+	if err != nil {
+		return "", err
+	}
+	return path, nil
+}
+
 // Currently swallows errors because our Project model is incomplete.
 // Most of the time it is irrelevant for our use case (GAV).
 // nolint:errcheck
diff --git a/pkg/controller/kameletbinding/integration.go b/pkg/controller/kameletbinding/integration.go
index 1f66e5d8b..ef8b0fc7d 100644
--- a/pkg/controller/kameletbinding/integration.go
+++ b/pkg/controller/kameletbinding/integration.go
@@ -189,14 +189,14 @@ func CreateIntegrationFor(ctx context.Context, c client.Client, kameletbinding *
 	dslSteps = append(dslSteps, s)
 
 	fromWrapper := map[string]interface{}{
-		"uri": from.URI,
+		"uri":   from.URI,
+		"steps": dslSteps,
 	}
 
 	flowRoute := map[string]interface{}{
 		"route": map[string]interface{}{
-			"id":    "binding",
-			"from":  fromWrapper,
-			"steps": dslSteps,
+			"id":   "binding",
+			"from": fromWrapper,
 		},
 	}
 	encodedRoute, err := json.Marshal(flowRoute)
diff --git a/pkg/util/olm/operator.go b/pkg/util/olm/operator.go
index a2f30a9e7..9bc7a86e0 100644
--- a/pkg/util/olm/operator.go
+++ b/pkg/util/olm/operator.go
@@ -176,6 +176,7 @@ func Install(ctx context.Context, client client.Client, namespace string, global
 			Channel:                options.Channel,
 			StartingCSV:            options.StartingCSV,
 			InstallPlanApproval:    operatorsv1alpha1.ApprovalAutomatic,
+			Config:                 &operatorsv1alpha1.SubscriptionConfig{},
 		},
 	}
 	// Additional configuration
@@ -235,6 +236,15 @@ func maybeSetTolerations(sub *operatorsv1alpha1.Subscription, tolArray []string)
 		if err != nil {
 			return err
 		}
+		if sub == nil {
+			panic("sub is nil")
+		}
+		if sub.Spec == nil {
+			panic("sub.Spec is nil")
+		}
+		if sub.Spec.Config == nil {
+			panic("sub.Spec.Config is nil")
+		}
 		sub.Spec.Config.Tolerations = tolerations
 	}
 	return nil


Test patch:
diff --git a/.github/actions/kamel-preflight-test/preflight-test.sh b/.github/actions/kamel-preflight-test/preflight-test.sh
index f85e41ef7..a815006da 100755
--- a/.github/actions/kamel-preflight-test/preflight-test.sh
+++ b/.github/actions/kamel-preflight-test/preflight-test.sh
@@ -113,11 +113,28 @@ if [ -n "${BUILD_CATALOG_SOURCE}" ]; then
   #
   # Check catalog source is actually available
   #
-  STATE=$(kubectl get catalogsource ${BUILD_CATALOG_SOURCE} -n ${IMAGE_NAMESPACE} -o=jsonpath='{.status.connectionState.lastObservedState}')
-  if [ "${STATE}" != "READY" ]; then
-    echo "Error: catalog source status is not ready."
-    exit 1
-  fi
+  timeout=5
+  catalog_ready=0
+  until [ ${catalog_ready} -eq 1 ] || [ ${timeout} -eq 0 ]
+  do
+    echo "Info: Awaiting catalog source to become ready"
+    let timeout=${timeout}-1
+
+    STATE=$(kubectl get catalogsource ${BUILD_CATALOG_SOURCE} -n ${IMAGE_NAMESPACE} -o=jsonpath='{.status.connectionState.lastObservedState}')
+    if [ "${STATE}" == "READY" ]; then
+      let catalog_ready=1
+      echo "Info: Catalog source is ready"
+      continue
+    else
+      echo "Warning: catalog source status is not ready."
+      if [ ${timeout} -eq 0 ]; then
+        echo "Error: timedout while awaiting catalog source to start"
+        exit 1
+      fi
+    fi
+
+    sleep 1m
+  done
 
   export KAMEL_INSTALL_OLM_SOURCE_NAMESPACE=${IMAGE_NAMESPACE}
   export KAMEL_INSTALL_OLM_SOURCE=${BUILD_CATALOG_SOURCE}
diff --git a/e2e/common/cli/bind_test.go b/e2e/common/cli/bind_test.go
index dc118cd65..d40d9be58 100644
--- a/e2e/common/cli/bind_test.go
+++ b/e2e/common/cli/bind_test.go
@@ -51,5 +51,13 @@ func TestKamelCLIBind(t *testing.T) {
 		t.Run("unsuccessful binding, no property", func(t *testing.T) {
 			Expect(Kamel("bind", "timer-source", "log:info", "-n", ns).Execute()).NotTo(Succeed())
 		})
+
+		t.Run("bind uris", func(t *testing.T) {
+			Expect(Kamel("bind", "timer:foo", "log:bar", "-n", ns).Execute()).To(Succeed())
+			Eventually(IntegrationPodPhase(ns, "timer-to-log"), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+			Eventually(IntegrationLogs(ns, "timer-to-log")).Should(ContainSubstring("Body is null"))
+
+			Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
+		})
 	})
 }
diff --git a/e2e/common/files/cron-quartz.yaml b/e2e/common/files/cron-quartz.yaml
index a036b5ceb..6fcce4f73 100644
--- a/e2e/common/files/cron-quartz.yaml
+++ b/e2e/common/files/cron-quartz.yaml
@@ -21,13 +21,13 @@
       uri: "quartz:trigger"
       parameters:
         cron: "0/1 * * * * ?"
-    steps:
-      - set-header:
-          name: "m"
-          constant: "string!"
-      - set-body:
-          simple: "Magic${header.m}"
-      - to: 
-          uri: "log:info"
-          parameters:
-            show-all: "false"
+      steps:
+        - set-header:
+            name: "m"
+            constant: "string!"
+        - set-body:
+            simple: "Magic${header.m}"
+        - to: 
+            uri: "log:info"
+            parameters:
+              show-all: "false"
diff --git a/e2e/common/files/cron-timer.yaml b/e2e/common/files/cron-timer.yaml
index 1b360233a..707d1974c 100644
--- a/e2e/common/files/cron-timer.yaml
+++ b/e2e/common/files/cron-timer.yaml
@@ -21,14 +21,14 @@
       uri: "timer:tick"
       parameters:
         period: "60000"
-    steps:
-      - set-header:
-          name: "m"
-          constant: "string!"
-      - set-body:
-          simple: "Magic${header.m}"
-      - to: 
-          uri: "log:info"
-          parameters:
-            show-all: "false"
+      steps:
+        - set-header:
+            name: "m"
+            constant: "string!"
+        - set-body:
+            simple: "Magic${header.m}"
+        - to: 
+            uri: "log:info"
+            parameters:
+              show-all: "false"
             
\ No newline at end of file
diff --git a/e2e/common/files/rest-consumer.yaml b/e2e/common/files/rest-consumer.yaml
index 2b01f13cf..b7b8e26bb 100644
--- a/e2e/common/files/rest-consumer.yaml
+++ b/e2e/common/files/rest-consumer.yaml
@@ -16,10 +16,9 @@
 # ---------------------------------------------------------------------------
 
 - rest:
-    verb:
-      - method: get
-        uri: "/customers/{name}"
-        to: "direct:start"
+    get:
+      - to: "direct:start"
+        path: "/customers/{name}"
 
 - from:
     uri: "direct:start"
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index b865fb7ce..f2fe2e6b4 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -680,3 +680,69 @@ func TestMissingTrait(t *testing.T) {
 	assert.Equal(t, "Error: bogus.fail=i-must-fail is not a valid trait property\n", output)
 	assert.NotNil(t, err)
 }
+
+func TestGetsPaths(t *testing.T) {
+	tests := []struct {
+		path       string
+		localPath  string
+		remotePath string
+		os         string
+		isAbs      bool
+	}{
+		{
+			path:       "C:\\USER\\HOME\\:remote/path",
+			localPath:  "C:\\USER\\HOME\\",
+			remotePath: "remote/path",
+			os:         "windows",
+			isAbs:      true,
+		},
+		{
+			path:       "src\\main\\resources:remote/path",
+			localPath:  "src\\main\\resources",
+			remotePath: "remote/path",
+			os:         "windows",
+		},
+		{
+			path:       "C:\\USER\\HOME\\",
+			localPath:  "C:\\USER\\HOME\\",
+			remotePath: "",
+			os:         "windows",
+			isAbs:      true,
+		},
+		{
+			path:       "src\\main\\resources",
+			localPath:  "src\\main\\resources",
+			remotePath: "",
+			os:         "windows",
+		},
+		{
+			path:       "/home/user/name/dir:/remote/path",
+			localPath:  "/home/user/name/dir",
+			remotePath: "/remote/path",
+			os:         "linux",
+			isAbs:      true,
+		}, {
+			path:       "/home/user/name/dir",
+			localPath:  "/home/user/name/dir",
+			remotePath: "",
+			os:         "linux",
+			isAbs:      true,
+		}, {
+			path:       "src/main/resources:remote/path",
+			localPath:  "src/main/resources",
+			remotePath: "remote/path",
+			os:         "linux",
+		}, {
+			path:       "src/main/resources",
+			localPath:  "src/main/resources",
+			remotePath: "",
+			os:         "linux",
+		},
+	}
+	for _, test := range tests {
+		localPath, targetPath := getPaths(test.path, test.os, test.isAbs)
+		assert.Equal(t, test.localPath, localPath)
+		assert.Equal(t, test.remotePath, targetPath)
+
+	}
+}