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


The following commit(s) were added to refs/heads/master by this push:
     new 1e1565d  add an option to always generate a docker image #246
1e1565d is described below

commit 1e1565d776b0e93b5761fc6dfd9291a51ff047bc
Author: lburgazzoli <lburgazz...@gmail.com>
AuthorDate: Tue Dec 4 17:51:26 2018 +0100

    add an option to always generate a docker image #246
---
 Gopkg.lock                                         |   9 +
 pkg/apis/camel/v1alpha1/types.go                   |  10 +-
 pkg/builder/builder.go                             |   9 +
 pkg/builder/builder_steps.go                       | 106 +++++---
 pkg/builder/builder_types.go                       |  51 ++--
 pkg/builder/builder_utils.go                       |  17 +-
 pkg/builder/springboot/initializer.go              |   3 -
 pkg/stub/action/context/build.go                   |  12 +-
 .../integration/{build.go => build_context.go}     |  45 ++--
 pkg/stub/action/integration/build_image.go         | 144 +++++++++++
 pkg/stub/action/integration/initialize.go          |   7 +-
 pkg/stub/action/integration/monitor.go             |   6 +-
 pkg/stub/handler.go                                |   3 +-
 pkg/trait/builder.go                               |  38 ++-
 pkg/trait/catalog.go                               |   8 +-
 pkg/trait/deployment.go                            | 153 +++++++----
 pkg/trait/types.go                                 |   7 +-
 pkg/util/tar/appender.go                           |  14 +-
 test/build_manager_integration_test.go             |   7 -
 test/testing_env.go                                |  16 --
 vendor/github.com/scylladb/go-set/LICENSE          | 177 +++++++++++++
 vendor/github.com/scylladb/go-set/strset/strset.go | 279 +++++++++++++++++++++
 22 files changed, 958 insertions(+), 163 deletions(-)

diff --git a/Gopkg.lock b/Gopkg.lock
index 6e9a08d..a097acd 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -493,6 +493,14 @@
   version = "v1.2.1"
 
 [[projects]]
+  digest = "1:6f3ce746342be7b14a2d1ca33a4a11fd6cb0300e5d34c766f01e19e936fb10af"
+  name = "github.com/scylladb/go-set"
+  packages = ["strset"]
+  pruneopts = "NUT"
+  revision = "e560bb8f49bb7f34d4f59b7e771f6e1307c329da"
+  version = "v1.0.2"
+
+[[projects]]
   digest = "1:ecf78eacf406c42f07f66d6b79fda24d2b92dc711bfd0760d0c931678f9621fe"
   name = "github.com/sirupsen/logrus"
   packages = ["."]
@@ -935,6 +943,7 @@
     "github.com/pkg/errors",
     "github.com/radovskyb/watcher",
     "github.com/rs/xid",
+    "github.com/scylladb/go-set/strset",
     "github.com/sirupsen/logrus",
     "github.com/spf13/cobra",
     "github.com/stoewer/go-strcase",
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index b549e1d..f2b506f 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -130,8 +130,10 @@ const (
        // IntegrationKind --
        IntegrationKind string = "Integration"
 
-       // IntegrationPhaseBuilding --
-       IntegrationPhaseBuilding IntegrationPhase = "Building"
+       // IntegrationPhaseBuildingContext --
+       IntegrationPhaseBuildingContext IntegrationPhase = "Building Context"
+       // IntegrationPhaseBuildingImage --
+       IntegrationPhaseBuildingImage IntegrationPhase = "Building Image"
        // IntegrationPhaseDeploying --
        IntegrationPhaseDeploying IntegrationPhase = "Deploying"
        // IntegrationPhaseRunning --
@@ -290,3 +292,7 @@ type Artifact struct {
        Location string `json:"location,omitempty" yaml:"location,omitempty"`
        Target   string `json:"target,omitempty" yaml:"target,omitempty"`
 }
+
+func (in *Artifact) String() string {
+       return in.ID
+}
diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go
index db82150..f4dad8b 100644
--- a/pkg/builder/builder.go
+++ b/pkg/builder/builder.go
@@ -151,6 +151,10 @@ func (b *defaultBuilder) submit(request Request) {
                Image:            "fabric8/s2i-java:2.3", // TODO: externalize
        }
 
+       if request.Image != "" {
+               c.Image = request.Image
+       }
+
        // Sort steps by phase
        sort.SliceStable(request.Steps, func(i, j int) bool {
                return request.Steps[i].Phase() < request.Steps[j].Phase()
@@ -201,4 +205,9 @@ func (b *defaultBuilder) submit(request Request) {
        b.request.Store(request.Meta.Name, r)
 
        b.log.Infof("request to build context %s executed in %f seconds", 
request.Meta.Name, r.Task.Elapsed().Seconds())
+       b.log.Infof("dependencies       : %s", request.Dependencies)
+       b.log.Infof("artifacts          : %s", ArtifactIDs(c.Artifacts))
+       b.log.Infof("artifacts selected : %s", ArtifactIDs(c.SelectedArtifacts))
+       b.log.Infof("requested image    : %s", request.Image)
+       b.log.Infof("resolved image     : %s", c.Image)
 }
diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go
index cac7e75..4a24e02 100644
--- a/pkg/builder/builder_steps.go
+++ b/pkg/builder/builder_steps.go
@@ -25,6 +25,8 @@ import (
        "path"
        "strings"
 
+       "github.com/scylladb/go-set/strset"
+
        "github.com/rs/xid"
 
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -171,49 +173,58 @@ func ComputeDependencies(ctx *Context) error {
 }
 
 // ArtifactsSelector --
-type ArtifactsSelector func([]v1alpha1.Artifact) (string, []v1alpha1.Artifact, 
error)
+type ArtifactsSelector func(ctx *Context) error
 
 // StandardPackager --
 func StandardPackager(ctx *Context) error {
-       return packager(ctx, func(libraries []v1alpha1.Artifact) (string, 
[]v1alpha1.Artifact, error) {
-               return ctx.Image, libraries, nil
+       return packager(ctx, func(ctx *Context) error {
+               ctx.SelectedArtifacts = ctx.Artifacts
+
+               return nil
        })
 }
 
 // IncrementalPackager --
 func IncrementalPackager(ctx *Context) error {
+       if ctx.HasRequiredImage() {
+               //
+               // If the build requires a specific image, don't try to 
determine the
+               // base image using artifact so just use the standard packages
+               //
+               return StandardPackager(ctx)
+       }
+
        images, err := ListPublishedImages(ctx.Namespace)
        if err != nil {
                return err
        }
 
-       return packager(ctx, func(libraries []v1alpha1.Artifact) (string, 
[]v1alpha1.Artifact, error) {
-               bestImage, commonLibs := FindBestImage(images, libraries)
-               if bestImage != nil {
-                       selectedClasspath := make([]v1alpha1.Artifact, 0)
-                       for _, entry := range libraries {
+       return packager(ctx, func(ctx *Context) error {
+               ctx.SelectedArtifacts = ctx.Artifacts
+
+               bestImage, commonLibs := FindBestImage(images, 
ctx.Request.Dependencies, ctx.Artifacts)
+               if bestImage.Image != "" {
+                       selectedArtifacts := make([]v1alpha1.Artifact, 0)
+                       for _, entry := range ctx.Artifacts {
                                if _, isCommon := commonLibs[entry.ID]; 
!isCommon {
-                                       selectedClasspath = 
append(selectedClasspath, entry)
+                                       selectedArtifacts = 
append(selectedArtifacts, entry)
                                }
                        }
 
-                       return bestImage.Image, selectedClasspath, nil
+                       ctx.Image = bestImage.Image
+                       ctx.SelectedArtifacts = selectedArtifacts
                }
 
-               // return default selection
-               return ctx.Image, libraries, nil
+               return nil
        })
 }
 
 // ClassPathPackager --
 func packager(ctx *Context, selector ArtifactsSelector) error {
-       imageName, selectedArtifacts, err := selector(ctx.Artifacts)
+       err := selector(ctx)
        if err != nil {
                return err
        }
-       if imageName == "" {
-               imageName = ctx.Image
-       }
 
        tarFileName := path.Join(ctx.Path, "package", "occi.tar")
        tarFileDir := path.Dir(tarFileName)
@@ -229,7 +240,7 @@ func packager(ctx *Context, selector ArtifactsSelector) 
error {
        }
        defer tarAppender.Close()
 
-       for _, entry := range selectedArtifacts {
+       for _, entry := range ctx.SelectedArtifacts {
                _, tarFileName := path.Split(entry.Target)
                tarFilePath := path.Dir(entry.Target)
 
@@ -239,19 +250,23 @@ func packager(ctx *Context, selector ArtifactsSelector) 
error {
                }
        }
 
-       if ctx.ComputeClasspath {
+       for _, entry := range ctx.Request.Resources {
+               if err := tarAppender.AddData(entry.Content, entry.Target); err 
!= nil {
+                       return err
+               }
+       }
+
+       if ctx.ComputeClasspath && len(ctx.Artifacts) > 0 {
                cp := ""
                for _, entry := range ctx.Artifacts {
-                       cp += path.Join(entry.Target) + "\n"
+                       cp += entry.Target + "\n"
                }
 
-               err = tarAppender.AppendData([]byte(cp), "classpath")
-               if err != nil {
+               if err := tarAppender.AddData([]byte(cp), "classpath"); err != 
nil {
                        return err
                }
        }
 
-       ctx.Image = imageName
        ctx.Archive = tarFileName
 
        return nil
@@ -275,38 +290,67 @@ func ListPublishedImages(namespace string) 
([]PublishedImage, error) {
                }
 
                images = append(images, PublishedImage{
-                       Image:     ctx.Status.Image,
-                       Artifacts: ctx.Status.Artifacts,
+                       Image:        ctx.Status.Image,
+                       Artifacts:    ctx.Status.Artifacts,
+                       Dependencies: ctx.Spec.Dependencies,
                })
        }
        return images, nil
 }
 
 // FindBestImage --
-func FindBestImage(images []PublishedImage, entries []v1alpha1.Artifact) 
(*PublishedImage, map[string]bool) {
+func FindBestImage(images []PublishedImage, dependencies []string, artifacts 
[]v1alpha1.Artifact) (PublishedImage, map[string]bool) {
+       var bestImage PublishedImage
+
        if len(images) == 0 {
-               return nil, nil
+               return bestImage, nil
        }
-       requiredLibs := make(map[string]bool, len(entries))
-       for _, entry := range entries {
+
+       requiredLibs := make(map[string]bool, len(artifacts))
+       for _, entry := range artifacts {
                requiredLibs[entry.ID] = true
        }
 
-       var bestImage PublishedImage
+       requiredRuntimes := strset.New()
+       for _, entry := range dependencies {
+               if strings.HasPrefix(entry, "runtime:") {
+                       requiredRuntimes.Add(entry)
+               }
+       }
+
        bestImageCommonLibs := make(map[string]bool)
        bestImageSurplusLibs := 0
+
        for _, image := range images {
+               runtimes := strset.New()
+               for _, entry := range image.Dependencies {
+                       if strings.HasPrefix(entry, "runtime:") {
+                               runtimes.Add(entry)
+                       }
+               }
+
+               //
+               // check if the image has the same runtime requirements to 
avoid the heuristic
+               // selector to include unwanted runtime bits such as 
spring-boot (which may have
+               // an additional artifact only thus it may match)
+               //
+               if !requiredRuntimes.IsSubset(runtimes) {
+                       continue
+               }
+
                common := make(map[string]bool)
                for _, artifact := range image.Artifacts {
                        if _, ok := requiredLibs[artifact.ID]; ok {
                                common[artifact.ID] = true
                        }
                }
+
                numCommonLibs := len(common)
                surplus := len(image.Artifacts) - numCommonLibs
 
                if numCommonLibs != len(image.Artifacts) && surplus >= 
numCommonLibs/3 {
-                       // Heuristic approach: if there are too many unrelated 
libraries, just use the base image
+                       // Heuristic approach: if there are too many unrelated 
libraries, just use
+                       // the base image
                        continue
                }
 
@@ -317,7 +361,7 @@ func FindBestImage(images []PublishedImage, entries 
[]v1alpha1.Artifact) (*Publi
                }
        }
 
-       return &bestImage, bestImageCommonLibs
+       return bestImage, bestImageCommonLibs
 }
 
 // Notify --
diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index 1f970f0..47c59e0 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -94,15 +94,22 @@ func NewStep(ID string, phase int32, task StepTask) Step {
        return &s
 }
 
+// Resource --
+type Resource struct {
+       Target  string
+       Content []byte
+}
+
 // Request --
 type Request struct {
        Meta         v1.ObjectMeta
        Platform     v1alpha1.IntegrationPlatformSpec
-       Code         v1alpha1.SourceSpec
        Dependencies []string
        Repositories []string
        Steps        []Step
        BuildDir     string
+       Image        string
+       Resources    []Resource
 }
 
 // Task --
@@ -128,23 +135,39 @@ type Result struct {
 
 // Context --
 type Context struct {
-       C                context.Context
-       Request          Request
-       Image            string
-       Error            error
-       Namespace        string
-       Project          maven.Project
-       Path             string
-       Artifacts        []v1alpha1.Artifact
-       Archive          string
-       ComputeClasspath bool
-       MainClass        string
+       C                 context.Context
+       Request           Request
+       Image             string
+       Error             error
+       Namespace         string
+       Project           maven.Project
+       Path              string
+       Artifacts         []v1alpha1.Artifact
+       SelectedArtifacts []v1alpha1.Artifact
+       Archive           string
+       ComputeClasspath  bool
+       MainClass         string
+}
+
+// HasRequiredImage --
+func (c *Context) HasRequiredImage() bool {
+       return c.Request.Image != ""
+}
+
+// GetImage --
+func (c *Context) GetImage() string {
+       if c.Request.Image != "" {
+               return c.Request.Image
+       }
+
+       return c.Image
 }
 
 // PublishedImage --
 type PublishedImage struct {
-       Image     string
-       Artifacts []v1alpha1.Artifact
+       Image        string
+       Artifacts    []v1alpha1.Artifact
+       Dependencies []string
 }
 
 // Status --
diff --git a/pkg/builder/builder_utils.go b/pkg/builder/builder_utils.go
index 1a9fb62..8ba81c5 100644
--- a/pkg/builder/builder_utils.go
+++ b/pkg/builder/builder_utils.go
@@ -17,7 +17,11 @@ limitations under the License.
 
 package builder
 
-import "os"
+import (
+       "os"
+
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
 
 // MavenExtraOptions --
 func MavenExtraOptions() string {
@@ -26,3 +30,14 @@ func MavenExtraOptions() string {
        }
        return "-Dcamel.noop=true"
 }
+
+// ArtifactIDs --
+func ArtifactIDs(artifacts []v1alpha1.Artifact) []string {
+       result := make([]string, 0, len(artifacts))
+
+       for _, a := range artifacts {
+               result = append(result, a.ID)
+       }
+
+       return result
+}
diff --git a/pkg/builder/springboot/initializer.go 
b/pkg/builder/springboot/initializer.go
index de40ea6..2b8b5af 100644
--- a/pkg/builder/springboot/initializer.go
+++ b/pkg/builder/springboot/initializer.go
@@ -23,9 +23,6 @@ import (
 
 // Initialize --
 func Initialize(ctx *builder.Context) error {
-       // set the base image
-       //ctx.Image = "kamel-k/s2i-boot:" + version.Version
-
        // no need to compute classpath as we do use spring boot own
        // loader: PropertiesLauncher
        ctx.ComputeClasspath = false
diff --git a/pkg/stub/action/context/build.go b/pkg/stub/action/context/build.go
index 9625545..b3e9226 100644
--- a/pkg/stub/action/context/build.go
+++ b/pkg/stub/action/context/build.go
@@ -79,10 +79,10 @@ func (action *buildAction) Handle(context 
*v1alpha1.IntegrationContext) error {
                target := context.DeepCopy()
                target.Status.Phase = v1alpha1.IntegrationContextPhaseError
 
-               logrus.Info("Context ", target.Name, " transitioning to state 
", v1alpha1.IntegrationContextPhaseError)
+               logrus.Info("Context ", target.Name, " transitioning to state 
", target.Status.Phase)
 
                // remove the build from cache
-               b.Purge(r)
+               defer b.Purge(r)
 
                return sdk.Update(target)
        case builder.StatusCompleted:
@@ -100,7 +100,10 @@ func (action *buildAction) Handle(context 
*v1alpha1.IntegrationContext) error {
                        })
                }
 
-               logrus.Info("Context ", target.Name, " transitioning to state 
", v1alpha1.IntegrationContextPhaseReady)
+               logrus.Info("Context ", target.Name, " transitioning to state 
", target.Status.Phase)
+
+               // remove the build from cache
+               defer b.Purge(r)
 
                if err := sdk.Update(target); err != nil {
                        return err
@@ -108,9 +111,6 @@ func (action *buildAction) Handle(context 
*v1alpha1.IntegrationContext) error {
                if err := action.informIntegrations(target); err != nil {
                        return err
                }
-
-               // remove the build from cache
-               b.Purge(r)
        }
 
        return nil
diff --git a/pkg/stub/action/integration/build.go 
b/pkg/stub/action/integration/build_context.go
similarity index 84%
rename from pkg/stub/action/integration/build.go
rename to pkg/stub/action/integration/build_context.go
index 85eb881..679945e 100644
--- a/pkg/stub/action/integration/build.go
+++ b/pkg/stub/action/integration/build_context.go
@@ -20,6 +20,8 @@ package integration
 import (
        "fmt"
 
+       "github.com/apache/camel-k/pkg/trait"
+
        "github.com/sirupsen/logrus"
 
        "github.com/apache/camel-k/pkg/util"
@@ -31,26 +33,26 @@ import (
        "github.com/operator-framework/operator-sdk/pkg/sdk"
 )
 
-// NewBuildAction create an action that handles integration build
-func NewBuildAction(namespace string) Action {
-       return &buildAction{
+// NewBuildContextAction create an action that handles integration context 
build
+func NewBuildContextAction(namespace string) Action {
+       return &buildContextAction{
                namespace: namespace,
        }
 }
 
-type buildAction struct {
+type buildContextAction struct {
        namespace string
 }
 
-func (action *buildAction) Name() string {
-       return "build"
+func (action *buildContextAction) Name() string {
+       return "build-context"
 }
 
-func (action *buildAction) CanHandle(integration *v1alpha1.Integration) bool {
-       return integration.Status.Phase == v1alpha1.IntegrationPhaseBuilding
+func (action *buildContextAction) CanHandle(integration *v1alpha1.Integration) 
bool {
+       return integration.Status.Phase == 
v1alpha1.IntegrationPhaseBuildingContext
 }
 
-func (action *buildAction) Handle(integration *v1alpha1.Integration) error {
+func (action *buildContextAction) Handle(integration *v1alpha1.Integration) 
error {
        ctx, err := LookupContextForIntegration(integration)
        if err != nil {
                //TODO: we may need to add a wait strategy, i.e give up after 
some time
@@ -74,30 +76,40 @@ func (action *buildAction) Handle(integration 
*v1alpha1.Integration) error {
                        }
                }
 
-               if ctx.Status.Phase == v1alpha1.IntegrationContextPhaseReady {
+               if ctx.Status.Phase == v1alpha1.IntegrationContextPhaseError {
                        target := integration.DeepCopy()
                        target.Status.Image = ctx.Status.Image
                        target.Spec.Context = ctx.Name
-                       logrus.Info("Integration ", target.Name, " 
transitioning to state ", v1alpha1.IntegrationPhaseDeploying)
-                       target.Status.Phase = v1alpha1.IntegrationPhaseDeploying
-                       dgst, err := digest.ComputeForIntegration(target)
+                       target.Status.Phase = v1alpha1.IntegrationPhaseError
+
+                       target.Status.Digest, err = 
digest.ComputeForIntegration(target)
                        if err != nil {
                                return err
                        }
-                       target.Status.Digest = dgst
+
+                       logrus.Info("Integration ", target.Name, " 
transitioning to state ", target.Status.Phase)
+
                        return sdk.Update(target)
                }
 
-               if ctx.Status.Phase == v1alpha1.IntegrationContextPhaseError {
+               if ctx.Status.Phase == v1alpha1.IntegrationContextPhaseReady {
                        target := integration.DeepCopy()
                        target.Status.Image = ctx.Status.Image
                        target.Spec.Context = ctx.Name
-                       target.Status.Phase = v1alpha1.IntegrationPhaseError
+
                        dgst, err := digest.ComputeForIntegration(target)
                        if err != nil {
                                return err
                        }
+
                        target.Status.Digest = dgst
+
+                       if _, err := trait.Apply(target, ctx); err != nil {
+                               return err
+                       }
+
+                       logrus.Info("Integration ", target.Name, " 
transitioning to state ", target.Status.Phase)
+
                        return sdk.Update(target)
                }
 
@@ -138,5 +150,6 @@ func (action *buildAction) Handle(integration 
*v1alpha1.Integration) error {
        // 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/build_image.go 
b/pkg/stub/action/integration/build_image.go
new file mode 100644
index 0000000..a30e4bf
--- /dev/null
+++ b/pkg/stub/action/integration/build_image.go
@@ -0,0 +1,144 @@
+/*
+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 integration
+
+import (
+       "context"
+       "fmt"
+       "path"
+
+       "github.com/pkg/errors"
+
+       "github.com/apache/camel-k/pkg/util/digest"
+
+       "github.com/apache/camel-k/pkg/trait"
+
+       "github.com/apache/camel-k/pkg/builder"
+       "github.com/operator-framework/operator-sdk/pkg/sdk"
+       "github.com/sirupsen/logrus"
+
+       "github.com/apache/camel-k/pkg/platform"
+
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
+
+// NewBuildImageAction create an action that handles integration image build
+func NewBuildImageAction(ctx context.Context, namespace string) Action {
+       return &buildImageAction{
+               Context:   ctx,
+               namespace: namespace,
+       }
+}
+
+type buildImageAction struct {
+       context.Context
+       namespace string
+}
+
+func (action *buildImageAction) Name() string {
+       return "build-image"
+}
+
+func (action *buildImageAction) CanHandle(integration *v1alpha1.Integration) 
bool {
+       return integration.Status.Phase == 
v1alpha1.IntegrationPhaseBuildingImage
+}
+
+func (action *buildImageAction) Handle(integration *v1alpha1.Integration) 
error {
+
+       // in this phase the integration need to be associated to a context 
whose image
+       // will be used as base image for the integration images
+       if integration.Spec.Context == "" {
+               return fmt.Errorf("context is not set for integration: %s", 
integration.Name)
+       }
+
+       // look-up the integration context associated to this integration, this 
is needed
+       // to determine the base image
+       ctx := v1alpha1.NewIntegrationContext(integration.Namespace, 
integration.Spec.Context)
+       if err := sdk.Get(&ctx); err != nil {
+               return errors.Wrapf(err, "unable to find integration context 
%s, %s", ctx.Name, err)
+       }
+
+       b, err := platform.GetPlatformBuilder(action.Context, action.namespace)
+       if err != nil {
+               return err
+       }
+       env, err := trait.Apply(integration, &ctx)
+       if err != nil {
+               return err
+       }
+
+       // This build do not require to determine dependencies nor a project, 
the builder
+       // step do remove them
+       r := builder.Request{
+               Meta:     integration.ObjectMeta,
+               Steps:    env.Steps,
+               Platform: env.Platform.Spec,
+               Image:    ctx.Status.Image,
+       }
+
+       // Sources are added as part of the standard deployment bits
+       r.Resources = make([]builder.Resource, 0, len(integration.Spec.Sources))
+
+       for _, source := range integration.Spec.Sources {
+               r.Resources = append(r.Resources, builder.Resource{
+                       Content: []byte(source.Content),
+                       Target:  path.Join("sources", source.Name),
+               })
+       }
+
+       res := b.Submit(r)
+
+       switch res.Status {
+       case builder.StatusSubmitted:
+               logrus.Info("Build submitted")
+       case builder.StatusStarted:
+               logrus.Info("Build started")
+       case builder.StatusError:
+               target := integration.DeepCopy()
+               target.Status.Phase = v1alpha1.IntegrationPhaseError
+
+               logrus.Info("Integration ", target.Name, " transitioning to 
state ", target.Status.Phase)
+
+               // remove the build from cache
+               defer b.Purge(r)
+
+               return sdk.Update(target)
+       case builder.StatusCompleted:
+               target := integration.DeepCopy()
+               target.Status.Phase = v1alpha1.IntegrationPhaseDeploying
+               target.Status.Image = res.Image
+
+               dgst, err := digest.ComputeForIntegration(integration)
+               if err != nil {
+                       return err
+               }
+
+               target.Status.Digest = dgst
+
+               logrus.Info("Integration ", target.Name, " transitioning to 
state ", target.Status.Phase)
+
+               // remove the build from cache
+               defer b.Purge(r)
+
+               if err := sdk.Update(target); err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
diff --git a/pkg/stub/action/integration/initialize.go 
b/pkg/stub/action/integration/initialize.go
index 8a21301..eea1957 100644
--- a/pkg/stub/action/integration/initialize.go
+++ b/pkg/stub/action/integration/initialize.go
@@ -73,12 +73,15 @@ func (action *initializeAction) Handle(integration 
*v1alpha1.Integration) error
        }
 
        // update the status
-       logrus.Info("Integration ", target.Name, " transitioning to state ", 
v1alpha1.IntegrationPhaseBuilding)
-       target.Status.Phase = v1alpha1.IntegrationPhaseBuilding
        dgst, err := digest.ComputeForIntegration(integration)
        if err != nil {
                return err
        }
+
+       target.Status.Phase = v1alpha1.IntegrationPhaseBuildingContext
        target.Status.Digest = dgst
+
+       logrus.Info("Integration ", target.Name, " transitioning to state ", 
target.Status.Phase)
+
        return sdk.Update(target)
 }
diff --git a/pkg/stub/action/integration/monitor.go 
b/pkg/stub/action/integration/monitor.go
index 756695b..d0e8107 100644
--- a/pkg/stub/action/integration/monitor.go
+++ b/pkg/stub/action/integration/monitor.go
@@ -53,8 +53,10 @@ func (action *monitorAction) Handle(integration 
*v1alpha1.Integration) error {
 
                target := integration.DeepCopy()
                target.Status.Digest = hash
-               logrus.Info("Integration ", target.Name, " transitioning to 
state ", v1alpha1.IntegrationPhaseBuilding)
-               target.Status.Phase = v1alpha1.IntegrationPhaseBuilding
+               target.Status.Phase = v1alpha1.IntegrationPhaseBuildingContext
+
+               logrus.Info("Integration ", target.Name, " transitioning to 
state ", target.Status.Phase)
+
                return sdk.Update(target)
        }
 
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
index a2f98ca..f4d3eaa 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/handler.go
@@ -34,7 +34,8 @@ func NewHandler(ctx ctx.Context, namespace string) 
sdk.Handler {
        return &handler{
                integrationActionPool: []integration.Action{
                        integration.NewInitializeAction(),
-                       integration.NewBuildAction(namespace),
+                       integration.NewBuildContextAction(namespace),
+                       integration.NewBuildImageAction(ctx, namespace),
                        integration.NewDeployAction(),
                        integration.NewMonitorAction(),
                },
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index c8c4a50..84702cb 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -19,6 +19,7 @@ package trait
 
 import (
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+       "github.com/apache/camel-k/pkg/builder"
        "github.com/apache/camel-k/pkg/builder/kaniko"
        "github.com/apache/camel-k/pkg/builder/s2i"
        "github.com/apache/camel-k/pkg/platform"
@@ -36,14 +37,41 @@ func newBuilderTrait() *builderTrait {
 }
 
 func (*builderTrait) appliesTo(e *Environment) bool {
-       return e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseBuilding
+       if e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseBuilding {
+               return true
+       }
+
+       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseBuildingImage &&
+               e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseReady {
+               return true
+       }
+
+       return false
 }
 
 func (*builderTrait) apply(e *Environment) error {
-       if platform.SupportsS2iPublishStrategy(e.Platform) {
-               e.Steps = s2i.DefaultSteps
-       } else if platform.SupportsKanikoPublishStrategy(e.Platform) {
-               e.Steps = kaniko.DefaultSteps
+       if e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseBuilding {
+               if platform.SupportsS2iPublishStrategy(e.Platform) {
+                       e.Steps = s2i.DefaultSteps
+               } else if platform.SupportsKanikoPublishStrategy(e.Platform) {
+                       e.Steps = kaniko.DefaultSteps
+               }
+       }
+
+       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseBuildingImage &&
+               e.Context != nil && e.Context.Status.Phase == 
v1alpha1.IntegrationContextPhaseReady {
+
+               if platform.SupportsS2iPublishStrategy(e.Platform) {
+                       e.Steps = []builder.Step{
+                               builder.NewStep("packager", 
builder.ApplicationPackagePhase, builder.StandardPackager),
+                               builder.NewStep("publisher/s2i", 
builder.ApplicationPublishPhase, s2i.Publisher),
+                       }
+               } else if platform.SupportsKanikoPublishStrategy(e.Platform) {
+                       e.Steps = []builder.Step{
+                               builder.NewStep("packager", 
builder.ApplicationPackagePhase, builder.StandardPackager),
+                               builder.NewStep("publisher/kaniko", 
builder.ApplicationPublishPhase, kaniko.Publisher),
+                       }
+               }
        }
 
        return nil
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 62c2f9f..d0180be 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -73,9 +73,7 @@ func (c *Catalog) allTraits() []Trait {
 }
 
 func (c *Catalog) traitsFor(environment *Environment) []Trait {
-       profile := environment.DetermineProfile()
-
-       switch profile {
+       switch environment.DetermineProfile() {
        case v1alpha1.TraitProfileOpenShift:
                return []Trait{
                        c.tDebug,
@@ -105,6 +103,7 @@ func (c *Catalog) traitsFor(environment *Environment) 
[]Trait {
                        c.tKnative,
                        c.tBuilder,
                        c.tSpringBoot,
+                       c.tDeployment,
                        c.tOwner,
                }
        }
@@ -128,14 +127,17 @@ func (c *Catalog) apply(environment *Environment) error {
                                return err
                        }
                }
+
                if trait.IsEnabled() {
                        logrus.Infof("apply trait: %s", trait.ID())
                        if err := trait.apply(environment); err != nil {
                                return err
                        }
+
                        environment.ExecutedTraits = 
append(environment.ExecutedTraits, trait.ID())
                }
        }
+
        return nil
 }
 
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 7304aa2..b3a3be6 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -19,6 +19,7 @@ package trait
 
 import (
        "fmt"
+       "path"
        "strings"
 
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -29,7 +30,8 @@ import (
 )
 
 type deploymentTrait struct {
-       BaseTrait `property:",squash"`
+       BaseTrait      `property:",squash"`
+       ContainerImage bool `property:"container-image"`
 }
 
 func newDeploymentTrait() *deploymentTrait {
@@ -39,12 +41,40 @@ func newDeploymentTrait() *deploymentTrait {
 }
 
 func (d *deploymentTrait) appliesTo(e *Environment) bool {
-       return e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying
+       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying {
+               //
+               // Don't deploy on knative
+               //
+               return e.DetermineProfile() != v1alpha1.TraitProfileKnative
+       }
+
+       if d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+               return true
+       }
+
+       if !d.ContainerImage && 
e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+               return true
+       }
+
+       return false
 }
 
 func (d *deploymentTrait) apply(e *Environment) error {
-       e.Resources.AddAll(getConfigMapsFor(e))
-       e.Resources.Add(getDeploymentFor(e))
+       if d.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+               // trigger container image build
+               e.Integration.Status.Phase = 
v1alpha1.IntegrationPhaseBuildingImage
+       }
+
+       if !d.ContainerImage && 
e.InPhase(v1alpha1.IntegrationContextPhaseReady, 
v1alpha1.IntegrationPhaseBuildingContext) {
+               // trigger integration deploy
+               e.Integration.Status.Phase = v1alpha1.IntegrationPhaseDeploying
+       }
+
+       if e.Integration != nil && e.Integration.Status.Phase == 
v1alpha1.IntegrationPhaseDeploying {
+               e.Resources.AddAll(d.getConfigMapsFor(e))
+               e.Resources.Add(d.getDeploymentFor(e))
+       }
+
        return nil
 }
 
@@ -54,7 +84,7 @@ func (d *deploymentTrait) apply(e *Environment) error {
 //
 // **********************************
 
-func getConfigMapsFor(e *Environment) []runtime.Object {
+func (d *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
        maps := make([]runtime.Object, 0, len(e.Integration.Spec.Sources)+1)
 
        // combine properties of integration with context, integration
@@ -81,30 +111,35 @@ func getConfigMapsFor(e *Environment) []runtime.Object {
                },
        )
 
-       for i, s := range e.Integration.Spec.Sources {
-               maps = append(
-                       maps,
-                       &corev1.ConfigMap{
-                               TypeMeta: metav1.TypeMeta{
-                                       Kind:       "ConfigMap",
-                                       APIVersion: "v1",
-                               },
-                               ObjectMeta: metav1.ObjectMeta{
-                                       Name:      
fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
-                                       Namespace: e.Integration.Namespace,
-                                       Labels: map[string]string{
-                                               "camel.apache.org/integration": 
e.Integration.Name,
+       if !d.ContainerImage {
+
+               // do not create 'source' ConfigMap if a docker images for 
deployment
+               // is required
+               for i, s := range e.Integration.Spec.Sources {
+                       maps = append(
+                               maps,
+                               &corev1.ConfigMap{
+                                       TypeMeta: metav1.TypeMeta{
+                                               Kind:       "ConfigMap",
+                                               APIVersion: "v1",
                                        },
-                                       Annotations: map[string]string{
-                                               
"camel.apache.org/source.language": string(s.Language),
-                                               "camel.apache.org/source.name": 
    s.Name,
+                                       ObjectMeta: metav1.ObjectMeta{
+                                               Name:      
fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
+                                               Namespace: 
e.Integration.Namespace,
+                                               Labels: map[string]string{
+                                                       
"camel.apache.org/integration": e.Integration.Name,
+                                               },
+                                               Annotations: map[string]string{
+                                                       
"camel.apache.org/source.language": string(s.Language),
+                                                       
"camel.apache.org/source.name":     s.Name,
+                                               },
+                                       },
+                                       Data: map[string]string{
+                                               "integration": s.Content,
                                        },
                                },
-                               Data: map[string]string{
-                                       "integration": s.Content,
-                               },
-                       },
-               )
+                       )
+               }
        }
 
        return maps
@@ -116,17 +151,34 @@ func getConfigMapsFor(e *Environment) []runtime.Object {
 //
 // **********************************
 
-func getDeploymentFor(e *Environment) *appsv1.Deployment {
+func (d *deploymentTrait) getSources(e *Environment) []string {
        sources := make([]string, 0, len(e.Integration.Spec.Sources))
+
        for i, s := range e.Integration.Spec.Sources {
-               src := fmt.Sprintf("file:/etc/camel/integrations/%03d/%s", i, 
strings.TrimPrefix(s.Name, "/"))
+               root := fmt.Sprintf("/etc/camel/integrations/%03d", i)
+
+               if d.ContainerImage {
+
+                       // assume sources are copied over the standard 
deployments folder
+                       root = "/deployments/sources"
+               }
+
+               src := path.Join(root, s.Name)
+               src = "file:" + src
+
                if s.Language != "" {
-                       src = src + "?language=" + string(s.Language)
+                       src = fmt.Sprintf("%s?language=%s", src, 
string(s.Language))
                }
 
                sources = append(sources, src)
        }
 
+       return sources
+}
+
+func (d *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
+       sources := d.getSources(e)
+
        // combine Environment of integration with context, integration
        // Environment has the priority
        environment := CombineConfigurationAsMap("env", e.Context, 
e.Integration)
@@ -227,28 +279,35 @@ func getDeploymentFor(e *Environment) *appsv1.Deployment {
        // Volumes :: Sources
        //
 
-       for i, s := range e.Integration.Spec.Sources {
-               vols = append(vols, corev1.Volume{
-                       Name: fmt.Sprintf("integration-source-%03d", i),
-                       VolumeSource: corev1.VolumeSource{
-                               ConfigMap: &corev1.ConfigMapVolumeSource{
-                                       LocalObjectReference: 
corev1.LocalObjectReference{
-                                               Name: 
fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
-                                       },
-                                       Items: []corev1.KeyToPath{
-                                               {
-                                                       Key:  "integration",
-                                                       Path: 
strings.TrimPrefix(s.Name, "/"),
+       if !d.ContainerImage {
+
+               // We can configure the operator to generate a container images 
that include
+               // integration sources instead of mounting it at runtime and in 
such case we
+               // do not need to mount any 'source' ConfigMap to the pod
+
+               for i, s := range e.Integration.Spec.Sources {
+                       vols = append(vols, corev1.Volume{
+                               Name: fmt.Sprintf("integration-source-%03d", i),
+                               VolumeSource: corev1.VolumeSource{
+                                       ConfigMap: 
&corev1.ConfigMapVolumeSource{
+                                               LocalObjectReference: 
corev1.LocalObjectReference{
+                                                       Name: 
fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
+                                               },
+                                               Items: []corev1.KeyToPath{
+                                                       {
+                                                               Key:  
"integration",
+                                                               Path: 
strings.TrimPrefix(s.Name, "/"),
+                                                       },
                                                },
                                        },
                                },
-                       },
-               })
+                       })
 
-               mnts = append(mnts, corev1.VolumeMount{
-                       Name:      fmt.Sprintf("integration-source-%03d", i),
-                       MountPath: fmt.Sprintf("/etc/camel/integrations/%03d", 
i),
-               })
+                       mnts = append(mnts, corev1.VolumeMount{
+                               Name:      
fmt.Sprintf("integration-source-%03d", i),
+                               MountPath: 
fmt.Sprintf("/etc/camel/integrations/%03d", i),
+                       })
+               }
        }
 
        //
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
index eaaba02..53fb69c 100644
--- a/pkg/trait/types.go
+++ b/pkg/trait/types.go
@@ -110,7 +110,12 @@ func (e *Environment) IntegrationContextInPhase(phase 
v1alpha1.IntegrationContex
        return e.Context != nil && e.Context.Status.Phase == phase
 }
 
-// DeterimeProfile determines the TraitProfile of the environment.
+// InPhase --
+func (e *Environment) InPhase(c v1alpha1.IntegrationContextPhase, i 
v1alpha1.IntegrationPhase) bool {
+       return e.IntegrationContextInPhase(c) && e.IntegrationInPhase(i)
+}
+
+// DetermineProfile determines the TraitProfile of the environment.
 // First looking at the Integration.Spec for a Profile,
 // next looking at the Context.Spec
 // and lastly the Platform Profile
diff --git a/pkg/util/tar/appender.go b/pkg/util/tar/appender.go
index 78df669..47bb8a4 100644
--- a/pkg/util/tar/appender.go
+++ b/pkg/util/tar/appender.go
@@ -128,19 +128,21 @@ func (t *Appender) AddFileWithName(fileName string, 
filePath string, tarDir stri
        return fileName, nil
 }
 
-// AppendData appends the given content to a file inside the tar, creating it 
if it does not exist
-func (t *Appender) AppendData(data []byte, tarPath string) error {
-       if err := t.writer.WriteHeader(&atar.Header{
+// AddData appends the given content to a file inside the tar, creating it if 
it does not exist
+func (t *Appender) AddData(data []byte, tarPath string) error {
+       err := t.writer.WriteHeader(&atar.Header{
                Name: tarPath,
                Size: int64(len(data)),
                Mode: 0644,
-       }); err != nil {
+       })
+
+       if err != nil {
                return err
        }
 
-       _, err := t.writer.Write(data)
-       if err != nil {
+       if _, err := t.writer.Write(data); err != nil {
                return errors.Wrap(err, "cannot add data to the tar archive")
        }
+
        return nil
 }
diff --git a/test/build_manager_integration_test.go 
b/test/build_manager_integration_test.go
index 1e8a10c..4bbcd19 100644
--- a/test/build_manager_integration_test.go
+++ b/test/build_manager_integration_test.go
@@ -28,7 +28,6 @@ import (
 
        "k8s.io/apimachinery/pkg/apis/meta/v1"
 
-       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "github.com/apache/camel-k/pkg/builder"
        "github.com/apache/camel-k/pkg/builder/s2i"
        "github.com/stretchr/testify/assert"
@@ -44,9 +43,6 @@ func TestBuildManagerBuild(t *testing.T) {
                        Name:            "man-test",
                        ResourceVersion: "1",
                },
-               Code: v1alpha1.SourceSpec{
-                       Content: createTimerToLogIntegrationCode(),
-               },
                Dependencies: []string{
                        "mvn:org.apache.camel/camel-core",
                        "camel:telegram",
@@ -83,9 +79,6 @@ func TestBuildManagerFailedBuild(t *testing.T) {
                        Name:            "man-test",
                        ResourceVersion: "1",
                },
-               Code: v1alpha1.SourceSpec{
-                       Content: createTimerToLogIntegrationCode(),
-               },
                Dependencies: []string{
                        "mvn:org.apache.camel/camel-cippalippa",
                },
diff --git a/test/testing_env.go b/test/testing_env.go
index 66ff683..d8265c4 100644
--- a/test/testing_env.go
+++ b/test/testing_env.go
@@ -56,22 +56,6 @@ func getTargetNamespace() string {
        return ns
 }
 
-func createTimerToLogIntegrationCode() string {
-       return `
-import org.apache.camel.builder.RouteBuilder;
-
-public class Routes extends RouteBuilder {
-
-       @Override
-    public void configure() throws Exception {
-        from("timer:tick")
-                 .to("log:info");
-    }
-
-}
-`
-}
-
 func createDummyDeployment(name string, replicas *int32, labelKey string, 
labelValue string, command ...string) (*appsv1.Deployment, error) {
        deployment := getDummyDeployment(name, replicas, labelKey, labelValue, 
command...)
        gracePeriod := int64(0)
diff --git a/vendor/github.com/scylladb/go-set/LICENSE 
b/vendor/github.com/scylladb/go-set/LICENSE
new file mode 100644
index 0000000..4947287
--- /dev/null
+++ b/vendor/github.com/scylladb/go-set/LICENSE
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/vendor/github.com/scylladb/go-set/strset/strset.go 
b/vendor/github.com/scylladb/go-set/strset/strset.go
new file mode 100644
index 0000000..ed828e5
--- /dev/null
+++ b/vendor/github.com/scylladb/go-set/strset/strset.go
@@ -0,0 +1,279 @@
+// Copyright (C) 2017 ScyllaDB
+// Use of this source code is governed by a ALv2-style
+// license that can be found at https://github.com/scylladb/go-set/LICENSE.
+
+package strset
+
+import (
+       "fmt"
+       "math"
+       "strings"
+)
+
+var (
+       // helpful to not write everywhere struct{}{}
+       keyExists   = struct{}{}
+       nonExistent string
+)
+
+// Set is the main set structure that holds all the data
+// and methods used to working with the set.
+type Set struct {
+       m map[string]struct{}
+}
+
+// New creates and initializes a new Set.
+func New(ts ...string) *Set {
+       s := NewWithSize(len(ts))
+       s.Add(ts...)
+       return s
+}
+
+// NewWithSize creates a new Set and gives make map a size hint.
+func NewWithSize(size int) *Set {
+       return &Set{make(map[string]struct{}, size)}
+}
+
+// Add includes the specified items (one or more) to the Set. The underlying
+// Set s is modified. If passed nothing it silently returns.
+func (s *Set) Add(items ...string) {
+       for _, item := range items {
+               s.m[item] = keyExists
+       }
+}
+
+// Remove deletes the specified items from the Set. The underlying Set s is
+// modified. If passed nothing it silently returns.
+func (s *Set) Remove(items ...string) {
+       for _, item := range items {
+               delete(s.m, item)
+       }
+}
+
+// Pop deletes and returns an item from the Set. The underlying Set s is
+// modified. If Set is empty, the zero value is returned.
+func (s *Set) Pop() string {
+       for item := range s.m {
+               delete(s.m, item)
+               return item
+       }
+       return nonExistent
+}
+
+// Pop2 tries to delete and return an item from the Set. The underlying Set s
+// is modified. The second value is a bool that is true if the item existed in
+// the set, and false if not. If Set is empty, the zero value and false are
+// returned.
+func (s *Set) Pop2() (string, bool) {
+       for item := range s.m {
+               delete(s.m, item)
+               return item, true
+       }
+       return nonExistent, false
+}
+
+// Has looks for the existence of items passed. It returns false if nothing is
+// passed. For multiple items it returns true only if all of  the items exist.
+func (s *Set) Has(items ...string) bool {
+       has := false
+       for _, item := range items {
+               if _, has = s.m[item]; !has {
+                       break
+               }
+       }
+       return has
+}
+
+// HasAny looks for the existence of any of the items passed.
+// It returns false if nothing is passed.
+// For multiple items it returns true if any of the items exist.
+func (s *Set) HasAny(items ...string) bool {
+       has := false
+       for _, item := range items {
+               if _, has = s.m[item]; has {
+                       break
+               }
+       }
+       return has
+}
+
+// Size returns the number of items in a Set.
+func (s *Set) Size() int {
+       return len(s.m)
+}
+
+// Clear removes all items from the Set.
+func (s *Set) Clear() {
+       s.m = make(map[string]struct{})
+}
+
+// IsEmpty reports whether the Set is empty.
+func (s *Set) IsEmpty() bool {
+       return s.Size() == 0
+}
+
+// IsEqual test whether s and t are the same in size and have the same items.
+func (s *Set) IsEqual(t *Set) bool {
+       // return false if they are no the same size
+       if s.Size() != t.Size() {
+               return false
+       }
+
+       equal := true
+       t.Each(func(item string) bool {
+               _, equal = s.m[item]
+               return equal // if false, Each() will end
+       })
+
+       return equal
+}
+
+// IsSubset tests whether t is a subset of s.
+func (s *Set) IsSubset(t *Set) bool {
+       if s.Size() < t.Size() {
+               return false
+       }
+
+       subset := true
+
+       t.Each(func(item string) bool {
+               _, subset = s.m[item]
+               return subset
+       })
+
+       return subset
+}
+
+// IsSuperset tests whether t is a superset of s.
+func (s *Set) IsSuperset(t *Set) bool {
+       return t.IsSubset(s)
+}
+
+// Each traverses the items in the Set, calling the provided function for each
+// Set member. Traversal will continue until all items in the Set have been
+// visited, or if the closure returns false.
+func (s *Set) Each(f func(item string) bool) {
+       for item := range s.m {
+               if !f(item) {
+                       break
+               }
+       }
+}
+
+// Copy returns a new Set with a copy of s.
+func (s *Set) Copy() *Set {
+       u := NewWithSize(s.Size())
+       for item := range s.m {
+               u.m[item] = keyExists
+       }
+       return u
+}
+
+// String returns a string representation of s
+func (s *Set) String() string {
+       v := make([]string, 0, s.Size())
+       for item := range s.m {
+               v = append(v, fmt.Sprintf("%v", item))
+       }
+       return fmt.Sprintf("[%s]", strings.Join(v, ", "))
+}
+
+// List returns a slice of all items. There is also StringSlice() and
+// IntSlice() methods for returning slices of type string or int.
+func (s *Set) List() []string {
+       v := make([]string, 0, s.Size())
+       for item := range s.m {
+               v = append(v, item)
+       }
+       return v
+}
+
+// Merge is like Union, however it modifies the current Set it's applied on
+// with the given t Set.
+func (s *Set) Merge(t *Set) {
+       for item := range t.m {
+               s.m[item] = keyExists
+       }
+}
+
+// Separate removes the Set items containing in t from Set s. Please aware that
+// it's not the opposite of Merge.
+func (s *Set) Separate(t *Set) {
+       for item := range t.m {
+               delete(s.m, item)
+       }
+}
+
+// Union is the merger of multiple sets. It returns a new set with all the
+// elements present in all the sets that are passed.
+func Union(sets ...*Set) *Set {
+       maxPos := -1
+       maxSize := 0
+       for i, set := range sets {
+               if l := set.Size(); l > maxSize {
+                       maxSize = l
+                       maxPos = i
+               }
+       }
+       if maxSize == 0 {
+               return New()
+       }
+
+       u := sets[maxPos].Copy()
+       for i, set := range sets {
+               if i == maxPos {
+                       continue
+               }
+               for item := range set.m {
+                       u.m[item] = keyExists
+               }
+       }
+       return u
+}
+
+// Difference returns a new set which contains items which are in in the first
+// set but not in the others.
+func Difference(set1 *Set, sets ...*Set) *Set {
+       s := set1.Copy()
+       for _, set := range sets {
+               s.Separate(set)
+       }
+       return s
+}
+
+// Intersection returns a new set which contains items that only exist in all
+// given sets.
+func Intersection(sets ...*Set) *Set {
+       minPos := -1
+       minSize := math.MaxInt64
+       for i, set := range sets {
+               if l := set.Size(); l < minSize {
+                       minSize = l
+                       minPos = i
+               }
+       }
+       if minSize == math.MaxInt64 || minSize == 0 {
+               return New()
+       }
+
+       t := sets[minPos].Copy()
+       for i, set := range sets {
+               if i == minPos {
+                       continue
+               }
+               for item := range t.m {
+                       if _, has := set.m[item]; !has {
+                               delete(t.m, item)
+                       }
+               }
+       }
+       return t
+}
+
+// SymmetricDifference returns a new set which s is the difference of items
+// which are in one of either, but not in both.
+func SymmetricDifference(s *Set, t *Set) *Set {
+       u := Difference(s, t)
+       v := Difference(t, s)
+       return Union(u, v)
+}

Reply via email to