You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2020/10/06 08:03:12 UTC
[camel-k] 01/08: chore(binding): refactor binding mechanism and add
many more tests
This is an automated email from the ASF dual-hosted git repository.
nferraro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 19b66cffb432f98f8e29ca0bf71e6112f375c989
Author: nicolaferraro <ni...@gmail.com>
AuthorDate: Thu Oct 1 18:04:54 2020 +0200
chore(binding): refactor binding mechanism and add many more tests
---
deploy/crd-kamelet-binding.yaml | 80 ++++----
.../kameletbindings.camel.apache.org.crd.yaml | 80 ++++----
.../display.groovy} | 9 +-
e2e/yaks/kamelet-binding/kamelet.feature | 5 +
.../logger-sink-binding.yaml} | 15 +-
.../logger-sink.kamelet.yaml} | 13 +-
.../messages-channel.yaml | 0
.../timer-source-binding-display.yaml} | 10 +-
.../timer-source-binding.yaml | 0
.../timer-source.kamelet.yaml | 0
.../{kamelets => kamelet-binding}/yaks-config.yaml | 10 +-
.../{kamelets => kamelet}/echo-sink.kamelet.yaml | 0
e2e/yaks/kamelet/kamelet.feature | 5 +
e2e/yaks/{kamelets => kamelet}/source-sink.groovy | 3 +-
.../timer-source.kamelet.yaml | 0
e2e/yaks/{kamelets => kamelet}/yaks-config.yaml | 6 -
e2e/yaks/kamelets/kamelet.feature | 9 -
helm/camel-k/crds/crd-kamelet-binding.yaml | 80 ++++----
pkg/apis/camel/v1alpha1/kamelet_binding_types.go | 9 +-
.../v1alpha1/kamelet_binding_types_support.go | 21 ++
pkg/controller/kameletbinding/initialize.go | 85 +-------
pkg/util/bindings/api.go | 48 +++++
pkg/util/bindings/bindings_test.go | 222 +++++++++++++++++++++
pkg/util/bindings/camel_uri.go | 58 ++++++
pkg/util/bindings/catalog.go | 61 ++++++
pkg/util/bindings/kamelet.go | 68 +++++++
pkg/util/bindings/knative_ref.go | 116 +++++++++++
pkg/util/bindings/knative_uri.go | 102 ++++++++++
pkg/util/knative/apis.go | 30 +++
29 files changed, 915 insertions(+), 230 deletions(-)
diff --git a/deploy/crd-kamelet-binding.yaml b/deploy/crd-kamelet-binding.yaml
index 74575d8..53bf707 100644
--- a/deploy/crd-kamelet-binding.yaml
+++ b/deploy/crd-kamelet-binding.yaml
@@ -189,31 +189,35 @@ spec:
apiVersion:
description: API version of the referent.
type: string
- blockOwnerDeletion:
- description: If true, AND if the owner has the "foregroundDeletion"
- finalizer, then the owner cannot be deleted from the key-value
- store until this reference is removed. Defaults to false.
- To set this field, a user needs "delete" permission of the
- owner, otherwise 422 (Unprocessable Entity) will be returned.
- type: boolean
- controller:
- description: If true, this reference points to the managing
- controller.
- type: boolean
+ fieldPath:
+ description: 'If referring to a piece of an object instead of
+ an entire object, this string should contain a valid JSON/Go
+ field access statement, such as desiredState.manifest.containers[2].
+ For example, if the object reference is to a container within
+ a pod, this would take on a value like: "spec.containers{name}"
+ (where "name" refers to the name of the container that triggered
+ the event) or if no container name is specified "spec.containers[2]"
+ (container with index 2 in this pod). This syntax is chosen
+ only to have some well-defined way of referencing a part of
+ an object. TODO: this design is not final and this field is
+ subject to change in the future.'
+ type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ namespace:
+ description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
+ type: string
+ resourceVersion:
+ description: 'Specific resourceVersion to which this reference
+ is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
+ description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
- required:
- - apiVersion
- - kind
- - name
- - uid
type: object
uri:
description: URI can alternatively be used to specify the (Camel)
@@ -235,31 +239,35 @@ spec:
apiVersion:
description: API version of the referent.
type: string
- blockOwnerDeletion:
- description: If true, AND if the owner has the "foregroundDeletion"
- finalizer, then the owner cannot be deleted from the key-value
- store until this reference is removed. Defaults to false.
- To set this field, a user needs "delete" permission of the
- owner, otherwise 422 (Unprocessable Entity) will be returned.
- type: boolean
- controller:
- description: If true, this reference points to the managing
- controller.
- type: boolean
+ fieldPath:
+ description: 'If referring to a piece of an object instead of
+ an entire object, this string should contain a valid JSON/Go
+ field access statement, such as desiredState.manifest.containers[2].
+ For example, if the object reference is to a container within
+ a pod, this would take on a value like: "spec.containers{name}"
+ (where "name" refers to the name of the container that triggered
+ the event) or if no container name is specified "spec.containers[2]"
+ (container with index 2 in this pod). This syntax is chosen
+ only to have some well-defined way of referencing a part of
+ an object. TODO: this design is not final and this field is
+ subject to change in the future.'
+ type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ namespace:
+ description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
+ type: string
+ resourceVersion:
+ description: 'Specific resourceVersion to which this reference
+ is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
+ description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
- required:
- - apiVersion
- - kind
- - name
- - uid
type: object
uri:
description: URI can alternatively be used to specify the (Camel)
diff --git a/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kameletbindings.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kameletbindings.camel.apache.org.crd.yaml
index 74575d8..53bf707 100644
--- a/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kameletbindings.camel.apache.org.crd.yaml
+++ b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kameletbindings.camel.apache.org.crd.yaml
@@ -189,31 +189,35 @@ spec:
apiVersion:
description: API version of the referent.
type: string
- blockOwnerDeletion:
- description: If true, AND if the owner has the "foregroundDeletion"
- finalizer, then the owner cannot be deleted from the key-value
- store until this reference is removed. Defaults to false.
- To set this field, a user needs "delete" permission of the
- owner, otherwise 422 (Unprocessable Entity) will be returned.
- type: boolean
- controller:
- description: If true, this reference points to the managing
- controller.
- type: boolean
+ fieldPath:
+ description: 'If referring to a piece of an object instead of
+ an entire object, this string should contain a valid JSON/Go
+ field access statement, such as desiredState.manifest.containers[2].
+ For example, if the object reference is to a container within
+ a pod, this would take on a value like: "spec.containers{name}"
+ (where "name" refers to the name of the container that triggered
+ the event) or if no container name is specified "spec.containers[2]"
+ (container with index 2 in this pod). This syntax is chosen
+ only to have some well-defined way of referencing a part of
+ an object. TODO: this design is not final and this field is
+ subject to change in the future.'
+ type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ namespace:
+ description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
+ type: string
+ resourceVersion:
+ description: 'Specific resourceVersion to which this reference
+ is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
+ description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
- required:
- - apiVersion
- - kind
- - name
- - uid
type: object
uri:
description: URI can alternatively be used to specify the (Camel)
@@ -235,31 +239,35 @@ spec:
apiVersion:
description: API version of the referent.
type: string
- blockOwnerDeletion:
- description: If true, AND if the owner has the "foregroundDeletion"
- finalizer, then the owner cannot be deleted from the key-value
- store until this reference is removed. Defaults to false.
- To set this field, a user needs "delete" permission of the
- owner, otherwise 422 (Unprocessable Entity) will be returned.
- type: boolean
- controller:
- description: If true, this reference points to the managing
- controller.
- type: boolean
+ fieldPath:
+ description: 'If referring to a piece of an object instead of
+ an entire object, this string should contain a valid JSON/Go
+ field access statement, such as desiredState.manifest.containers[2].
+ For example, if the object reference is to a container within
+ a pod, this would take on a value like: "spec.containers{name}"
+ (where "name" refers to the name of the container that triggered
+ the event) or if no container name is specified "spec.containers[2]"
+ (container with index 2 in this pod). This syntax is chosen
+ only to have some well-defined way of referencing a part of
+ an object. TODO: this design is not final and this field is
+ subject to change in the future.'
+ type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ namespace:
+ description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
+ type: string
+ resourceVersion:
+ description: 'Specific resourceVersion to which this reference
+ is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
+ description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
- required:
- - apiVersion
- - kind
- - name
- - uid
type: object
uri:
description: URI can alternatively be used to specify the (Camel)
diff --git a/e2e/yaks/kamelets/logger.groovy b/e2e/yaks/kamelet-binding/display.groovy
old mode 100755
new mode 100644
similarity index 84%
rename from e2e/yaks/kamelets/logger.groovy
rename to e2e/yaks/kamelet-binding/display.groovy
index a28afe3..b3ba363
--- a/e2e/yaks/kamelets/logger.groovy
+++ b/e2e/yaks/kamelet-binding/display.groovy
@@ -1,5 +1,4 @@
-// camel-k: language=groovy dependency=mvn:org.apache.camel.k:camel-kamelet:1.5.1-SNAPSHOT
-
+// camel-k: language=groovy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -17,5 +16,7 @@
* limitations under the License.
*/
-from('knative:channel/messages')
- .log('${body}')
+from('knative:endpoint/display')
+ .log('${body}')
+ .setBody().header('CE-Type')
+ .log('type: ${body}');
diff --git a/e2e/yaks/kamelet-binding/kamelet.feature b/e2e/yaks/kamelet-binding/kamelet.feature
new file mode 100644
index 0000000..2d04ce1
--- /dev/null
+++ b/e2e/yaks/kamelet-binding/kamelet.feature
@@ -0,0 +1,5 @@
+Feature: Camel K can bind Kamelets
+
+ Scenario: Running integration using a simple Kamelet with KameletBinding
+ Given integration logger-sink-binding is running
+ Then integration logger-sink-binding should print message: Hello Kamelets
diff --git a/e2e/yaks/kamelets/timer-source-binding.yaml b/e2e/yaks/kamelet-binding/logger-sink-binding.yaml
similarity index 69%
copy from e2e/yaks/kamelets/timer-source-binding.yaml
copy to e2e/yaks/kamelet-binding/logger-sink-binding.yaml
index 32aeefb..bcdd95e 100644
--- a/e2e/yaks/kamelets/timer-source-binding.yaml
+++ b/e2e/yaks/kamelet-binding/logger-sink-binding.yaml
@@ -1,18 +1,15 @@
apiVersion: camel.apache.org/v1alpha1
kind: KameletBinding
metadata:
- name: timer-source-binding
+ name: logger-sink-binding
spec:
source:
ref:
- kind: Kamelet
- apiVersion: camel.apache.org/v1alpha1
- name: timer-source
- properties:
- message: Hello Kamelets
- period: 1000
- sink:
- ref:
kind: InMemoryChannel
apiVersion: messaging.knative.dev/v1beta1
name: messages
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1alpha1
+ name: logger-sink
diff --git a/e2e/yaks/kamelets/echo-sink.kamelet.yaml b/e2e/yaks/kamelet-binding/logger-sink.kamelet.yaml
similarity index 57%
copy from e2e/yaks/kamelets/echo-sink.kamelet.yaml
copy to e2e/yaks/kamelet-binding/logger-sink.kamelet.yaml
index c21ea6b..a28fc94 100644
--- a/e2e/yaks/kamelets/echo-sink.kamelet.yaml
+++ b/e2e/yaks/kamelet-binding/logger-sink.kamelet.yaml
@@ -1,19 +1,19 @@
apiVersion: camel.apache.org/v1alpha1
kind: Kamelet
metadata:
- name: echo-sink
+ name: logger-sink
label:
camel.apache.org/kamelet.type: "sink"
spec:
definition:
- title: "Echo"
- description: "Replies with an echo message to each incoming event"
+ title: "Logger"
+ description: "Logs the received payload of each incoming event"
properties:
prefix:
title: Prefix
- description: The prefix to prepend to the incoming event
+ description: The prefix to prepend to the logged message
type: string
- default: "echo: "
+ default: "message: "
types:
in:
mediaType: text/plain
@@ -23,5 +23,4 @@ spec:
from:
uri: "direct:{{routeId}}"
steps:
- - set-body:
- simple: "{{prefix}}${body}"
+ - log: "{{prefix}}${body}"
diff --git a/e2e/yaks/kamelets/messages-channel.yaml b/e2e/yaks/kamelet-binding/messages-channel.yaml
similarity index 100%
rename from e2e/yaks/kamelets/messages-channel.yaml
rename to e2e/yaks/kamelet-binding/messages-channel.yaml
diff --git a/e2e/yaks/kamelets/timer-source-binding.yaml b/e2e/yaks/kamelet-binding/timer-source-binding-display.yaml
similarity index 52%
copy from e2e/yaks/kamelets/timer-source-binding.yaml
copy to e2e/yaks/kamelet-binding/timer-source-binding-display.yaml
index 32aeefb..b217445 100644
--- a/e2e/yaks/kamelets/timer-source-binding.yaml
+++ b/e2e/yaks/kamelet-binding/timer-source-binding-display.yaml
@@ -1,7 +1,7 @@
apiVersion: camel.apache.org/v1alpha1
kind: KameletBinding
metadata:
- name: timer-source-binding
+ name: timer-source-binding-display
spec:
source:
ref:
@@ -9,10 +9,6 @@ spec:
apiVersion: camel.apache.org/v1alpha1
name: timer-source
properties:
- message: Hello Kamelets
- period: 1000
+ message: Hello
sink:
- ref:
- kind: InMemoryChannel
- apiVersion: messaging.knative.dev/v1beta1
- name: messages
+ uri: http://display.{namespace}.svc.cluster.local
diff --git a/e2e/yaks/kamelets/timer-source-binding.yaml b/e2e/yaks/kamelet-binding/timer-source-binding.yaml
similarity index 100%
rename from e2e/yaks/kamelets/timer-source-binding.yaml
rename to e2e/yaks/kamelet-binding/timer-source-binding.yaml
diff --git a/e2e/yaks/kamelets/timer-source.kamelet.yaml b/e2e/yaks/kamelet-binding/timer-source.kamelet.yaml
similarity index 100%
copy from e2e/yaks/kamelets/timer-source.kamelet.yaml
copy to e2e/yaks/kamelet-binding/timer-source.kamelet.yaml
diff --git a/e2e/yaks/kamelets/yaks-config.yaml b/e2e/yaks/kamelet-binding/yaks-config.yaml
similarity index 72%
copy from e2e/yaks/kamelets/yaks-config.yaml
copy to e2e/yaks/kamelet-binding/yaks-config.yaml
index 8679b79..59fec0a 100644
--- a/e2e/yaks/kamelets/yaks-config.yaml
+++ b/e2e/yaks/kamelet-binding/yaks-config.yaml
@@ -26,10 +26,14 @@ pre:
kubectl apply -f messages-channel.yaml -n $YAKS_NAMESPACE
kubectl apply -f timer-source.kamelet.yaml -n $YAKS_NAMESPACE
- kubectl apply -f echo-sink.kamelet.yaml -n $YAKS_NAMESPACE
+ kubectl apply -f logger-sink.kamelet.yaml -n $YAKS_NAMESPACE
kubectl apply -f timer-source-binding.yaml -n $YAKS_NAMESPACE
+ kubectl apply -f logger-sink-binding.yaml -n $YAKS_NAMESPACE
+
kubectl wait kameletbinding timer-source-binding --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE
+ kubectl wait kameletbinding logger-sink-binding --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE
- kamel run logger.groovy -w -n $YAKS_NAMESPACE
- kamel run source-sink.groovy -w -n $YAKS_NAMESPACE
+ kamel run display.groovy -w -n $YAKS_NAMESPACE
+ cat timer-source-binding-display.yaml | sed 's/{namespace}/'"${YAKS_NAMESPACE}"'/' | kubectl apply -f -
+ kubectl wait kameletbinding timer-source-binding-display --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE
diff --git a/e2e/yaks/kamelets/echo-sink.kamelet.yaml b/e2e/yaks/kamelet/echo-sink.kamelet.yaml
similarity index 100%
rename from e2e/yaks/kamelets/echo-sink.kamelet.yaml
rename to e2e/yaks/kamelet/echo-sink.kamelet.yaml
diff --git a/e2e/yaks/kamelet/kamelet.feature b/e2e/yaks/kamelet/kamelet.feature
new file mode 100644
index 0000000..01aca6f
--- /dev/null
+++ b/e2e/yaks/kamelet/kamelet.feature
@@ -0,0 +1,5 @@
+Feature: Camel K can run Kamelets
+
+ Scenario: Integrations can use multiple kamelets
+ Given integration source-sink is running
+ Then integration source-sink should print nice echo: Camel K
diff --git a/e2e/yaks/kamelets/source-sink.groovy b/e2e/yaks/kamelet/source-sink.groovy
similarity index 94%
rename from e2e/yaks/kamelets/source-sink.groovy
rename to e2e/yaks/kamelet/source-sink.groovy
index 3666bad..e607a9f 100755
--- a/e2e/yaks/kamelets/source-sink.groovy
+++ b/e2e/yaks/kamelet/source-sink.groovy
@@ -17,8 +17,7 @@
* limitations under the License.
*/
-from('timer:tick')
- .setBody().constant('Camel K')
+from('kamelet:timer-source?message=Camel+K')
.to("kamelet:echo-sink")
.to("kamelet:echo-sink?prefix=nice+")
.log('${body}')
diff --git a/e2e/yaks/kamelets/timer-source.kamelet.yaml b/e2e/yaks/kamelet/timer-source.kamelet.yaml
similarity index 100%
rename from e2e/yaks/kamelets/timer-source.kamelet.yaml
rename to e2e/yaks/kamelet/timer-source.kamelet.yaml
diff --git a/e2e/yaks/kamelets/yaks-config.yaml b/e2e/yaks/kamelet/yaks-config.yaml
similarity index 81%
rename from e2e/yaks/kamelets/yaks-config.yaml
rename to e2e/yaks/kamelet/yaks-config.yaml
index 8679b79..6ff2355 100644
--- a/e2e/yaks/kamelets/yaks-config.yaml
+++ b/e2e/yaks/kamelet/yaks-config.yaml
@@ -23,13 +23,7 @@ pre:
run: |
kamel install -n $YAKS_NAMESPACE
- kubectl apply -f messages-channel.yaml -n $YAKS_NAMESPACE
-
kubectl apply -f timer-source.kamelet.yaml -n $YAKS_NAMESPACE
kubectl apply -f echo-sink.kamelet.yaml -n $YAKS_NAMESPACE
- kubectl apply -f timer-source-binding.yaml -n $YAKS_NAMESPACE
- kubectl wait kameletbinding timer-source-binding --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE
-
- kamel run logger.groovy -w -n $YAKS_NAMESPACE
kamel run source-sink.groovy -w -n $YAKS_NAMESPACE
diff --git a/e2e/yaks/kamelets/kamelet.feature b/e2e/yaks/kamelets/kamelet.feature
deleted file mode 100644
index 2a4d932..0000000
--- a/e2e/yaks/kamelets/kamelet.feature
+++ /dev/null
@@ -1,9 +0,0 @@
-Feature: Camel K can run Kamelets and bind them
-
- Scenario: Running integration using a simple Kamelet with KameletBinding
- Given integration logger is running
- Then integration logger should print Hello Kamelets
-
- Scenario: Integrations can use multiple kamelets
- Given integration source-sink is running
- Then integration source-sink should print nice echo: Camel K
diff --git a/helm/camel-k/crds/crd-kamelet-binding.yaml b/helm/camel-k/crds/crd-kamelet-binding.yaml
index 74575d8..53bf707 100644
--- a/helm/camel-k/crds/crd-kamelet-binding.yaml
+++ b/helm/camel-k/crds/crd-kamelet-binding.yaml
@@ -189,31 +189,35 @@ spec:
apiVersion:
description: API version of the referent.
type: string
- blockOwnerDeletion:
- description: If true, AND if the owner has the "foregroundDeletion"
- finalizer, then the owner cannot be deleted from the key-value
- store until this reference is removed. Defaults to false.
- To set this field, a user needs "delete" permission of the
- owner, otherwise 422 (Unprocessable Entity) will be returned.
- type: boolean
- controller:
- description: If true, this reference points to the managing
- controller.
- type: boolean
+ fieldPath:
+ description: 'If referring to a piece of an object instead of
+ an entire object, this string should contain a valid JSON/Go
+ field access statement, such as desiredState.manifest.containers[2].
+ For example, if the object reference is to a container within
+ a pod, this would take on a value like: "spec.containers{name}"
+ (where "name" refers to the name of the container that triggered
+ the event) or if no container name is specified "spec.containers[2]"
+ (container with index 2 in this pod). This syntax is chosen
+ only to have some well-defined way of referencing a part of
+ an object. TODO: this design is not final and this field is
+ subject to change in the future.'
+ type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ namespace:
+ description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
+ type: string
+ resourceVersion:
+ description: 'Specific resourceVersion to which this reference
+ is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
+ description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
- required:
- - apiVersion
- - kind
- - name
- - uid
type: object
uri:
description: URI can alternatively be used to specify the (Camel)
@@ -235,31 +239,35 @@ spec:
apiVersion:
description: API version of the referent.
type: string
- blockOwnerDeletion:
- description: If true, AND if the owner has the "foregroundDeletion"
- finalizer, then the owner cannot be deleted from the key-value
- store until this reference is removed. Defaults to false.
- To set this field, a user needs "delete" permission of the
- owner, otherwise 422 (Unprocessable Entity) will be returned.
- type: boolean
- controller:
- description: If true, this reference points to the managing
- controller.
- type: boolean
+ fieldPath:
+ description: 'If referring to a piece of an object instead of
+ an entire object, this string should contain a valid JSON/Go
+ field access statement, such as desiredState.manifest.containers[2].
+ For example, if the object reference is to a container within
+ a pod, this would take on a value like: "spec.containers{name}"
+ (where "name" refers to the name of the container that triggered
+ the event) or if no container name is specified "spec.containers[2]"
+ (container with index 2 in this pod). This syntax is chosen
+ only to have some well-defined way of referencing a part of
+ an object. TODO: this design is not final and this field is
+ subject to change in the future.'
+ type: string
kind:
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names'
+ description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+ type: string
+ namespace:
+ description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
+ type: string
+ resourceVersion:
+ description: 'Specific resourceVersion to which this reference
+ is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids'
+ description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
- required:
- - apiVersion
- - kind
- - name
- - uid
type: object
uri:
description: URI can alternatively be used to specify the (Camel)
diff --git a/pkg/apis/camel/v1alpha1/kamelet_binding_types.go b/pkg/apis/camel/v1alpha1/kamelet_binding_types.go
index 64702c2..4214074 100644
--- a/pkg/apis/camel/v1alpha1/kamelet_binding_types.go
+++ b/pkg/apis/camel/v1alpha1/kamelet_binding_types.go
@@ -38,13 +38,20 @@ type KameletBindingSpec struct {
// Endpoint represents a source/sink external entity
type Endpoint struct {
// Ref can be used to declare a Kubernetes resource as source/sink endpoint
- Ref *metav1.OwnerReference `json:"ref,omitempty"`
+ Ref *corev1.ObjectReference `json:"ref,omitempty"`
// URI can alternatively be used to specify the (Camel) endpoint explicitly
URI *string `json:"uri,omitempty"`
// Properties are a key value representation of endpoint properties
Properties EndpointProperties `json:"properties,omitempty"`
}
+type EndpointType string
+
+const (
+ EndpointTypeSource EndpointType = "source"
+ EndpointTypeSink EndpointType = "sink"
+)
+
// EndpointProperties is a key/value struct represented as JSON raw to allow numeric/boolean values
// +kubebuilder:validation:Type=object
type EndpointProperties struct {
diff --git a/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go b/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go
index f630013..d470dc6 100644
--- a/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go
+++ b/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go
@@ -18,6 +18,9 @@ limitations under the License.
package v1alpha1
import (
+ "encoding/json"
+ "fmt"
+
v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -137,6 +140,24 @@ func (in *KameletBindingStatus) RemoveCondition(condType KameletBindingCondition
in.Conditions = newConditions
}
+// GetPropertyMap returns the EndpointProperties as map
+func (p EndpointProperties) GetPropertyMap() (map[string]string, error) {
+ if len(p.RawMessage) == 0 {
+ return nil, nil
+ }
+
+ // Convert json property values to objects before getting their string representation
+ var props map[string]interface{}
+ if err := json.Unmarshal(p.RawMessage, &props); err != nil {
+ return nil, err
+ }
+ stringProps := make(map[string]string, len(props))
+ for k, v := range props {
+ stringProps[k] = fmt.Sprintf("%v", v)
+ }
+ return stringProps, nil
+}
+
// NewKameletBinding --
func NewKameletBinding(namespace string, name string) KameletBinding {
return KameletBinding{
diff --git a/pkg/controller/kameletbinding/initialize.go b/pkg/controller/kameletbinding/initialize.go
index d047ae6..1054c3f 100644
--- a/pkg/controller/kameletbinding/initialize.go
+++ b/pkg/controller/kameletbinding/initialize.go
@@ -20,14 +20,11 @@ package kameletbinding
import (
"context"
"encoding/json"
- "fmt"
- "net/url"
- "strings"
v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "github.com/apache/camel-k/pkg/util/bindings"
"github.com/apache/camel-k/pkg/util/kubernetes"
- "github.com/apache/camel-k/pkg/util/uri"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -61,40 +58,33 @@ func (action *initializeAction) Handle(ctx context.Context, kameletbinding *v1al
it.Spec = *kameletbinding.Spec.Integration.DeepCopy()
}
- fromURI, err := getEndpointURI(kameletbinding.Spec.Source)
+ from, err := bindings.Translate(v1alpha1.EndpointTypeSource, kameletbinding.Spec.Source)
if err != nil {
return nil, errors.Wrap(err, "could not determine source URI")
}
- toURI, err := getEndpointURI(kameletbinding.Spec.Sink)
+ to, err := bindings.Translate(v1alpha1.EndpointTypeSink, kameletbinding.Spec.Sink)
if err != nil {
return nil, errors.Wrap(err, "could not determine sink URI")
}
- // TODO remove this after making sinkbinding the default (https://github.com/apache/camel-k/issues/1654)
- if strings.HasPrefix(toURI, "knative:") {
- knativeConfig := map[string]interface{}{
- "sinkBinding": true,
- }
- knativeConfigJSON, err := json.Marshal(knativeConfig)
- if err != nil {
- return nil, err
- }
+ if len(from.Traits) > 0 || len(to.Traits) > 0 {
if it.Spec.Traits == nil {
it.Spec.Traits = make(map[string]v1.TraitSpec)
}
- it.Spec.Traits["knative"] = v1.TraitSpec{
- Configuration: v1.TraitConfiguration{
- RawMessage: knativeConfigJSON,
- },
+ for k, v := range from.Traits {
+ it.Spec.Traits[k] = v
+ }
+ for k, v := range to.Traits {
+ it.Spec.Traits[k] = v
}
}
flow := map[string]interface{}{
"from": map[string]interface{}{
- "uri": fromURI,
+ "uri": from.URI,
"steps": []map[string]interface{}{
{
- "to": toURI,
+ "to": to.URI,
},
},
},
@@ -113,56 +103,3 @@ func (action *initializeAction) Handle(ctx context.Context, kameletbinding *v1al
target.Status.Phase = v1alpha1.KameletBindingPhaseCreating
return target, nil
}
-
-func getEndpointURI(e v1alpha1.Endpoint) (string, error) {
- baseURI, err := getEndpointBaseURI(e)
- if err != nil {
- return baseURI, err
- }
-
- // Convert json properties to string before using them in URI
- if len(e.Properties.RawMessage) > 0 {
- var props map[string]interface{}
- if err := json.Unmarshal(e.Properties.RawMessage, &props); err != nil {
- return "", err
- }
- stringProps := make(map[string]string, len(props))
- for k, v := range props {
- stringProps[k] = fmt.Sprintf("%v", v)
- }
- return uri.AppendParameters(baseURI, stringProps), nil
- }
-
- return baseURI, nil
-}
-
-func getEndpointBaseURI(e v1alpha1.Endpoint) (string, error) {
- if err := validateEndpoint(e); err != nil {
- return "", err
- }
-
- // return the URI if explicitly stated
- if e.URI != nil {
- return *e.URI, nil
- }
-
- // Kamelets are a known type
- if e.Ref.Kind == v1alpha1.KameletKind {
- return fmt.Sprintf("kamelet:%s", url.PathEscape(e.Ref.Name)), nil
- }
-
- // assume we're using Knative for the time being (Kafka resources may be added in the future)
- return uri.AppendParameters(fmt.Sprintf("knative:endpoint/%s", url.PathEscape(e.Ref.Name)), map[string]string{
- "apiVersion": e.Ref.APIVersion,
- "kind": e.Ref.Kind,
- }), nil
-}
-
-func validateEndpoint(e v1alpha1.Endpoint) error {
- if e.Ref == nil && e.URI == nil {
- return errors.New("no ref or URI specified in endpoint")
- } else if e.Ref != nil && e.URI != nil {
- return errors.New("cannot use both ref and URI to specify an endpoint: only one of them should be used")
- }
- return nil
-}
diff --git a/pkg/util/bindings/api.go b/pkg/util/bindings/api.go
new file mode 100644
index 0000000..c9daf72
--- /dev/null
+++ b/pkg/util/bindings/api.go
@@ -0,0 +1,48 @@
+/*
+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.
+*/
+
+// Package bindings provides APIs to transform Kubernetes objects into Camel URIs equivalents
+package bindings
+
+import (
+ v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
+
+const (
+ OrderFirst = 0
+ OrderStandard = 50
+ OrderLast = 100
+)
+
+// Binding represents how a Kubernetes object is represented in Camel K resources
+type Binding struct {
+ // URI is the Camel URI equivalent
+ URI string
+ // Traits is a partial trait specification that should be merged into the integration
+ Traits map[string]v1.TraitSpec
+}
+
+// BindingProvider maps a KameletBinding endpoint into Camel K resources
+type BindingProvider interface {
+ // ID returns the name of the binding provider
+ ID() string
+ // Translate does the actual mapping
+ Translate(endpointType v1alpha1.EndpointType, endpoint v1alpha1.Endpoint) (*Binding, error)
+ // Order returns the relative order of execution of the binding provider
+ Order() int
+}
diff --git a/pkg/util/bindings/bindings_test.go b/pkg/util/bindings/bindings_test.go
new file mode 100644
index 0000000..1f7696a
--- /dev/null
+++ b/pkg/util/bindings/bindings_test.go
@@ -0,0 +1,222 @@
+/*
+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.
+*/
+
+package bindings
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/url"
+ "testing"
+
+ camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+ knativeapis "github.com/apache/camel-k/pkg/apis/camel/v1/knative"
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "github.com/stretchr/testify/assert"
+ corev1 "k8s.io/api/core/v1"
+)
+
+func TestBindings(t *testing.T) {
+ testcases := []struct {
+ endpointType v1alpha1.EndpointType
+ endpoint v1alpha1.Endpoint
+ uri string
+ traits map[string]camelv1.TraitSpec
+ }{
+ {
+ endpointType: v1alpha1.EndpointTypeSink,
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "Service",
+ APIVersion: "serving.knative.dev/v1",
+ Name: "myservice",
+ },
+ },
+ uri: "knative:endpoint/myservice?apiVersion=serving.knative.dev%2Fv1&kind=Service",
+ traits: asTraitSpec("knative", map[string]interface{}{
+ "sinkBinding": true,
+ }),
+ },
+ {
+ endpointType: v1alpha1.EndpointTypeSink,
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "Service",
+ APIVersion: "serving.knative.dev/v1",
+ Name: "myservice",
+ },
+ Properties: asEndpointProperties(map[string]string{
+ "ce.override.ce-type": "mytype",
+ }),
+ },
+ uri: "knative:endpoint/myservice?apiVersion=serving.knative.dev%2Fv1&ce.override.ce-type=mytype&kind=Service",
+ traits: asTraitSpec("knative", map[string]interface{}{
+ "sinkBinding": true,
+ }),
+ },
+ {
+ endpointType: v1alpha1.EndpointTypeSink,
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "Channel",
+ APIVersion: "messaging.knative.dev/v1",
+ Name: "mychannel",
+ },
+ },
+ uri: "knative:channel/mychannel?apiVersion=messaging.knative.dev%2Fv1&kind=Channel",
+ traits: asTraitSpec("knative", map[string]interface{}{
+ "sinkBinding": true,
+ }),
+ },
+ {
+ endpointType: v1alpha1.EndpointTypeSource,
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "Channel",
+ APIVersion: "messaging.knative.dev/v1",
+ Name: "mychannel",
+ },
+ },
+ uri: "knative:channel/mychannel?apiVersion=messaging.knative.dev%2Fv1&kind=Channel",
+ },
+ {
+ endpointType: v1alpha1.EndpointTypeSource,
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "KafkaChannel",
+ APIVersion: "messaging.knative.dev/v1beta1",
+ Name: "mychannel",
+ },
+ },
+ uri: "knative:channel/mychannel?apiVersion=messaging.knative.dev%2Fv1beta1&kind=KafkaChannel",
+ },
+ {
+ endpointType: v1alpha1.EndpointTypeSource,
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "Broker",
+ APIVersion: "eventing.knative.dev/v1beta1",
+ Name: "default",
+ },
+ Properties: asEndpointProperties(map[string]string{
+ "type": "myeventtype",
+ }),
+ },
+ uri: "knative:event/myeventtype?apiVersion=eventing.knative.dev%2Fv1beta1&kind=Broker",
+ },
+ {
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "Kamelet",
+ APIVersion: "camel.apache.org/v1any1",
+ Name: "mykamelet",
+ },
+ },
+ uri: "kamelet:mykamelet",
+ },
+ {
+ endpoint: v1alpha1.Endpoint{
+ Ref: &corev1.ObjectReference{
+ Kind: "Kamelet",
+ APIVersion: "camel.apache.org/v1any1",
+ Name: "mykamelet",
+ },
+ Properties: asEndpointProperties(map[string]string{
+ "mymessage": "myval",
+ "encodedkey?": "encoded=val",
+ }),
+ },
+ uri: "kamelet:mykamelet?encodedkey%3F=encoded%3Dval&mymessage=myval",
+ },
+ {
+ endpointType: v1alpha1.EndpointTypeSink,
+ endpoint: v1alpha1.Endpoint{
+ URI: asStringPointer("https://myurl/hey"),
+ Properties: asEndpointProperties(map[string]string{
+ "ce.override.ce-type": "mytype",
+ }),
+ },
+ uri: "knative:endpoint/sink?ce.override.ce-type=mytype",
+ traits: asTraitSpec("knative", map[string]interface{}{
+ "configuration": asKnativeConfig("https://myurl/hey"),
+ }),
+ },
+ {
+ endpointType: v1alpha1.EndpointTypeSink,
+ endpoint: v1alpha1.Endpoint{
+ URI: asStringPointer("docker://xxx"),
+ },
+ uri: "docker://xxx",
+ },
+ }
+
+ for i, tc := range testcases {
+ t.Run(fmt.Sprintf("test-%d-%s", i, tc.uri), func(t *testing.T) {
+ binding, err := Translate(tc.endpointType, tc.endpoint)
+ assert.NoError(t, err)
+ assert.NotNil(t, binding)
+ assert.Equal(t, tc.uri, binding.URI)
+ assert.Equal(t, tc.traits, binding.Traits)
+ })
+ }
+}
+
+func asEndpointProperties(props map[string]string) v1alpha1.EndpointProperties {
+ serialized, err := json.Marshal(props)
+ if err != nil {
+ panic(err)
+ }
+ return v1alpha1.EndpointProperties{
+ RawMessage: serialized,
+ }
+}
+
+func asTraitSpec(key string, data map[string]interface{}) map[string]camelv1.TraitSpec {
+ res := make(map[string]camelv1.TraitSpec)
+ serialized, err := json.Marshal(data)
+ if err != nil {
+ panic(err)
+ }
+ res[key] = camelv1.TraitSpec{
+ Configuration: camelv1.TraitConfiguration{
+ RawMessage: serialized,
+ },
+ }
+ return res
+}
+
+func asStringPointer(str string) *string {
+ return &str
+}
+
+func asKnativeConfig(endpointURL string) string {
+ serviceURL, err := url.Parse(endpointURL)
+ if err != nil {
+ panic(err)
+ }
+ def, err := knativeapis.BuildCamelServiceDefinition("sink", knativeapis.CamelEndpointKindSink, knativeapis.CamelServiceTypeEndpoint, *serviceURL, "", "")
+ if err != nil {
+ panic(err)
+ }
+ env := knativeapis.NewCamelEnvironment()
+ env.Services = append(env.Services, def)
+ serialized, err := json.Marshal(env)
+ if err != nil {
+ panic(err)
+ }
+ return string(serialized)
+}
diff --git a/pkg/util/bindings/camel_uri.go b/pkg/util/bindings/camel_uri.go
new file mode 100644
index 0000000..6dfea5f
--- /dev/null
+++ b/pkg/util/bindings/camel_uri.go
@@ -0,0 +1,58 @@
+/*
+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.
+*/
+
+package bindings
+
+import (
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "github.com/apache/camel-k/pkg/util/uri"
+)
+
+// CamelURIBindingProvider converts an explicit URI into a Camel endpoint.
+// It's used as fallback if the URI scheme is not known by other providers.
+type CamelURIBindingProvider struct{}
+
+func (k CamelURIBindingProvider) ID() string {
+ return "camel-uri"
+}
+
+func (k CamelURIBindingProvider) Translate(endpointType v1alpha1.EndpointType, e v1alpha1.Endpoint) (*Binding, error) {
+ if e.URI == nil {
+ // works only on uris
+ return nil, nil
+ }
+
+ endpointURI := *e.URI
+ props, err := e.Properties.GetPropertyMap()
+ if err != nil {
+ return nil, err
+ }
+ endpointURI = uri.AppendParameters(endpointURI, props)
+
+ return &Binding{
+ URI: endpointURI,
+ }, nil
+}
+
+func (k CamelURIBindingProvider) Order() int {
+ // Using it as fallback
+ return OrderLast
+}
+
+func init() {
+ RegisterBindingProvider(CamelURIBindingProvider{})
+}
diff --git a/pkg/util/bindings/catalog.go b/pkg/util/bindings/catalog.go
new file mode 100644
index 0000000..0fa42ee
--- /dev/null
+++ b/pkg/util/bindings/catalog.go
@@ -0,0 +1,61 @@
+/*
+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.
+*/
+
+package bindings
+
+import (
+ "errors"
+ "sort"
+
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
+
+var bindingProviders []BindingProvider
+
+func RegisterBindingProvider(bp BindingProvider) {
+ bindingProviders = append(bindingProviders, bp)
+ sort.Slice(bindingProviders, func(i, j int) bool {
+ bi := bindingProviders[i]
+ bj := bindingProviders[j]
+ return (bi.Order() < bj.Order()) ||
+ (bi.Order() == bj.Order() && bi.ID() < bj.ID())
+ })
+}
+
+// Translate execute all chained binding providers, returning the first success or the first error
+func Translate(endpointType v1alpha1.EndpointType, endpoint v1alpha1.Endpoint) (*Binding, error) {
+ if err := validateEndpoint(endpoint); err != nil {
+ return nil, err
+ }
+
+ for _, bp := range bindingProviders {
+ b, err := bp.Translate(endpointType, endpoint)
+ if b != nil || err != nil {
+ return b, err
+ }
+ }
+ return nil, nil
+}
+
+func validateEndpoint(e v1alpha1.Endpoint) error {
+ if e.Ref == nil && e.URI == nil {
+ return errors.New("no ref or URI specified in endpoint")
+ } else if e.Ref != nil && e.URI != nil {
+ return errors.New("cannot use both ref and URI to specify an endpoint: only one of them should be used")
+ }
+ return nil
+}
diff --git a/pkg/util/bindings/kamelet.go b/pkg/util/bindings/kamelet.go
new file mode 100644
index 0000000..7e8d166
--- /dev/null
+++ b/pkg/util/bindings/kamelet.go
@@ -0,0 +1,68 @@
+/*
+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.
+*/
+
+package bindings
+
+import (
+ "fmt"
+ "net/url"
+
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "github.com/apache/camel-k/pkg/util/uri"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// KameletBindingProvider converts a reference to a Kamelet into a Camel URI
+type KameletBindingProvider struct{}
+
+func (k KameletBindingProvider) ID() string {
+ return "kamelet"
+}
+
+func (k KameletBindingProvider) Translate(endpointType v1alpha1.EndpointType, e v1alpha1.Endpoint) (*Binding, error) {
+ if e.Ref == nil {
+ // works only on refs
+ return nil, nil
+ }
+ gv, err := schema.ParseGroupVersion(e.Ref.APIVersion)
+ if err != nil {
+ return nil, err
+ }
+ // it translates only Kamelet refs
+ if e.Ref.Kind == v1alpha1.KameletKind && gv.Group == v1alpha1.SchemeGroupVersion.Group {
+ kameletURI := fmt.Sprintf("kamelet:%s", url.PathEscape(e.Ref.Name))
+
+ props, err := e.Properties.GetPropertyMap()
+ if err != nil {
+ return nil, err
+ }
+ kameletURI = uri.AppendParameters(kameletURI, props)
+
+ return &Binding{
+ URI: kameletURI,
+ }, nil
+ }
+ return nil, nil
+}
+
+func (k KameletBindingProvider) Order() int {
+ return OrderStandard
+}
+
+func init() {
+ RegisterBindingProvider(KameletBindingProvider{})
+}
diff --git a/pkg/util/bindings/knative_ref.go b/pkg/util/bindings/knative_ref.go
new file mode 100644
index 0000000..bd7f7a3
--- /dev/null
+++ b/pkg/util/bindings/knative_ref.go
@@ -0,0 +1,116 @@
+/*
+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.
+*/
+
+package bindings
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/url"
+
+ v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+ knativeapis "github.com/apache/camel-k/pkg/apis/camel/v1/knative"
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "github.com/apache/camel-k/pkg/util/knative"
+ "github.com/apache/camel-k/pkg/util/uri"
+)
+
+// KnativeRefBindingProvider converts a reference to a Kubernetes object into a Camel URI.
+// It's used as fallback if no other providers can decode the object reference.
+type KnativeRefBindingProvider struct{}
+
+func (k KnativeRefBindingProvider) ID() string {
+ return "knative-ref"
+}
+
+func (k KnativeRefBindingProvider) Translate(endpointType v1alpha1.EndpointType, e v1alpha1.Endpoint) (*Binding, error) {
+ if e.Ref == nil {
+ // works only on refs
+ return nil, nil
+ }
+
+ serviceType, err := knative.GetServiceType(*e.Ref)
+ if err != nil {
+ return nil, err
+ }
+
+ if serviceType == nil {
+ endpointType := knativeapis.CamelServiceTypeEndpoint
+ serviceType = &endpointType
+ }
+
+ props, err := e.Properties.GetPropertyMap()
+ if err != nil {
+ return nil, err
+ }
+ if props == nil {
+ props = make(map[string]string)
+ }
+ if props["apiVersion"] == "" {
+ props["apiVersion"] = e.Ref.APIVersion
+ }
+ if props["kind"] == "" {
+ props["kind"] = e.Ref.Kind
+ }
+
+ var serviceURI string
+ if *serviceType == knativeapis.CamelServiceTypeEvent {
+ if eventType, ok := props["type"]; ok {
+ // consume prop
+ delete(props, "type")
+ serviceURI = fmt.Sprintf("knative:%s/%s", *serviceType, eventType)
+ } else {
+ serviceURI = fmt.Sprintf("knative:%s", *serviceType)
+ }
+ } else {
+ serviceURI = fmt.Sprintf("knative:%s/%s", *serviceType, url.PathEscape(e.Ref.Name))
+ }
+
+ serviceURI = uri.AppendParameters(serviceURI, props)
+
+ var traits map[string]v1.TraitSpec
+ if endpointType == v1alpha1.EndpointTypeSink {
+ knativeConfig := make(map[string]interface{})
+ // TODO remove this after making sinkbinding the default (https://github.com/apache/camel-k/issues/1654)
+ knativeConfig["sinkBinding"] = true
+ knativeConfigJSON, err := json.Marshal(knativeConfig)
+ if err != nil {
+ return nil, err
+ }
+ traits = map[string]v1.TraitSpec{
+ "knative": {
+ Configuration: v1.TraitConfiguration{
+ RawMessage: knativeConfigJSON,
+ },
+ },
+ }
+ }
+
+ return &Binding{
+ URI: serviceURI,
+ Traits: traits,
+ }, nil
+}
+
+func (k KnativeRefBindingProvider) Order() int {
+ // Executes as last, as it can be used as fallback for all unknown object references
+ return OrderLast
+}
+
+func init() {
+ RegisterBindingProvider(KnativeRefBindingProvider{})
+}
diff --git a/pkg/util/bindings/knative_uri.go b/pkg/util/bindings/knative_uri.go
new file mode 100644
index 0000000..0f43cfa
--- /dev/null
+++ b/pkg/util/bindings/knative_uri.go
@@ -0,0 +1,102 @@
+/*
+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.
+*/
+
+package bindings
+
+import (
+ "encoding/json"
+ "net/url"
+ "strings"
+
+ v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+ knativeapis "github.com/apache/camel-k/pkg/apis/camel/v1/knative"
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "github.com/apache/camel-k/pkg/util/uri"
+)
+
+// KnativeURIBindingProvider converts a HTTP/HTTPS URI into a Camel Knative endpoint (to call it via CloudEvents).
+type KnativeURIBindingProvider struct{}
+
+func (k KnativeURIBindingProvider) ID() string {
+ return "knative-uri"
+}
+
+func (k KnativeURIBindingProvider) Translate(endpointType v1alpha1.EndpointType, e v1alpha1.Endpoint) (*Binding, error) {
+ if e.URI == nil {
+ // works only on uris
+ return nil, nil
+ }
+ if !strings.HasPrefix(*e.URI, "http:") && !strings.HasPrefix(*e.URI, "https:") {
+ // only translates http/https uri to Knative calls
+ return nil, nil
+ }
+ if endpointType == v1alpha1.EndpointTypeSource {
+ // HTTP/HTTPS uri are translated to Knative endpoints only when used as sinks
+ return nil, nil
+ }
+
+ knativeConfig := make(map[string]interface{})
+ originalURI, err := url.Parse(*e.URI)
+ if err != nil {
+ return nil, err
+ }
+ env := knativeapis.NewCamelEnvironment()
+ svc, err := knativeapis.BuildCamelServiceDefinition("sink",
+ knativeapis.CamelEndpointKindSink,
+ knativeapis.CamelServiceTypeEndpoint,
+ *originalURI, "", "")
+ if err != nil {
+ return nil, err
+ }
+ env.Services = append(env.Services, svc)
+ config, err := env.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ knativeConfig["configuration"] = config
+ knativeConfigJSON, err := json.Marshal(knativeConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ // Rewrite URI to match the service definition
+ serviceURI := "knative:endpoint/sink"
+ props, err := e.Properties.GetPropertyMap()
+ if err != nil {
+ return nil, err
+ }
+ serviceURI = uri.AppendParameters(serviceURI, props)
+
+ return &Binding{
+ URI: serviceURI,
+ Traits: map[string]v1.TraitSpec{
+ "knative": {
+ Configuration: v1.TraitConfiguration{
+ RawMessage: knativeConfigJSON,
+ },
+ },
+ },
+ }, nil
+}
+
+func (k KnativeURIBindingProvider) Order() int {
+ return OrderStandard
+}
+
+func init() {
+ RegisterBindingProvider(KnativeURIBindingProvider{})
+}
diff --git a/pkg/util/knative/apis.go b/pkg/util/knative/apis.go
index 72607f1..67bf5b2 100644
--- a/pkg/util/knative/apis.go
+++ b/pkg/util/knative/apis.go
@@ -176,6 +176,36 @@ func FillMissingReferenceData(serviceType knativev1.CamelServiceType, ref v1.Obj
return refs
}
+func GetServiceType(ref v1.ObjectReference) (*knativev1.CamelServiceType, error) {
+ refGV, err := schema.ParseGroupVersion(ref.APIVersion)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, c := range KnownChannelKinds {
+ if c.Group == refGV.Group && c.Kind == ref.Kind {
+ channelType := knativev1.CamelServiceTypeChannel
+ return &channelType, nil
+ }
+ }
+
+ for _, c := range KnownBrokerKinds {
+ if c.Group == refGV.Group && c.Kind == ref.Kind {
+ eventType := knativev1.CamelServiceTypeEvent
+ return &eventType, nil
+ }
+ }
+
+ for _, c := range KnownEndpointKinds {
+ if c.Group == refGV.Group && c.Kind == ref.Kind {
+ endpointType := knativev1.CamelServiceTypeEndpoint
+ return &endpointType, nil
+ }
+ }
+
+ return nil, nil
+}
+
// nolint: gocritic
func fillMissingReferenceDataWith(serviceTypes []GroupVersionKindResource, ref v1.ObjectReference) []v1.ObjectReference {
list := make([]v1.ObjectReference, 0)