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 713d260d6dd3f604e5c0a09c4969eb59b4aa96c2 Author: nicolaferraro <[email protected]> AuthorDate: Tue Sep 1 16:47:40 2020 +0200 fix #1676: add kamelet binding resource --- ...pache.org.crd.yaml => crd-kamelet-binding.yaml} | 14 +- .../builds.camel.apache.org.crd.yaml | 0 .../camel-k.v1.1.0.clusterserviceversion.yaml} | 8 +- .../camelcatalogs.camel.apache.org.crd.yaml | 0 .../integrationkits.camel.apache.org.crd.yaml | 0 .../integrationplatforms.camel.apache.org.crd.yaml | 0 .../integrations.camel.apache.org.crd.yaml | 0 .../builds.camel.apache.org.crd.yaml | 0 ...l-k.v1.2.0-snapshot.clusterserviceversion.yaml} | 93 ++++++++++- .../camelcatalogs.camel.apache.org.crd.yaml | 0 .../integrationkits.camel.apache.org.crd.yaml | 0 .../integrationplatforms.camel.apache.org.crd.yaml | 0 .../integrations.camel.apache.org.crd.yaml | 0 .../kameletbindings.camel.apache.org.crd.yaml} | 14 +- .../kamelets.camel.apache.org.crd.yaml | 0 .../camel-k-dev/camel-k-dev.package.yaml | 4 +- deploy/olm-catalog/csv-config.yaml | 1 + deploy/resources.go | 6 +- e2e/yaks/kamelets/kamelet.feature | 8 +- e2e/yaks/kamelets/{usage.groovy => logger.groovy} | 2 +- .../yaks/kamelets/messages-channel.yaml | 11 +- e2e/yaks/kamelets/timer-source.yaml | 18 ++ e2e/yaks/kamelets/yaks-config.yaml | 7 +- examples/kamelets/kamelet-binding-example.yaml | 15 ++ .../kamelets/messages-channel.yaml | 11 +- .../camel-k/crds/crd-kamelet-binding.yaml | 14 +- pkg/apis/camel/v1alpha1/kamelet_binding_types.go | 115 +++++++++++++ .../v1alpha1/kamelet_binding_types_support.go | 162 ++++++++++++++++++ pkg/apis/camel/v1alpha1/kamelet_types_support.go | 75 +++++++++ pkg/apis/camel/v1alpha1/register.go | 2 + pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go | 165 +++++++++++++++++- pkg/cmd/reset.go | 36 +++- pkg/controller/add_kameletbinding.go | 26 +++ pkg/controller/kamelet/common.go | 7 + pkg/controller/kameletbinding/action.go | 54 ++++++ pkg/controller/kameletbinding/initialize.go | 154 +++++++++++++++++ .../kameletbinding/kamelet_binding_controller.go | 186 +++++++++++++++++++++ pkg/controller/kameletbinding/log.go | 23 +++ pkg/controller/kameletbinding/monitor.go | 87 ++++++++++ pkg/event/manager.go | 36 ++++ pkg/install/cluster.go | 5 + pkg/util/log/log.go | 12 +- pkg/util/uri/uri.go | 18 ++ pkg/util/uri/uri_test.go | 53 ++++++ 44 files changed, 1374 insertions(+), 68 deletions(-) diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml b/deploy/crd-kamelet-binding.yaml similarity index 86% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml copy to deploy/crd-kamelet-binding.yaml index 7d36aab..4366d36 100644 --- a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml +++ b/deploy/crd-kamelet-binding.yaml @@ -18,7 +18,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: kamelets.camel.apache.org + name: kameletbindings.camel.apache.org labels: app: "camel-k" spec: @@ -32,14 +32,14 @@ spec: subresources: status: {} names: - kind: Kamelet - listKind: KameletList - plural: kamelets - singular: kamelet + kind: KameletBinding + listKind: KameletBindingList + plural: kameletbindings + singular: kameletbinding shortNames: - - kl + - klb additionalPrinterColumns: - name: Phase type: string - description: The Kamelet phase + description: The KameletBinding phase JSONPath: .status.phase diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/builds.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0/builds.camel.apache.org.crd.yaml similarity index 100% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/builds.camel.apache.org.crd.yaml copy to deploy/olm-catalog/camel-k-dev/1.1.0/builds.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0/camel-k.v1.1.0.clusterserviceversion.yaml similarity index 98% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml copy to deploy/olm-catalog/camel-k-dev/1.1.0/camel-k.v1.1.0.clusterserviceversion.yaml index 4cc2a53..b0c1f48 100644 --- a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml +++ b/deploy/olm-catalog/camel-k-dev/1.1.0/camel-k.v1.1.0.clusterserviceversion.yaml @@ -84,14 +84,14 @@ metadata: capabilities: Full Lifecycle categories: Integration & Delivery certified: "false" - containerImage: docker.io/apache/camel-k:1.1.0-SNAPSHOT + containerImage: docker.io/apache/camel-k:1.1.0 createdAt: "2020-06-09T02:45:00Z" description: Apache Camel K is a lightweight integration platform, born on Kubernetes, with serverless superpowers. repository: https://github.com/apache/camel-k operators.operatorframework.io/internal-objects: '["builds.camel.apache.org","integrationkits.camel.apache.org","camelcatalogs.camel.apache.org"]' support: Camel - name: camel-k-operator.v1.1.0-snapshot + name: camel-k-operator.v1.1.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -231,7 +231,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: docker.io/apache/camel-k:1.1.0-SNAPSHOT + image: docker.io/apache/camel-k:1.1.0 imagePullPolicy: IfNotPresent name: camel-k-operator resources: {} @@ -474,4 +474,4 @@ spec: selector: matchLabels: name: camel-k-operator - version: 1.1.0-snapshot + version: 1.1.0 diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camelcatalogs.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0/camelcatalogs.camel.apache.org.crd.yaml similarity index 100% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camelcatalogs.camel.apache.org.crd.yaml copy to deploy/olm-catalog/camel-k-dev/1.1.0/camelcatalogs.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationkits.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0/integrationkits.camel.apache.org.crd.yaml similarity index 100% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationkits.camel.apache.org.crd.yaml copy to deploy/olm-catalog/camel-k-dev/1.1.0/integrationkits.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationplatforms.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0/integrationplatforms.camel.apache.org.crd.yaml similarity index 100% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationplatforms.camel.apache.org.crd.yaml copy to deploy/olm-catalog/camel-k-dev/1.1.0/integrationplatforms.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrations.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0/integrations.camel.apache.org.crd.yaml similarity index 100% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrations.camel.apache.org.crd.yaml copy to deploy/olm-catalog/camel-k-dev/1.1.0/integrations.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/builds.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/builds.camel.apache.org.crd.yaml similarity index 100% rename from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/builds.camel.apache.org.crd.yaml rename to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/builds.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/camel-k.v1.2.0-snapshot.clusterserviceversion.yaml similarity index 85% rename from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml rename to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/camel-k.v1.2.0-snapshot.clusterserviceversion.yaml index 4cc2a53..554812f 100644 --- a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml +++ b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/camel-k.v1.2.0-snapshot.clusterserviceversion.yaml @@ -80,20 +80,96 @@ metadata: "name": "example" }, "spec": {} + }, + { + "apiVersion": "camel.apache.org/v1alpha1", + "kind": "Kamelet", + "metadata": { + "name": "example" + }, + "spec": { + "definition": { + "description": "Produces periodic events with a custom payload", + "properties": { + "message": { + "description": "The message to generate", + "title": "Message", + "type": "string" + }, + "period": { + "default": 1000, + "description": "The time interval between two events", + "title": "Period", + "type": "integer" + } + }, + "required": [ + "message" + ], + "title": "Example Timer" + }, + "flow": { + "from": { + "parameters": { + "period": "#property:period" + }, + "steps": [ + { + "set-body": { + "constant": "#property:message" + } + }, + { + "to": "direct:#property:routeId" + } + ], + "uri": "timer:tick" + } + } + } + }, + { + "apiVersion": "camel.apache.org/v1alpha1", + "kind": "KameletBinding", + "metadata": { + "name": "example" + }, + "spec": { + "source": { + "ref": { + "apiVersion": "camel.apache.org/v1alpha1", + "kind": "Kamelet", + "name": "example" + }, + "properties": { + "message": "Hello world" + } + }, + "sink": { + "ref": { + "apiVersion": "messaging.knative.dev/v1beta1", + "kind": "InMemoryChannel", + "name": "example" + } + } + } }] capabilities: Full Lifecycle categories: Integration & Delivery certified: "false" - containerImage: docker.io/apache/camel-k:1.1.0-SNAPSHOT + containerImage: docker.io/apache/camel-k:1.2.0-SNAPSHOT createdAt: "2020-06-09T02:45:00Z" description: Apache Camel K is a lightweight integration platform, born on Kubernetes, with serverless superpowers. repository: https://github.com/apache/camel-k operators.operatorframework.io/internal-objects: '["builds.camel.apache.org","integrationkits.camel.apache.org","camelcatalogs.camel.apache.org"]' support: Camel - name: camel-k-operator.v1.1.0-snapshot + name: camel-k-operator.v1.2.0-snapshot namespace: placeholder spec: + relatedImages: + - name: integration-base-image + image: adoptopenjdk/openjdk11:slim apiservicedefinitions: {} customresourcedefinitions: owned: @@ -122,11 +198,16 @@ spec: kind: IntegrationPlatform name: integrationplatforms.camel.apache.org version: v1 - - description: A Camel K Kamelet resource + - description: A Camel K connector displayName: Kamelet kind: Kamelet name: kamelets.camel.apache.org version: v1alpha1 + - description: A Camel K connector binding resource + displayName: Kamelet Binding + kind: KameletBinding + name: kameletbindings.camel.apache.org + version: v1alpha1 description: | Apache Camel K ============== @@ -231,7 +312,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: docker.io/apache/camel-k:1.1.0-SNAPSHOT + image: docker.io/apache/camel-k:1.2.0-SNAPSHOT imagePullPolicy: IfNotPresent name: camel-k-operator resources: {} @@ -470,8 +551,8 @@ spec: minKubeVersion: 1.11.0 provider: name: The Apache Software Foundation - replaces: camel-k-operator.v1.0.0 + replaces: camel-k-operator.v1.1.0 selector: matchLabels: name: camel-k-operator - version: 1.1.0-snapshot + version: 1.2.0 diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camelcatalogs.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/camelcatalogs.camel.apache.org.crd.yaml similarity index 100% rename from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camelcatalogs.camel.apache.org.crd.yaml rename to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/camelcatalogs.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationkits.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/integrationkits.camel.apache.org.crd.yaml similarity index 100% rename from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationkits.camel.apache.org.crd.yaml rename to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/integrationkits.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationplatforms.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/integrationplatforms.camel.apache.org.crd.yaml similarity index 100% rename from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrationplatforms.camel.apache.org.crd.yaml rename to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/integrationplatforms.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrations.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/integrations.camel.apache.org.crd.yaml similarity index 100% rename from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/integrations.camel.apache.org.crd.yaml rename to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/integrations.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kameletbindings.camel.apache.org.crd.yaml similarity index 86% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml copy to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kameletbindings.camel.apache.org.crd.yaml index 7d36aab..4366d36 100644 --- a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml +++ b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kameletbindings.camel.apache.org.crd.yaml @@ -18,7 +18,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: kamelets.camel.apache.org + name: kameletbindings.camel.apache.org labels: app: "camel-k" spec: @@ -32,14 +32,14 @@ spec: subresources: status: {} names: - kind: Kamelet - listKind: KameletList - plural: kamelets - singular: kamelet + kind: KameletBinding + listKind: KameletBindingList + plural: kameletbindings + singular: kameletbinding shortNames: - - kl + - klb additionalPrinterColumns: - name: Phase type: string - description: The Kamelet phase + description: The KameletBinding phase JSONPath: .status.phase diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml b/deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kamelets.camel.apache.org.crd.yaml similarity index 100% copy from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml copy to deploy/olm-catalog/camel-k-dev/1.2.0-snapshot/kamelets.camel.apache.org.crd.yaml diff --git a/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml b/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml index fec2def..38dc116 100644 --- a/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml +++ b/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml @@ -16,9 +16,7 @@ # --------------------------------------------------------------------------- channels: -- currentCSV: camel-k-operator.v1.1.0-snapshot +- currentCSV: camel-k-operator.v1.2.0-snapshot name: stable -- currentCSV: camel-k-operator.v1.0.0-rc2 - name: alpha defaultChannel: stable packageName: camel-k-dev diff --git a/deploy/olm-catalog/csv-config.yaml b/deploy/olm-catalog/csv-config.yaml index a5e2dd2..6a5a21c 100644 --- a/deploy/olm-catalog/csv-config.yaml +++ b/deploy/olm-catalog/csv-config.yaml @@ -23,6 +23,7 @@ crd-cr-paths: - deploy/crd-integration-kit.yaml - deploy/crd-integration-platform.yaml - deploy/crd-kamelet.yaml + - deploy/crd-kamelet-binding.yaml role-paths: - deploy/operator-role-olm.yaml - deploy/operator-role-olm-cluster.yaml diff --git a/deploy/resources.go b/deploy/resources.go index 1a944c4..a07493e 100644 --- a/deploy/resources.go +++ b/deploy/resources.go @@ -144,8 +144,8 @@ var assets = func() http.FileSystem { compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x3a\x4f\x8f\xe2\x38\xf6\xf7\x7c\x8a\xa7\xe2\xd0\x33\x52\x01\xbf\x9e\xb9\xfc\xc4\x9e\x58\xba\x4b\xcb\x76\x37\x55\x02\x7a\x46\x73\x34\xce\x23\x78\xcb\xb1\xb3\xb6\x03\x5d\xbb\xda\xef\xbe\x7a\x76\x12\x92\x90\x50\x14\xd5\xad\x95\x46\x95\x1b\xf1\xfb\xff\xdf\x2f\x0c\x60\xf8\xfd\x9e\x68\x00\x9f\x05\x47\x65\x31\x06\xa7\xc1\xed\x10\xa6\x19\xe3\x3b\x84\x95\xde\xba\x03\x33\x08\x77\x3a\x57\x31\x73\x42\x2b\xf8\x69\xba\xba\xfb\x [...] }, - "/crd-kamelet.yaml": &vfsgen۰CompressedFileInfo{ - name: "crd-kamelet.yaml", + "/kamelets.camel.apache.org.crd.yaml": &vfsgen۰CompressedFileInfo{ + name: "kamelets.camel.apache.org.crd.yaml", modTime: time.Time{}, uncompressedSize: 1498, @@ -387,7 +387,7 @@ var assets = func() http.FileSystem { fs["/crd-integration-kit.yaml"].(os.FileInfo), fs["/crd-integration-platform.yaml"].(os.FileInfo), fs["/crd-integration.yaml"].(os.FileInfo), - fs["/crd-kamelet.yaml"].(os.FileInfo), + fs["/kamelets.camel.apache.org.crd.yaml"].(os.FileInfo), fs["/operator-deployment.yaml"].(os.FileInfo), fs["/operator-role-binding-events.yaml"].(os.FileInfo), fs["/operator-role-binding-knative.yaml"].(os.FileInfo), diff --git a/e2e/yaks/kamelets/kamelet.feature b/e2e/yaks/kamelets/kamelet.feature index 3f9e170..edae8bc 100644 --- a/e2e/yaks/kamelets/kamelet.feature +++ b/e2e/yaks/kamelets/kamelet.feature @@ -1,5 +1,5 @@ -Feature: Camel K can run Kamelets +Feature: Camel K can run Kamelets and bind them - Scenario: Running integration using a simple Kamelet - Given integration usage is running - Then integration usage should print Hello Kamelets + Scenario: Running integration using a simple Kamelet with KameletBinding + Given integration logger is running + Then integration logger should print Hello Kamelets diff --git a/e2e/yaks/kamelets/usage.groovy b/e2e/yaks/kamelets/logger.groovy similarity index 94% rename from e2e/yaks/kamelets/usage.groovy rename to e2e/yaks/kamelets/logger.groovy index d4c5de8..a28afe3 100755 --- a/e2e/yaks/kamelets/usage.groovy +++ b/e2e/yaks/kamelets/logger.groovy @@ -17,5 +17,5 @@ * limitations under the License. */ -from('kamelet:timer?message=Hello+Kamelets&period=1000') +from('knative:channel/messages') .log('${body}') diff --git a/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml b/e2e/yaks/kamelets/messages-channel.yaml similarity index 84% copy from deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml copy to e2e/yaks/kamelets/messages-channel.yaml index fec2def..abd3483 100644 --- a/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml +++ b/e2e/yaks/kamelets/messages-channel.yaml @@ -15,10 +15,7 @@ # limitations under the License. # --------------------------------------------------------------------------- -channels: -- currentCSV: camel-k-operator.v1.1.0-snapshot - name: stable -- currentCSV: camel-k-operator.v1.0.0-rc2 - name: alpha -defaultChannel: stable -packageName: camel-k-dev +apiVersion: messaging.knative.dev/v1beta1 +kind: InMemoryChannel +metadata: + name: messages diff --git a/e2e/yaks/kamelets/timer-source.yaml b/e2e/yaks/kamelets/timer-source.yaml new file mode 100644 index 0000000..35bfe52 --- /dev/null +++ b/e2e/yaks/kamelets/timer-source.yaml @@ -0,0 +1,18 @@ +apiVersion: camel.apache.org/v1alpha1 +kind: KameletBinding +metadata: + name: timer-source +spec: + source: + ref: + kind: Kamelet + apiVersion: camel.apache.org/v1alpha1 + name: timer + properties: + message: Hello Kamelets + period: "1000" + sink: + ref: + kind: InMemoryChannel + apiVersion: messaging.knative.dev/v1beta1 + name: messages diff --git a/e2e/yaks/kamelets/yaks-config.yaml b/e2e/yaks/kamelets/yaks-config.yaml index 1df07fc..947d1c1 100644 --- a/e2e/yaks/kamelets/yaks-config.yaml +++ b/e2e/yaks/kamelets/yaks-config.yaml @@ -23,6 +23,11 @@ pre: run: | kamel install -n $YAKS_NAMESPACE + kubectl apply -f messages-channel.yaml -n $YAKS_NAMESPACE + kubectl apply -f timer.kamelet.yaml -n $YAKS_NAMESPACE - kamel run usage.groovy -w -n $YAKS_NAMESPACE + kubectl apply -f timer-source.yaml -n $YAKS_NAMESPACE + kubectl wait kameletbinding timer-source --for=condition=Ready --timeout=10m -n $YAKS_NAMESPACE + + kamel run logger.groovy -w -n $YAKS_NAMESPACE diff --git a/examples/kamelets/kamelet-binding-example.yaml b/examples/kamelets/kamelet-binding-example.yaml new file mode 100644 index 0000000..7e3d95d --- /dev/null +++ b/examples/kamelets/kamelet-binding-example.yaml @@ -0,0 +1,15 @@ +apiVersion: camel.apache.org/v1alpha1 +kind: KameletBinding +metadata: + name: timer-source +spec: + source: + ref: + kind: Kamelet + apiVersion: camel.apache.org/v1alpha1 + name: timer + sink: + ref: + kind: InMemoryChannel + apiVersion: messaging.knative.dev/v1beta1 + name: messages diff --git a/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml b/examples/kamelets/messages-channel.yaml similarity index 84% copy from deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml copy to examples/kamelets/messages-channel.yaml index fec2def..abd3483 100644 --- a/deploy/olm-catalog/camel-k-dev/camel-k-dev.package.yaml +++ b/examples/kamelets/messages-channel.yaml @@ -15,10 +15,7 @@ # limitations under the License. # --------------------------------------------------------------------------- -channels: -- currentCSV: camel-k-operator.v1.1.0-snapshot - name: stable -- currentCSV: camel-k-operator.v1.0.0-rc2 - name: alpha -defaultChannel: stable -packageName: camel-k-dev +apiVersion: messaging.knative.dev/v1beta1 +kind: InMemoryChannel +metadata: + name: messages diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml b/helm/camel-k/crds/crd-kamelet-binding.yaml similarity index 86% rename from deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml rename to helm/camel-k/crds/crd-kamelet-binding.yaml index 7d36aab..4366d36 100644 --- a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml +++ b/helm/camel-k/crds/crd-kamelet-binding.yaml @@ -18,7 +18,7 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: kamelets.camel.apache.org + name: kameletbindings.camel.apache.org labels: app: "camel-k" spec: @@ -32,14 +32,14 @@ spec: subresources: status: {} names: - kind: Kamelet - listKind: KameletList - plural: kamelets - singular: kamelet + kind: KameletBinding + listKind: KameletBindingList + plural: kameletbindings + singular: kameletbinding shortNames: - - kl + - klb additionalPrinterColumns: - name: Phase type: string - description: The Kamelet phase + description: The KameletBinding phase JSONPath: .status.phase diff --git a/pkg/apis/camel/v1alpha1/kamelet_binding_types.go b/pkg/apis/camel/v1alpha1/kamelet_binding_types.go new file mode 100644 index 0000000..844f5f0 --- /dev/null +++ b/pkg/apis/camel/v1alpha1/kamelet_binding_types.go @@ -0,0 +1,115 @@ +/* +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 v1alpha1 + +import ( + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// KameletBindingSpec -- +type KameletBindingSpec struct { + // Integration is an optional integration used to specify custom parameters + Integration *v1.IntegrationSpec `json:"integration,omitempty"` + // Source is the starting point of the integration defined by this binding + Source Endpoint `json:"source,omitempty"` + // Sink is the destination of the integration defined by this binding + Sink Endpoint `json:"sink,omitempty"` +} + +// 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"` + // 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 map[string]string `json:"properties,omitempty"` +} + +// KameletBindingStatus -- +type KameletBindingStatus struct { + // Phase -- + Phase KameletBindingPhase `json:"phase,omitempty"` + // Conditions -- + Conditions []KameletBindingCondition `json:"conditions,omitempty"` +} + +// KameletBindingCondition describes the state of a resource at a certain point. +type KameletBindingCondition struct { + // Type of kameletBinding condition. + Type KameletBindingConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status corev1.ConditionStatus `json:"status"` + // The last time this condition was updated. + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} + +type KameletBindingConditionType string + +const ( + // KameletBindingConditionReady -- + KameletBindingConditionReady KameletBindingConditionType = "Ready" +) + +type KameletBindingPhase string + +const ( + // KameletKind -- + KameletBindingKind string = "KameletBinding" + + // KameletBindingPhaseNone -- + KameletBindingPhaseNone KameletBindingPhase = "" + // KameletBindingPhaseCreating -- + KameletBindingPhaseCreating KameletBindingPhase = "Creating" + // KameletBindingPhaseError -- + KameletBindingPhaseError KameletBindingPhase = "Error" + // KameletBindingPhaseReady -- + KameletBindingPhaseReady KameletBindingPhase = "Ready" +) + +// KameletBinding is the Schema for the kamelets binding API +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +// +genclient +// +kubebuilder:resource:path=kameletbindings,scope=Namespaced,shortName=klb,categories=kamel;camel +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`,description="The Kamelet Binding phase" +type KameletBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KameletBindingSpec `json:"spec,omitempty"` + Status KameletBindingStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// KameletBindingList contains a list of KameletBinding +type KameletBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KameletBinding `json:"items"` +} diff --git a/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go b/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go new file mode 100644 index 0000000..f630013 --- /dev/null +++ b/pkg/apis/camel/v1alpha1/kamelet_binding_types_support.go @@ -0,0 +1,162 @@ +/* +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 v1alpha1 + +import ( + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GetConditions -- +func (in *KameletBindingStatus) GetConditions() []v1.ResourceCondition { + res := make([]v1.ResourceCondition, 0, len(in.Conditions)) + for _, c := range in.Conditions { + res = append(res, c) + } + return res +} + +// GetType -- +func (c KameletBindingCondition) GetType() string { + return string(c.Type) +} + +// GetStatus -- +func (c KameletBindingCondition) GetStatus() corev1.ConditionStatus { + return c.Status +} + +// GetLastUpdateTime -- +func (c KameletBindingCondition) GetLastUpdateTime() metav1.Time { + return c.LastUpdateTime +} + +// GetLastTransitionTime -- +func (c KameletBindingCondition) GetLastTransitionTime() metav1.Time { + return c.LastTransitionTime +} + +// GetReason -- +func (c KameletBindingCondition) GetReason() string { + return c.Reason +} + +// GetMessage -- +func (c KameletBindingCondition) GetMessage() string { + return c.Message +} + +// GetCondition returns the condition with the provided type. +func (in *KameletBindingStatus) GetCondition(condType KameletBindingConditionType) *KameletBindingCondition { + for i := range in.Conditions { + c := in.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} + +// SetCondition -- +func (in *KameletBindingStatus) SetCondition(condType KameletBindingConditionType, status corev1.ConditionStatus, reason string, message string) { + in.SetConditions(KameletBindingCondition{ + Type: condType, + Status: status, + LastUpdateTime: metav1.Now(), + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }) +} + +// SetErrorCondition -- +func (in *KameletBindingStatus) SetErrorCondition(condType KameletBindingConditionType, reason string, err error) { + in.SetConditions(KameletBindingCondition{ + Type: condType, + Status: corev1.ConditionFalse, + LastUpdateTime: metav1.Now(), + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: err.Error(), + }) +} + +// SetConditions updates the resource to include the provided conditions. +// +// If a condition that we are about to add already exists and has the same status and +// reason then we are not going to update. +func (in *KameletBindingStatus) SetConditions(conditions ...KameletBindingCondition) { + for _, condition := range conditions { + if condition.LastUpdateTime.IsZero() { + condition.LastUpdateTime = metav1.Now() + } + if condition.LastTransitionTime.IsZero() { + condition.LastTransitionTime = metav1.Now() + } + + currentCond := in.GetCondition(condition.Type) + + if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { + return + } + // Do not update lastTransitionTime if the status of the condition doesn't change. + if currentCond != nil && currentCond.Status == condition.Status { + condition.LastTransitionTime = currentCond.LastTransitionTime + } + + in.RemoveCondition(condition.Type) + in.Conditions = append(in.Conditions, condition) + } +} + +// RemoveCondition removes the resource condition with the provided type. +func (in *KameletBindingStatus) RemoveCondition(condType KameletBindingConditionType) { + newConditions := in.Conditions[:0] + for _, c := range in.Conditions { + if c.Type != condType { + newConditions = append(newConditions, c) + } + } + + in.Conditions = newConditions +} + +// NewKameletBinding -- +func NewKameletBinding(namespace string, name string) KameletBinding { + return KameletBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: KameletBindingKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + } +} + +// NewKameletBindingList -- +func NewKameletBindingList() KameletBindingList { + return KameletBindingList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: KameletBindingKind, + }, + } +} diff --git a/pkg/apis/camel/v1alpha1/kamelet_types_support.go b/pkg/apis/camel/v1alpha1/kamelet_types_support.go index b4f25c1..9a9fcfd 100644 --- a/pkg/apis/camel/v1alpha1/kamelet_types_support.go +++ b/pkg/apis/camel/v1alpha1/kamelet_types_support.go @@ -61,3 +61,78 @@ func (c KameletCondition) GetReason() string { func (c KameletCondition) GetMessage() string { return c.Message } + +// GetCondition returns the condition with the provided type. +func (in *KameletStatus) GetCondition(condType KameletConditionType) *KameletCondition { + for i := range in.Conditions { + c := in.Conditions[i] + if c.Type == condType { + return &c + } + } + return nil +} + +// SetCondition -- +func (in *KameletStatus) SetCondition(condType KameletConditionType, status corev1.ConditionStatus, reason string, message string) { + in.SetConditions(KameletCondition{ + Type: condType, + Status: status, + LastUpdateTime: metav1.Now(), + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }) +} + +// SetErrorCondition -- +func (in *KameletStatus) SetErrorCondition(condType KameletConditionType, reason string, err error) { + in.SetConditions(KameletCondition{ + Type: condType, + Status: corev1.ConditionFalse, + LastUpdateTime: metav1.Now(), + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: err.Error(), + }) +} + +// SetConditions updates the resource to include the provided conditions. +// +// If a condition that we are about to add already exists and has the same status and +// reason then we are not going to update. +func (in *KameletStatus) SetConditions(conditions ...KameletCondition) { + for _, condition := range conditions { + if condition.LastUpdateTime.IsZero() { + condition.LastUpdateTime = metav1.Now() + } + if condition.LastTransitionTime.IsZero() { + condition.LastTransitionTime = metav1.Now() + } + + currentCond := in.GetCondition(condition.Type) + + if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { + return + } + // Do not update lastTransitionTime if the status of the condition doesn't change. + if currentCond != nil && currentCond.Status == condition.Status { + condition.LastTransitionTime = currentCond.LastTransitionTime + } + + in.RemoveCondition(condition.Type) + in.Conditions = append(in.Conditions, condition) + } +} + +// RemoveCondition removes the resource condition with the provided type. +func (in *KameletStatus) RemoveCondition(condType KameletConditionType) { + newConditions := in.Conditions[:0] + for _, c := range in.Conditions { + if c.Type != condType { + newConditions = append(newConditions, c) + } + } + + in.Conditions = newConditions +} diff --git a/pkg/apis/camel/v1alpha1/register.go b/pkg/apis/camel/v1alpha1/register.go index 5bafade..5ae4530 100644 --- a/pkg/apis/camel/v1alpha1/register.go +++ b/pkg/apis/camel/v1alpha1/register.go @@ -49,6 +49,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &Kamelet{}, &KameletList{}, + &KameletBinding{}, + &KameletBindingList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go index 2014759..32596ba 100644 --- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go @@ -7,7 +7,8 @@ package v1alpha1 import ( json "encoding/json" - v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -28,6 +29,39 @@ func (in *AuthorizationSpec) DeepCopy() *AuthorizationSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Endpoint) DeepCopyInto(out *Endpoint) { + *out = *in + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + *out = new(v1.OwnerReference) + (*in).DeepCopyInto(*out) + } + if in.URI != nil { + in, out := &in.URI, &out.URI + *out = new(string) + **out = **in + } + if in.Properties != nil { + in, out := &in.Properties, &out.Properties + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint. +func (in *Endpoint) DeepCopy() *Endpoint { + if in == nil { + return nil + } + out := new(Endpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EventTypeSpec) DeepCopyInto(out *EventTypeSpec) { *out = *in if in.Schema != nil { @@ -431,6 +465,131 @@ func (in *Kamelet) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletBinding) DeepCopyInto(out *KameletBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletBinding. +func (in *KameletBinding) DeepCopy() *KameletBinding { + if in == nil { + return nil + } + out := new(KameletBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KameletBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletBindingCondition) DeepCopyInto(out *KameletBindingCondition) { + *out = *in + in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletBindingCondition. +func (in *KameletBindingCondition) DeepCopy() *KameletBindingCondition { + if in == nil { + return nil + } + out := new(KameletBindingCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletBindingList) DeepCopyInto(out *KameletBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KameletBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletBindingList. +func (in *KameletBindingList) DeepCopy() *KameletBindingList { + if in == nil { + return nil + } + out := new(KameletBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KameletBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletBindingSpec) DeepCopyInto(out *KameletBindingSpec) { + *out = *in + if in.Integration != nil { + in, out := &in.Integration, &out.Integration + *out = new(camelv1.IntegrationSpec) + (*in).DeepCopyInto(*out) + } + in.Source.DeepCopyInto(&out.Source) + in.Sink.DeepCopyInto(&out.Sink) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletBindingSpec. +func (in *KameletBindingSpec) DeepCopy() *KameletBindingSpec { + if in == nil { + return nil + } + out := new(KameletBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletBindingStatus) DeepCopyInto(out *KameletBindingStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]KameletBindingCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletBindingStatus. +func (in *KameletBindingStatus) DeepCopy() *KameletBindingStatus { + if in == nil { + return nil + } + out := new(KameletBindingStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KameletCondition) DeepCopyInto(out *KameletCondition) { *out = *in in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) @@ -503,14 +662,14 @@ func (in *KameletSpec) DeepCopyInto(out *KameletSpec) { in.Definition.DeepCopyInto(&out.Definition) if in.Sources != nil { in, out := &in.Sources, &out.Sources - *out = make([]v1.SourceSpec, len(*in)) + *out = make([]camelv1.SourceSpec, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.Flow != nil { in, out := &in.Flow, &out.Flow - *out = new(v1.Flow) + *out = new(camelv1.Flow) (*in).DeepCopyInto(*out) } out.Authorization = in.Authorization diff --git a/pkg/cmd/reset.go b/pkg/cmd/reset.go index e45f174..f6fa6f6 100644 --- a/pkg/cmd/reset.go +++ b/pkg/cmd/reset.go @@ -20,13 +20,12 @@ package cmd import ( "fmt" + 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/client" "github.com/pkg/errors" "github.com/spf13/cobra" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" - - v1 "github.com/apache/camel-k/pkg/apis/camel/v1" - "github.com/apache/camel-k/pkg/client" ) func newCmdReset(rootCmdOptions *RootCmdOptions) (*cobra.Command, *resetCmdOptions) { @@ -49,8 +48,9 @@ func newCmdReset(rootCmdOptions *RootCmdOptions) (*cobra.Command, *resetCmdOptio type resetCmdOptions struct { *RootCmdOptions - SkipKits bool `mapstructure:"skip-kits"` - SkipIntegrations bool `mapstructure:"skip-integrations"` + SkipKits bool `mapstructure:"skip-kits"` + SkipIntegrations bool `mapstructure:"skip-integrations"` + SkipKameletBindings bool `mapstructure:"skip-kamelet-bindings"` } func (o *resetCmdOptions) reset(_ *cobra.Command, _ []string) { @@ -74,7 +74,15 @@ func (o *resetCmdOptions) reset(_ *cobra.Command, _ []string) { fmt.Print(err) return } - fmt.Printf("%d integration Kits deleted from namespace %s\n", n, o.Namespace) + fmt.Printf("%d integration kits deleted from namespace %s\n", n, o.Namespace) + } + + if !o.SkipKameletBindings { + if n, err = o.deleteAllKameletBindings(c); err != nil { + fmt.Print(err) + return + } + fmt.Printf("%d kamelet bindings deleted from namespace %s\n", n, o.Namespace) } if err = o.resetIntegrationPlatform(c); err != nil { @@ -113,6 +121,20 @@ func (o *resetCmdOptions) deleteAllIntegrationKits(c client.Client) (int, error) return len(list.Items), nil } +func (o *resetCmdOptions) deleteAllKameletBindings(c client.Client) (int, error) { + list := v1alpha1.NewKameletBindingList() + if err := c.List(o.Context, &list, k8sclient.InNamespace(o.Namespace)); err != nil { + return 0, errors.Wrap(err, fmt.Sprintf("could not retrieve kamelet bindings from namespace %s", o.Namespace)) + } + for _, i := range list.Items { + klb := i + if err := c.Delete(o.Context, &klb); err != nil { + return 0, errors.Wrap(err, fmt.Sprintf("could not delete kamelet binding %s from namespace %s", klb.Name, klb.Namespace)) + } + } + return len(list.Items), nil +} + func (o *resetCmdOptions) resetIntegrationPlatform(c client.Client) error { list := v1.NewIntegrationPlatformList() if err := c.List(o.Context, &list, k8sclient.InNamespace(o.Namespace)); err != nil { diff --git a/pkg/controller/add_kameletbinding.go b/pkg/controller/add_kameletbinding.go new file mode 100644 index 0000000..480c4df --- /dev/null +++ b/pkg/controller/add_kameletbinding.go @@ -0,0 +1,26 @@ +/* +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 controller + +import ( + "github.com/apache/camel-k/pkg/controller/kameletbinding" +) + +func init() { + // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. + AddToManagerFuncs = append(AddToManagerFuncs, kameletbinding.Add) +} diff --git a/pkg/controller/kamelet/common.go b/pkg/controller/kamelet/common.go index d95b27a..8b75ffd 100644 --- a/pkg/controller/kamelet/common.go +++ b/pkg/controller/kamelet/common.go @@ -7,11 +7,18 @@ import ( "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" ) func updateStatus(kamelet *v1alpha1.Kamelet) (*v1alpha1.Kamelet, error) { target := kamelet.DeepCopy() target.Status.Phase = v1alpha1.KameletPhaseReady + target.Status.SetCondition( + v1alpha1.KameletConditionReady, + corev1.ConditionTrue, + "", + "", + ) if err := recomputeProperties(target); err != nil { return nil, err } diff --git a/pkg/controller/kameletbinding/action.go b/pkg/controller/kameletbinding/action.go new file mode 100644 index 0000000..b0688c0 --- /dev/null +++ b/pkg/controller/kameletbinding/action.go @@ -0,0 +1,54 @@ +/* +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 kameletbinding + +import ( + "context" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/client" + "github.com/apache/camel-k/pkg/util/log" +) + +// Action -- +type Action interface { + client.Injectable + log.Injectable + + // a user friendly name for the action + Name() string + + // returns true if the action can handle the kameletBinding + CanHandle(kamelet *v1alpha1.KameletBinding) bool + + // executes the handling function + Handle(ctx context.Context, kamelet *v1alpha1.KameletBinding) (*v1alpha1.KameletBinding, error) +} + +type baseAction struct { + client client.Client + L log.Logger +} + +func (action *baseAction) InjectClient(client client.Client) { + action.client = client +} + +func (action *baseAction) InjectLogger(log log.Logger) { + action.L = log +} diff --git a/pkg/controller/kameletbinding/initialize.go b/pkg/controller/kameletbinding/initialize.go new file mode 100644 index 0000000..dd43839 --- /dev/null +++ b/pkg/controller/kameletbinding/initialize.go @@ -0,0 +1,154 @@ +/* +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 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/kubernetes" + "github.com/apache/camel-k/pkg/util/uri" + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NewInitializeAction returns a action that initializes the kamelet binding configuration when not provided by the user +func NewInitializeAction() Action { + return &initializeAction{} +} + +type initializeAction struct { + baseAction +} + +func (action *initializeAction) Name() string { + return "initialize" +} + +func (action *initializeAction) CanHandle(kameletbinding *v1alpha1.KameletBinding) bool { + return kameletbinding.Status.Phase == v1alpha1.KameletBindingPhaseNone +} + +func (action *initializeAction) Handle(ctx context.Context, kameletbinding *v1alpha1.KameletBinding) (*v1alpha1.KameletBinding, error) { + it := v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kameletbinding.Namespace, + Name: kameletbinding.Name, + }, + } + // start from the integration spec defined in the binding + if kameletbinding.Spec.Integration != nil { + it.Spec = *kameletbinding.Spec.Integration.DeepCopy() + } + + fromURI, err := getEndpointURI(kameletbinding.Spec.Source) + if err != nil { + return nil, errors.Wrap(err, "could not determine source URI") + } + toURI, err := getEndpointURI(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 it.Spec.Traits == nil { + it.Spec.Traits = make(map[string]v1.TraitSpec) + } + it.Spec.Traits["knative"] = v1.TraitSpec{ + Configuration: v1.TraitConfiguration{ + RawMessage: knativeConfigJSON, + }, + } + } + + flow := map[string]interface{}{ + "from": map[string]interface{}{ + "uri": fromURI, + "steps": []map[string]interface{}{ + { + "to": toURI, + }, + }, + }, + } + encodedFlow, err := json.Marshal(flow) + if err != nil { + return nil, err + } + it.Spec.Flows = append(it.Spec.Flows, v1.Flow{RawMessage: encodedFlow}) + + if err := kubernetes.ReplaceResource(ctx, action.client, &it); err != nil { + return nil, errors.Wrap(err, "could not create integration for kamelet binding") + } + + target := kameletbinding.DeepCopy() + 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 + } + return uri.AppendParameters(baseURI, e.Properties), 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/controller/kameletbinding/kamelet_binding_controller.go b/pkg/controller/kameletbinding/kamelet_binding_controller.go new file mode 100644 index 0000000..459c69c --- /dev/null +++ b/pkg/controller/kameletbinding/kamelet_binding_controller.go @@ -0,0 +1,186 @@ +/* +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 kameletbinding + +import ( + "context" + "time" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/client" + camelevent "github.com/apache/camel-k/pkg/event" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +// Add creates a new KameletBinding Controller and adds it to the Manager. The Manager will set fields on the Controller +// and Start it when the Manager is Started. +func Add(mgr manager.Manager) error { + c, err := client.FromManager(mgr) + if err != nil { + return err + } + return add(mgr, newReconciler(mgr, c)) +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { + return &ReconcileKameletBinding{ + client: c, + scheme: mgr.GetScheme(), + recorder: mgr.GetEventRecorderFor("camel-k-kamelet-binding-controller"), + } +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New("kamelet-binding-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + // Watch for changes to primary resource KameletBinding + err = c.Watch(&source.Kind{Type: &v1alpha1.KameletBinding{}}, &handler.EnqueueRequestForObject{}, predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + oldKameletBinding := e.ObjectOld.(*v1alpha1.KameletBinding) + newKameletBinding := e.ObjectNew.(*v1alpha1.KameletBinding) + // Ignore updates to the kameletBinding status in which case metadata.Generation + // does not change, or except when the kameletBinding phase changes as it's used + // to transition from one phase to another + return oldKameletBinding.Generation != newKameletBinding.Generation || + oldKameletBinding.Status.Phase != newKameletBinding.Status.Phase + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Evaluates to false if the object has been confirmed deleted + return !e.DeleteStateUnknown + }, + }) + if err != nil { + return err + } + + return nil +} + +var _ reconcile.Reconciler = &ReconcileKameletBinding{} + +// ReconcileKameletBinding reconciles a KameletBinding object +type ReconcileKameletBinding struct { + // This client, initialized using mgr.Client() above, is a split client + // that reads objects from the cache and writes to the apiserver + client client.Client + scheme *runtime.Scheme + recorder record.EventRecorder +} + +// Reconcile reads that state of the cluster for a KameletBinding object and makes changes based +// on the state read and what is in the KameletBinding.Spec +// Note: +// The Controller will requeue the Request to be processed again if the returned error is non-nil or +// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. +func (r *ReconcileKameletBinding) Reconcile(request reconcile.Request) (reconcile.Result, error) { + rlog := Log.WithValues("request-namespace", request.Namespace, "request-name", request.Name) + rlog.Info("Reconciling KameletBinding") + + ctx := context.TODO() + + // Fetch the KameletBinding instance + var instance v1alpha1.KameletBinding + + if err := r.client.Get(ctx, request.NamespacedName, &instance); err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup + // logic use finalizers. + + // Return and don't requeue + return reconcile.Result{}, nil + } + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + actions := []Action{ + NewInitializeAction(), + NewMonitorAction(), + } + + var targetPhase v1alpha1.KameletBindingPhase + var err error + + target := instance.DeepCopy() + targetLog := rlog.ForKameletBinding(target) + + for _, a := range actions { + a.InjectClient(r.client) + a.InjectLogger(targetLog) + + if a.CanHandle(target) { + targetLog.Infof("Invoking action %s", a.Name()) + + phaseFrom := target.Status.Phase + + target, err = a.Handle(ctx, target) + if err != nil { + camelevent.NotifyKameletBindingError(ctx, r.client, r.recorder, &instance, target, err) + return reconcile.Result{}, err + } + + if target != nil { + if err := r.client.Status().Patch(ctx, target, k8sclient.MergeFrom(&instance)); err != nil { + camelevent.NotifyKameletBindingError(ctx, r.client, r.recorder, &instance, target, err) + return reconcile.Result{}, err + } + + targetPhase = target.Status.Phase + + if targetPhase != phaseFrom { + targetLog.Info( + "state transition", + "phase-from", phaseFrom, + "phase-to", target.Status.Phase, + ) + } + } + + // handle one action at time so the resource + // is always at its latest state + camelevent.NotifyKameletBindingUpdated(ctx, r.client, r.recorder, &instance, target) + break + } + } + + if targetPhase == v1alpha1.KameletBindingPhaseReady { + return reconcile.Result{}, nil + } + + // Requeue + return reconcile.Result{ + RequeueAfter: 5 * time.Second, + }, nil +} diff --git a/pkg/controller/kameletbinding/log.go b/pkg/controller/kameletbinding/log.go new file mode 100644 index 0000000..0162b6d --- /dev/null +++ b/pkg/controller/kameletbinding/log.go @@ -0,0 +1,23 @@ +/* +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 kameletbinding + +import "github.com/apache/camel-k/pkg/util/log" + +// Log -- +var Log = log.Log.WithName("controller").WithName("kameletbinding") diff --git a/pkg/controller/kameletbinding/monitor.go b/pkg/controller/kameletbinding/monitor.go new file mode 100644 index 0000000..9980dc5 --- /dev/null +++ b/pkg/controller/kameletbinding/monitor.go @@ -0,0 +1,87 @@ +/* +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 kameletbinding + +import ( + "context" + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" +) + +// NewMonitorAction returns an action that monitors the kamelet binding after it's fully initialized +func NewMonitorAction() Action { + return &monitorAction{} +} + +type monitorAction struct { + baseAction +} + +func (action *monitorAction) Name() string { + return "monitor" +} + +func (action *monitorAction) CanHandle(kameletbinding *v1alpha1.KameletBinding) bool { + return kameletbinding.Status.Phase == v1alpha1.KameletBindingPhaseCreating || + kameletbinding.Status.Phase == v1alpha1.KameletBindingPhaseError || + kameletbinding.Status.Phase == v1alpha1.KameletBindingPhaseReady +} + +func (action *monitorAction) Handle(ctx context.Context, kameletbinding *v1alpha1.KameletBinding) (*v1alpha1.KameletBinding, error) { + key := client.ObjectKey{ + Namespace: kameletbinding.Namespace, + Name: kameletbinding.Name, + } + it := v1.Integration{} + if err := action.client.Get(ctx, key, &it); err != nil { + return nil, errors.Wrapf(err, "could not load integration for KameletBinding %q", kameletbinding.Name) + } + + // Map integration phases to KameletBinding phases + target := kameletbinding.DeepCopy() + if it.Status.Phase == v1.IntegrationPhaseRunning { + target.Status.Phase = v1alpha1.KameletBindingPhaseReady + target.Status.SetCondition( + v1alpha1.KameletBindingConditionReady, + corev1.ConditionTrue, + "", + "", + ) + } else if it.Status.Phase == v1.IntegrationPhaseError { + target.Status.Phase = v1alpha1.KameletBindingPhaseError + target.Status.SetCondition( + v1alpha1.KameletBindingConditionReady, + corev1.ConditionFalse, + string(target.Status.Phase), + "", + ) + } else { + target.Status.Phase = v1alpha1.KameletBindingPhaseCreating + target.Status.SetCondition( + v1alpha1.KameletBindingConditionReady, + corev1.ConditionFalse, + string(target.Status.Phase), + "", + ) + } + return target, nil +} diff --git a/pkg/event/manager.go b/pkg/event/manager.go index a2b2eb8..6278ba8 100644 --- a/pkg/event/manager.go +++ b/pkg/event/manager.go @@ -68,6 +68,13 @@ const ( // ReasonKameletPhaseUpdated -- ReasonKameletPhaseUpdated = "KameletPhaseUpdated" + // ReasonKameletBindingError -- + ReasonKameletBindingError = "KameletBindingError" + // ReasonKameletBindingConditionChanged -- + ReasonKameletBindingConditionChanged = "KameletBindingConditionChanged" + // ReasonKameletBindingPhaseUpdated -- + ReasonKameletBindingPhaseUpdated = "KameletBindingPhaseUpdated" + // ReasonRelatedObjectChanged -- ReasonRelatedObjectChanged = "ReasonRelatedObjectChanged" ) @@ -188,6 +195,35 @@ func NotifyKameletError(ctx context.Context, c client.Client, recorder record.Ev recorder.Eventf(k, corev1.EventTypeWarning, ReasonKameletError, "Cannot reconcile Kamelet %s: %v", k.Name, err) } +// NotifyKameletBindingUpdated automatically generates events when a KameletBinding changes +func NotifyKameletBindingUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, new *v1alpha1.KameletBinding) { + if new == nil { + return + } + oldPhase := "" + var oldConditions []v1.ResourceCondition + if old != nil { + oldPhase = string(old.Status.Phase) + oldConditions = old.Status.GetConditions() + } + if new.Status.Phase != v1alpha1.KameletBindingPhaseNone { + notifyIfConditionUpdated(recorder, new, oldConditions, new.Status.GetConditions(), "KameletBinding", new.Name, ReasonKameletBindingConditionChanged) + } + notifyIfPhaseUpdated(ctx, c, recorder, new, oldPhase, string(new.Status.Phase), "KameletBinding", new.Name, ReasonKameletBindingPhaseUpdated, "") +} + +// NotifyKameletBindingError automatically generates error events when the kameletBinding reconcile cycle phase has an error +func NotifyKameletBindingError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, new *v1alpha1.KameletBinding, err error) { + k := old + if new != nil { + k = new + } + if k == nil { + return + } + recorder.Eventf(k, corev1.EventTypeWarning, ReasonKameletError, "Cannot reconcile KameletBinding %s: %v", k.Name, err) +} + // NotifyBuildUpdated automatically generates events when a build changes func NotifyBuildUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, new *v1.Build) { if new == nil { diff --git a/pkg/install/cluster.go b/pkg/install/cluster.go index ae55328..8fdc902 100644 --- a/pkg/install/cluster.go +++ b/pkg/install/cluster.go @@ -75,6 +75,11 @@ func SetupClusterWideResourcesOrCollect(ctx context.Context, clientProvider clie return err } + // Install CRD for KameletBinding (if needed) + if err := installCRD(ctx, c, "KameletBinding", "v1alpha1", "crd-kamelet-binding.yaml", collection); err != nil { + return err + } + // Installing ClusterRole clusterRoleInstalled, err := IsClusterRoleInstalled(ctx, c) if err != nil { diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go index 7d290f0..f3ca598 100644 --- a/pkg/util/log/log.go +++ b/pkg/util/log/log.go @@ -129,7 +129,7 @@ func (l Logger) ForIntegrationPlatform(target *v1.IntegrationPlatform) Logger { ) } -// ForIntegrationPlatform -- +// ForKamelet -- func (l Logger) ForKamelet(target *v1alpha1.Kamelet) Logger { return l.WithValues( "api-version", target.APIVersion, @@ -139,6 +139,16 @@ func (l Logger) ForKamelet(target *v1alpha1.Kamelet) Logger { ) } +// ForKameletBinding -- +func (l Logger) ForKameletBinding(target *v1alpha1.KameletBinding) Logger { + return l.WithValues( + "api-version", target.APIVersion, + "kind", target.Kind, + "ns", target.Namespace, + "name", target.Name, + ) +} + // *********************************** // // Helpers diff --git a/pkg/util/uri/uri.go b/pkg/util/uri/uri.go index 6e9122d..1e0cf1e 100644 --- a/pkg/util/uri/uri.go +++ b/pkg/util/uri/uri.go @@ -21,6 +21,7 @@ import ( "fmt" "net/url" "regexp" + "sort" "strings" "github.com/apache/camel-k/pkg/util/log" @@ -56,3 +57,20 @@ func matchOrEmpty(reg *regexp.Regexp, str string) string { } return "" } + +func AppendParameters(uri string, params map[string]string) string { + prefix := "&" + if !strings.Contains(uri, "?") { + prefix = "?" + } + keys := make([]string, 0, len(params)) + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + uri += fmt.Sprintf("%s%s=%s", prefix, url.QueryEscape(k), url.QueryEscape(params[k])) + prefix = "&" + } + return uri +} diff --git a/pkg/util/uri/uri_test.go b/pkg/util/uri/uri_test.go index 8908fec..ae22ff8 100644 --- a/pkg/util/uri/uri_test.go +++ b/pkg/util/uri/uri_test.go @@ -18,6 +18,7 @@ limitations under the License. package uri import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -89,3 +90,55 @@ func TestQueryParameter(t *testing.T) { } } + +func TestAppendParameters(t *testing.T) { + tests := []struct { + prefix string + params map[string]string + expected string + }{ + { + prefix: "kamelet://mykamelet", + params: nil, + expected: "kamelet://mykamelet", + }, + { + prefix: "kamelet://mykamelet", + params: map[string]string{ + "a": "b", + }, + expected: "kamelet://mykamelet?a=b", + }, + { + prefix: "kamelet://mykamelet", + params: map[string]string{ + "a": "b", + "c": "d", + }, + expected: "kamelet://mykamelet?a=b&c=d", + }, + { + prefix: "kamelet://mykamelet", + params: map[string]string{ + "z": "y", + "c": "d", + }, + expected: "kamelet://mykamelet?c=d&z=y", + }, + { + prefix: "kamelet://mykamelet?h=m", + params: map[string]string{ + "z": "y", + "c": "d", + }, + expected: "kamelet://mykamelet?h=m&c=d&z=y", + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("appendParameters-%d-%s", i, test.expected), func(t *testing.T) { + uri := AppendParameters(test.prefix, test.params) + assert.Equal(t, test.expected, uri) + }) + } +}
