[camel-k] branch master updated (9ab0df1 -> e2cb3f0)

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

[camel-k] branch master updated (9ab0df1 -> e2cb3f0)

lburgazzoli-2
This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git.


    from 9ab0df1  chore(runtime): code cleanup
     new ec51786  (chore) reorganizing examples
     new 46fe56b  Fix #219: use deployment in Knative when cannot scale to 0
     new 11da911  Fix #209: add a Istio trait
     new 1a844c7  Fix knative service generation and documentation
     new 2da7dd0  Fix after rebase
     new e2cb3f0  Disable debug by default and add doc

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.adoc                                        |  16 +-
 docs/developers.adoc                               |   4 +-
 docs/traits.adoc                                   |  24 ++-
 .../examples => examples}/CaffeineCacheSample.java |   0
 .../examples => examples}/RestWithRestlet.java     |   2 +-
 {runtime/examples => examples}/Sample.java         |   0
 .../examples => examples}/camel-caffeine.groovy    |   2 +-
 {runtime/examples => examples}/dns.js              |   4 +-
 {runtime/examples => examples}/hello.xml           |   0
 examples/knative/README.adoc                       | 136 ++++++++++++++
 examples/knative/feed.groovy                       |   4 +
 examples/knative/messages-channel.yaml             |   9 +
 examples/knative/printer.groovy                    |   4 +
 examples/knative/splitter.groovy                   |   5 +
 examples/knative/words-channel.yaml                |   9 +
 {runtime/examples => examples}/kotlin-routes.kts   |   4 +-
 {runtime/examples => examples}/props.js            |   2 +-
 {runtime/examples => examples}/routes-rest.js      |   2 +-
 {runtime/examples => examples}/routes.groovy       |   4 +-
 {runtime/examples => examples}/routes.js           |   0
 {runtime/examples => examples}/simple.groovy       |   0
 {runtime/examples => examples}/simple.js           |   0
 pkg/metadata/http.go                               | 131 ++++++++++++++
 pkg/metadata/metadata.go                           |  57 +++++-
 pkg/metadata/metadata_http_test.go                 | 195 +++++++++++++++++++++
 pkg/metadata/types.go                              |   4 +
 pkg/trait/catalog.go                               |  14 +-
 pkg/trait/debug.go                                 |   8 +
 pkg/trait/istio.go                                 |  64 +++++++
 pkg/trait/knative.go                               |  51 +++++-
 pkg/trait/service.go                               |  42 +----
 pkg/trait/trait_test.go                            |  22 ++-
 pkg/util/kubernetes/collection.go                  |  42 +++++
 pkg/util/kubernetes/replace.go                     |  10 ++
 34 files changed, 788 insertions(+), 83 deletions(-)
 rename {runtime/examples => examples}/CaffeineCacheSample.java (100%)
 rename {runtime/examples => examples}/RestWithRestlet.java (92%)
 rename {runtime/examples => examples}/Sample.java (100%)
 rename {runtime/examples => examples}/camel-caffeine.groovy (95%)
 rename {runtime/examples => examples}/dns.js (68%)
 rename {runtime/examples => examples}/hello.xml (100%)
 create mode 100644 examples/knative/README.adoc
 create mode 100644 examples/knative/feed.groovy
 create mode 100644 examples/knative/messages-channel.yaml
 create mode 100644 examples/knative/printer.groovy
 create mode 100644 examples/knative/splitter.groovy
 create mode 100644 examples/knative/words-channel.yaml
 rename {runtime/examples => examples}/kotlin-routes.kts (64%)
 rename {runtime/examples => examples}/props.js (50%)
 rename {runtime/examples => examples}/routes-rest.js (87%)
 rename {runtime/examples => examples}/routes.groovy (83%)
 rename {runtime/examples => examples}/routes.js (100%)
 rename {runtime/examples => examples}/simple.groovy (100%)
 rename {runtime/examples => examples}/simple.js (100%)
 create mode 100644 pkg/metadata/http.go
 create mode 100644 pkg/metadata/metadata_http_test.go
 create mode 100644 pkg/trait/istio.go

Reply | Threaded
Open this post in threaded view
|

[camel-k] 01/06: (chore) reorganizing examples

lburgazzoli-2
This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit ec51786bcb1f755a5b282c8248a71dc7fe8ba378
Author: nferraro <[hidden email]>
AuthorDate: Sun Dec 2 00:02:23 2018 +0100

    (chore) reorganizing examples
---
 README.adoc                                             | 16 ++++++++--------
 docs/developers.adoc                                    |  4 ++--
 {runtime/examples => examples}/CaffeineCacheSample.java |  0
 {runtime/examples => examples}/RestWithRestlet.java     |  2 +-
 {runtime/examples => examples}/Sample.java              |  0
 {runtime/examples => examples}/camel-caffeine.groovy    |  2 +-
 {runtime/examples => examples}/dns.js                   |  4 ++--
 {runtime/examples => examples}/hello.xml                |  0
 {runtime/examples => examples}/kotlin-routes.kts        |  4 ++--
 {runtime/examples => examples}/props.js                 |  2 +-
 {runtime/examples => examples}/routes-rest.js           |  2 +-
 {runtime/examples => examples}/routes.groovy            |  4 ++--
 {runtime/examples => examples}/routes.js                |  0
 {runtime/examples => examples}/simple.groovy            |  0
 {runtime/examples => examples}/simple.js                |  0
 15 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/README.adoc b/README.adoc
index c50c564..91790ec 100644
--- a/README.adoc
+++ b/README.adoc
@@ -51,17 +51,17 @@ For Minishift, this means executing `oc login -u system:admin` then `kamel insta
 After the initial setup, you can run a Camel integration on the cluster by executing:
 
 ```
-kamel run runtime/examples/Sample.java
+kamel run examples/Sample.java
 ```
 
-A "Sample.java" file is included in the link:/runtime/examples[/runtime/examples] folder of this repository. You can change the content of the file and execute the command again to see the changes.
+A "Sample.java" file is included in the link:/examples[/examples] folder of this repository. You can change the content of the file and execute the command again to see the changes.
 
 ==== Configure Integration properties
 
 Properties associated to an integration can be configured either using a ConfigMap/Secret or by setting using the "--property" flag, i.e.
 
 ```
-kamel run --property my.message=test runtime/examples/props.js
+kamel run --property my.message=test examples/props.js
 ```
 
 ==== Configure Integration Logging
@@ -92,7 +92,7 @@ camel.component.seda.queueSize = 10
 If you want to iterate quickly on an integration to have fast feedback on the code you're writing, you can use by running it in **"dev" mode**:
 
 ```
-kamel run runtime/examples/Sample.java --dev
+kamel run examples/Sample.java --dev
 ```
 
 The `--dev` flag deploys immediately the integration and shows the integration logs in the console. You can then change the code and see
@@ -103,7 +103,7 @@ The console follows automatically all redeploys of the integration.
 Here's an example of the output:
 
 ```
-[nferraro@localhost camel-k]$ kamel run runtime/examples/Sample.java --dev
+[nferraro@localhost camel-k]$ kamel run examples/Sample.java --dev
 integration "sample" created
 integration "sample" in phase Building
 integration "sample" in phase Deploying
@@ -160,9 +160,9 @@ Camel K supports multiple languages for writing integrations:
 
 More information about supported languages is provided in the link:docs/languages.adoc[languages guide].
 
-Integrations written in different languages are provided in the link:/runtime/examples[examples] directory.
+Integrations written in different languages are provided in the link:/examples[examples] directory.
 
-An example of integration written in JavaScript is the link:/runtime/examples/dns.js[/runtime/examples/dns.js] integration.
+An example of integration written in JavaScript is the link:/examples/dns.js[/examples/dns.js] integration.
 Here's the content:
 
 ```
@@ -178,7 +178,7 @@ from('timer:dns?period=1s')
 To run it, you need just to execute:
 
 ```
-kamel run runtime/examples/dns.js
+kamel run examples/dns.js
 ```
 
 === Traits
diff --git a/docs/developers.adoc b/docs/developers.adoc
index b3a6a6c..9589be3 100644
--- a/docs/developers.adoc
+++ b/docs/developers.adoc
@@ -140,13 +140,13 @@ This command assumes you have an already running Minikube instance.
 Now you can play with Camel K:
 
 ```
-./kamel run runtime/examples/Sample.java
+./kamel run examples/Sample.java
 ```
 
 To add additional dependencies to your routes:
 
 ```
-./kamel run -d camel:dns runtime/examples/dns.js
+./kamel run -d camel:dns examples/dns.js
 ```
 
 [[debugging]]
diff --git a/runtime/examples/CaffeineCacheSample.java b/examples/CaffeineCacheSample.java
similarity index 100%
rename from runtime/examples/CaffeineCacheSample.java
rename to examples/CaffeineCacheSample.java
diff --git a/runtime/examples/RestWithRestlet.java b/examples/RestWithRestlet.java
similarity index 92%
rename from runtime/examples/RestWithRestlet.java
rename to examples/RestWithRestlet.java
index 16c77c7..fa48418 100644
--- a/runtime/examples/RestWithRestlet.java
+++ b/examples/RestWithRestlet.java
@@ -1,7 +1,7 @@
 //
 // To run this integrations use:
 //
-//     kamel run --name=rest-with-restlet --dependency=camel-restlet runtime/examples/RestWithRestlet.java
+//     kamel run --name=rest-with-restlet --dependency=camel-restlet examples/RestWithRestlet.java
 //
 public class RestWithRestlet extends org.apache.camel.builder.RouteBuilder {
     @Override
diff --git a/runtime/examples/Sample.java b/examples/Sample.java
similarity index 100%
rename from runtime/examples/Sample.java
rename to examples/Sample.java
diff --git a/runtime/examples/camel-caffeine.groovy b/examples/camel-caffeine.groovy
similarity index 95%
rename from runtime/examples/camel-caffeine.groovy
rename to examples/camel-caffeine.groovy
index 1a70c25..d52deb6 100644
--- a/runtime/examples/camel-caffeine.groovy
+++ b/examples/camel-caffeine.groovy
@@ -1,7 +1,7 @@
 //
 // To run this integrations use:
 //
-//     kamel run --runtime groovy runtime/examples/camel-caffeine.groovy
+//     kamel run --runtime groovy examples/camel-caffeine.groovy
 //
 
 import com.github.benmanes.caffeine.cache.Caffeine
diff --git a/runtime/examples/dns.js b/examples/dns.js
similarity index 68%
rename from runtime/examples/dns.js
rename to examples/dns.js
index f7c90d2..9ae759c 100644
--- a/runtime/examples/dns.js
+++ b/examples/dns.js
@@ -1,11 +1,11 @@
 //
 // To run this integrations use:
 //
-//     kamel run -d camel:dns runtime/examples/dns.js
+//     kamel run -d camel:dns examples/dns.js
 //
 // Or simply (since dependency auto-detection is enabled by default):
 //
-//     kamel run runtime/examples/dns.js
+//     kamel run examples/dns.js
 //
 
 from('timer:dns?period=1s')
diff --git a/runtime/examples/hello.xml b/examples/hello.xml
similarity index 100%
rename from runtime/examples/hello.xml
rename to examples/hello.xml
diff --git a/runtime/examples/kotlin-routes.kts b/examples/kotlin-routes.kts
similarity index 64%
rename from runtime/examples/kotlin-routes.kts
rename to examples/kotlin-routes.kts
index 8b15930..1d6ce6c 100644
--- a/runtime/examples/kotlin-routes.kts
+++ b/examples/kotlin-routes.kts
@@ -1,11 +1,11 @@
 //
 // To run this integrations use:
 //
-//     kamel run --runtime kotlin runtime/examples/kotlin-routes.kts
+//     kamel run --runtime kotlin examples/kotlin-routes.kts
 //
 // Or leveraging runtime detection
 //
-//     kamel run runtime/examples/kotlin-routes.kts
+//     kamel run examples/kotlin-routes.kts
 //
 
 val rnd = java.util.Random()
diff --git a/runtime/examples/props.js b/examples/props.js
similarity index 50%
rename from runtime/examples/props.js
rename to examples/props.js
index a89b6ae..f3cde88 100644
--- a/runtime/examples/props.js
+++ b/examples/props.js
@@ -1,7 +1,7 @@
 //
 // To run this integrations use:
 //
-//     kamel run -p my.message=test-props runtime/examples/props.js
+//     kamel run -p my.message=test-props examples/props.js
 //
 
 from('timer:props?period=1s')
diff --git a/runtime/examples/routes-rest.js b/examples/routes-rest.js
similarity index 87%
rename from runtime/examples/routes-rest.js
rename to examples/routes-rest.js
index 97d41ec..34981a2 100644
--- a/runtime/examples/routes-rest.js
+++ b/examples/routes-rest.js
@@ -1,7 +1,7 @@
 //
 // To run this integrations use:
 //
-//     kamel run --name=withrest --dependency=camel-undertow runtime/examples/routes-rest.js
+//     kamel run --name=withrest --dependency=camel-undertow examples/routes-rest.js
 //
 
 // ****************
diff --git a/runtime/examples/routes.groovy b/examples/routes.groovy
similarity index 83%
rename from runtime/examples/routes.groovy
rename to examples/routes.groovy
index aef7ad3..c5d3b69 100644
--- a/runtime/examples/routes.groovy
+++ b/examples/routes.groovy
@@ -3,11 +3,11 @@ import java.util.concurrent.ThreadLocalRandom
 //
 // To run this integrations use:
 //
-//     kamel run --runtime groovy runtime/examples/routes.groovy
+//     kamel run --runtime groovy examples/routes.groovy
 //
 // Or leveraging runtime detection
 //
-//     kamel run runtime/examples/routes.groovy
+//     kamel run examples/routes.groovy
 //
 
 context {
diff --git a/runtime/examples/routes.js b/examples/routes.js
similarity index 100%
rename from runtime/examples/routes.js
rename to examples/routes.js
diff --git a/runtime/examples/simple.groovy b/examples/simple.groovy
similarity index 100%
rename from runtime/examples/simple.groovy
rename to examples/simple.groovy
diff --git a/runtime/examples/simple.js b/examples/simple.js
similarity index 100%
rename from runtime/examples/simple.js
rename to examples/simple.js

Reply | Threaded
Open this post in threaded view
|

[camel-k] 02/06: Fix #219: use deployment in Knative when cannot scale to 0

lburgazzoli-2
In reply to this post by lburgazzoli-2
This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 46fe56be0cb06238d02401b52514100cd6b9a106
Author: nferraro <[hidden email]>
AuthorDate: Mon Dec 3 15:21:47 2018 +0100

    Fix #219: use deployment in Knative when cannot scale to 0
---
 pkg/metadata/http.go               | 131 +++++++++++++++++++++++++
 pkg/metadata/metadata.go           |  57 ++++++++++-
 pkg/metadata/metadata_http_test.go | 195 +++++++++++++++++++++++++++++++++++++
 pkg/metadata/types.go              |   4 +
 pkg/trait/catalog.go               |  10 +-
 pkg/trait/knative.go               |  70 ++++++++++---
 pkg/trait/service.go               |  54 +++-------
 pkg/trait/trait_test.go            |  22 +++--
 pkg/util/kubernetes/collection.go  |  33 +++++++
 9 files changed, 508 insertions(+), 68 deletions(-)

diff --git a/pkg/metadata/http.go b/pkg/metadata/http.go
new file mode 100644
index 0000000..6ca7065
--- /dev/null
+++ b/pkg/metadata/http.go
@@ -0,0 +1,131 @@
+/*
+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 metadata
+
+import (
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "regexp"
+ "strings"
+)
+
+var httpURIs = map[string]bool{
+ "ahc":                  true,
+ "ahc-ws":               true,
+ "atmosphere-websocket": true,
+ "cxf":         true,
+ "cxfrs":       true,
+ "grpc":        true,
+ "jetty":       true,
+ "netty-http":  true,
+ "netty4-http": true,
+ "rest":        true,
+ "restlet":     true,
+ "servlet":     true,
+ "spark-rest":  true,
+ "spring-ws":   true,
+ "undertow":    true,
+ "websocket":   true,
+ "knative":     true,
+}
+
+var passiveURIs = map[string]bool{
+ "bean":       true,
+ "binding":    true,
+ "browse":     true,
+ "class":      true,
+ "controlbus": true,
+ "dataformat": true,
+ "dataset":    true,
+ "direct":     true,
+ "direct-vm":  true,
+ "language":   true,
+ "log":        true,
+ "mock":       true,
+ "properties": true,
+ "ref":        true,
+ "seda":       true,
+ "stub":       true,
+ "test":       true,
+ "validator":  true,
+ "vm":         true,
+}
+
+var restIndicator = regexp.MustCompile(".*rest\\s*\\([^)]*\\).*")
+var xmlRestIndicator = regexp.MustCompile(".*<\\s*rest\\s+[^>]*>.*")
+
+// requiresHTTPService returns true if the integration needs to expose itself through HTTP
+func requiresHTTPService(source v1alpha1.SourceSpec, fromURIs []string) bool {
+ if hasRestIndicator(source) {
+ return true
+ }
+ return containsHTTPURIs(fromURIs)
+}
+
+// hasOnlyPassiveEndpoints returns true if the integration has no endpoint that needs to remain always active
+func hasOnlyPassiveEndpoints(source v1alpha1.SourceSpec, fromURIs []string) bool {
+ passivePlusHTTP := make(map[string]bool)
+ for k, v := range passiveURIs {
+ passivePlusHTTP[k] = v
+ }
+ for k, v := range httpURIs {
+ passivePlusHTTP[k] = v
+ }
+ return containsOnlyURIsIn(fromURIs, passivePlusHTTP)
+}
+
+func containsHTTPURIs(fromURI []string) bool {
+ for _, uri := range fromURI {
+ prefix := getURIPrefix(uri)
+ if enabled, ok := httpURIs[prefix]; ok && enabled {
+ return true
+ }
+ }
+ return false
+}
+
+func containsOnlyURIsIn(fromURI []string, allowed map[string]bool) bool {
+ for _, uri := range fromURI {
+ prefix := getURIPrefix(uri)
+ if enabled, ok := allowed[prefix]; !ok || !enabled {
+ return false
+ }
+ }
+ return true
+}
+
+func getURIPrefix(uri string) string {
+ parts := strings.SplitN(uri, ":", 2)
+ if len(parts) > 0 {
+ return parts[0]
+ }
+ return ""
+}
+
+func hasRestIndicator(source v1alpha1.SourceSpec) bool {
+ pat := getRestIndicatorRegexpsForLanguage(source.Language)
+ return pat.MatchString(source.Content)
+}
+
+func getRestIndicatorRegexpsForLanguage(language v1alpha1.Language) *regexp.Regexp {
+ switch language {
+ case v1alpha1.LanguageXML:
+ return xmlRestIndicator
+ default:
+ return restIndicator
+ }
+}
diff --git a/pkg/metadata/metadata.go b/pkg/metadata/metadata.go
index 6e43b0b..9eec7bd 100644
--- a/pkg/metadata/metadata.go
+++ b/pkg/metadata/metadata.go
@@ -19,19 +19,68 @@ package metadata
 
 import (
  "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "sort"
 )
 
+// ExtractAll returns metadata information from all listed source codes
+func ExtractAll(sources []v1alpha1.SourceSpec) IntegrationMetadata {
+ // neutral metadata
+ meta := IntegrationMetadata{
+ Language:            "",
+ Dependencies:        []string{},
+ FromURIs:            []string{},
+ ToURIs:              []string{},
+ PassiveEndpoints:    true,
+ RequiresHTTPService: false,
+ }
+ for _, source := range sources {
+ meta = merge(meta, Extract(source))
+ }
+ return meta
+}
+
+func merge(m1 IntegrationMetadata, m2 IntegrationMetadata) IntegrationMetadata {
+ language := m2.Language
+ if m1.Language != "" && m1.Language != language {
+ language = ""
+ }
+ deps := make(map[string]bool)
+ for _, d := range m1.Dependencies {
+ deps[d] = true
+ }
+ for _, d := range m2.Dependencies {
+ deps[d] = true
+ }
+ allDependencies := make([]string, 0)
+ for k := range deps {
+ allDependencies = append(allDependencies, k)
+ }
+ sort.Strings(allDependencies)
+ return IntegrationMetadata{
+ Language:            language,
+ FromURIs:            append(m1.FromURIs, m2.FromURIs...),
+ ToURIs:              append(m1.ToURIs, m2.ToURIs...),
+ Dependencies:        allDependencies,
+ RequiresHTTPService: m1.RequiresHTTPService || m2.RequiresHTTPService,
+ PassiveEndpoints:    m1.PassiveEndpoints && m2.PassiveEndpoints,
+ }
+}
+
 // Extract returns metadata information from the source code
 func Extract(source v1alpha1.SourceSpec) IntegrationMetadata {
  language := discoverLanguage(source)
  fromURIs := discoverFromURIs(source, language)
  toURIs := discoverToURIs(source, language)
  dependencies := discoverDependencies(source, fromURIs, toURIs)
+ requiresHTTPService := requiresHTTPService(source, fromURIs)
+ passiveEndpoints := hasOnlyPassiveEndpoints(source, fromURIs)
  return IntegrationMetadata{
- Language:     language,
- FromURIs:     fromURIs,
- ToURIs:       toURIs,
- Dependencies: dependencies,
+ Language:            language,
+ FromURIs:            fromURIs,
+ ToURIs:              toURIs,
+ Dependencies:        dependencies,
+ RequiresHTTPService: requiresHTTPService,
+ PassiveEndpoints:    passiveEndpoints,
  }
 }
 
diff --git a/pkg/metadata/metadata_http_test.go b/pkg/metadata/metadata_http_test.go
new file mode 100644
index 0000000..d75f57b
--- /dev/null
+++ b/pkg/metadata/metadata_http_test.go
@@ -0,0 +1,195 @@
+/*
+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 metadata
+
+import (
+ "testing"
+
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestHttpJavaSource(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "Request.java",
+ Language: v1alpha1.LanguageJavaSource,
+ Content: `
+ from("telegram:bots/cippa").to("log:stash");
+ from("undertow:uri").to("log:stash");
+ from("ine:xistent").to("log:stash");
+ `,
+ }
+ meta := Extract(code)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.False(t, meta.PassiveEndpoints)
+}
+
+func TestHttpOnlyJavaSource(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "Request.java",
+ Language: v1alpha1.LanguageJavaSource,
+ Content: `
+ from("direct:bots/cippa").to("log:stash");
+ from("undertow:uri").to("log:stash");
+ from("seda:path").to("log:stash");
+ `,
+ }
+ meta := Extract(code)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.True(t, meta.PassiveEndpoints)
+}
+
+func TestHttpOnlyJavaSourceRest(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "Request.java",
+ Language: v1alpha1.LanguageJavaSource,
+ Content: `
+ from("direct:bots/cippa").to("log:stash");
+ rest().get("").to("log:stash");
+ `,
+ }
+ meta := Extract(code)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.True(t, meta.PassiveEndpoints)
+}
+
+func TestHttpOnlyJavaSourceRest2(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "Request.java",
+ Language: v1alpha1.LanguageJavaSource,
+ Content: `
+ from("vm:bots/cippa").to("log:stash");
+ rest( ).get("").to("log:stash");
+ `,
+ }
+ meta := Extract(code)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.True(t, meta.PassiveEndpoints)
+}
+
+
+func TestNoHttpGroovySource(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "Request.groovy",
+ Language: v1alpha1.LanguageGroovy,
+ Content: `
+ from('direct:bots/cippa').to("log:stash");
+ from('teelgram:uri').to("log:stash");
+ from('seda:path').to("log:stash");
+ `,
+ }
+ meta := Extract(code)
+ assert.False(t, meta.RequiresHTTPService)
+ assert.False(t, meta.PassiveEndpoints)
+}
+
+func TestHttpOnlyGroovySource(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "Request.groovy",
+ Language: v1alpha1.LanguageGroovy,
+ Content: `
+ from('direct:bots/cippa').to("log:stash");
+ from('undertow:uri').to("log:stash");
+ from('seda:path').to("log:stash");
+ `,
+ }
+ meta := Extract(code)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.True(t, meta.PassiveEndpoints)
+}
+
+func TestHttpXMLSource(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "routes.xml",
+ Language: v1alpha1.LanguageXML,
+ Content: `
+ <from uri="telegram:ciao" />
+ <rest path="/">
+ </rest>
+ `,
+ }
+ meta := Extract(code)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.False(t, meta.PassiveEndpoints)
+}
+
+func TestHttpOnlyXMLSource(t *testing.T) {
+ code := v1alpha1.SourceSpec{
+ Name:     "routes.xml",
+ Language: v1alpha1.LanguageXML,
+ Content: `
+ <from uri="direct:ciao" />
+ <rest path="/">
+ </rest>
+ `,
+ }
+ meta := Extract(code)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.True(t, meta.PassiveEndpoints)
+}
+
+
+
+func TestMultilangHTTPOnlySource(t *testing.T) {
+ codes := []v1alpha1.SourceSpec{
+ {
+ Name:     "routes.xml",
+ Language: v1alpha1.LanguageXML,
+ Content: `
+ <from uri="direct:ciao" />
+ <rest path="/">
+ </rest>
+ `,
+ },
+ {
+ Name:     "routes2.groovy",
+ Language: v1alpha1.LanguageGroovy,
+ Content: `
+ from('seda:in').to('seda:out')
+ `,
+ },
+ }
+ meta := ExtractAll(codes)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.True(t, meta.PassiveEndpoints)
+}
+
+func TestMultilangHTTPSource(t *testing.T) {
+ codes := []v1alpha1.SourceSpec{
+ {
+ Name:     "routes.xml",
+ Language: v1alpha1.LanguageXML,
+ Content: `
+ <from uri="direct:ciao" />
+ <rest path="/">
+ </rest>
+ `,
+ },
+ {
+ Name:     "routes2.groovy",
+ Language: v1alpha1.LanguageGroovy,
+ Content: `
+ from('seda:in').to('seda:out')
+ from('timer:tick').to('log:info')
+ `,
+ },
+ }
+ meta := ExtractAll(codes)
+ assert.True(t, meta.RequiresHTTPService)
+ assert.False(t, meta.PassiveEndpoints)
+}
\ No newline at end of file
diff --git a/pkg/metadata/types.go b/pkg/metadata/types.go
index de10bb1..04ebe1c 100644
--- a/pkg/metadata/types.go
+++ b/pkg/metadata/types.go
@@ -29,4 +29,8 @@ type IntegrationMetadata struct {
  Dependencies []string
  // The language in which the integration is written
  Language v1alpha1.Language
+ // RequiresHTTPService indicates if the integration needs to be invoked through HTTP
+ RequiresHTTPService bool
+ // PassiveEndpoints indicates that the integration contains only passive endpoints that are activated from external calls, including HTTP (useful to determine if the integration can scale to 0)
+ PassiveEndpoints bool
 }
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index d0180be..1766929 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -78,31 +78,31 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
  return []Trait{
  c.tDebug,
  c.tDependencies,
- c.tService,
- c.tRoute,
  c.tBuilder,
  c.tSpringBoot,
  c.tDeployment,
+ c.tService,
+ c.tRoute,
  c.tOwner,
  }
  case v1alpha1.TraitProfileKubernetes:
  return []Trait{
  c.tDebug,
  c.tDependencies,
- c.tService,
- c.tIngress,
  c.tBuilder,
  c.tSpringBoot,
  c.tDeployment,
+ c.tService,
+ c.tIngress,
  c.tOwner,
  }
  case v1alpha1.TraitProfileKnative:
  return []Trait{
  c.tDebug,
  c.tDependencies,
- c.tKnative,
  c.tBuilder,
  c.tSpringBoot,
+ c.tKnative,
  c.tDeployment,
  c.tOwner,
  }
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 0e211f1..992b889 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -24,6 +24,8 @@ import (
 
  "github.com/operator-framework/operator-sdk/pkg/sdk"
  "github.com/pkg/errors"
+ "k8s.io/api/apps/v1"
+ "strings"
 
  "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 
@@ -35,15 +37,23 @@ import (
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
+const (
+ knativeKindDeployment = "deployment"
+ knativeKindService    = "service"
+)
+
 type knativeTrait struct {
- BaseTrait `property:",squash"`
- Sources   string `property:"sources"`
- Sinks     string `property:"sinks"`
+ BaseTrait          `property:",squash"`
+ Kind               string `property:"kind"`
+ Sources            string `property:"sources"`
+ Sinks              string `property:"sinks"`
+ deploymentDelegate *deploymentTrait
 }
 
 func newKnativeTrait() *knativeTrait {
  return &knativeTrait{
- BaseTrait: newBaseTrait("knative"),
+ BaseTrait:          newBaseTrait("knative"),
+ deploymentDelegate: newDeploymentTrait(),
  }
 }
 
@@ -60,18 +70,58 @@ func (t *knativeTrait) autoconfigure(e *Environment) error {
  channels := t.getSinkChannels(e)
  t.Sinks = strings.Join(channels, ",")
  }
+ if t.Kind == "" {
+ meta := metadata.ExtractAll(e.Integration.Spec.Sources)
+ if meta.RequiresHTTPService && meta.PassiveEndpoints {
+ t.Kind = knativeKindService
+ } else {
+ t.Kind = knativeKindDeployment
+ }
+ }
  return nil
 }
 
 func (t *knativeTrait) apply(e *Environment) error {
+ if err := t.prepareEnvVars(e); err != nil {
+ return err
+ }
  for _, sub := range t.getSubscriptionsFor(e) {
  e.Resources.Add(sub)
  }
- svc, err := t.getServiceFor(e)
+ switch t.Kind {
+ case knativeKindService:
+ svc, err := t.getServiceFor(e)
+ if err != nil {
+ return err
+ }
+ e.Resources.Add(svc)
+ return nil
+ case knativeKindDeployment:
+ return t.addDeployment(e)
+ }
+ return nil
+}
+
+func (t *knativeTrait) prepareEnvVars(e *Environment) error {
+ // common env var for Knative integration
+ conf, err := t.getConfigurationSerialized(e)
  if err != nil {
  return err
  }
- e.Resources.Add(svc)
+ e.EnvVars["CAMEL_KNATIVE_CONFIGURATION"] = conf
+ return nil
+}
+
+func (t *knativeTrait) addDeployment(e *Environment) error {
+ if err := t.deploymentDelegate.apply(e); err != nil {
+ return err
+ }
+ e.Resources.VisitDeployment(func(d *v1.Deployment) {
+ if d.Spec.Template.Annotations == nil {
+ d.Spec.Template.Annotations = make(map[string]string)
+ }
+ d.Spec.Template.Annotations["sidecar.istio.io/inject"] = "true"
+ })
  return nil
 }
 
@@ -112,12 +162,10 @@ func (t *knativeTrait) getServiceFor(e *Environment) (*serving.Service, error) {
  // optimizations
  environment["AB_JOLOKIA_OFF"] = True
 
- // Knative integration
- conf, err := t.getConfigurationSerialized(e)
- if err != nil {
- return nil, err
+ // add env vars from traits
+ for k, v := range e.EnvVars {
+ environment[k] = v
  }
- environment["CAMEL_KNATIVE_CONFIGURATION"] = conf
 
  labels := map[string]string{
  "camel.apache.org/integration": e.Integration.Name,
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index a7c927a..df0d66f 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -19,24 +19,13 @@ package trait
 
 import (
  "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
- "github.com/apache/camel-k/version"
+ "github.com/apache/camel-k/pkg/metadata"
+ "k8s.io/api/apps/v1"
  corev1 "k8s.io/api/core/v1"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/apimachinery/pkg/util/intstr"
 )
 
-var webComponents = map[string]bool{
- "camel:servlet":     true,
- "camel:undertow":    true,
- "camel:jetty":       true,
- "camel:jetty9":      true,
- "camel:netty-http":  true,
- "camel:netty4-http": true,
- "mvn:org.apache.camel.k:camel-knative:" + version.Version: true,
- // TODO find a better way to discover need for exposure
- // maybe using the resolved classpath of the context instead of the requested dependencies
-}
-
 type serviceTrait struct {
  BaseTrait `property:",squash"`
 
@@ -56,8 +45,18 @@ func (s *serviceTrait) appliesTo(e *Environment) bool {
 
 func (s *serviceTrait) autoconfigure(e *Environment) error {
  if s.Enabled == nil {
- required := s.requiresService(e)
- s.Enabled = &required
+ hasDeployment := false
+ e.Resources.VisitDeployment(func(s *v1.Deployment) {
+ hasDeployment = true
+ })
+ if hasDeployment {
+ meta := metadata.ExtractAll(e.Integration.Spec.Sources)
+ required := meta.RequiresHTTPService
+ s.Enabled = &required
+ } else {
+ enabled := false
+ s.Enabled = &enabled
+ }
  }
  return nil
 }
@@ -98,28 +97,3 @@ func (s *serviceTrait) getServiceFor(e *Environment) *corev1.Service {
 
  return &svc
 }
-
-func (*serviceTrait) requiresService(environment *Environment) bool {
- cweb := false
- iweb := false
-
- if environment.Context != nil {
- for _, dep := range environment.Context.Spec.Dependencies {
- if decision, present := webComponents[dep]; present {
- cweb = decision
- break
- }
- }
- }
-
- if environment.Integration != nil {
- for _, dep := range environment.Integration.Spec.Dependencies {
- if decision, present := webComponents[dep]; present {
- iweb = decision
- break
- }
- }
- }
-
- return cweb || iweb
-}
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 3e78fad..a770e76 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -51,7 +51,7 @@ func TestOpenShiftTraits(t *testing.T) {
 }
 
 func TestOpenShiftTraitsWithWeb(t *testing.T) {
- env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core", "camel:undertow")
+ env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "from('undertow:http').to('log:info')")
  res := processTestEnv(t, env)
  assert.Contains(t, env.ExecutedTraits, ID("deployment"))
  assert.Contains(t, env.ExecutedTraits, ID("service"))
@@ -72,7 +72,7 @@ func TestOpenShiftTraitsWithWeb(t *testing.T) {
 }
 
 func TestOpenShiftTraitsWithWebAndConfig(t *testing.T) {
- env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core", "camel:undertow")
+ env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "from('undertow:http').to('log:info')")
  env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
  env.Integration.Spec.Traits["service"] = v1alpha1.IntegrationTraitSpec{
  Configuration: map[string]string{
@@ -88,7 +88,7 @@ func TestOpenShiftTraitsWithWebAndConfig(t *testing.T) {
 }
 
 func TestOpenShiftTraitsWithWebAndDisabledTrait(t *testing.T) {
- env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "camel:core", "camel:undertow")
+ env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "from('undertow:http').to('log:info')")
  env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
  env.Integration.Spec.Traits["service"] = v1alpha1.IntegrationTraitSpec{
  Configuration: map[string]string{
@@ -105,7 +105,7 @@ func TestOpenShiftTraitsWithWebAndDisabledTrait(t *testing.T) {
 }
 
 func TestKubernetesTraits(t *testing.T) {
- env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "camel:core")
+ env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "from('timer:tick').to('log:info')")
  res := processTestEnv(t, env)
  assert.Contains(t, env.ExecutedTraits, ID("deployment"))
  assert.NotContains(t, env.ExecutedTraits, ID("service"))
@@ -120,7 +120,7 @@ func TestKubernetesTraits(t *testing.T) {
 }
 
 func TestKubernetesTraitsWithWeb(t *testing.T) {
- env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "camel:core", "camel:servlet")
+ env := createTestEnv(v1alpha1.IntegrationPlatformClusterKubernetes, "from('servlet:http').to('log:info')")
  res := processTestEnv(t, env)
  assert.Contains(t, env.ExecutedTraits, ID("deployment"))
  assert.Contains(t, env.ExecutedTraits, ID("service"))
@@ -138,7 +138,7 @@ func TestKubernetesTraitsWithWeb(t *testing.T) {
 }
 
 func TestTraitDecode(t *testing.T) {
- env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift)
+ env := createTestEnv(v1alpha1.IntegrationPlatformClusterOpenShift, "")
  env.Integration.Spec.Traits = make(map[string]v1alpha1.IntegrationTraitSpec)
  svcTrait := v1alpha1.IntegrationTraitSpec{
  Configuration: map[string]string{
@@ -164,7 +164,7 @@ func processTestEnv(t *testing.T, env *Environment) *kubernetes.Collection {
  return env.Resources
 }
 
-func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, dependencies ...string) *Environment {
+func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, script string) *Environment {
  return &Environment{
  Integration: &v1alpha1.Integration{
  ObjectMeta: metav1.ObjectMeta{
@@ -172,7 +172,13 @@ func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, dependencies ...
  Namespace: "ns",
  },
  Spec: v1alpha1.IntegrationSpec{
- Dependencies: dependencies,
+ Sources: []v1alpha1.SourceSpec{
+ {
+ Language: v1alpha1.LanguageGroovy,
+ Name: "file.groovy",
+ Content: script,
+ },
+ },
  },
  Status: v1alpha1.IntegrationStatus{
  Phase: v1alpha1.IntegrationPhaseDeploying,
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index fbc9098..4a770e5 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -18,6 +18,7 @@ limitations under the License.
 package kubernetes
 
 import (
+ serving "github.com/knative/serving/pkg/apis/serving/v1alpha1"
  routev1 "github.com/openshift/api/route/v1"
  appsv1 "k8s.io/api/apps/v1"
  corev1 "k8s.io/api/core/v1"
@@ -146,6 +147,38 @@ func (c *Collection) GetRoute(filter func(*routev1.Route) bool) *routev1.Route {
  return retValue
 }
 
+// VisitKnativeService executes the visitor function on all Knative serving Service resources
+func (c *Collection) VisitKnativeService(visitor func(*serving.Service)) {
+ c.Visit(func(res runtime.Object) {
+ if conv, ok := res.(*serving.Service); ok {
+ visitor(conv)
+ }
+ })
+}
+
+// VisitContainer executes the visitor function on all Containers inside deployments or other resources
+func (c *Collection) VisitContainer(visitor func(container *corev1.Container)) {
+ c.VisitDeployment(func(d *appsv1.Deployment) {
+ for _, c := range d.Spec.Template.Spec.Containers {
+ visitor(&c)
+ }
+ })
+ c.VisitKnativeService(func(s *serving.Service) {
+ if s.Spec.RunLatest != nil {
+ c := s.Spec.RunLatest.Configuration.RevisionTemplate.Spec.Container
+ visitor(&c)
+ }
+ if s.Spec.Pinned != nil {
+ c := s.Spec.Pinned.Configuration.RevisionTemplate.Spec.Container
+ visitor(&c)
+ }
+ if s.Spec.Release != nil {
+ c := s.Spec.Release.Configuration.RevisionTemplate.Spec.Container
+ visitor(&c)
+ }
+ })
+}
+
 // VisitMetaObject executes the visitor function on all meta.Object resources
 func (c *Collection) VisitMetaObject(visitor func(metav1.Object)) {
  c.Visit(func(res runtime.Object) {

Reply | Threaded
Open this post in threaded view
|

[camel-k] 03/06: Fix #209: add a Istio trait

lburgazzoli-2
In reply to this post by lburgazzoli-2
This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 11da9119ca8e9ee60e1fccbaef72788e298e0fdd
Author: nferraro <[hidden email]>
AuthorDate: Tue Dec 4 16:49:53 2018 +0100

    Fix #209: add a Istio trait
---
 docs/traits.adoc                       |  17 ++++-
 examples/knative/README.adoc           | 110 +++++++++++++++++++++++++++++++++
 examples/knative/feed.groovy           |   4 ++
 examples/knative/messages-channel.yaml |   9 +++
 examples/knative/printer.groovy        |   4 ++
 examples/knative/splitter.groovy       |   5 ++
 examples/knative/words-channel.yaml    |   9 +++
 pkg/trait/catalog.go                   |   4 ++
 pkg/trait/istio.go                     |  64 +++++++++++++++++++
 pkg/trait/knative.go                   |   3 +
 pkg/util/kubernetes/collection.go      |  25 +++++---
 pkg/util/kubernetes/replace.go         |  10 +++
 12 files changed, 255 insertions(+), 9 deletions(-)

diff --git a/docs/traits.adoc b/docs/traits.adoc
index 130958e..5b91a80 100644
--- a/docs/traits.adoc
+++ b/docs/traits.adoc
@@ -84,8 +84,23 @@ The following is a list of common traits that can be configured by the end users
 
 !===
 
+| istio
+| Knative (Kubernetes, Openshift)
+| Allows to configure outbound traffic for Istio.
+  +
+  +
+  It's enabled by default when the Knative profile is active.
+
+[cols="m,"]
+!===
+
+! istio.allow
+! Configures a (comma-separated) list of CIDR subnets that should not be intercepted by the Istio proxy (`10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` by default).
+
+!===
+
 | service
-| Kubernetes, OpenShift
+| All (Knative in deployment mode)
 | Exposes the integration with a Service resource so that it can be accessed by other applications (or integrations) in the same namespace.
   +
   +
diff --git a/examples/knative/README.adoc b/examples/knative/README.adoc
new file mode 100644
index 0000000..d7f8e9b
--- /dev/null
+++ b/examples/knative/README.adoc
@@ -0,0 +1,110 @@
+Knative Example (Apache Camel K)
+================================
+
+This example shows how Camel K can be used to connect Knative building blocks to create awesome applications.
+
+It's assumed that both Camel K and Knative are properly installed (including Knative Build, Serving and Eventing) into the cluster.
+Refer to the specific documentation to install and configure all components.
+
+We're going to create two channels:
+- messages
+- words
+
+The first channel will contain phrases, while the second one will contains the single words contained in the phrases.
+
+To create the channels (they use the in-memory channel provisioner):
+
+```
+kubectl create -f messages-channel.yaml
+kubectl create -f words-channel.yaml
+```
+
+We can now proceed to install all camel K integrations.
+
+== Install a "Printer"
+
+We'll install a Camel K integration that will print all words from the `words` channel.
+
+Writing a "function" that does this is as simple as writing:
+
+```
+from('knative:channel/words')
+  .convertBodyTo(String.class)
+  .to('log:info')
+```
+
+You can run this integration by running:
+
+```
+kamel run printer.groovy
+```
+
+Under the hood, the Camel K operator does this:
+- Understands that the integration is passive, meaning that it can be activated only using an external HTTP call (the knative consumer endpoint)
+- Materializes the integration as a Knative autoscaling service, integrated in the Istio service mesh
+- Adds a Knative Eventing `Subscription` that points to the autoscaling service
+
+The resulting integration will be scaled to 0 when not used (if you wait ~5 minutes, you'll see it).
+
+== Install a "Splitter"
+
+We're now going to deploy a splitter, using the Camel core Split EIP. The splitter will take all messages from the `messages` channel,
+split them and push the single words into the `words` channel.
+
+The integration code is super simple:
+
+```
+from('knative:channel/messages')
+  .split().tokenize(" ")
+  .log('sending ${body} to words channel')
+  .to('knative:channel/words')
+```
+
+Let's run it with:
+
+```
+kamel run splitter.groovy
+```
+
+This integration will be also materialized as a Knative autoscaling service, because the only entrypoint is passive (waits for a push notification).
+
+== Install a "Feed"
+
+We're going to feed this chain of functions using a timed feed like this:
+
+```
+from('timer:clock?period=3s')
+  .setBody().constant("Hello World from Camel K")
+  .to('knative:channel/messages')
+  .log('sent message to messages channel')
+```
+
+Every 3 seconds, the integration sends a message to the Knative `messages` channel.
+
+Let's run it with:
+
+```
+kamel run feed.groovy
+```
+
+This cannot be materialized into an autoscaling service, but the operator understands it automatically and maps it to a plain Kubernetes Deployment
+(Istio sidecar will be injected).
+
+== Playing around
+
+If you've installed all the services, you'll find that the printer pod will print single words as they arrive from the feed (every 3 seconds, passing by the splitter function).
+
+If you now stop the feed integration (`kamel delete feed`) you will notice that the other services (splitter and printer) will scale down to 0 in few minutes.
+
+And if you reinstall the feed again (`kamel run feed.groovy`), the other integration will scale up again as soon as they receive messages (splitter first, then printer).
+
+You can also play with different kind of feeds. E.g. the following simple feed can be used to bind messages from Telegram to the system:
+
+```
+from('telegram:bots/<put-here-your-botfather-authorization>')
+  .convertBodyTo(String.class)
+  .to('log:info')
+  .to('knative:channel/messages')
+```
+
+Now just send messages to your bot with the Telegram client app to see all single words appearing in the printer service.
diff --git a/examples/knative/feed.groovy b/examples/knative/feed.groovy
new file mode 100644
index 0000000..1e86907
--- /dev/null
+++ b/examples/knative/feed.groovy
@@ -0,0 +1,4 @@
+from('timer:clock?period=3s')
+ .setBody().constant("Hello World from Camel K")
+ .to('knative:channel/messages')
+ .log('sent message to messages channel')
\ No newline at end of file
diff --git a/examples/knative/messages-channel.yaml b/examples/knative/messages-channel.yaml
new file mode 100644
index 0000000..2dcd271
--- /dev/null
+++ b/examples/knative/messages-channel.yaml
@@ -0,0 +1,9 @@
+apiVersion: eventing.knative.dev/v1alpha1
+kind: Channel
+metadata:
+  name: messages
+spec:
+  provisioner:
+    apiVersion: eventing.knative.dev/v1alpha1
+    kind: ClusterChannelProvisioner
+    name: in-memory-channel
\ No newline at end of file
diff --git a/examples/knative/printer.groovy b/examples/knative/printer.groovy
new file mode 100644
index 0000000..58a0068
--- /dev/null
+++ b/examples/knative/printer.groovy
@@ -0,0 +1,4 @@
+
+from('knative:channel/words')
+  .convertBodyTo(String.class)
+  .to('log:info')
diff --git a/examples/knative/splitter.groovy b/examples/knative/splitter.groovy
new file mode 100644
index 0000000..9e848e3
--- /dev/null
+++ b/examples/knative/splitter.groovy
@@ -0,0 +1,5 @@
+
+from('knative:channel/messages')
+  .split().tokenize(" ")
+  .log('sending ${body} to words channel')
+  .to('knative:channel/words')
\ No newline at end of file
diff --git a/examples/knative/words-channel.yaml b/examples/knative/words-channel.yaml
new file mode 100644
index 0000000..ad8640f
--- /dev/null
+++ b/examples/knative/words-channel.yaml
@@ -0,0 +1,9 @@
+apiVersion: eventing.knative.dev/v1alpha1
+kind: Channel
+metadata:
+  name: words
+spec:
+  provisioner:
+    apiVersion: eventing.knative.dev/v1alpha1
+    kind: ClusterChannelProvisioner
+    name: in-memory-channel
\ No newline at end of file
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 1766929..796670a 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -39,6 +39,7 @@ type Catalog struct {
  tOwner        Trait
  tBuilder      Trait
  tSpringBoot   Trait
+ tIstio Trait
 }
 
 // NewCatalog creates a new trait Catalog
@@ -54,6 +55,7 @@ func NewCatalog() *Catalog {
  tOwner:        newOwnerTrait(),
  tBuilder:      newBuilderTrait(),
  tSpringBoot:   newSpringBootTrait(),
+ tIstio: newIstioTrait(),
  }
 }
 
@@ -69,6 +71,7 @@ func (c *Catalog) allTraits() []Trait {
  c.tOwner,
  c.tBuilder,
  c.tSpringBoot,
+ c.tIstio,
  }
 }
 
@@ -104,6 +107,7 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
  c.tSpringBoot,
  c.tKnative,
  c.tDeployment,
+ c.tIstio,
  c.tOwner,
  }
  }
diff --git a/pkg/trait/istio.go b/pkg/trait/istio.go
new file mode 100644
index 0000000..765cbe8
--- /dev/null
+++ b/pkg/trait/istio.go
@@ -0,0 +1,64 @@
+/*
+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 trait
+
+import (
+ "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+ serving "github.com/knative/serving/pkg/apis/serving/v1alpha1"
+ appsv1 "k8s.io/api/apps/v1"
+)
+
+type istioTrait struct {
+ BaseTrait `property:",squash"`
+ Allow     string `property:"allow"`
+}
+
+const (
+ istioIncludeAnnotation = "traffic.sidecar.istio.io/includeOutboundIPRanges"
+)
+
+func newIstioTrait() *istioTrait {
+ return &istioTrait{
+ BaseTrait: newBaseTrait("istio"),
+ Allow:     "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16",
+ }
+}
+
+func (t *istioTrait) appliesTo(e *Environment) bool {
+ return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
+
+func (t *istioTrait) apply(e *Environment) error {
+ if t.Allow != "" {
+ e.Resources.VisitDeployment(func(d *appsv1.Deployment) {
+ d.Spec.Template.Annotations = t.injectIstioAnnotation(d.Spec.Template.Annotations)
+ })
+ e.Resources.VisitKnativeConfigurationSpec(func(cs *serving.ConfigurationSpec) {
+ cs.RevisionTemplate.Annotations = t.injectIstioAnnotation(cs.RevisionTemplate.Annotations)
+ })
+ }
+ return nil
+}
+
+func (t *istioTrait) injectIstioAnnotation(annotations map[string]string) map[string]string {
+ if annotations == nil {
+ annotations = make(map[string]string)
+ }
+ annotations[istioIncludeAnnotation] = t.Allow
+ return annotations
+}
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 992b889..786d475 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -186,6 +186,9 @@ func (t *knativeTrait) getServiceFor(e *Environment) (*serving.Service, error) {
  RunLatest: &serving.RunLatestType{
  Configuration: serving.ConfigurationSpec{
  RevisionTemplate: serving.RevisionTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: labels,
+ },
  Spec: serving.RevisionSpec{
  Container: corev1.Container{
  Image: e.Integration.Status.Image,
diff --git a/pkg/util/kubernetes/collection.go b/pkg/util/kubernetes/collection.go
index 4a770e5..d6b7b72 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -159,22 +159,31 @@ func (c *Collection) VisitKnativeService(visitor func(*serving.Service)) {
 // VisitContainer executes the visitor function on all Containers inside deployments or other resources
 func (c *Collection) VisitContainer(visitor func(container *corev1.Container)) {
  c.VisitDeployment(func(d *appsv1.Deployment) {
- for _, c := range d.Spec.Template.Spec.Containers {
- visitor(&c)
+ for idx := range d.Spec.Template.Spec.Containers {
+ c := &d.Spec.Template.Spec.Containers[idx]
+ visitor(c)
  }
  })
+ c.VisitKnativeConfigurationSpec(func(cs *serving.ConfigurationSpec) {
+ c := &cs.RevisionTemplate.Spec.Container
+ visitor(c)
+ })
+}
+
+// VisitKnativeConfigurationSpec executes the visitor function on all knative ConfigurationSpec inside serving Services
+func (c *Collection) VisitKnativeConfigurationSpec(visitor func(container *serving.ConfigurationSpec)) {
  c.VisitKnativeService(func(s *serving.Service) {
  if s.Spec.RunLatest != nil {
- c := s.Spec.RunLatest.Configuration.RevisionTemplate.Spec.Container
- visitor(&c)
+ c := &s.Spec.RunLatest.Configuration
+ visitor(c)
  }
  if s.Spec.Pinned != nil {
- c := s.Spec.Pinned.Configuration.RevisionTemplate.Spec.Container
- visitor(&c)
+ c := &s.Spec.Pinned.Configuration
+ visitor(c)
  }
  if s.Spec.Release != nil {
- c := s.Spec.Release.Configuration.RevisionTemplate.Spec.Container
- visitor(&c)
+ c := &s.Spec.Release.Configuration
+ visitor(c)
  }
  })
 }
diff --git a/pkg/util/kubernetes/replace.go b/pkg/util/kubernetes/replace.go
index 45b908d..ec14b16 100644
--- a/pkg/util/kubernetes/replace.go
+++ b/pkg/util/kubernetes/replace.go
@@ -25,6 +25,7 @@ import (
  k8serrors "k8s.io/apimachinery/pkg/api/errors"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/apimachinery/pkg/runtime"
+ eventing "github.com/knative/eventing/pkg/apis/eventing/v1alpha1"
 )
 
 // ReplaceResources allows to completely replace a list of resources on Kubernetes, taking care of immutable fields and resource versions
@@ -50,6 +51,7 @@ func ReplaceResource(res runtime.Object) error {
  mapRequiredMeta(existing, res)
  mapRequiredServiceData(existing, res)
  mapRequiredRouteData(existing, res)
+ mapRequiredKnativeData(existing, res)
  err = sdk.Update(res)
  }
  if err != nil {
@@ -82,6 +84,14 @@ func mapRequiredRouteData(from runtime.Object, to runtime.Object) {
  }
 }
 
+func mapRequiredKnativeData(from runtime.Object, to runtime.Object) {
+ if fromC, ok := from.(*eventing.Subscription); ok {
+ if toC, ok := to.(*eventing.Subscription); ok {
+ toC.Spec.Generation = fromC.Spec.Generation
+ }
+ }
+}
+
 func findResourceDetails(res runtime.Object) string {
  if res == nil {
  return "nil resource"

Reply | Threaded
Open this post in threaded view
|

[camel-k] 04/06: Fix knative service generation and documentation

lburgazzoli-2
In reply to this post by lburgazzoli-2
This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 1a844c75ac0aeff48bc6f2ea72c0bc09a21e3578
Author: nferraro <[hidden email]>
AuthorDate: Thu Dec 6 01:10:53 2018 +0100

    Fix knative service generation and documentation
---
 examples/knative/README.adoc | 26 +++++++++++++++++
 pkg/trait/knative.go         | 67 ++++++++++++++++++--------------------------
 2 files changed, 54 insertions(+), 39 deletions(-)

diff --git a/examples/knative/README.adoc b/examples/knative/README.adoc
index d7f8e9b..e392d6b 100644
--- a/examples/knative/README.adoc
+++ b/examples/knative/README.adoc
@@ -3,6 +3,8 @@ Knative Example (Apache Camel K)
 
 This example shows how Camel K can be used to connect Knative building blocks to create awesome applications.
 
+A video version of this https://youtu.be/btf_e2GniXM[demo is available on YouTube].
+
 It's assumed that both Camel K and Knative are properly installed (including Knative Build, Serving and Eventing) into the cluster.
 Refer to the specific documentation to install and configure all components.
 
@@ -98,6 +100,8 @@ If you now stop the feed integration (`kamel delete feed`) you will notice that
 
 And if you reinstall the feed again (`kamel run feed.groovy`), the other integration will scale up again as soon as they receive messages (splitter first, then printer).
 
+== Playing harder
+
 You can also play with different kind of feeds. E.g. the following simple feed can be used to bind messages from Telegram to the system:
 
 ```
@@ -108,3 +112,25 @@ from('telegram:bots/<put-here-your-botfather-authorization>')
 ```
 
 Now just send messages to your bot with the Telegram client app to see all single words appearing in the printer service.
+
+You can also replace the printer with a Slack-based printer like:
+
+```
+from('knative:channel/words')
+  .log('Received: ${body}')
+  .to('slack:#camel-k-tests')
+
+
+context {
+  components {
+    slack {
+      webhookUrl '<put-here-your-slack-incoming-webhook-url>'
+    }
+  }
+}
+```
+
+Now the single words will be printed in the log but also forwarded to the
+slack channel named `#camel-k-tests`.
+
+You have infinite possibilities with Camel!
\ No newline at end of file
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 786d475..1687581 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -24,7 +24,7 @@ import (
 
  "github.com/operator-framework/operator-sdk/pkg/sdk"
  "github.com/pkg/errors"
- "k8s.io/api/apps/v1"
+ "strconv"
  "strings"
 
  "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -38,22 +38,21 @@ import (
 )
 
 const (
- knativeKindDeployment = "deployment"
- knativeKindService    = "service"
+ knativeMinScaleAnnotation = "autoscaling.knative.dev/minScale"
+ knativeMaxScaleAnnotation = "autoscaling.knative.dev/maxScale"
 )
 
 type knativeTrait struct {
- BaseTrait          `property:",squash"`
- Kind               string `property:"kind"`
- Sources            string `property:"sources"`
- Sinks              string `property:"sinks"`
- deploymentDelegate *deploymentTrait
+ BaseTrait `property:",squash"`
+ Sources   string `property:"sources"`
+ Sinks     string `property:"sinks"`
+ MinScale  *int   `property:"minScale"`
+ MaxScale  *int   `property:"maxScale"`
 }
 
 func newKnativeTrait() *knativeTrait {
  return &knativeTrait{
- BaseTrait:          newBaseTrait("knative"),
- deploymentDelegate: newDeploymentTrait(),
+ BaseTrait: newBaseTrait("knative"),
  }
 }
 
@@ -70,12 +69,12 @@ func (t *knativeTrait) autoconfigure(e *Environment) error {
  channels := t.getSinkChannels(e)
  t.Sinks = strings.Join(channels, ",")
  }
- if t.Kind == "" {
+ // Check the right value for minScale, as not all services are allowed to scale down to 0
+ if t.MinScale == nil {
  meta := metadata.ExtractAll(e.Integration.Spec.Sources)
- if meta.RequiresHTTPService && meta.PassiveEndpoints {
- t.Kind = knativeKindService
- } else {
- t.Kind = knativeKindDeployment
+ if !meta.RequiresHTTPService || !meta.PassiveEndpoints {
+ single := 1
+ t.MinScale = &single
  }
  }
  return nil
@@ -88,17 +87,11 @@ func (t *knativeTrait) apply(e *Environment) error {
  for _, sub := range t.getSubscriptionsFor(e) {
  e.Resources.Add(sub)
  }
- switch t.Kind {
- case knativeKindService:
- svc, err := t.getServiceFor(e)
- if err != nil {
- return err
- }
- e.Resources.Add(svc)
- return nil
- case knativeKindDeployment:
- return t.addDeployment(e)
+ svc, err := t.getServiceFor(e)
+ if err != nil {
+ return err
  }
+ e.Resources.Add(svc)
  return nil
 }
 
@@ -112,19 +105,6 @@ func (t *knativeTrait) prepareEnvVars(e *Environment) error {
  return nil
 }
 
-func (t *knativeTrait) addDeployment(e *Environment) error {
- if err := t.deploymentDelegate.apply(e); err != nil {
- return err
- }
- e.Resources.VisitDeployment(func(d *v1.Deployment) {
- if d.Spec.Template.Annotations == nil {
- d.Spec.Template.Annotations = make(map[string]string)
- }
- d.Spec.Template.Annotations["sidecar.istio.io/inject"] = "true"
- })
- return nil
-}
-
 func (t *knativeTrait) getServiceFor(e *Environment) (*serving.Service, error) {
  // combine properties of integration with context, integration
  // properties have the priority
@@ -171,6 +151,14 @@ func (t *knativeTrait) getServiceFor(e *Environment) (*serving.Service, error) {
  "camel.apache.org/integration": e.Integration.Name,
  }
 
+ annotations := make(map[string]string)
+ if t.MinScale != nil {
+ annotations[knativeMinScaleAnnotation] = strconv.Itoa(*t.MinScale)
+ }
+ if t.MaxScale != nil {
+ annotations[knativeMaxScaleAnnotation] = strconv.Itoa(*t.MaxScale)
+ }
+
  svc := serving.Service{
  TypeMeta: metav1.TypeMeta{
  Kind:       "Service",
@@ -187,7 +175,8 @@ func (t *knativeTrait) getServiceFor(e *Environment) (*serving.Service, error) {
  Configuration: serving.ConfigurationSpec{
  RevisionTemplate: serving.RevisionTemplateSpec{
  ObjectMeta: metav1.ObjectMeta{
- Labels: labels,
+ Labels:      labels,
+ Annotations: annotations,
  },
  Spec: serving.RevisionSpec{
  Container: corev1.Container{

Reply | Threaded
Open this post in threaded view
|

[camel-k] 05/06: Fix after rebase

lburgazzoli-2
In reply to this post by lburgazzoli-2
This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 2da7dd0ad2b0a00d7de9721c93ca5440a322ad90
Author: nferraro <[hidden email]>
AuthorDate: Thu Dec 6 12:32:16 2018 +0100

    Fix after rebase
---
 pkg/trait/knative.go |  1 -
 pkg/trait/service.go | 16 +++-------------
 2 files changed, 3 insertions(+), 14 deletions(-)

diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index 1687581..716392a 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -20,7 +20,6 @@ package trait
 import (
  "encoding/json"
  "fmt"
- "strings"
 
  "github.com/operator-framework/operator-sdk/pkg/sdk"
  "github.com/pkg/errors"
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index df0d66f..0efaf45 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -20,7 +20,6 @@ package trait
 import (
  "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
  "github.com/apache/camel-k/pkg/metadata"
- "k8s.io/api/apps/v1"
  corev1 "k8s.io/api/core/v1"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/apimachinery/pkg/util/intstr"
@@ -45,18 +44,9 @@ func (s *serviceTrait) appliesTo(e *Environment) bool {
 
 func (s *serviceTrait) autoconfigure(e *Environment) error {
  if s.Enabled == nil {
- hasDeployment := false
- e.Resources.VisitDeployment(func(s *v1.Deployment) {
- hasDeployment = true
- })
- if hasDeployment {
- meta := metadata.ExtractAll(e.Integration.Spec.Sources)
- required := meta.RequiresHTTPService
- s.Enabled = &required
- } else {
- enabled := false
- s.Enabled = &enabled
- }
+ meta := metadata.ExtractAll(e.Integration.Spec.Sources)
+ required := meta.RequiresHTTPService
+ s.Enabled = &required
  }
  return nil
 }

Reply | Threaded
Open this post in threaded view
|

[camel-k] 06/06: Disable debug by default and add doc

lburgazzoli-2
In reply to this post by lburgazzoli-2
This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit e2cb3f02d177f9d68925fac75879a807f466dde2
Author: nferraro <[hidden email]>
AuthorDate: Thu Dec 6 12:42:09 2018 +0100

    Disable debug by default and add doc
---
 docs/traits.adoc   | 7 +++++++
 pkg/trait/debug.go | 8 ++++++++
 2 files changed, 15 insertions(+)

diff --git a/docs/traits.adoc b/docs/traits.adoc
index 5b91a80..5e638a2 100644
--- a/docs/traits.adoc
+++ b/docs/traits.adoc
@@ -144,6 +144,13 @@ The following is a list of common traits that can be configured by the end users
 
 !===
 
+| debug
+| All
+| Run the integration in debug mode (you can port-forward to port 5005 to connect)
+  +
+  +
+  It's disabled by default.
+
 |=======================
 
 
diff --git a/pkg/trait/debug.go b/pkg/trait/debug.go
index f74896f..e5e40d9 100644
--- a/pkg/trait/debug.go
+++ b/pkg/trait/debug.go
@@ -35,6 +35,14 @@ func (r *debugTrait) appliesTo(e *Environment) bool {
  return e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
 }
 
+func (r *debugTrait) autoconfigure(e *Environment) error {
+ if r.Enabled == nil {
+ enabled := false
+ r.Enabled = &enabled
+ }
+ return nil
+}
+
 func (r *debugTrait) apply(e *Environment) error {
  // this is all that's needed as long as the base image is `fabric8/s2i-java` look into builder/builder.go
  e.EnvVars["JAVA_DEBUG"] = True