Skip to content

Commit

Permalink
feat: add support for glob pattern in run sources
Browse files Browse the repository at this point in the history
  • Loading branch information
rinaldodev authored and squakez committed Jan 4, 2024
1 parent c0a9177 commit bf0e79b
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 4 deletions.
27 changes: 27 additions & 0 deletions 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}");
}
}
27 changes: 27 additions & 0 deletions 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}");
}
}
25 changes: 25 additions & 0 deletions 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"
25 changes: 25 additions & 0 deletions 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"
40 changes: 40 additions & 0 deletions e2e/common/cli/run_test.go
Expand Up @@ -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
Expand Down
113 changes: 113 additions & 0 deletions pkg/cmd/run_test.go
Expand Up @@ -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)
Expand Down
34 changes: 34 additions & 0 deletions pkg/cmd/source/source.go
Expand Up @@ -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 {
Expand Down
20 changes: 20 additions & 0 deletions pkg/cmd/source/util.go
Expand Up @@ -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) {
Expand Down
8 changes: 4 additions & 4 deletions pkg/resources/resources.go

Large diffs are not rendered by default.

0 comments on commit bf0e79b

Please sign in to comment.