This is an automated email from the ASF dual-hosted git repository. tsato 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 36439e167 fix(cli): `kamel local build` doesn't support same dependency notation 36439e167 is described below commit 36439e167964489ad45e6348031d317f1b72003d Author: Tadayoshi Sato <sato.tadayo...@gmail.com> AuthorDate: Wed Jul 13 14:40:54 2022 +0900 fix(cli): `kamel local build` doesn't support same dependency notation This also fixes jitpack dependencies resolution. Fix #2213. --- e2e/local/files/dependency.groovy | 2 +- e2e/local/local_build_test.go | 6 +- e2e/local/local_run_test.go | 25 ++++++++ pkg/apis/camel/v1/integration_types_support.go | 25 +++++--- .../camel/v1/integration_types_support_test.go | 7 +++ pkg/cmd/local.go | 48 +++++++++++++--- pkg/cmd/local_build.go | 48 ++++++---------- pkg/cmd/local_build_test.go | 38 ++++++++++--- pkg/cmd/local_inspect.go | 35 ++++-------- pkg/cmd/local_inspect_test.go | 40 ++++++++++--- pkg/cmd/local_run.go | 57 +++++++------------ pkg/cmd/local_run_test.go | 66 +++++++++++----------- pkg/cmd/{util_dependencies.go => local_util.go} | 45 ++++++--------- ...til_dependencies_test.go => local_util_test.go} | 0 pkg/cmd/root.go | 5 +- pkg/cmd/run.go | 4 +- pkg/util/camel/camel_dependencies.go | 9 +-- 17 files changed, 261 insertions(+), 199 deletions(-) diff --git a/e2e/local/files/dependency.groovy b/e2e/local/files/dependency.groovy index d686acb8a..b5d56df17 100644 --- a/e2e/local/files/dependency.groovy +++ b/e2e/local/files/dependency.groovy @@ -1,4 +1,4 @@ -// camel-k: language=groovy dependency=camel:twitter dependency=mvn:com.google.guava:guava:31.1-jre dependency=github:squakez/samplejp:v1.0 +// camel-k: language=groovy dependency=camel-twitter dependency=mvn:com.google.guava:guava:31.1-jre dependency=github:squakez/samplejp:v1.0 /* * Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/e2e/local/local_build_test.go b/e2e/local/local_build_test.go index ab3440b4b..2ea34f91b 100644 --- a/e2e/local/local_build_test.go +++ b/e2e/local/local_build_test.go @@ -195,7 +195,7 @@ func TestLocalBuildDependenciesOnly(t *testing.T) { file := testutil.MakeTempCopy(t, "files/yaml.yaml") dir := testutil.MakeTempDir(t) - kamelBuild := KamelWithContext(ctx, "local", "build", file, "--integration-directory", dir, "--dependencies-only") + kamelBuild := KamelWithContext(ctx, "local", "build", file, "--integration-directory", dir, "--dependencies-only", "-d", "camel-amqp") go func() { err := kamelBuild.Execute() @@ -206,6 +206,7 @@ func TestLocalBuildDependenciesOnly(t *testing.T) { Eventually(dir+"/dependencies", TestTimeoutShort).Should(BeADirectory()) Eventually(dependency(dir, "org.apache.camel.camel-timer-%s.jar", camelVersion), TestTimeoutShort).Should(BeAnExistingFile()) Eventually(dependency(dir, "org.apache.camel.camel-log-%s.jar", camelVersion), TestTimeoutShort).Should(BeAnExistingFile()) + Eventually(dependency(dir, "org.apache.camel.camel-amqp-%s.jar", camelVersion), TestTimeoutShort).Should(BeAnExistingFile()) Expect(dir + "/properties").ShouldNot(BeADirectory()) Expect(dir + "/routes/yaml.yaml").ShouldNot(BeAnExistingFile()) } @@ -219,7 +220,7 @@ func TestLocalBuildModelineDependencies(t *testing.T) { file := testutil.MakeTempCopy(t, "files/dependency.groovy") dir := testutil.MakeTempDir(t) - kamelBuild := KamelWithContext(ctx, "local", "build", file, "--integration-directory", dir) + kamelBuild := KamelWithContext(ctx, "local", "build", file, "--integration-directory", dir, "-d", "camel-amqp") go func() { err := kamelBuild.Execute() @@ -229,6 +230,7 @@ func TestLocalBuildModelineDependencies(t *testing.T) { Eventually(dir+"/dependencies", TestTimeoutShort).Should(BeADirectory()) Eventually(dependency(dir, "org.apache.camel.camel-timer-%s.jar", camelVersion), TestTimeoutShort).Should(BeAnExistingFile()) Eventually(dependency(dir, "org.apache.camel.camel-log-%s.jar", camelVersion), TestTimeoutShort).Should(BeAnExistingFile()) + Eventually(dependency(dir, "org.apache.camel.camel-amqp-%s.jar", camelVersion), TestTimeoutShort).Should(BeAnExistingFile()) // camel dependency Eventually(dependency(dir, "org.apache.camel.camel-twitter-%s.jar", camelVersion), TestTimeoutMedium).Should(BeAnExistingFile()) // mvn dependency diff --git a/e2e/local/local_run_test.go b/e2e/local/local_run_test.go index e016b0119..c703e3825 100644 --- a/e2e/local/local_run_test.go +++ b/e2e/local/local_run_test.go @@ -60,6 +60,31 @@ func TestLocalRun(t *testing.T) { Eventually(logScanner.IsFound("Magicstring!"), TestTimeoutMedium).Should(BeTrue()) } +func TestLocalRunWithDependencies(t *testing.T) { + RegisterTestingT(t) + + ctx, cancel := context.WithCancel(TestContext) + defer cancel() + piper, pipew := io.Pipe() + defer pipew.Close() + defer piper.Close() + + file := testutil.MakeTempCopy(t, "files/dependency.groovy") + + kamelRun := KamelWithContext(ctx, "local", "run", file, "-d", "camel-amqp") + kamelRun.SetOut(pipew) + + logScanner := testutil.NewLogScanner(ctx, piper, "Magicstring!") + + go func() { + err := kamelRun.Execute() + assert.NoError(t, err) + cancel() + }() + + Eventually(logScanner.IsFound("Magicstring!"), TestTimeoutMedium).Should(BeTrue()) +} + func TestLocalRunContainerize(t *testing.T) { RegisterTestingT(t) diff --git a/pkg/apis/camel/v1/integration_types_support.go b/pkg/apis/camel/v1/integration_types_support.go index 5b6fd9130..50316a081 100644 --- a/pkg/apis/camel/v1/integration_types_support.go +++ b/pkg/apis/camel/v1/integration_types_support.go @@ -105,14 +105,7 @@ func (in *IntegrationSpec) AddDependency(dependency string) { if in.Dependencies == nil { in.Dependencies = make([]string, 0) } - newDep := dependency - if strings.HasPrefix(newDep, "camel-quarkus-") { - newDep = "camel:" + strings.TrimPrefix(dependency, "camel-quarkus-") - } else if strings.HasPrefix(newDep, "camel-quarkus:") { - newDep = "camel:" + strings.TrimPrefix(dependency, "camel-quarkus:") - } else if strings.HasPrefix(newDep, "camel-") { - newDep = "camel:" + strings.TrimPrefix(dependency, "camel-") - } + newDep := NormalizeDependency(dependency) for _, d := range in.Dependencies { if d == newDep { return @@ -121,6 +114,22 @@ func (in *IntegrationSpec) AddDependency(dependency string) { in.Dependencies = append(in.Dependencies, newDep) } +// NormalizeDependency converts different forms of camel dependencies +// -- `camel-xxx`, `camel-quarkus-xxx`, and `camel-quarkus:xxx` -- +// into the unified form `camel:xxx`. +func NormalizeDependency(dependency string) string { + newDep := dependency + switch { + case strings.HasPrefix(newDep, "camel-quarkus-"): + newDep = "camel:" + strings.TrimPrefix(dependency, "camel-quarkus-") + case strings.HasPrefix(newDep, "camel-quarkus:"): + newDep = "camel:" + strings.TrimPrefix(dependency, "camel-quarkus:") + case strings.HasPrefix(newDep, "camel-"): + newDep = "camel:" + strings.TrimPrefix(dependency, "camel-") + } + return newDep +} + // GetConfigurationProperty returns a configuration property func (in *IntegrationSpec) GetConfigurationProperty(property string) string { for _, confSpec := range in.Configuration { diff --git a/pkg/apis/camel/v1/integration_types_support_test.go b/pkg/apis/camel/v1/integration_types_support_test.go index 6f4d4827c..7a5e81d86 100644 --- a/pkg/apis/camel/v1/integration_types_support_test.go +++ b/pkg/apis/camel/v1/integration_types_support_test.go @@ -74,6 +74,13 @@ func TestAddDependency(t *testing.T) { assert.Equal(t, integration.Dependencies, []string{"file:dep"}) } +func TestNormalizeDependency(t *testing.T) { + assert.Equal(t, "camel:file", NormalizeDependency("camel-file")) + assert.Equal(t, "camel:file", NormalizeDependency("camel:file")) + assert.Equal(t, "camel:file", NormalizeDependency("camel-quarkus-file")) + assert.Equal(t, "camel:file", NormalizeDependency("camel-quarkus:file")) +} + func TestGetConfigurationProperty(t *testing.T) { integration := IntegrationSpec{} integration.AddConfiguration("property", "key1=value1") diff --git a/pkg/cmd/local.go b/pkg/cmd/local.go index cac6e51d5..1a480490f 100644 --- a/pkg/cmd/local.go +++ b/pkg/cmd/local.go @@ -20,25 +20,57 @@ package cmd import ( "fmt" + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" "github.com/spf13/cobra" ) // newCmdLocal -- Add local kamel subcommand with several other subcommands of its own. -func newCmdLocal(rootCmdOptions *RootCmdOptions) *cobra.Command { +func newCmdLocal(rootCmdOptions *RootCmdOptions) (*cobra.Command, *LocalCmdOptions) { + options := LocalCmdOptions{ + RootCmdOptions: rootCmdOptions, + } + cmd := cobra.Command{ - Use: "local [sub-command]", - Short: "Perform integration actions locally.", - Long: `Perform integration actions locally given a set of input integration files.`, + Use: "local [sub-command]", + Short: "Perform integration actions locally.", + Long: `Perform integration actions locally given a set of input integration files.`, + PersistentPreRunE: options.persistentPreRun, Annotations: map[string]string{ offlineCommandLabel: "true", }, } - cmd.AddCommand(cmdOnly(newCmdLocalBuild(rootCmdOptions))) - cmd.AddCommand(cmdOnly(newCmdLocalInspect(rootCmdOptions))) - cmd.AddCommand(cmdOnly(newCmdLocalRun(rootCmdOptions))) + cmd.PersistentFlags().StringArrayVarP(&options.Dependencies, "dependency", "d", nil, usageDependency) + + // hidden flags for compatibility with kamel run + cmd.PersistentFlags().StringArrayVarP(&options.Traits, "trait", "t", nil, "") + if err := cmd.PersistentFlags().MarkHidden("trait"); err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), err.Error()) + } + + cmd.AddCommand(cmdOnly(newCmdLocalBuild(&options))) + cmd.AddCommand(cmdOnly(newCmdLocalInspect(&options))) + cmd.AddCommand(cmdOnly(newCmdLocalRun(&options))) + + return &cmd, &options +} + +type LocalCmdOptions struct { + *RootCmdOptions + Dependencies []string `mapstructure:"dependencies"` + Traits []string `mapstructure:"traits"` +} + +func (o *LocalCmdOptions) persistentPreRun(cmd *cobra.Command, args []string) error { + // pre-process dependencies + for i, dependency := range o.Dependencies { + o.Dependencies[i] = v1.NormalizeDependency(dependency) + } + + // validate traits + warnTraitUsages(cmd, o.Traits) - return &cmd + return nil } func warnTraitUsages(cmd *cobra.Command, traits []string) { diff --git a/pkg/cmd/local_build.go b/pkg/cmd/local_build.go index 222c510de..5c0d72cd4 100644 --- a/pkg/cmd/local_build.go +++ b/pkg/cmd/local_build.go @@ -26,9 +26,9 @@ import ( "github.com/apache/camel-k/pkg/util" ) -func newCmdLocalBuild(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localBuildCmdOptions) { +func newCmdLocalBuild(localCmdOptions *LocalCmdOptions) (*cobra.Command, *localBuildCmdOptions) { options := localBuildCmdOptions{ - RootCmdOptions: rootCmdOptions, + LocalCmdOptions: localCmdOptions, } cmd := cobra.Command{ @@ -37,7 +37,7 @@ func newCmdLocalBuild(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localBui Long: `Build integration images locally for containerized integrations.`, PreRunE: decode(&options), RunE: func(cmd *cobra.Command, args []string) error { - if err := options.validate(cmd, args); err != nil { + if err := options.validate(args); err != nil { return err } if err := options.init(args); err != nil { @@ -64,50 +64,38 @@ func newCmdLocalBuild(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localBui cmd.Flags().String("integration-directory", "", "Directory to hold local integration files.") cmd.Flags().StringArray("property-file", nil, "Add a property file to the integration.") cmd.Flags().StringArrayP("property", "p", nil, "Add a Camel property to the integration.") - cmd.Flags().StringArrayP("dependency", "d", nil, "Add an additional dependency") cmd.Flags().StringArray("maven-repository", nil, "Use a maven repository") - // hidden flags for compatibility with kamel run - cmd.Flags().StringArrayP("trait", "t", nil, "") - if err := cmd.Flags().MarkHidden("trait"); err != nil { - fmt.Fprintln(cmd.ErrOrStderr(), err.Error()) - } - return &cmd, &options } type localBuildCmdOptions struct { - *RootCmdOptions - BaseImage bool `mapstructure:"base-image"` - DependenciesOnly bool `mapstructure:"dependencies-only"` - ContainerRegistry string `mapstructure:"container-registry"` - Image string `mapstructure:"image"` - IntegrationDirectory string `mapstructure:"integration-directory"` - AdditionalDependencies []string `mapstructure:"dependencies"` - Properties []string `mapstructure:"properties"` - PropertyFiles []string `mapstructure:"property-files"` - MavenRepositories []string `mapstructure:"maven-repositories"` - Traits []string `mapstructure:"traits"` + *LocalCmdOptions + BaseImage bool `mapstructure:"base-image"` + DependenciesOnly bool `mapstructure:"dependencies-only"` + ContainerRegistry string `mapstructure:"container-registry"` + Image string `mapstructure:"image"` + IntegrationDirectory string `mapstructure:"integration-directory"` + Properties []string `mapstructure:"properties"` + PropertyFiles []string `mapstructure:"property-files"` + MavenRepositories []string `mapstructure:"maven-repositories"` } -func (command *localBuildCmdOptions) validate(cmd *cobra.Command, args []string) error { +func (command *localBuildCmdOptions) validate(args []string) error { // Validate integration files. if len(args) > 0 { - err := validateIntegrationFiles(args) - if err != nil { + if err := validateIntegrationFiles(args); err != nil { return err } } // Validate additional dependencies specified by the user. - err := validateAdditionalDependencies(command.AdditionalDependencies) - if err != nil { + if err := validateDependencies(command.Dependencies); err != nil { return err } // Validate properties file. - err = validateFiles(command.PropertyFiles) - if err != nil { + if err := validateFiles(command.PropertyFiles); err != nil { return err } @@ -136,8 +124,6 @@ func (command *localBuildCmdOptions) validate(cmd *cobra.Command, args []string) return errors.New("to output dependencies the integration directory flag must be set") } - warnTraitUsages(cmd, command.Traits) - return nil } @@ -176,7 +162,7 @@ func (command *localBuildCmdOptions) run(cmd *cobra.Command, args []string) erro var dependenciesList, propertyFilesList []string routeFiles := args if !command.BaseImage { - dependencies, err := getDependencies(command.Context, args, command.AdditionalDependencies, command.MavenRepositories, true) + dependencies, err := GetDependencies(command.Context, args, command.Dependencies, command.MavenRepositories, true) if err != nil { return err } diff --git a/pkg/cmd/local_build_test.go b/pkg/cmd/local_build_test.go index 8519bd1a4..5c9db5ff2 100644 --- a/pkg/cmd/local_build_test.go +++ b/pkg/cmd/local_build_test.go @@ -22,28 +22,52 @@ import ( "github.com/apache/camel-k/pkg/util/test" "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func addTestLocalBuildCmd(rootCmdOptions *RootCmdOptions, rootCmd *cobra.Command) *localBuildCmdOptions { + localCmd, localCmdOptions := newCmdLocal(rootCmdOptions) + // remove predefined sub commands + localCmd.RemoveCommand(localCmd.Commands()...) // add a testing version of build Command - localBuildCmd, localBuildCmdOptions := newCmdLocalBuild(rootCmdOptions) + localBuildCmd, localBuildCmdOptions := newCmdLocalBuild(localCmdOptions) localBuildCmd.RunE = func(c *cobra.Command, args []string) error { return nil } localBuildCmd.Args = test.ArbitraryArgs - rootCmd.AddCommand(localBuildCmd) + localCmd.AddCommand(localBuildCmd) + rootCmd.AddCommand(localCmd) return localBuildCmdOptions } func TestLocalBuildAcceptsTraits(t *testing.T) { options, rootCmd := kamelTestPreAddCommandInit() - addTestLocalBuildCmd(options, rootCmd) + kamelTestPostAddCommandInit(t, rootCmd) + + _, err := test.ExecuteCommand(rootCmd, "local", "build", "route.java", + "-t", "jolokia.enabled=true", + "--trait", "prometheus.enabled=true") + + require.NoError(t, err) +} +func TestLocalBuildWithDependencies(t *testing.T) { + options, rootCmd := kamelTestPreAddCommandInit() + localBuildCmdOptions := addTestLocalBuildCmd(options, rootCmd) kamelTestPostAddCommandInit(t, rootCmd) - _, err := test.ExecuteCommand(rootCmd, "build", "route.java", "-t", "jolokia.enabled=true", "--trait", "prometheus.enabled=true") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } + _, err := test.ExecuteCommand(rootCmd, "local", "build", "route.java", + "-d", "camel-amqp", + "-d", "camel:bean", + "-d", "camel-quarkus-controlbus", + "-d", "camel-quarkus:directvm", + "--dependency", "mvn:test:component:1.0.0") + + require.NoError(t, err) + assert.Len(t, localBuildCmdOptions.Dependencies, 5) + assert.ElementsMatch(t, localBuildCmdOptions.Dependencies, []string{ + "camel:amqp", "camel:bean", "camel:controlbus", "camel:directvm", "mvn:test:component:1.0.0", + }) } diff --git a/pkg/cmd/local_inspect.go b/pkg/cmd/local_inspect.go index e4e6c95cf..54fca9862 100644 --- a/pkg/cmd/local_inspect.go +++ b/pkg/cmd/local_inspect.go @@ -23,9 +23,9 @@ import ( "github.com/spf13/cobra" ) -func newCmdLocalInspect(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localInspectCmdOptions) { +func newCmdLocalInspect(localCmdOptions *LocalCmdOptions) (*cobra.Command, *localInspectCmdOptions) { options := localInspectCmdOptions{ - RootCmdOptions: rootCmdOptions, + LocalCmdOptions: localCmdOptions, } cmd := cobra.Command{ @@ -36,7 +36,7 @@ top level dependencies only. When --all-dependencies is enabled, the transitive will be generated by calling Maven and then printed in the selected output format.`, PreRunE: decode(&options), RunE: func(cmd *cobra.Command, args []string) error { - if err := options.validate(cmd, args); err != nil { + if err := options.validate(args); err != nil { return err } if err := options.init(); err != nil { @@ -57,41 +57,28 @@ will be generated by calling Maven and then printed in the selected output forma } cmd.Flags().Bool("all-dependencies", false, "Enable computation of transitive dependencies.") - cmd.Flags().StringArrayP("dependency", "d", nil, additionalDependencyUsageMessage) cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml") cmd.Flags().StringArray("maven-repository", nil, "Use a maven repository") - // hidden flags for compatibility with kamel run - cmd.Flags().StringArrayP("trait", "t", nil, "") - if err := cmd.Flags().MarkHidden("trait"); err != nil { - fmt.Fprintln(cmd.ErrOrStderr(), err.Error()) - } - return &cmd, &options } type localInspectCmdOptions struct { - *RootCmdOptions - AllDependencies bool `mapstructure:"all-dependencies"` - OutputFormat string `mapstructure:"output"` - AdditionalDependencies []string `mapstructure:"dependencies"` - MavenRepositories []string `mapstructure:"maven-repositories"` - Traits []string `mapstructure:"traits"` + *LocalCmdOptions + AllDependencies bool `mapstructure:"all-dependencies"` + OutputFormat string `mapstructure:"output"` + MavenRepositories []string `mapstructure:"maven-repositories"` } -func (command *localInspectCmdOptions) validate(cmd *cobra.Command, args []string) error { - err := validateIntegrationFiles(args) - if err != nil { +func (command *localInspectCmdOptions) validate(args []string) error { + if err := validateIntegrationFiles(args); err != nil { return err } - err = validateAdditionalDependencies(command.AdditionalDependencies) - if err != nil { + if err := validateDependencies(command.Dependencies); err != nil { return err } - warnTraitUsages(cmd, command.Traits) - return nil } @@ -100,7 +87,7 @@ func (command *localInspectCmdOptions) init() error { } func (command *localInspectCmdOptions) run(cmd *cobra.Command, args []string) error { - dependencies, err := getDependencies(command.Context, args, command.AdditionalDependencies, command.MavenRepositories, command.AllDependencies) + dependencies, err := GetDependencies(command.Context, args, command.Dependencies, command.MavenRepositories, command.AllDependencies) if err != nil { return err } diff --git a/pkg/cmd/local_inspect_test.go b/pkg/cmd/local_inspect_test.go index 993ea88b9..92fad62be 100644 --- a/pkg/cmd/local_inspect_test.go +++ b/pkg/cmd/local_inspect_test.go @@ -22,28 +22,52 @@ import ( "github.com/apache/camel-k/pkg/util/test" "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func addTestLocalInspectCmd(rootCmdOptions *RootCmdOptions, rootCmd *cobra.Command) *localInspectCmdOptions { + localCmd, localCmdOptions := newCmdLocal(rootCmdOptions) + // remove predefined sub commands + localCmd.RemoveCommand(localCmd.Commands()...) // add a testing version of inspect Command - localInspectCmd, localInspectCmdOptions := newCmdLocalInspect(rootCmdOptions) + localInspectCmd, localInspectCmdOptions := newCmdLocalInspect(localCmdOptions) localInspectCmd.RunE = func(c *cobra.Command, args []string) error { return nil } localInspectCmd.Args = test.ArbitraryArgs - rootCmd.AddCommand(localInspectCmd) + localCmd.AddCommand(localInspectCmd) + rootCmd.AddCommand(localCmd) return localInspectCmdOptions } func TestLocalInspectAcceptsTraits(t *testing.T) { - options, rootCmd := kamelTestPreAddCommandInit() + rootOptions, rootCmd := kamelTestPreAddCommandInit() + addTestLocalInspectCmd(rootOptions, rootCmd) + kamelTestPostAddCommandInit(t, rootCmd) + + _, err := test.ExecuteCommand(rootCmd, "local", "inspect", "route.java", + "-t", "jolokia.enabled=true", + "--trait", "prometheus.enabled=true") - addTestLocalInspectCmd(options, rootCmd) + require.NoError(t, err) +} +func TestLocalInspectWithDependencies(t *testing.T) { + rootOptions, rootCmd := kamelTestPreAddCommandInit() + options := addTestLocalInspectCmd(rootOptions, rootCmd) kamelTestPostAddCommandInit(t, rootCmd) - _, err := test.ExecuteCommand(rootCmd, "inspect", "route.java", "-t", "jolokia.enabled=true", "--trait", "prometheus.enabled=true") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } + _, err := test.ExecuteCommand(rootCmd, "local", "inspect", "route.java", + "-d", "camel-amqp", + "-d", "camel:bean", + "-d", "camel-quarkus-controlbus", + "-d", "camel-quarkus:directvm", + "--dependency", "mvn:test:component:1.0.0") + + require.NoError(t, err) + assert.Len(t, options.Dependencies, 5) + assert.ElementsMatch(t, options.Dependencies, []string{ + "camel:amqp", "camel:bean", "camel:controlbus", "camel:directvm", "mvn:test:component:1.0.0", + }) } diff --git a/pkg/cmd/local_run.go b/pkg/cmd/local_run.go index 1e347608c..ec021ed0d 100644 --- a/pkg/cmd/local_run.go +++ b/pkg/cmd/local_run.go @@ -29,9 +29,9 @@ import ( "github.com/apache/camel-k/pkg/util" ) -func newCmdLocalRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localRunCmdOptions) { +func newCmdLocalRun(localCmdOptions *LocalCmdOptions) (*cobra.Command, *localRunCmdOptions) { options := localRunCmdOptions{ - RootCmdOptions: rootCmdOptions, + LocalCmdOptions: localCmdOptions, } cmd := cobra.Command{ @@ -40,7 +40,7 @@ func newCmdLocalRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localRunCm Long: `Run integration locally using the input integration files.`, PreRunE: decode(&options), RunE: func(cmd *cobra.Command, args []string) error { - if err := options.validate(cmd, args); err != nil { + if err := options.validate(args); err != nil { return err } if err := options.init(); err != nil { @@ -80,51 +80,39 @@ func newCmdLocalRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *localRunCm cmd.Flags().StringArrayP("env", "e", nil, "Flag to specify an environment variable [--env VARIABLE=value].") cmd.Flags().StringArray("property-file", nil, "Add a property file to the integration.") cmd.Flags().StringArrayP("property", "p", nil, "Add a Camel property to the integration.") - cmd.Flags().StringArrayP("dependency", "d", nil, additionalDependencyUsageMessage) cmd.Flags().StringArray("maven-repository", nil, "Use a maven repository") - // hidden flags for compatibility with kamel run - cmd.Flags().StringArrayP("trait", "t", nil, "") - if err := cmd.Flags().MarkHidden("trait"); err != nil { - fmt.Fprintln(cmd.ErrOrStderr(), err.Error()) - } - return &cmd, &options } type localRunCmdOptions struct { - *RootCmdOptions - Containerize bool `mapstructure:"containerize"` - Image string `mapstructure:"image"` - Network string `mapstructure:"network"` - IntegrationDirectory string `mapstructure:"integration-directory"` - EnvironmentVariables []string `mapstructure:"envs"` - PropertyFiles []string `mapstructure:"property-files"` - Properties []string `mapstructure:"properties"` - AdditionalDependencies []string `mapstructure:"dependencies"` - MavenRepositories []string `mapstructure:"maven-repositories"` - Traits []string `mapstructure:"traits"` + *LocalCmdOptions + Containerize bool `mapstructure:"containerize"` + Image string `mapstructure:"image"` + Network string `mapstructure:"network"` + IntegrationDirectory string `mapstructure:"integration-directory"` + EnvironmentVariables []string `mapstructure:"envs"` + PropertyFiles []string `mapstructure:"property-files"` + Properties []string `mapstructure:"properties"` + MavenRepositories []string `mapstructure:"maven-repositories"` } -func (command *localRunCmdOptions) validate(cmd *cobra.Command, args []string) error { +func (command *localRunCmdOptions) validate(args []string) error { // Validate integration files when no image is provided and we are // not running an already locally-built integration. if command.Image == "" && command.IntegrationDirectory == "" { - err := validateIntegrationFiles(args) - if err != nil { + if err := validateIntegrationFiles(args); err != nil { return err } } // Validate additional dependencies specified by the user. - err := validateAdditionalDependencies(command.AdditionalDependencies) - if err != nil { + if err := validateDependencies(command.Dependencies); err != nil { return err } // Validate properties file. - err = validatePropertyFiles(command.PropertyFiles) - if err != nil { + if err := validatePropertyFiles(command.PropertyFiles); err != nil { return err } @@ -133,20 +121,16 @@ func (command *localRunCmdOptions) validate(cmd *cobra.Command, args []string) e return errors.New("containerization is active but no image name has been provided") } - warnTraitUsages(cmd, command.Traits) - return nil } func (command *localRunCmdOptions) init() error { if command.Containerize { - err := createDockerBaseWorkingDirectory() - if err != nil { + if err := createDockerBaseWorkingDirectory(); err != nil { return err } - err = createDockerWorkingDirectory() - if err != nil { + if err := createDockerWorkingDirectory(); err != nil { return err } } @@ -162,8 +146,7 @@ func (command *localRunCmdOptions) run(cmd *cobra.Command, args []string) error // If local run is provided with an image name, it will just run the image locally and exit. if command.Image != "" && !command.Containerize { // Run image locally. - err := runIntegrationImage(command.Context, command.Image, cmd.OutOrStdout(), cmd.ErrOrStderr()) - if err != nil { + if err := runIntegrationImage(command.Context, command.Image, cmd.OutOrStdout(), cmd.ErrOrStderr()); err != nil { return err } @@ -209,7 +192,7 @@ func (command *localRunCmdOptions) run(cmd *cobra.Command, args []string) error return err } } else { - computedDependencies, err := getDependencies(command.Context, args, command.AdditionalDependencies, command.MavenRepositories, true) + computedDependencies, err := GetDependencies(command.Context, args, command.Dependencies, command.MavenRepositories, true) if err != nil { return err } diff --git a/pkg/cmd/local_run_test.go b/pkg/cmd/local_run_test.go index 1e4c08093..d6bdab05e 100644 --- a/pkg/cmd/local_run_test.go +++ b/pkg/cmd/local_run_test.go @@ -22,31 +22,35 @@ import ( "github.com/apache/camel-k/pkg/util/test" "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func addTestLocalRunCmd(rootCmdOptions *RootCmdOptions, rootCmd *cobra.Command) *localRunCmdOptions { + localCmd, localCmdOptions := newCmdLocal(rootCmdOptions) + // remove predefined sub commands + localCmd.RemoveCommand(localCmd.Commands()...) // add a testing version of run Command - localRunCmd, localRunCmdOptions := newCmdLocalRun(rootCmdOptions) + localRunCmd, localRunCmdOptions := newCmdLocalRun(localCmdOptions) localRunCmd.RunE = func(c *cobra.Command, args []string) error { return nil } localRunCmd.Args = test.ArbitraryArgs - rootCmd.AddCommand(localRunCmd) + localCmd.AddCommand(localRunCmd) + rootCmd.AddCommand(localCmd) return localRunCmdOptions } func TestLocalRunPropertyFileFlag(t *testing.T) { options, rootCmd := kamelTestPreAddCommandInit() - localRunCmdOptions := addTestLocalRunCmd(options, rootCmd) - kamelTestPostAddCommandInit(t, rootCmd) - _, err := test.ExecuteCommand(rootCmd, "run", "route.java", "--property-file", "file1.properties", "--property-file", "file2.properties") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } + _, err := test.ExecuteCommand(rootCmd, "local", "run", "route.java", + "--property-file", "file1.properties", + "--property-file", "file2.properties") + require.NoError(t, err) if len(localRunCmdOptions.PropertyFiles) != 2 { t.Fatalf("Property files expected to contain: \n %v elements\nGot:\n %v elements\n", 2, len(localRunCmdOptions.PropertyFiles)) } @@ -57,16 +61,14 @@ func TestLocalRunPropertyFileFlag(t *testing.T) { func TestLocalRunPropertiesFlag(t *testing.T) { options, rootCmd := kamelTestPreAddCommandInit() - localRunCmdOptions := addTestLocalRunCmd(options, rootCmd) - kamelTestPostAddCommandInit(t, rootCmd) - _, err := test.ExecuteCommand(rootCmd, "run", "route.java", "-p", "prop1=value1", "-p", "prop2=value2") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } + _, err := test.ExecuteCommand(rootCmd, "local", "run", "route.java", + "-p", "prop1=value1", + "-p", "prop2=value2") + require.NoError(t, err) if len(localRunCmdOptions.Properties) != 2 { t.Fatalf("Additional dependencies expected to contain: \n %v elements\nGot:\n %v elements\n", 2, len(localRunCmdOptions.Properties)) } @@ -77,33 +79,31 @@ func TestLocalRunPropertiesFlag(t *testing.T) { func TestLocalRunAdditionalDependenciesFlag(t *testing.T) { options, rootCmd := kamelTestPreAddCommandInit() - localRunCmdOptions := addTestLocalRunCmd(options, rootCmd) - kamelTestPostAddCommandInit(t, rootCmd) - _, err := test.ExecuteCommand(rootCmd, "run", "route.java", "-d", "mvn:camel-component-1", "-d", "mvn:camel-component-2") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - if len(localRunCmdOptions.AdditionalDependencies) != 2 { - t.Fatalf("Additional dependencies expected to contain: \n %v elements\nGot:\n %v elements\n", 2, len(localRunCmdOptions.AdditionalDependencies)) - } - if localRunCmdOptions.AdditionalDependencies[0] != "mvn:camel-component-1" || localRunCmdOptions.AdditionalDependencies[1] != "mvn:camel-component-2" { - t.Fatalf("Additional dependencies expected to be: \n %v\nGot:\n %v\n", "[mvn:camel-component-1, mvn:camel-component-2]", localRunCmdOptions.AdditionalDependencies) - } + _, err := test.ExecuteCommand(rootCmd, "local", "run", "route.java", + "-d", "camel-amqp", + "-d", "camel:bean", + "-d", "camel-quarkus-controlbus", + "-d", "camel-quarkus:directvm", + "--dependency", "mvn:test:component:1.0.0") + + require.NoError(t, err) + assert.Len(t, localRunCmdOptions.Dependencies, 5) + assert.ElementsMatch(t, localRunCmdOptions.Dependencies, []string{ + "camel:amqp", "camel:bean", "camel:controlbus", "camel:directvm", "mvn:test:component:1.0.0", + }) } func TestLocalRunAcceptsTraits(t *testing.T) { options, rootCmd := kamelTestPreAddCommandInit() - addTestLocalRunCmd(options, rootCmd) - kamelTestPostAddCommandInit(t, rootCmd) - _, err := test.ExecuteCommand(rootCmd, "run", "route.java", "-t", "jolokia.enabled=true", "--trait", "prometheus.enabled=true") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } + _, err := test.ExecuteCommand(rootCmd, "local", "run", "route.java", + "-t", "jolokia.enabled=true", + "--trait", "prometheus.enabled=true") + + require.NoError(t, err) } diff --git a/pkg/cmd/util_dependencies.go b/pkg/cmd/local_util.go similarity index 91% rename from pkg/cmd/util_dependencies.go rename to pkg/cmd/local_util.go index e8f609d3f..c9b04feda 100644 --- a/pkg/cmd/util_dependencies.go +++ b/pkg/cmd/local_util.go @@ -38,13 +38,14 @@ import ( "github.com/apache/camel-k/pkg/util/maven" ) -var acceptedDependencyTypes = []string{"bom", "camel", "camel-k", "camel-quarkus", "mvn", "github"} - -var additionalDependencyUsageMessage = `Additional top-level dependencies are specified with the format: -<type>:<dependency-name> -where <type> is one of {` + strings.Join(acceptedDependencyTypes, "|") + `}.` +var acceptedDependencyTypes = []string{ + "bom", "camel", "camel-k", "camel-quarkus", "mvn", + // jitpack + "github", "gitlab", "bitbucket", "gitee", "azure", +} -func getDependencies(ctx context.Context, args []string, additionalDependencies []string, repositories []string, allDependencies bool) ([]string, error) { +// GetDependencies resolves and gets the list of dependencies from catalog and sources. +func GetDependencies(ctx context.Context, args []string, additionalDependencies []string, repositories []string, allDependencies bool) ([]string, error) { // Fetch existing catalog or create new one if one does not already exist catalog, err := createCamelCatalog(ctx) if err != nil { @@ -109,8 +110,7 @@ func getTransitiveDependencies(ctx context.Context, catalog *camel.RuntimeCatalo catalog.CamelCatalogSpec.Runtime.Metadata["quarkus.version"], ) - err := camel.ManageIntegrationDependencies(&project, dependencies, catalog) - if err != nil { + if err := camel.ManageIntegrationDependencies(&project, dependencies, catalog); err != nil { return nil, err } @@ -132,8 +132,7 @@ func getTransitiveDependencies(ctx context.Context, catalog *camel.RuntimeCatalo // Make maven command less verbose mc.AdditionalArguments = append(mc.AdditionalArguments, "-q") - err = builder.BuildQuarkusRunnerCommon(ctx, mc, project) - if err != nil { + if err := builder.BuildQuarkusRunnerCommon(ctx, mc, project); err != nil { return nil, err } @@ -293,31 +292,19 @@ func validateFiles(args []string) error { return nil } -func validateAdditionalDependencies(additionalDependencies []string) error { - // Validate list of additional dependencies i.e. make sure that each dependency has a valid type - for _, additionalDependency := range additionalDependencies { - isValid := validateDependency(additionalDependency) - if !isValid { - return errors.New("Unexpected type for user-provided dependency: " + additionalDependency + ". " + additionalDependencyUsageMessage) +// validateDependencies validates list of additional dependencies i.e. makes sure +// that each dependency has a valid type. +func validateDependencies(dependencies []string) error { + for _, dependency := range dependencies { + depType := strings.Split(dependency, ":")[0] + if !util.StringSliceExists(acceptedDependencyTypes, depType) { + return fmt.Errorf("dependency is not valid: %s", dependency) } } return nil } -func validateDependency(additionalDependency string) bool { - dependencyComponents := strings.Split(additionalDependency, ":") - - TypeIsValid := false - for _, dependencyType := range acceptedDependencyTypes { - if dependencyType == dependencyComponents[0] { - TypeIsValid = true - } - } - - return TypeIsValid -} - func validateIntegrationFiles(args []string) error { // If no source files have been provided there is nothing to inspect. if len(args) == 0 { diff --git a/pkg/cmd/util_dependencies_test.go b/pkg/cmd/local_util_test.go similarity index 100% rename from pkg/cmd/util_dependencies_test.go rename to pkg/cmd/local_util_test.go diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index abdde0747..c32a187e1 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -59,7 +59,6 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) { ContextCancel: childCancel, } - var err error cmd := kamelPreAddCommandInit(&options) addKamelSubcommands(cmd, &options) @@ -67,7 +66,7 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) { return cmd, err } - err = kamelPostAddCommandInit(cmd) + err := kamelPostAddCommandInit(cmd) return cmd, err } @@ -148,7 +147,7 @@ func addKamelSubcommands(cmd *cobra.Command, options *RootCmdOptions) { cmd.AddCommand(cmdOnly(newCmdInit(options))) cmd.AddCommand(cmdOnly(newCmdDebug(options))) cmd.AddCommand(cmdOnly(newCmdDump(options))) - cmd.AddCommand(newCmdLocal(options)) + cmd.AddCommand(cmdOnly(newCmdLocal(options))) cmd.AddCommand(cmdOnly(newCmdBind(options))) cmd.AddCommand(cmdOnly(newCmdPromote(options))) cmd.AddCommand(newCmdKamelet(options)) diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index 27df5e167..9a02f5064 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -75,6 +75,8 @@ import ( "github.com/apache/camel-k/pkg/util/watch" ) +const usageDependency = `A dependency that should be included, e.g., "-d camel-mail" for a Camel component, "-d mvn:org.my:app:1.0" for a Maven dependency or "file://localPath[?targetPath=<path>®istry=<registry_URL>&skipChecksums=<true>&skipPOM=<true>]" for local files (experimental)` + func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions) { options := runCmdOptions{ RootCmdOptions: rootCmdOptions, @@ -94,7 +96,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions) cmd.Flags().String("name", "", "The integration name") cmd.Flags().StringArrayP("connect", "c", nil, "A Service that the integration should bind to, specified as [[apigroup/]version:]kind:[namespace/]name") - cmd.Flags().StringArrayP("dependency", "d", nil, "A dependency that should be included, e.g., \"-d camel-mail\" for a Camel component, \"-d mvn:org.my:app:1.0\" for a Maven dependency or \"file://localPath[?targetPath=<path>®istry=<registry URL>&skipChecksums=<true>&skipPOM=<true>]\" for local files (experimental)") + cmd.Flags().StringArrayP("dependency", "d", nil, usageDependency) cmd.Flags().BoolP("wait", "w", false, "Wait for the integration to be running") cmd.Flags().StringP("kit", "k", "", "The kit used to run the integration") cmd.Flags().StringArrayP("property", "p", nil, "Add a runtime property or properties file (syntax: [my-key=my-value|file:/path/to/my-conf.properties])") diff --git a/pkg/util/camel/camel_dependencies.go b/pkg/util/camel/camel_dependencies.go index ae43f9c7f..e6055f596 100644 --- a/pkg/util/camel/camel_dependencies.go +++ b/pkg/util/camel/camel_dependencies.go @@ -30,14 +30,9 @@ import ( ) // ManageIntegrationDependencies --. -func ManageIntegrationDependencies( - project *maven.Project, - dependencies []string, - catalog *RuntimeCatalog) error { - +func ManageIntegrationDependencies(project *maven.Project, dependencies []string, catalog *RuntimeCatalog) error { // Add dependencies from build - err := addDependencies(project, dependencies, catalog) - if err != nil { + if err := addDependencies(project, dependencies, catalog); err != nil { return err } // Add dependencies from catalog