Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli): kamel local build doesn't support same dependency notation #3444

Merged
merged 1 commit into from
Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/local/files/dependency.groovy
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 4 additions & 2 deletions e2e/local/local_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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())
}
Expand All @@ -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()
Expand All @@ -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
Expand Down
25 changes: 25 additions & 0 deletions e2e/local/local_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
25 changes: 17 additions & 8 deletions pkg/apis/camel/v1/integration_types_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions pkg/apis/camel/v1/integration_types_support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
48 changes: 40 additions & 8 deletions pkg/cmd/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
48 changes: 17 additions & 31 deletions pkg/cmd/local_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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 {
Expand All @@ -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
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
Expand Down
38 changes: 31 additions & 7 deletions pkg/cmd/local_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
})
}
Loading