nicolaferraro closed pull request #76: Context handling improvement URL: https://github.com/apache/camel-k/pull/76
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/Gopkg.lock b/Gopkg.lock index c9859df..bc54a02 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -317,6 +317,14 @@ pruneopts = "NUT" revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92" +[[projects]] + digest = "1:0975c74a2cd70df6c2ae353c6283a25ce759dda7e1e706e5c07458baf3faca22" + name = "github.com/rs/xid" + packages = ["."] + pruneopts = "NUT" + revision = "15d26544def341f036c5f8dca987a4cbe575032c" + version = "v1.2.1" + [[projects]] digest = "1:b2339e83ce9b5c4f79405f949429a7f68a9a904fed903c672aac1e7ceb7f5f02" name = "github.com/sirupsen/logrus" @@ -711,6 +719,7 @@ "github.com/operator-framework/operator-sdk/pkg/util/k8sutil", "github.com/operator-framework/operator-sdk/version", "github.com/pkg/errors", + "github.com/rs/xid", "github.com/sirupsen/logrus", "github.com/spf13/cobra", "github.com/stoewer/go-strcase", 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 @@ -356,6 +356,33 @@ spec: 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"] = ` diff --git a/pkg/client/cmd/completion.go b/pkg/client/cmd/completion.go index 3429dad..f86bd0b 100644 --- a/pkg/client/cmd/completion.go +++ b/pkg/client/cmd/completion.go @@ -34,13 +34,14 @@ To configure your bash shell to load completions for each session add to your ba . <(kamel completion) ` -func NewCmdCompletion() *cobra.Command { +// NewCmdCompletion -- +func NewCmdCompletion(root *cobra.Command) *cobra.Command { return &cobra.Command{ Use: "completion", Short: "Generates bash completion scripts", Long: completionCmdLongDescription, Run: func(cmd *cobra.Command, args []string) { - cmd.GenBashCompletion(os.Stdout) + root.GenBashCompletion(os.Stdout) }, } } 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/client/cmd/root.go b/pkg/client/cmd/root.go index 50cfb40..3168cc2 100644 --- a/pkg/client/cmd/root.go +++ b/pkg/client/cmd/root.go @@ -20,10 +20,11 @@ package cmd import ( "os" + "context" + "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/pkg/errors" "github.com/spf13/cobra" - "context" ) type RootCmdOptions struct { @@ -62,7 +63,7 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) { return nil, err } - cmd.AddCommand(NewCmdCompletion()) + cmd.AddCommand(NewCmdCompletion(&cmd)) cmd.AddCommand(NewCmdVersion()) cmd.AddCommand(NewCmdRun(&options)) cmd.AddCommand(NewCmdGet(&options)) 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..b99ab4e 100644 --- a/pkg/stub/action/integration/build.go +++ b/pkg/stub/action/integration/build.go @@ -18,24 +18,23 @@ limitations under the License. package action import ( - "context" + "fmt" + + "github.com/rs/xid" "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 +52,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", xid.New()) + 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, } - 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) + + // Set the context to have the same dependencies as the integrations + platformCtx.Spec = v1alpha1.IntegrationContextSpec{ + Dependencies: integration.Spec.Dependencies, + } + + 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..8c69f69 100644 --- a/pkg/stub/action/integration/util.go +++ b/pkg/stub/action/integration/util.go @@ -24,9 +24,51 @@ func LookupContextForIntegration(integration *v1alpha1.Integration) (*v1alpha1.I return &ctx, nil } + ctxList := v1alpha1.NewIntegrationContextList() + if err := sdk.List(integration.Namespace, &ctxList); err != nil { + return nil, err + } + + for _, ctx := range ctxList.Items { + if ctx.Labels["camel.apache.org/context.type"] == "platform" { + ideps := len(integration.Spec.Dependencies) + cdeps := len(ctx.Spec.Dependencies) + + if ideps != cdeps { + continue + } + + if StringSliceContains(ctx.Spec.Dependencies, integration.Spec.Dependencies) { + return &ctx, nil + } + } + } + return nil, 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 -- func PropertiesString(m map[string]string) string { properties := "" 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 +} ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services