You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2018/09/15 14:25:05 UTC

[camel-k] 01/03: Context handling improvement

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 f2717d01c315f47df0962154c566fd1498b55e46
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Fri Sep 14 14:25:18 2018 +0200

    Context handling improvement
---
 deploy/platform-integration-context-core.yaml   |  9 +++
 deploy/platform-integration-context-groovy.yaml | 12 ++++
 deploy/resources.go                             | 27 ++++++++
 pkg/client/cmd/install.go                       | 30 +++++----
 pkg/install/operator.go                         | 20 +++++-
 pkg/stub/action/integration/build.go            | 84 +++++++++++++++----------
 pkg/stub/action/integration/deploy.go           |  4 ++
 pkg/stub/action/integration/util.go             | 77 ++++++++++++++++++++++-
 pkg/stub/handler.go                             |  9 +--
 runtime/examples/routes.groovy                  | 16 +++++
 test/testing_env.go                             |  6 +-
 11 files changed, 239 insertions(+), 55 deletions(-)

diff --git a/deploy/platform-integration-context-core.yaml b/deploy/platform-integration-context-core.yaml
new file mode 100644
index 0000000..836d195
--- /dev/null
+++ b/deploy/platform-integration-context-core.yaml
@@ -0,0 +1,9 @@
+apiVersion: camel.apache.org/v1alpha1
+kind: IntegrationContext
+metadata:
+  name: root.integrationcontexts.camel.apache.org
+  labels:
+    app: "camel-k"
+    camel.apache.org/context.created.by.kind: Operator
+    camel.apache.org/context.created.by.name: core
+    camel.apache.org/context.type: platform
\ No newline at end of file
diff --git a/deploy/platform-integration-context-groovy.yaml b/deploy/platform-integration-context-groovy.yaml
new file mode 100644
index 0000000..5c5cbf4
--- /dev/null
+++ b/deploy/platform-integration-context-groovy.yaml
@@ -0,0 +1,12 @@
+apiVersion: camel.apache.org/v1alpha1
+kind: IntegrationContext
+metadata:
+  name: groovy.integrationcontexts.camel.apache.org
+  labels:
+    app: "camel-k"
+    camel.apache.org/context.created.by.kind: Operator
+    camel.apache.org/context.created.by.name: core
+    camel.apache.org/context.type: platform
+spec:
+  dependencies:
+    - camel:groovy
\ No newline at end of file
diff --git a/deploy/resources.go b/deploy/resources.go
index 77a12cd..189d4ef 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -357,6 +357,33 @@ status:
   loadBalancer: {}
 
 `
+	Resources["platform-integration-context-core.yaml"] =
+		`
+apiVersion: camel.apache.org/v1alpha1
+kind: IntegrationContext
+metadata:
+  name: root.integrationcontexts.camel.apache.org
+  labels:
+    app: "camel-k"
+    camel.apache.org/context.created.by.kind: Operator
+    camel.apache.org/context.created.by.name: core
+    camel.apache.org/context.type: platform
+`
+	Resources["platform-integration-context-groovy.yaml"] =
+		`
+apiVersion: camel.apache.org/v1alpha1
+kind: IntegrationContext
+metadata:
+  name: groovy.integrationcontexts.camel.apache.org
+  labels:
+    app: "camel-k"
+    camel.apache.org/context.created.by.kind: Operator
+    camel.apache.org/context.created.by.name: core
+    camel.apache.org/context.type: platform
+spec:
+  dependencies:
+    - camel:groovy
+`
 	Resources["user-cluster-role.yaml"] =
 		`
 kind: ClusterRole
diff --git a/pkg/client/cmd/install.go b/pkg/client/cmd/install.go
index 0693b36..15a4de0 100644
--- a/pkg/client/cmd/install.go
+++ b/pkg/client/cmd/install.go
@@ -20,19 +20,16 @@ package cmd
 import (
 	"fmt"
 
+	"os"
+
 	"github.com/apache/camel-k/pkg/install"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/api/errors"
-	"os"
 )
 
-type InstallCmdOptions struct {
-	*RootCmdOptions
-	ClusterSetupOnly bool
-}
-
+// NewCmdInstall --
 func NewCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	options := InstallCmdOptions{
+	options := installCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 	cmd := cobra.Command{
@@ -42,13 +39,18 @@ func NewCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		RunE:  options.install,
 	}
 
-	cmd.Flags().BoolVar(&options.ClusterSetupOnly, "cluster-setup", false, "Execute cluster-wide operations only (may require admin rights)")
+	cmd.Flags().BoolVar(&options.clusterSetupOnly, "cluster-setup", false, "Execute cluster-wide operations only (may require admin rights)")
 	cmd.ParseFlags(os.Args)
 
 	return &cmd
 }
 
-func (o *InstallCmdOptions) install(cmd *cobra.Command, args []string) error {
+type installCmdOptions struct {
+	*RootCmdOptions
+	clusterSetupOnly bool
+}
+
+func (o *installCmdOptions) install(cmd *cobra.Command, args []string) error {
 	err := install.SetupClusterwideResources()
 	if err != nil && errors.IsForbidden(err) {
 		// TODO explain that this is a one time operation and add a flag to do cluster-level operations only when logged as admin
@@ -57,15 +59,21 @@ func (o *InstallCmdOptions) install(cmd *cobra.Command, args []string) error {
 		return nil // TODO better error handling: if here we return err the help page is shown
 	}
 
-	if o.ClusterSetupOnly {
+	if o.clusterSetupOnly {
 		fmt.Println("Camel K cluster setup completed successfully")
 	} else {
 		namespace := o.Namespace
 
-		err = install.InstallOperator(namespace)
+		err = install.Operator(namespace)
 		if err != nil {
 			return err
 		}
+
+		err = install.PlatformContexts(namespace)
+		if err != nil {
+			return err
+		}
+
 		fmt.Println("Camel K installed in namespace", namespace)
 	}
 
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index a7044f9..528424a 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -19,13 +19,15 @@ package install
 
 import (
 	"github.com/apache/camel-k/deploy"
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-func InstallOperator(namespace string) error {
+// Operator --
+func Operator(namespace string) error {
 	return installResources(namespace,
 		"operator-service-account.yaml",
 		"operator-role-openshift.yaml", // TODO distinguish between Openshift and Kubernetes
@@ -35,6 +37,14 @@ func InstallOperator(namespace string) error {
 	)
 }
 
+// PlatformContexts --
+func PlatformContexts(namespace string) error {
+	return installResources(namespace,
+		"platform-integration-context-core.yaml",
+		"platform-integration-context-groovy.yaml",
+	)
+}
+
 func installResources(namespace string, names ...string) error {
 	for _, name := range names {
 		if err := installResource(namespace, name); err != nil {
@@ -50,8 +60,8 @@ func installResource(namespace string, name string) error {
 		return err
 	}
 
-	if kObj, ok := obj.(metav1.Object); ok {
-		kObj.SetNamespace(namespace)
+	if metaObject, ok := obj.(metav1.Object); ok {
+		metaObject.SetNamespace(namespace)
 	}
 
 	err = sdk.Create(obj)
@@ -60,6 +70,10 @@ func installResource(namespace string, name string) error {
 		if obj.GetObjectKind().GroupVersionKind().Kind == "Service" {
 			return nil
 		}
+		// Don't recreate integration contexts
+		if obj.GetObjectKind().GroupVersionKind().Kind == v1alpha1.IntegrationContextKind {
+			return nil
+		}
 		return sdk.Update(obj)
 	}
 	return err
diff --git a/pkg/stub/action/integration/build.go b/pkg/stub/action/integration/build.go
index ba24358..ce953db 100644
--- a/pkg/stub/action/integration/build.go
+++ b/pkg/stub/action/integration/build.go
@@ -18,24 +18,21 @@ limitations under the License.
 package action
 
 import (
-	"context"
+	"fmt"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	"github.com/apache/camel-k/pkg/build"
-	"github.com/apache/camel-k/pkg/build/api"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/sirupsen/logrus"
 )
 
 // NewBuildAction create an action that handles integration build
-func NewBuildAction(ctx context.Context, namespace string) IntegrationAction {
+func NewBuildAction(namespace string) IntegrationAction {
 	return &buildAction{
-		buildManager: build.NewManager(ctx, namespace),
+		namespace: namespace,
 	}
 }
 
 type buildAction struct {
-	buildManager *build.Manager
+	namespace string
 }
 
 func (action *buildAction) Name() string {
@@ -53,44 +50,65 @@ func (action *buildAction) Handle(integration *v1alpha1.Integration) error {
 		return err
 	}
 
-
 	if ctx != nil {
+		if ctx.Labels["camel.apache.org/context.type"] == "platform" {
+			// This is a platform context and as it is auto generated it may get
+			// out of sync if the integration that has generated it, has been
+			// amended to add/remove dependencies
+
+			//TODO: this is a very simple check, we may need to provide a deps comparison strategy
+			if !StringSliceContains(ctx.Spec.Dependencies, integration.Spec.Dependencies) {
+				// We need to re-generate a context or search for a new one that
+				// satisfies integrations needs so let's remove the association
+				// with a context
+				target := integration.DeepCopy()
+				target.Spec.Context = ""
+				return sdk.Update(target)
+			}
+		}
+
 		if ctx.Status.Phase == v1alpha1.IntegrationContextPhaseReady {
 			target := integration.DeepCopy()
 			target.Status.Image = ctx.Status.Image
+			target.Spec.Context = ctx.Name
 			target.Status.Phase = v1alpha1.IntegrationPhaseDeploying
 			return sdk.Update(target)
 		}
 
+		if integration.Spec.Context == "" {
+			// We need to set the context
+			target := integration.DeepCopy()
+			target.Spec.Context = ctx.Name
+			return sdk.Update(target)
+		}
+
 		return nil
 	}
 
-	buildIdentifier := api.BuildIdentifier{
-		Name:      integration.Name,
-		Qualifier: integration.Status.Digest,
+	platformCtxName := fmt.Sprintf("ctx-%s-%s", integration.Name, integration.ResourceVersion)
+	platformCtx := v1alpha1.NewIntegrationContext(action.namespace, platformCtxName)
+
+	// Add some information for post-processing, this may need to be refactored
+	// to a proper data structure
+	platformCtx.Labels = map[string]string{
+		"camel.apache.org/context.type":               "platform",
+		"camel.apache.org/context.created.by.kind":    v1alpha1.IntegrationKind,
+		"camel.apache.org/context.created.by.name":    integration.Name,
+		"camel.apache.org/context.created.by.version": integration.ResourceVersion,
+	}
+
+	// Set the context to have the same dependencies as the integrations
+	platformCtx.Spec = v1alpha1.IntegrationContextSpec{
+		Dependencies: integration.Spec.Dependencies,
 	}
-	buildResult := action.buildManager.Get(buildIdentifier)
-	if buildResult.Status == api.BuildStatusNotRequested {
-		action.buildManager.Start(api.BuildSource{
-			Identifier: buildIdentifier,
-			Code: api.Code{
-				Name:     integration.Spec.Source.Name,
-				Content:  integration.Spec.Source.Content,
-				Language: integration.Spec.Source.Language,
-			},
-			Dependencies: integration.Spec.Dependencies,
-		})
-		logrus.Info("Build started")
-	} else if buildResult.Status == api.BuildStatusError {
-		target := integration.DeepCopy()
-		target.Status.Phase = v1alpha1.IntegrationPhaseError
-		return sdk.Update(target)
-	} else if buildResult.Status == api.BuildStatusCompleted {
-		target := integration.DeepCopy()
-		target.Status.Image = buildResult.Image
-		target.Status.Phase = v1alpha1.IntegrationPhaseDeploying
-		return sdk.Update(target)
+
+	if err := sdk.Create(&platformCtx); err != nil {
+		return err
 	}
 
-	return nil
+	// Set the context name so the next handle loop, will fall through the
+	// same path as integration with a user defined context
+	target := integration.DeepCopy()
+	target.Spec.Context = platformCtxName
+	return sdk.Update(target)
 }
diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index 8bc4b74..b8adb43 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/stub/action/integration/deploy.go
@@ -147,6 +147,10 @@ func getDeploymentFor(ctx *v1alpha1.IntegrationContext, integration *v1alpha1.In
 	environment["CAMEL_K_CONF"] = "/etc/camel/conf/application.properties"
 	environment["CAMEL_K_CONF_D"] = "/etc/camel/conf.d"
 
+	// add a dummy env var to trigger deployment if everything but the code
+	// has been changed
+	environment["CAMEL_K_DIGEST"] = integration.Status.Digest
+
 	labels := map[string]string{
 		"camel.apache.org/integration": integration.Name,
 	}
diff --git a/pkg/stub/action/integration/util.go b/pkg/stub/action/integration/util.go
index 8021809..7ddbbab 100644
--- a/pkg/stub/action/integration/util.go
+++ b/pkg/stub/action/integration/util.go
@@ -2,6 +2,7 @@ package action
 
 import (
 	"fmt"
+	"math"
 	"strings"
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -24,7 +25,81 @@ func LookupContextForIntegration(integration *v1alpha1.Integration) (*v1alpha1.I
 		return &ctx, nil
 	}
 
-	return nil, nil
+	ctxList := v1alpha1.NewIntegrationContextList()
+	if err := sdk.List(integration.Namespace, &ctxList); err != nil {
+		return nil, err
+	}
+
+	var c *v1alpha1.IntegrationContext
+
+	if len(integration.Spec.Dependencies) == 0 {
+		// The integration has no dependencies, try to find the one that
+		// has minimum dependencies requirement.
+
+		ndeps := math.MaxUint16
+
+		for _, ctx := range ctxList.Items {
+			cdeps := len(ctx.Spec.Dependencies)
+			if cdeps < ndeps {
+				ndeps = cdeps
+				c = &ctx
+			}
+			if ndeps == 0 {
+				break
+			}
+		}
+	} else {
+		ndeps := math.MaxUint16
+
+		for _, ctx := range ctxList.Items {
+			// The integration has some dependencies, try to find the one that matches
+			// the required dependencies with minimum "dependency overhead"
+
+			if ctx.Labels["camel.apache.org/context.type"] == "platform" {
+				if StringSliceContains(ctx.Spec.Dependencies, integration.Spec.Dependencies) {
+					ideps := len(integration.Spec.Dependencies)
+					cdeps := len(ctx.Spec.Dependencies)
+
+					if cdeps < ndeps || ideps == cdeps {
+						c = &ctx
+						ndeps = cdeps
+					}
+
+					if ideps == ndeps {
+						break
+					}
+				}
+			}
+		}
+	}
+
+	if c == nil {
+		// TODO: generate a new platform context
+	}
+
+	return c, nil
+}
+
+// StringSliceContains --
+func StringSliceContains(slice []string, items []string) bool {
+	for i := 0; i < len(items); i++ {
+		if !StringSliceExists(slice, items[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
+// StringSliceExists --
+func StringSliceExists(slice []string, item string) bool {
+	for i := 0; i < len(slice); i++ {
+		if slice[i] == item {
+			return true
+		}
+	}
+
+	return false
 }
 
 // PropertiesString --
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
index d90d776..cb78d7b 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/handler.go
@@ -28,11 +28,12 @@ import (
 	"github.com/sirupsen/logrus"
 )
 
+// NewHandler --
 func NewHandler(ctx context.Context, namespace string) sdk.Handler {
-	return &Handler{
+	return &handler{
 		integrationActionPool: []iaction.IntegrationAction{
 			iaction.NewInitializeAction(),
-			iaction.NewBuildAction(ctx, namespace),
+			iaction.NewBuildAction(namespace),
 			iaction.NewDeployAction(),
 			iaction.NewMonitorAction(),
 		},
@@ -44,12 +45,12 @@ func NewHandler(ctx context.Context, namespace string) sdk.Handler {
 	}
 }
 
-type Handler struct {
+type handler struct {
 	integrationActionPool        []iaction.IntegrationAction
 	integrationContextActionPool []caction.IntegrationContextAction
 }
 
-func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
+func (h *handler) Handle(ctx context.Context, event sdk.Event) error {
 	switch o := event.Object.(type) {
 	case *v1alpha1.Integration:
 		for _, a := range h.integrationActionPool {
diff --git a/runtime/examples/routes.groovy b/runtime/examples/routes.groovy
new file mode 100644
index 0000000..7ce04bb
--- /dev/null
+++ b/runtime/examples/routes.groovy
@@ -0,0 +1,16 @@
+//
+// To run this integrations use:
+//
+//     kamel run -d camel:groovy runtime/examples/routes.groovy
+//
+
+rnd = new Random()
+
+from('timer:groovy?period=1s')
+    .routeId('groovy')
+    .setBody()
+        .constant('Hello Camel K!')
+    .process {
+        it.in.headers['RandomValue'] = rnd.nextInt()
+    }
+    .to('log:info?showHeaders=true')
\ No newline at end of file
diff --git a/test/testing_env.go b/test/testing_env.go
index 13b0ed8..3b1e215 100644
--- a/test/testing_env.go
+++ b/test/testing_env.go
@@ -22,8 +22,8 @@ limitations under the License.
 package test
 
 import (
-	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/install"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
 func init() {
@@ -35,7 +35,7 @@ func init() {
 		panic(err)
 	}
 
-	err = install.InstallOperator(GetTargetNamespace())
+	err = install.Operator(GetTargetNamespace())
 	if err != nil {
 		panic(err)
 	}
@@ -63,4 +63,4 @@ public class Routes extends RouteBuilder {
 
 }
 `
-}
\ No newline at end of file
+}