You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2024/01/04 15:45:46 UTC
(camel-k) branch main updated: feat: add support for glob pattern in run sources
This is an automated email from the ASF dual-hosted git repository.
pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push:
new bf0e79b97 feat: add support for glob pattern in run sources
bf0e79b97 is described below
commit bf0e79b973344e12f556ba9e170c0eb9f8130771
Author: Rinaldo Pitzer Jr <16...@users.noreply.github.com>
AuthorDate: Tue Jan 2 15:02:07 2024 -0300
feat: add support for glob pattern in run sources
---
e2e/common/cli/files/glob/Java1.java | 27 +++++++++
e2e/common/cli/files/glob/Java2.java | 27 +++++++++
e2e/common/cli/files/glob/run1.yaml | 25 ++++++++
e2e/common/cli/files/glob/run2.yaml | 25 ++++++++
e2e/common/cli/run_test.go | 40 +++++++++++++
pkg/cmd/run_test.go | 113 +++++++++++++++++++++++++++++++++++
pkg/cmd/source/source.go | 34 +++++++++++
pkg/cmd/source/util.go | 20 +++++++
pkg/resources/resources.go | 8 +--
9 files changed, 315 insertions(+), 4 deletions(-)
diff --git a/e2e/common/cli/files/glob/Java1.java b/e2e/common/cli/files/glob/Java1.java
new file mode 100644
index 000000000..554fb9473
--- /dev/null
+++ b/e2e/common/cli/files/glob/Java1.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class Java1 extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ from("timer:tick?period=5000")
+ .setBody().simple("Hello java 1 {{property:default}}")
+ .log("${body}");
+ }
+}
diff --git a/e2e/common/cli/files/glob/Java2.java b/e2e/common/cli/files/glob/Java2.java
new file mode 100644
index 000000000..1c8e2d714
--- /dev/null
+++ b/e2e/common/cli/files/glob/Java2.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class Java2 extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ from("timer:tick?period=6000")
+ .setBody().simple("Hello java 2 {{property:default}}")
+ .log("${body}");
+ }
+}
diff --git a/e2e/common/cli/files/glob/run1.yaml b/e2e/common/cli/files/glob/run1.yaml
new file mode 100644
index 000000000..10e1fc1eb
--- /dev/null
+++ b/e2e/common/cli/files/glob/run1.yaml
@@ -0,0 +1,25 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+- from:
+ uri: "timer:yaml"
+ parameters:
+ period: "5000"
+ steps:
+ - setBody:
+ simple: "Hello run 1 {{property:default}}"
+ - to: "log:info"
diff --git a/e2e/common/cli/files/glob/run2.yaml b/e2e/common/cli/files/glob/run2.yaml
new file mode 100644
index 000000000..6590aad9a
--- /dev/null
+++ b/e2e/common/cli/files/glob/run2.yaml
@@ -0,0 +1,25 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+- from:
+ uri: "timer:yaml"
+ parameters:
+ period: "6000"
+ steps:
+ - setBody:
+ simple: "Hello run 2 {{property:default}}"
+ - to: "log:info"
diff --git a/e2e/common/cli/run_test.go b/e2e/common/cli/run_test.go
index d92c78d04..81ccbd330 100644
--- a/e2e/common/cli/run_test.go
+++ b/e2e/common/cli/run_test.go
@@ -138,6 +138,46 @@ func TestKamelCLIRun(t *testing.T) {
Eventually(DeleteIntegrations(ns), TestTimeoutLong).Should(Equal(0))
})
+ t.Run("Run with glob patterns", func(t *testing.T) {
+ t.Run("YAML", func(t *testing.T) {
+ name := RandomizedSuffixName("run")
+ Expect(KamelRunWithID(operatorID, ns, "files/glob/run*", "--name", name).Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort).
+ Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello run 1 default"))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello run 2 default"))
+ Eventually(DeleteIntegrations(ns), TestTimeoutLong).Should(Equal(0))
+ })
+
+ t.Run("Java", func(t *testing.T) {
+ name := RandomizedSuffixName("java")
+ Expect(KamelRunWithID(operatorID, ns, "files/glob/Java*", "--name", name).Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort).
+ Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello java 1 default"))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello java 2 default"))
+ Eventually(DeleteIntegrations(ns), TestTimeoutLong).Should(Equal(0))
+ })
+
+ t.Run("All", func(t *testing.T) {
+ name := RandomizedSuffixName("java")
+ Expect(KamelRunWithID(operatorID, ns, "files/glob/*", "--name", name).Execute()).To(Succeed())
+ Eventually(IntegrationPodPhase(ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning))
+ Eventually(IntegrationConditionStatus(ns, name, v1.IntegrationConditionReady), TestTimeoutShort).
+ Should(Equal(corev1.ConditionTrue))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello run 1 default"))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello run 2 default"))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello java 1 default"))
+ Eventually(IntegrationLogs(ns, name), TestTimeoutShort).Should(ContainSubstring("Hello java 2 default"))
+ Eventually(DeleteIntegrations(ns), TestTimeoutLong).Should(Equal(0))
+ })
+
+ // Clean up
+ Eventually(DeleteIntegrations(ns), TestTimeoutLong).Should(Equal(0))
+ })
+
/*
* TODO
* The dependency cannot be read by maven while building. See #3708
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index 43eb732f9..4ecc9fd1b 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -812,6 +812,119 @@ func TestRunOutput(t *testing.T) {
assert.Equal(t, fmt.Sprintf("Integration \"%s\" updated\n", integrationName), output)
}
+func TestRunGlob(t *testing.T) {
+ dir, err := os.MkdirTemp("", "camel-k-TestRunGlob-*")
+ if err != nil {
+ t.Error(err)
+ }
+
+ pattern := "camel-k-*.yaml"
+
+ tmpFile1, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ t.Error(err)
+ }
+ defer tmpFile1.Close()
+ assert.Nil(t, tmpFile1.Sync())
+ assert.Nil(t, os.WriteFile(tmpFile1.Name(), []byte(yamlIntegration), 0o400))
+
+ tmpFile2, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ t.Error(err)
+ }
+ defer tmpFile2.Close()
+ assert.Nil(t, tmpFile2.Sync())
+ assert.Nil(t, os.WriteFile(tmpFile2.Name(), []byte(yamlIntegration), 0o400))
+
+ integrationName := "myname"
+
+ _, rootCmd, _ := initializeRunCmdOptionsWithOutput(t)
+
+ file := fmt.Sprintf("%s%c%s*", dir, os.PathSeparator, "camel-k-*") // = dir/camel-k-*
+
+ output, err := test.ExecuteCommand(rootCmd, cmdRun, "--name", integrationName, file)
+ assert.Nil(t, err)
+ assert.Equal(t, fmt.Sprintf("Integration \"%s\" created\n", integrationName), output)
+}
+
+func TestRunGlobAllFiles(t *testing.T) {
+ dir, err := os.MkdirTemp("", "camel-k-TestRunGlobAllFiles-*")
+ if err != nil {
+ t.Error(err)
+ }
+
+ pattern := "camel-k-*.yaml"
+
+ tmpFile1, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ t.Error(err)
+ }
+ defer tmpFile1.Close()
+ assert.Nil(t, tmpFile1.Sync())
+ assert.Nil(t, os.WriteFile(tmpFile1.Name(), []byte(yamlIntegration), 0o400))
+
+ tmpFile2, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ t.Error(err)
+ }
+ defer tmpFile2.Close()
+ assert.Nil(t, tmpFile2.Sync())
+ assert.Nil(t, os.WriteFile(tmpFile2.Name(), []byte(yamlIntegration), 0o400))
+
+ integrationName := "myname"
+
+ _, rootCmd, _ := initializeRunCmdOptionsWithOutput(t)
+
+ file := fmt.Sprintf("%s%c*", dir, os.PathSeparator) // = dir/*
+
+ output, err := test.ExecuteCommand(rootCmd, cmdRun, "--name", integrationName, file)
+ assert.Nil(t, err)
+ assert.Equal(t, fmt.Sprintf("Integration \"%s\" created\n", integrationName), output)
+}
+
+func TestRunGlobChange(t *testing.T) {
+ dir, err := os.MkdirTemp("", "camel-k-TestRunGlobChange-*")
+ if err != nil {
+ t.Error(err)
+ }
+
+ pattern := "camel-k-*.yaml"
+
+ tmpFile1, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ t.Error(err)
+ }
+ defer tmpFile1.Close()
+ assert.Nil(t, tmpFile1.Sync())
+ assert.Nil(t, os.WriteFile(tmpFile1.Name(), []byte(yamlIntegration), 0o400))
+
+ integrationName := "myname"
+
+ _, rootCmd, _ := initializeRunCmdOptionsWithOutput(t)
+
+ file := fmt.Sprintf("%s%c%s", dir, os.PathSeparator, "camel-k-*")
+
+ output, err := test.ExecuteCommand(rootCmd, cmdRun, "--name", integrationName, file)
+ assert.Nil(t, err)
+ assert.Equal(t, fmt.Sprintf("Integration \"%s\" created\n", integrationName), output)
+
+ output, err = test.ExecuteCommand(rootCmd, cmdRun, "--name", integrationName, file)
+ assert.Nil(t, err)
+ assert.Equal(t, fmt.Sprintf("Integration \"%s\" unchanged\n", integrationName), output)
+
+ tmpFile2, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ t.Error(err)
+ }
+ defer tmpFile2.Close()
+ assert.Nil(t, tmpFile2.Sync())
+ assert.Nil(t, os.WriteFile(tmpFile2.Name(), []byte(yamlIntegration), 0o400))
+
+ output, err = test.ExecuteCommand(rootCmd, cmdRun, "--name", integrationName, file)
+ assert.Nil(t, err)
+ assert.Equal(t, fmt.Sprintf("Integration \"%s\" updated\n", integrationName), output)
+}
+
func TestRunOutputWithoutKubernetesCluster(t *testing.T) {
tmpFile, err := os.CreateTemp("", "camel-k-kubeconfig-*")
require.NoError(t, err)
diff --git a/pkg/cmd/source/source.go b/pkg/cmd/source/source.go
index da93c6d7d..7a22cb21a 100644
--- a/pkg/cmd/source/source.go
+++ b/pkg/cmd/source/source.go
@@ -88,8 +88,42 @@ func (s Source) IsYaml() bool {
return strings.HasSuffix(s.Name, ".yaml") || strings.HasSuffix(s.Name, ".yml")
}
+// globSources identifies glob patterns like sources/*.yaml and expand them into individual file paths.
+func globSources(locations []string) ([]string, error) {
+ var sources = make([]string, 0, len(locations))
+
+ for _, src := range locations {
+ glob, err := isGlobCandidate(src)
+ if err != nil {
+ return nil, err
+ }
+
+ if glob {
+ matches, err := filepath.Glob(src)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(matches) > 0 {
+ sources = append(sources, matches...)
+ } else {
+ // leave the original location if there wasn't any matches
+ sources = append(sources, src)
+ }
+ } else {
+ sources = append(sources, src)
+ }
+ }
+ return sources, nil
+}
+
// Resolve resolves sources from a variety of locations including local and remote.
func Resolve(ctx context.Context, locations []string, compress bool, cmd *cobra.Command) ([]Source, error) {
+ locations, err := globSources(locations)
+ if err != nil {
+ return nil, err
+ }
+
sources := make([]Source, 0, len(locations))
for _, location := range locations {
diff --git a/pkg/cmd/source/util.go b/pkg/cmd/source/util.go
index 85e96a984..d90793d6e 100644
--- a/pkg/cmd/source/util.go
+++ b/pkg/cmd/source/util.go
@@ -38,6 +38,26 @@ func IsLocalAndFileExists(uri string) (bool, error) {
// it's not a local file as it matches one of the supporting schemes
return false, nil
}
+ return isExistingFile(uri)
+}
+
+// isGlobCandidate checks if the provided uri doesn't have a supported scheme prefix,
+// and is not an existing file, because then it could be a glob pattern like "sources/*.yaml".
+func isGlobCandidate(uri string) (bool, error) {
+ if hasSupportedScheme(uri) {
+ // it's not a local file as it matches one of the supporting schemes
+ return false, nil
+ }
+
+ exists, err := isExistingFile(uri)
+ if err != nil {
+ return false, err
+ }
+
+ return !exists, nil
+}
+
+func isExistingFile(uri string) (bool, error) {
info, err := os.Stat(uri)
if err != nil {
if os.IsNotExist(err) {
diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go
index 72d549721..ff3426a81 100644
--- a/pkg/resources/resources.go
+++ b/pkg/resources/resources.go
@@ -743,12 +743,12 @@ var assets = func() http.FileSystem {
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x53\xc1\x8e\xdb\x36\x14\xbc\xf3\x2b\x06\xd6\x25\x01\xd6\x72\xdb\x53\xe1\x9e\xdc\xcd\x6e\x2b\x34\xb0\x81\x95\xd3\x20\xc7\x67\xe9\x59\x7a\x58\x8a\x54\x1f\xa9\x55\xb6\x5f\x5f\x90\x96\xbb\x0e\xda\x63\x78\xb1\x05\x8d\xe6\xcd\xbc\x19\x16\x58\x7f\xbf\x63\x0a\x7c\x94\x86\x5d\xe0\x16\xd1\x23\xf6\x8c\xdd\x48\x4d\xcf\xa8\xfd\x39\xce\xa4\x8c\x47\x3f\xb9\x96\xa2\x78\x87\x77\xbb\xfa\xf1\x3d\x26\xd7\xb2\xc2\x3b\x86\x57\x0c\x5e\x [...]
},
- "/camel-catalog-3.2.3.yaml": &vfsgen۰CompressedFileInfo{
- name: "camel-catalog-3.2.3.yaml",
+ "/camel-catalog-3.2.0.yaml": &vfsgen۰CompressedFileInfo{
+ name: "camel-catalog-3.2.0.yaml",
modTime: time.Time{},
uncompressedSize: 87987,
- compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\xbd\x4d\x77\xdb\xb8\xb2\x2e\x3c\xcf\xaf\xe0\xea\x4c\xce\x59\xef\x26\xba\x3b\xd9\xef\xe9\x7b\xfb\x8e\x1c\x27\x4e\xe2\xd8\x89\x13\x79\xa7\xb3\xf7\xa4\x17\x44\x42\x12\x24\x92\xa0\x01\x50\x96\xf3\xeb\xef\x02\x08\x7e\x8a\x2e\x8a\x74\xc1\xd7\x03\x93\x22\x0a\x4f\xa1\x9e\x02\xf1\x4d\xe0\x65\x10\xe2\xfd\xbd\x78\x19\x5c\xf1\x88\x65\x8a\xc5\x81\x16\x81\xde\xb0\xe0\x2c\xa7\xd1\x86\x05\x0b\xb1\xd2\xf7\x54\xb2\xe0\x42\x14\x59\x [...]
+ compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x7d\x5b\x77\xdc\xb6\xb2\xe6\xbb\x7f\x05\x57\xfc\x72\xce\x9a\x4d\x24\x71\xf6\x9c\xcc\xca\x3c\xc9\xb2\x65\x5b\xb6\x6c\xd9\xad\x9d\x78\xef\x97\x2c\x34\x89\xee\x86\x9a\x24\x28\x00\x6c\xb5\xfc\xeb\x67\x01\x04\xaf\x4d\x15\x2f\x2a\x68\xf4\x20\xb2\x89\xc2\x57\xa8\xaf\x40\xdc\x09\xbc\x0c\x42\xbc\xbf\x17\x2f\x83\x4f\x3c\x62\x99\x62\x71\xa0\x45\xa0\x77\x2c\x38\xcb\x69\xb4\x63\xc1\x4a\x6c\xf4\x3d\x95\x2c\xb8\x10\x45\x16\x53\x [...]
},
"/traits.yaml": &vfsgen۰CompressedFileInfo{
name: "traits.yaml",
@@ -761,7 +761,7 @@ var assets = func() http.FileSystem {
fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
fs["/addons"].(os.FileInfo),
fs["/builder"].(os.FileInfo),
- fs["/camel-catalog-3.2.3.yaml"].(os.FileInfo),
+ fs["/camel-catalog-3.2.0.yaml"].(os.FileInfo),
fs["/crd"].(os.FileInfo),
fs["/manager"].(os.FileInfo),
fs["/prometheus"].(os.FileInfo),