Skip to content

Commit

Permalink
chore: Improve Camel dependency validation
Browse files Browse the repository at this point in the history
- Reuse logic in validate function just logging a warning and those validate function raising errors
- Remove unused function ValidateDependencies
- Add some unit tests
- Add some documentation on Camel dependency resolution via catalog
  • Loading branch information
christophd committed May 24, 2023
1 parent 8272026 commit 7861866
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 31 deletions.
7 changes: 7 additions & 0 deletions docs/modules/ROOT/pages/configuration/dependencies.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ kamel run -d camel:http Integration.java
```
In this case, the dependency will be added with the correct version. Note that the standard notation for specifying a Camel dependency is `camel:xxx`, while `kamel` also accepts `camel-xxx` for usability.

While resolving Camel dependencies (`camel:xxx` or `camel-xxx`) the Camel K operator tries to find the dependency in the xref:architecture/cr/camel-catalog.adoc[Camel catalog].
In case the dependency is not listed in the catalog for some reason you will be provided with am error.
Please make sure to use Camel dependencies listed in the catalog as these components are eligible to being used in Camel K (e.g. due to proper version resolving and runtime optimization).
Using Camel dependencies not listed in the catalog may lead to unexpected behavior and is not supported.
In case you do have a custom Camel component that you want to use as part of an Integration you can add this as an external Maven dependency using the respective Maven coordinates of your project.
Please do not use one of the reserved the Camel groupIds (`org.apache.camel`) in that case.

*External dependencies* can be added using the `-d` flag, the `mvn` prefix, and the maven coordinates:
```
kamel run -d mvn:com.google.guava:guava:26.0-jre Integration.java
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/run_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import (

func addDependency(cmd *cobra.Command, it *v1.Integration, dependency string, catalog *camel.RuntimeCatalog) {
normalized := camel.NormalizeDependency(dependency)
camel.ValidateDependency(catalog, normalized, cmd)
camel.ValidateDependency(catalog, normalized, cmd.ErrOrStderr())
it.Spec.AddDependency(normalized)
}

Expand Down
63 changes: 33 additions & 30 deletions pkg/util/camel/camel_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,53 +49,56 @@ func NormalizeDependency(dependency string) string {
return newDep
}

type Output interface {
OutOrStdout() io.Writer
ErrOrStderr() io.Writer
}

// ValidateDependencies validates dependencies against Camel catalog.
// It only shows warning and does not throw error in case the Catalog is just not complete
// ValidateDependency validates a dependency against Camel catalog.
// It only shows warning and does not throw error in case the Catalog is just not complete,
// and we don't want to let it stop the process.
func ValidateDependencies(catalog *RuntimeCatalog, dependencies []string, out Output) {
for _, d := range dependencies {
ValidateDependency(catalog, d, out)
func ValidateDependency(catalog *RuntimeCatalog, dependency string, out io.Writer) {
if err := ValidateDependencyE(catalog, dependency); err != nil {
fmt.Fprintf(out, "Warning: %s\n", err.Error())
}
}

// ValidateDependency validates a dependency against Camel catalog.
// It only shows warning and does not throw error in case the Catalog is just not complete
// and we don't want to let it stop the process.
func ValidateDependency(catalog *RuntimeCatalog, dependency string, out Output) {
switch {
case strings.HasPrefix(dependency, "camel:"):
artifact := strings.TrimPrefix(dependency, "camel:")
if ok := catalog.IsValidArtifact(artifact); !ok {
fmt.Fprintf(out.ErrOrStderr(), "Warning: dependency %s not found in Camel catalog\n", dependency)
}
case strings.HasPrefix(dependency, "mvn:org.apache.camel:"):
component := strings.Split(dependency, ":")[2]
fmt.Fprintf(out.ErrOrStderr(), "Warning: do not use %s. Use %s instead\n",
fmt.Fprintf(out, "Warning: do not use %s. Use %s instead\n",
dependency, NormalizeDependency(component))
case strings.HasPrefix(dependency, "mvn:org.apache.camel.quarkus:"):
component := strings.Split(dependency, ":")[2]
fmt.Fprintf(out.ErrOrStderr(), "Warning: do not use %s. Use %s instead\n",
fmt.Fprintf(out, "Warning: do not use %s. Use %s instead\n",
dependency, NormalizeDependency(component))
}
}

// ValidateDependencyE validates a dependency against Camel catalog and throws error
// in case it does not exist in the catalog.
func ValidateDependencyE(catalog *RuntimeCatalog, dependency string) error {
var artifact string
switch {
case strings.HasPrefix(dependency, "camel:"):
artifact = strings.TrimPrefix(dependency, "camel:")
case strings.HasPrefix(dependency, "camel-quarkus:"):
artifact = strings.TrimPrefix(dependency, "camel-quarkus:")
case strings.HasPrefix(dependency, "camel-"):
artifact = dependency
}

if artifact == "" {
return nil
}

if ok := catalog.IsValidArtifact(artifact); !ok {
return fmt.Errorf("dependency %s not found in Camel catalog", dependency)
}

return nil
}

// ValidateDependenciesE validates dependencies against Camel catalog and throws error
// if it doesn't exist in the catalog.
// in case it does not exist in the catalog.
func ValidateDependenciesE(catalog *RuntimeCatalog, dependencies []string) error {
for _, dependency := range dependencies {
if !strings.HasPrefix(dependency, "camel:") {
continue
}

artifact := strings.TrimPrefix(dependency, "camel:")
if ok := catalog.IsValidArtifact(artifact); !ok {
return fmt.Errorf("dependency %s not found in Camel catalog", dependency)
if err := ValidateDependencyE(catalog, dependency); err != nil {
return err
}
}

Expand Down
128 changes: 128 additions & 0 deletions pkg/util/camel/camel_dependencies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ limitations under the License.
package camel

import (
"fmt"
"strings"
"testing"

"github.com/apache/camel-k/v2/pkg/util/maven"
"github.com/stretchr/testify/assert"
)

Expand All @@ -30,4 +33,129 @@ func TestNormalizeDependency(t *testing.T) {
assert.Equal(t, "camel:file", NormalizeDependency("camel-quarkus:file"))
assert.Equal(t, "camel-k:knative", NormalizeDependency("camel-k-knative"))
assert.Equal(t, "camel-k:knative", NormalizeDependency("camel-k:knative"))
assert.Equal(t, "mvn:org.apache.camel:camel-file", NormalizeDependency("mvn:org.apache.camel:camel-file"))
assert.Equal(t, "mvn:org.apache.camel.quarkus:camel-quarkus-file", NormalizeDependency("mvn:org.apache.camel.quarkus:camel-quarkus-file"))
assert.Equal(t, "mvn:org.apache.camel:camel-k-knative", NormalizeDependency("mvn:org.apache.camel:camel-k-knative"))
}

func TestValidateDependency(t *testing.T) {
catalog, err := DefaultCatalog()
assert.Nil(t, err)

output := strings.Builder{}
ValidateDependency(catalog, "", &output)
assert.Equal(t, "", output.String())

output.Reset()
ValidateDependency(catalog, "camel:file", &output)
assert.Equal(t, "", output.String())

output.Reset()
ValidateDependency(catalog, "camel-quarkus-file", &output)
assert.Equal(t, "", output.String())

output.Reset()
ValidateDependency(catalog, "camel-quarkus:file", &output)
assert.Equal(t, "", output.String())

output.Reset()
ValidateDependency(catalog, "camel:unknown", &output)
assert.Equal(t, "Warning: dependency camel:unknown not found in Camel catalog\n", output.String())

output.Reset()
ValidateDependency(catalog, "mvn:org.apache.camel:camel-foo", &output)
assert.Equal(t, "Warning: do not use mvn:org.apache.camel:camel-foo. Use camel:foo instead\n", output.String())

output.Reset()
ValidateDependency(catalog, "mvn:org.apache.camel.quarkus:camel-quarkus-foo", &output)
assert.Equal(t, "Warning: do not use mvn:org.apache.camel.quarkus:camel-quarkus-foo. Use camel:foo instead\n", output.String())
}

func TestManageIntegrationDependencies(t *testing.T) {
catalog, err := DefaultCatalog()
assert.Nil(t, err)

tests := []struct {
name string
dependencies []string
coordinates string
}{
{
name: "basic_camel",
dependencies: []string{
"camel:direct",
"camel:log",
"camel:core",
},
coordinates: "org.apache.camel.quarkus:camel-quarkus-direct," +
"org.apache.camel.quarkus:camel-quarkus-log," +
"org.apache.camel.quarkus:camel-quarkus-core",
},
{
name: "camel_quarkus",
dependencies: []string{
"camel:direct",
"camel-quarkus:log",
"camel:camel-quarkus-core",
},
coordinates: "org.apache.camel.quarkus:camel-quarkus-direct," +
"org.apache.camel.quarkus:camel-quarkus-log," +
"org.apache.camel.quarkus:camel-quarkus-core",
},
{
name: "camel_k",
dependencies: []string{
"camel:direct",
"camel-k:webhook",
},
coordinates: "org.apache.camel.quarkus:camel-quarkus-direct," +
"org.apache.camel.k:camel-k-webhook",
},
{
name: "not_in_catalog",
dependencies: []string{
"camel:direct",
"camel:resiliance4j",
},
coordinates: "org.apache.camel.quarkus:camel-quarkus-direct," +
"org.apache.camel.quarkus:camel-quarkus-resiliance4j",
},
{
name: "mvn",
dependencies: []string{
"mvn:org.foo:bar",
"mvn:org.apache.camel:camel-resiliance4j",
},
coordinates: "org.foo:bar," +
"org.apache.camel:camel-resiliance4j",
},
{
name: "jitpack",
dependencies: []string{
"github:apache/camel-sample/1.0",
},
coordinates: "com.github.apache:camel-sample",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
project := maven.Project{}

err = ManageIntegrationDependencies(&project, test.dependencies, catalog)
assert.Nil(t, err)

coordinates := strings.Builder{}
for i, d := range project.Dependencies {
if i == 0 {
_, err = fmt.Fprintf(&coordinates, "%s:%s", d.GroupID, d.ArtifactID)
assert.Nil(t, err)
} else {
_, err = fmt.Fprintf(&coordinates, ",%s:%s", d.GroupID, d.ArtifactID)
assert.Nil(t, err)
}
}
assert.Equal(t, test.coordinates, coordinates.String(), coordinates)
})
}
}

0 comments on commit 7861866

Please sign in to comment.