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/06 11:06:28 UTC

[camel-k] branch master updated (55fca8e -> f42dedf)

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

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


    from 55fca8e  This is Apache Camel K
     new 62b4354  Scaffolding project structure
     new bfca1f2  Add license headers
     new 8661409  Initial draft of local build system
     new c3d39e6  First e2e working version
     new f42dedf  Detect changes with digest and redeploy

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


Summary of changes:
 .gitignore                                        | 120 +++++
 Gopkg.lock                                        | 577 ++++++++++++++++++++++
 Gopkg.toml                                        |  58 +++
 LICENSE.txt => LICENSE                            |   0
 Makefile                                          |   1 +
 NOTICE                                            |  11 +
 build/Makefile                                    |  30 ++
 build/get_version.sh                              |   4 +
 build/minishift_add_role.sh                       |   7 +
 cmd/camel-k/main.go                               |  57 +++
 config/config.yaml                                |   3 +
 deploy/cr.yaml                                    |  22 +
 deploy/crd.yaml                                   |  13 +
 deploy/operator.yaml                              |  30 ++
 deploy/rbac.yaml                                  |  46 ++
 pkg/apis/camel/v1alpha1/doc.go                    |  21 +
 pkg/apis/camel/v1alpha1/register.go               |  52 ++
 pkg/apis/camel/v1alpha1/types.go                  |  63 +++
 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go  | 102 ++++
 pkg/build/api/types.go                            |  50 ++
 pkg/build/build_manager.go                        |  80 +++
 pkg/build/build_manager_integration_test.go       | 101 ++++
 pkg/build/local/local_builder.go                  | 480 ++++++++++++++++++
 pkg/build/local/local_builder_integration_test.go |  92 ++++
 pkg/build/local/scheme.go                         |  98 ++++
 pkg/stub/action/action.go                         |  35 ++
 pkg/stub/action/build.go                          |  71 +++
 pkg/stub/action/deploy.go                         | 111 +++++
 pkg/stub/action/initialize.go                     |  54 ++
 pkg/stub/action/monitor.go                        |  57 +++
 pkg/stub/handler.go                               |  58 +++
 pkg/util/digest/digest.go                         |  39 ++
 pkg/util/kubernetes/wait.go                       |  39 ++
 pkg/util/openshift/register.go                    |  77 +++
 pkg/util/test/testing_env.go                      |  28 ++
 tmp/build/Dockerfile                              |   6 +
 tmp/build/build.sh                                |  18 +
 tmp/build/docker_build.sh                         |  11 +
 tmp/codegen/boilerplate.go.txt                    |   1 +
 tmp/codegen/update-generated.sh                   |  12 +
 version/version.go                                |  22 +
 41 files changed, 2757 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Gopkg.lock
 create mode 100644 Gopkg.toml
 rename LICENSE.txt => LICENSE (100%)
 create mode 120000 Makefile
 create mode 100644 NOTICE
 create mode 100644 build/Makefile
 create mode 100755 build/get_version.sh
 create mode 100755 build/minishift_add_role.sh
 create mode 100644 cmd/camel-k/main.go
 create mode 100644 config/config.yaml
 create mode 100644 deploy/cr.yaml
 create mode 100644 deploy/crd.yaml
 create mode 100644 deploy/operator.yaml
 create mode 100644 deploy/rbac.yaml
 create mode 100644 pkg/apis/camel/v1alpha1/doc.go
 create mode 100644 pkg/apis/camel/v1alpha1/register.go
 create mode 100644 pkg/apis/camel/v1alpha1/types.go
 create mode 100644 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
 create mode 100644 pkg/build/api/types.go
 create mode 100644 pkg/build/build_manager.go
 create mode 100644 pkg/build/build_manager_integration_test.go
 create mode 100644 pkg/build/local/local_builder.go
 create mode 100644 pkg/build/local/local_builder_integration_test.go
 create mode 100644 pkg/build/local/scheme.go
 create mode 100644 pkg/stub/action/action.go
 create mode 100644 pkg/stub/action/build.go
 create mode 100644 pkg/stub/action/deploy.go
 create mode 100644 pkg/stub/action/initialize.go
 create mode 100644 pkg/stub/action/monitor.go
 create mode 100644 pkg/stub/handler.go
 create mode 100644 pkg/util/digest/digest.go
 create mode 100644 pkg/util/kubernetes/wait.go
 create mode 100644 pkg/util/openshift/register.go
 create mode 100644 pkg/util/test/testing_env.go
 create mode 100644 tmp/build/Dockerfile
 create mode 100755 tmp/build/build.sh
 create mode 100755 tmp/build/docker_build.sh
 create mode 100644 tmp/codegen/boilerplate.go.txt
 create mode 100755 tmp/codegen/update-generated.sh
 create mode 100644 version/version.go


[camel-k] 03/05: Initial draft of local build system

Posted by nf...@apache.org.
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 8661409fac1c95481ac5b25cb1db648f687a2d10
Author: nferraro <ni...@gmail.com>
AuthorDate: Mon Sep 3 09:49:57 2018 +0200

    Initial draft of local build system
---
 Gopkg.lock                                    |  36 ++-
 cmd/camel-k/main.go                           |   5 +-
 pkg/apis/camel/v1alpha1/types.go              |   4 +-
 pkg/build/{build.go => api/types.go}          |  28 +-
 pkg/build/build_manager.go                    |  67 ++++
 pkg/build/local/local_builder.go              | 433 ++++++++++++++++++++++++++
 pkg/build/local/local_builder_test.go         |  90 ++++++
 pkg/build/local/scheme.go                     |  98 ++++++
 pkg/stub/action/action.go                     |   6 +-
 pkg/stub/action/build.go                      |  63 ++++
 pkg/stub/{handler.go => action/initialize.go} |  40 +--
 pkg/stub/handler.go                           |  11 +-
 pkg/util/kubernetes/wait.go                   |  39 +++
 pkg/util/openshift/register.go                |  77 +++++
 14 files changed, 963 insertions(+), 34 deletions(-)

diff --git a/Gopkg.lock b/Gopkg.lock
index c662f8a..08cdcda 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -169,6 +169,22 @@
   version = "1.0.1"
 
 [[projects]]
+  name = "github.com/openshift/api"
+  packages = [
+    "apps/v1",
+    "authorization/v1",
+    "build/v1",
+    "image/docker10",
+    "image/dockerpre012",
+    "image/v1",
+    "pkg/serialization",
+    "route/v1",
+    "template/v1"
+  ]
+  revision = "0d921e363e951d89f583292c60d013c318df64dc"
+  version = "v3.9.0"
+
+[[projects]]
   branch = "master"
   name = "github.com/operator-framework/operator-sdk"
   packages = [
@@ -181,6 +197,18 @@
   revision = "0f60da86a138f7e79f275d539c18615bc2727014"
 
 [[projects]]
+  name = "github.com/pkg/errors"
+  packages = ["."]
+  revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
+  version = "v0.8.0"
+
+[[projects]]
+  name = "github.com/pmezard/go-difflib"
+  packages = ["difflib"]
+  revision = "792786c7400a136282c1664665ae0a8db921c6c2"
+  version = "v1.0.0"
+
+[[projects]]
   name = "github.com/prometheus/client_golang"
   packages = [
     "prometheus",
@@ -229,6 +257,12 @@
   version = "v1.0.2"
 
 [[projects]]
+  name = "github.com/stretchr/testify"
+  packages = ["assert"]
+  revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
+  version = "v1.2.2"
+
+[[projects]]
   branch = "master"
   name = "golang.org/x/crypto"
   packages = ["ssh/terminal"]
@@ -538,6 +572,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "22a3131aff0c3b6f0a97bc22324996cfa5dabe1f626a90facb0daf29bbe0d52a"
+  inputs-digest = "fe3fc8a50615445124a1a85023b09a54b2aa9f0dfc077f6c4823b46ee4ba7ef1"
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/cmd/camel-k/main.go b/cmd/camel-k/main.go
index 63c4fc0..8fdaa53 100644
--- a/cmd/camel-k/main.go
+++ b/cmd/camel-k/main.go
@@ -48,9 +48,10 @@ func main() {
 	if err != nil {
 		logrus.Fatalf("failed to get watch namespace: %v", err)
 	}
+	ctx := context.TODO()
 	resyncPeriod := time.Duration(5) * time.Second
 	logrus.Infof("Watching %s, %s, %s, %d", resource, kind, namespace, resyncPeriod)
 	sdk.Watch(resource, kind, namespace, resyncPeriod)
-	sdk.Handle(stub.NewHandler())
-	sdk.Run(context.TODO())
+	sdk.Handle(stub.NewHandler(ctx, namespace))
+	sdk.Run(ctx)
 }
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index d1a4431..b8ed15a 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -48,13 +48,15 @@ type SourceSpec struct {
 }
 
 type IntegrationStatus struct {
-	Phase   IntegrationPhase    `json:"phase,omitempty"`
+	Phase   	IntegrationPhase    `json:"phase,omitempty"`
+	Identifier	string				`json:"identifier,omitempty"`
 }
 
 type IntegrationPhase string
 
 const (
     IntegrationPhaseBuilding    IntegrationPhase = "Building"
+	IntegrationPhaseDeploying   IntegrationPhase = "Deploying"
     IntegrationPhaseRunning     IntegrationPhase = "Running"
     IntegrationPhaseError       IntegrationPhase = "Error"
 )
\ No newline at end of file
diff --git a/pkg/build/build.go b/pkg/build/api/types.go
similarity index 61%
rename from pkg/build/build.go
rename to pkg/build/api/types.go
index 88d762c..4500937 100644
--- a/pkg/build/build.go
+++ b/pkg/build/api/types.go
@@ -15,5 +15,31 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package build
+package api
 
+// a request to build a specific code
+type BuildSource struct {
+	Identifier	string
+	Code		string
+}
+
+// represents the result of a build
+type BuildResult struct {
+	Source		*BuildSource
+	Status		BuildStatus
+	Image		string
+	Error		error
+}
+
+// supertype of all builders
+type Builder interface {
+	Build(BuildSource) <- chan BuildResult
+}
+
+type BuildStatus int
+const (
+	BuildStatusNotRequested		BuildStatus = iota
+	BuildStatusStarted
+	BuildStatusCompleted
+	BuildStatusError
+)
\ No newline at end of file
diff --git a/pkg/build/build_manager.go b/pkg/build/build_manager.go
new file mode 100644
index 0000000..7ddd03a
--- /dev/null
+++ b/pkg/build/build_manager.go
@@ -0,0 +1,67 @@
+/*
+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 build
+
+import (
+	"sync"
+	"github.com/apache/camel-k/pkg/build/local"
+	"context"
+	"github.com/apache/camel-k/pkg/build/api"
+)
+
+// main facade to the image build system
+type BuildManager struct {
+	builds	map[string]*api.BuildResult
+	mutex	sync.Mutex
+	builder	api.Builder
+}
+
+func NewBuildManager(ctx context.Context, namespace string) *BuildManager {
+	return &BuildManager{
+		builds: make(map[string]*api.BuildResult),
+		builder: local.NewLocalBuilder(ctx, namespace),
+	}
+}
+
+func (m *BuildManager) Get(identifier string) api.BuildResult {
+	m.mutex.Lock()
+	defer m.mutex.Unlock()
+
+	if info, present := m.builds[identifier]; !present || info == nil {
+		return noBuildInfo()
+	} else {
+		return *info
+	}
+}
+
+func (m *BuildManager) Start(source api.BuildSource) {
+	resChannel := m.builder.Build(source)
+	go func() {
+		res := <-resChannel
+		m.mutex.Lock()
+		defer m.mutex.Unlock()
+
+		m.builds[res.Source.Identifier] = &res
+	}()
+}
+
+func noBuildInfo() api.BuildResult {
+	return api.BuildResult{
+		Status: api.BuildStatusNotRequested,
+	}
+}
\ No newline at end of file
diff --git a/pkg/build/local/local_builder.go b/pkg/build/local/local_builder.go
new file mode 100644
index 0000000..bdbb3d6
--- /dev/null
+++ b/pkg/build/local/local_builder.go
@@ -0,0 +1,433 @@
+/*
+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 local
+
+import (
+	"context"
+	"github.com/sirupsen/logrus"
+	"time"
+	"io/ioutil"
+	"path"
+	"os"
+	"os/exec"
+	"github.com/pkg/errors"
+	"archive/tar"
+	"io"
+	buildv1 "github.com/openshift/api/build/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/api/core/v1"
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"k8s.io/client-go/rest"
+	"github.com/operator-framework/operator-sdk/pkg/k8sclient"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	imagev1 "github.com/openshift/api/image/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
+
+	_ "github.com/apache/camel-k/pkg/util/openshift"
+	build "github.com/apache/camel-k/pkg/build/api"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+)
+
+type localBuilder struct {
+	buffer     chan buildOperation
+	namespace  string
+}
+
+type buildOperation struct {
+	source	build.BuildSource
+	output	chan build.BuildResult
+}
+
+func NewLocalBuilder(ctx context.Context, namespace string) build.Builder {
+	builder := localBuilder{
+		buffer: make(chan buildOperation, 100),
+		namespace: namespace,
+	}
+	go builder.buildCycle(ctx)
+	return &builder
+}
+
+func (b *localBuilder) Build(source build.BuildSource) <- chan build.BuildResult {
+	res := make(chan build.BuildResult, 1)
+	op := buildOperation{
+		source: source,
+		output: res,
+	}
+	b.buffer <- op
+	return res
+}
+
+func (b *localBuilder) buildCycle(ctx context.Context) {
+	for {
+		select {
+		case <- ctx.Done():
+			b.buffer = nil
+			return
+		case op := <- b.buffer:
+			now := time.Now()
+			logrus.Info("Starting new build")
+			err := b.execute(op.source)
+			elapsed := time.Now().Sub(now)
+			if err != nil {
+				logrus.Error("Error during build (total time ", elapsed.Seconds(), " seconds): ", err)
+			} else {
+				logrus.Info("Build completed in ", elapsed.Seconds(), " seconds")
+			}
+
+			if err != nil {
+				op.output <- build.BuildResult{
+					Source: &op.source,
+					Status: build.BuildStatusError,
+					Error: err,
+				}
+			} else {
+				op.output <- build.BuildResult{
+					Source: &op.source,
+					Status: build.BuildStatusCompleted,
+					Image: "kamel:latest",
+				}
+			}
+
+		}
+	}
+}
+
+func (b *localBuilder) execute(source build.BuildSource) error {
+	buildDir, err := ioutil.TempDir("", "kamel-")
+	if err != nil {
+		return errors.Wrap(err, "could not create temporary dir for maven artifacts")
+	}
+	//defer os.RemoveAll(buildDir)
+
+	logrus.Info("Using build dir : ", buildDir)
+
+	tarFileName, err := b.createTar(buildDir, source)
+	if err != nil {
+		return err
+	}
+	logrus.Info("Created tar file ", tarFileName)
+
+	err = b.publish(tarFileName, source)
+	if err != nil {
+		return errors.Wrap(err, "could not publish docker image")
+	}
+
+	return nil
+}
+
+func (b *localBuilder) publish(tarFile string, source build.BuildSource) error {
+
+	bc := buildv1.BuildConfig{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: buildv1.SchemeGroupVersion.String(),
+			Kind: "BuildConfig",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "kamel",
+			Namespace: b.namespace,
+		},
+		Spec: buildv1.BuildConfigSpec{
+			CommonSpec: buildv1.CommonSpec{
+				Source: buildv1.BuildSource{
+					Type: buildv1.BuildSourceBinary,
+				},
+				Strategy: buildv1.BuildStrategy{
+					SourceStrategy: &buildv1.SourceBuildStrategy{
+						From: v1.ObjectReference{
+							Kind: "DockerImage",
+							Name: "fabric8/s2i-java:2.1",
+						},
+					},
+				},
+				Output: buildv1.BuildOutput{
+					To: &v1.ObjectReference{
+						Kind: "ImageStreamTag",
+						Name: "kamel:latest",
+					},
+				},
+			},
+		},
+	}
+
+	sdk.Delete(&bc)
+	err := sdk.Create(&bc)
+	if err != nil {
+		return errors.Wrap(err, "cannot create build config")
+	}
+
+	is := imagev1.ImageStream{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: imagev1.SchemeGroupVersion.String(),
+			Kind: "ImageStream",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "kamel",
+			Namespace: b.namespace,
+		},
+		Spec: imagev1.ImageStreamSpec{
+			LookupPolicy: imagev1.ImageLookupPolicy{
+				Local: true,
+			},
+		},
+	}
+
+	sdk.Delete(&is)
+	err = sdk.Create(&is)
+	if err != nil {
+		return errors.Wrap(err, "cannot create image stream")
+	}
+
+	inConfig := k8sclient.GetKubeConfig()
+	config := rest.CopyConfig(inConfig)
+	config.GroupVersion = &schema.GroupVersion{
+		Group: "build.openshift.io",
+		Version: "v1",
+	}
+	config.APIPath = "/apis"
+	config.AcceptContentTypes = "application/json"
+	config.ContentType = "application/json"
+
+	// this gets used for discovery and error handling types
+	config.NegotiatedSerializer = basicNegotiatedSerializer{}
+	if config.UserAgent == "" {
+		config.UserAgent = rest.DefaultKubernetesUserAgent()
+	}
+
+	restClient, err := rest.RESTClientFor(config)
+	if err != nil {
+		return err
+	}
+
+	resource, err := ioutil.ReadFile(tarFile)
+	if err != nil {
+		return errors.Wrap(err, "cannot fully read tar file " + tarFile)
+	}
+
+	result := restClient.
+		Post().
+		Namespace(b.namespace).
+		Body(resource).
+		Resource("buildconfigs").
+		Name("kamel").
+		SubResource("instantiatebinary").
+		Do()
+
+	if result.Error() != nil {
+		return errors.Wrap(result.Error(), "cannot instantiate binary")
+	}
+
+	data, err := result.Raw()
+	if err != nil {
+		return errors.Wrap(err, "no raw data retrieved")
+	}
+
+	u := unstructured.Unstructured{}
+	err = u.UnmarshalJSON(data)
+	if err != nil {
+		return errors.Wrap(err, "cannot unmarshal instantiate binary response")
+	}
+
+	ocbuild, err := k8sutil.RuntimeObjectFromUnstructured(&u)
+	if err != nil {
+		return err
+	}
+	logrus.Info(ocbuild)
+
+	err = kubernetes.WaitCondition(ocbuild, func(obj interface{})(bool, error) {
+		if val, ok := obj.(*buildv1.Build); ok {
+			if val.Status.Phase == buildv1.BuildPhaseComplete {
+				return true, nil
+			} else if val.Status.Phase == buildv1.BuildPhaseCancelled ||
+				val.Status.Phase == buildv1.BuildPhaseFailed ||
+				val.Status.Phase == buildv1.BuildPhaseError {
+				return false, errors.New("build failed")
+			}
+		}
+		return false, nil
+	}, 5 * time.Minute)
+
+	return err
+}
+
+func (b *localBuilder) createTar(buildDir string, source build.BuildSource) (string, error) {
+	dir, err := b.createMavenStructure(buildDir, source)
+	if err != nil {
+		return "", err
+	}
+
+	mavenBuild := exec.Command("mvn", "clean", "install", "-DskipTests")
+	mavenBuild.Dir = dir
+	logrus.Info("Starting maven build: mvn clean install -DskipTests")
+	err = mavenBuild.Run()
+	if err != nil {
+		return "", errors.Wrap(err, "failure while executing maven build")
+	}
+
+	mavenDep := exec.Command("mvn", "dependency:copy-dependencies")
+	mavenDep.Dir = dir
+	logrus.Info("Copying maven dependencies: mvn dependency:copy-dependencies")
+	err = mavenDep.Run()
+	if err != nil {
+		return "", errors.Wrap(err, "failure while extracting maven dependencies")
+	}
+	logrus.Info("Maven build completed successfully")
+
+	tarFileName := path.Join(buildDir, "kamel-app.tar")
+	tarFile, err := os.Create(tarFileName)
+	if err != nil {
+		return "", errors.Wrap(err, "cannot create tar file")
+	}
+	defer tarFile.Close()
+
+	writer := tar.NewWriter(tarFile)
+	err = b.appendToTar(path.Join(buildDir, "target", "kamel-app-1.0.0.jar"), "", writer)
+	if err != nil {
+		return "", err
+	}
+
+	err = b.appendToTar(path.Join(buildDir, "environment"), ".s2i", writer)
+	if err != nil {
+		return "", err
+	}
+
+	dependenciesDir := path.Join(buildDir, "target", "dependency")
+	dependencies, err := ioutil.ReadDir(dependenciesDir)
+	if err != nil {
+		return "", err
+	}
+
+	for _, dep := range dependencies {
+		err = b.appendToTar(path.Join(dependenciesDir, dep.Name()), "", writer)
+		if err != nil {
+			return "", err
+		}
+	}
+
+	writer.Close()
+
+	return tarFileName, nil
+}
+
+func (b *localBuilder) appendToTar(filePath string, tarPath string, writer *tar.Writer) error {
+	info, err := os.Stat(filePath)
+	if err != nil {
+		return err
+	}
+	_, fileName := path.Split(filePath)
+	if tarPath != "" {
+		fileName = path.Join(tarPath, fileName)
+	}
+
+	writer.WriteHeader(&tar.Header{
+		Name: fileName,
+		Size: info.Size(),
+		Mode: int64(info.Mode()),
+		ModTime: info.ModTime(),
+	})
+
+	file, err := os.Open(filePath)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+
+	_, err = io.Copy(writer, file)
+	if err != nil {
+		return errors.Wrap(err, "cannot add file to the tar archive")
+	}
+	return nil
+}
+
+func (b *localBuilder) createMavenStructure(buildDir string, source build.BuildSource) (string, error) {
+	pomFileName := path.Join(buildDir, "pom.xml")
+	pom, err := os.Create(pomFileName)
+	if err != nil {
+		return "", err
+	}
+	defer pom.Close()
+
+	_, err = pom.WriteString(b.createPom())
+	if err != nil {
+		return "", err
+	}
+
+	packageDir := path.Join(buildDir, "src", "main", "java", "kamel")
+	err = os.MkdirAll(packageDir, 0777)
+	if err != nil {
+		return "", err
+	}
+
+	sourceFileName := path.Join(packageDir, "Routes.java")
+	sourceFile, err := os.Create(sourceFileName)
+	if err != nil {
+		return "", err
+	}
+	defer sourceFile.Close()
+
+	_, err = sourceFile.WriteString(source.Code)
+	if err != nil {
+		return "", err
+	}
+
+
+	envFileName := path.Join(buildDir, "environment")
+	envFile, err := os.Create(envFileName)
+	if err != nil {
+		return "", err
+	}
+	defer envFile.Close()
+
+	_, err = envFile.WriteString(b.createEnvFile())
+	if err != nil {
+		return "", err
+	}
+
+	return buildDir, nil
+}
+
+func (b *localBuilder) createEnvFile() string {
+	return `
+JAVA_MAIN_CLASS=me.nicolaferraro.kamel.Application
+KAMEL_CLASS=kamel.Routes
+`
+}
+
+
+func (b *localBuilder) createPom() string {
+	return `<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>me.nicolaferraro.kamel.app</groupId>
+  <artifactId>kamel-app</artifactId>
+  <version>1.0.0</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>me.nicolaferraro.kamel</groupId>
+      <artifactId>camel-java-runtime</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+
+</project>
+`
+}
diff --git a/pkg/build/local/local_builder_test.go b/pkg/build/local/local_builder_test.go
new file mode 100644
index 0000000..f179682
--- /dev/null
+++ b/pkg/build/local/local_builder_test.go
@@ -0,0 +1,90 @@
+/*
+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 local
+
+import (
+	"testing"
+	"context"
+	"github.com/stretchr/testify/assert"
+	build "github.com/apache/camel-k/pkg/build/api"
+)
+
+func TestBuild(t *testing.T) {
+
+	ctx := context.TODO()
+	builder := NewLocalBuilder(ctx, "test")
+
+	execution := builder.Build(build.BuildSource{
+		Code: code(),
+	})
+
+	res := <- execution
+
+	assert.Nil(t, res.Error, "Build failed")
+}
+
+func TestDoubleBuild(t *testing.T) {
+
+	ctx := context.TODO()
+	builder := NewLocalBuilder(ctx, "test")
+
+	execution1 := builder.Build(build.BuildSource{
+		Code: code(),
+	})
+
+	execution2 := builder.Build(build.BuildSource{
+		Code: code(),
+	})
+
+	res1 := <- execution1
+	res2 := <- execution2
+
+	assert.Nil(t, res1.Error, "Build failed")
+	assert.Nil(t, res2.Error, "Build failed")
+}
+
+func TestFailedBuild(t *testing.T) {
+
+	ctx := context.TODO()
+	builder := NewLocalBuilder(ctx, "test")
+
+	execution := builder.Build(build.BuildSource{
+		Code: code() + "-",
+	})
+
+	res := <- execution
+
+	assert.NotNil(t, res.Error, "Build should fail")
+}
+
+func code() string {
+	return `package kamel;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class Routes extends RouteBuilder {
+
+	@Override
+    public void configure() throws Exception {
+        from("timer:tick")
+		  .to("log:info");
+    }
+
+}
+`
+}
\ No newline at end of file
diff --git a/pkg/build/local/scheme.go b/pkg/build/local/scheme.go
new file mode 100644
index 0000000..7fdc19f
--- /dev/null
+++ b/pkg/build/local/scheme.go
@@ -0,0 +1,98 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed 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 local
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"k8s.io/apimachinery/pkg/runtime/serializer/json"
+	"k8s.io/apimachinery/pkg/runtime/serializer/versioning"
+)
+
+var watchScheme = runtime.NewScheme()
+var basicScheme = runtime.NewScheme()
+var deleteScheme = runtime.NewScheme()
+var parameterScheme = runtime.NewScheme()
+var deleteOptionsCodec = serializer.NewCodecFactory(deleteScheme)
+var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme)
+
+var versionV1 = schema.GroupVersion{Version: "v1"}
+
+func init() {
+	metav1.AddToGroupVersion(watchScheme, versionV1)
+	metav1.AddToGroupVersion(basicScheme, versionV1)
+	metav1.AddToGroupVersion(parameterScheme, versionV1)
+	metav1.AddToGroupVersion(deleteScheme, versionV1)
+}
+
+var watchJsonSerializerInfo = runtime.SerializerInfo{
+	MediaType:        "application/json",
+	EncodesAsText:    true,
+	Serializer:       json.NewSerializer(json.DefaultMetaFactory, watchScheme, watchScheme, false),
+	PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, watchScheme, watchScheme, true),
+	StreamSerializer: &runtime.StreamSerializerInfo{
+		EncodesAsText: true,
+		Serializer:    json.NewSerializer(json.DefaultMetaFactory, watchScheme, watchScheme, false),
+		Framer:        json.Framer,
+	},
+}
+
+// watchNegotiatedSerializer is used to read the wrapper of the watch stream
+type watchNegotiatedSerializer struct{}
+
+var watchNegotiatedSerializerInstance = watchNegotiatedSerializer{}
+
+func (s watchNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
+	return []runtime.SerializerInfo{watchJsonSerializerInfo}
+}
+
+func (s watchNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
+	return versioning.NewDefaultingCodecForScheme(watchScheme, encoder, nil, gv, nil)
+}
+
+func (s watchNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
+	return versioning.NewDefaultingCodecForScheme(watchScheme, nil, decoder, nil, gv)
+}
+
+// basicNegotiatedSerializer is used to handle discovery and error handling serialization
+type basicNegotiatedSerializer struct{}
+
+func (s basicNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
+	return []runtime.SerializerInfo{
+		{
+			MediaType:        "application/json",
+			EncodesAsText:    true,
+			Serializer:       json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
+			PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, true),
+			StreamSerializer: &runtime.StreamSerializerInfo{
+				EncodesAsText: true,
+				Serializer:    json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
+				Framer:        json.Framer,
+			},
+		},
+	}
+}
+
+func (s basicNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
+	return versioning.NewDefaultingCodecForScheme(watchScheme, encoder, nil, gv, nil)
+}
+
+func (s basicNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
+	return versioning.NewDefaultingCodecForScheme(watchScheme, nil, decoder, nil, gv)
+}
\ No newline at end of file
diff --git a/pkg/stub/action/action.go b/pkg/stub/action/action.go
index 185105f..d7558ce 100644
--- a/pkg/stub/action/action.go
+++ b/pkg/stub/action/action.go
@@ -23,8 +23,10 @@ import (
 
 type Action interface {
 
-	CanExecute(integration *v1alpha1.Integration) bool
+	// returns true if the action can handle the integration
+	CanHandle(integration *v1alpha1.Integration) bool
 
-	Execute(syndesis *v1alpha1.Integration) error
+	// executes the handling function
+	Handle(integration *v1alpha1.Integration) error
 
 }
diff --git a/pkg/stub/action/build.go b/pkg/stub/action/build.go
new file mode 100644
index 0000000..ccae198
--- /dev/null
+++ b/pkg/stub/action/build.go
@@ -0,0 +1,63 @@
+/*
+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 action
+
+import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"context"
+	"github.com/apache/camel-k/pkg/build"
+	"github.com/sirupsen/logrus"
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"github.com/apache/camel-k/pkg/build/api"
+)
+
+type BuildAction struct {
+	buildManager	*build.BuildManager
+}
+
+func NewBuildAction(ctx context.Context, namespace string) *BuildAction {
+	return &BuildAction{
+		buildManager: build.NewBuildManager(ctx, namespace),
+	}
+}
+
+func (b *BuildAction) CanHandle(integration *v1alpha1.Integration) bool {
+	return integration.Status.Phase == v1alpha1.IntegrationPhaseBuilding
+}
+
+func (b *BuildAction) Handle(integration *v1alpha1.Integration) error {
+
+	buildResult := b.buildManager.Get(integration.Status.Identifier)
+	if buildResult.Status == api.BuildStatusNotRequested {
+		b.buildManager.Start(api.BuildSource{
+			Identifier: integration.Status.Identifier,
+			Code: *integration.Spec.Source.Code, // FIXME possible panic
+		})
+		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.Phase = v1alpha1.IntegrationPhaseDeploying
+		return sdk.Update(target)
+	}
+
+	return nil
+}
diff --git a/pkg/stub/handler.go b/pkg/stub/action/initialize.go
similarity index 59%
copy from pkg/stub/handler.go
copy to pkg/stub/action/initialize.go
index 8f6fd16..60f8dc6 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/action/initialize.go
@@ -15,37 +15,31 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package stub
+package action
 
 import (
-	"context"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/apache/camel-k/pkg/stub/action"
+	"math/rand"
+	"strconv"
 )
 
-func NewHandler() sdk.Handler {
-	return &Handler{
-		actionPool: []action.Action{},
-	}
+// initializes the integration status to trigger the deployment
+type InitializeAction struct {
+
+}
+
+func NewInitializeAction() *InitializeAction {
+	return &InitializeAction{}
 }
 
-type Handler struct {
-	actionPool	[]action.Action
+func (b *InitializeAction) CanHandle(integration *v1alpha1.Integration) bool {
+	return integration.Status.Phase == ""
 }
 
-func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
-	switch o := event.Object.(type) {
-	case *v1alpha1.Integration:
-		for _, a := range h.actionPool {
-			if a.CanExecute(o) {
-				if err := a.Execute(o); err != nil {
-					return err
-				}
-			}
-		}
-	}
-	return nil
+func (b *InitializeAction) Handle(integration *v1alpha1.Integration) error {
+	target := integration.DeepCopy()
+	target.Status.Phase = v1alpha1.IntegrationPhaseBuilding
+	target.Status.Identifier = strconv.Itoa(rand.Int()) // TODO replace with hash
+	return sdk.Update(target)
 }
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
index 8f6fd16..7c52247 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/handler.go
@@ -26,9 +26,12 @@ import (
 	"github.com/apache/camel-k/pkg/stub/action"
 )
 
-func NewHandler() sdk.Handler {
+func NewHandler(ctx context.Context, namespace string) sdk.Handler {
 	return &Handler{
-		actionPool: []action.Action{},
+		actionPool: []action.Action{
+			action.NewInitializeAction(),
+			action.NewBuildAction(ctx, namespace),
+		},
 	}
 }
 
@@ -40,8 +43,8 @@ func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
 	switch o := event.Object.(type) {
 	case *v1alpha1.Integration:
 		for _, a := range h.actionPool {
-			if a.CanExecute(o) {
-				if err := a.Execute(o); err != nil {
+			if a.CanHandle(o) {
+				if err := a.Handle(o); err != nil {
 					return err
 				}
 			}
diff --git a/pkg/util/kubernetes/wait.go b/pkg/util/kubernetes/wait.go
new file mode 100644
index 0000000..f333b78
--- /dev/null
+++ b/pkg/util/kubernetes/wait.go
@@ -0,0 +1,39 @@
+package kubernetes
+
+import (
+	"time"
+	"github.com/pkg/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+)
+
+type ResourceRetrieveFunction func()(interface{}, error)
+
+type ResourceCheckFunction func(interface{})(bool, error)
+
+const (
+	sleepTime = 400 * time.Millisecond
+)
+
+func WaitCondition(obj runtime.Object, condition ResourceCheckFunction, maxDuration time.Duration) error {
+	start := time.Now()
+
+	for start.Add(maxDuration).After(time.Now()) {
+		err := sdk.Get(obj)
+		if err != nil {
+			time.Sleep(sleepTime)
+			continue
+		}
+
+		satisfied, err := condition(obj)
+		if err != nil {
+			return errors.Wrap(err, "error while evaluating condition")
+		} else if !satisfied {
+			time.Sleep(sleepTime)
+			continue
+		}
+
+		return nil
+	}
+	return errors.New("timeout while waiting condition")
+}
\ No newline at end of file
diff --git a/pkg/util/openshift/register.go b/pkg/util/openshift/register.go
new file mode 100644
index 0000000..076db9f
--- /dev/null
+++ b/pkg/util/openshift/register.go
@@ -0,0 +1,77 @@
+/*
+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.
+*/
+
+// Register all Openshift types that we want to manage.
+package openshift
+
+import (
+	apps "github.com/openshift/api/apps/v1"
+	authorization "github.com/openshift/api/authorization/v1"
+	build "github.com/openshift/api/build/v1"
+	image "github.com/openshift/api/image/v1"
+	route "github.com/openshift/api/route/v1"
+	template "github.com/openshift/api/template/v1"
+	"github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
+	"github.com/sirupsen/logrus"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+func init() {
+	k8sutil.AddToSDKScheme(AddToScheme)
+}
+
+var (
+	AddToScheme = addKnownTypes
+)
+
+type registerFunction func(*runtime.Scheme) error
+
+func addKnownTypes(scheme *runtime.Scheme) error {
+
+	var err error
+
+	// Standardized groups
+	err = doAdd(apps.AddToScheme, scheme, err)
+	err = doAdd(template.AddToScheme, scheme, err)
+	err = doAdd(image.AddToScheme, scheme, err)
+	err = doAdd(route.AddToScheme, scheme, err)
+	err = doAdd(build.AddToScheme, scheme, err)
+	err = doAdd(authorization.AddToScheme, scheme, err)
+
+
+	// Legacy "oapi" resources
+	err = doAdd(apps.AddToSchemeInCoreGroup, scheme, err)
+	err = doAdd(template.AddToSchemeInCoreGroup, scheme, err)
+	err = doAdd(image.AddToSchemeInCoreGroup, scheme, err)
+	err = doAdd(route.AddToSchemeInCoreGroup, scheme, err)
+	err = doAdd(build.AddToSchemeInCoreGroup, scheme, err)
+	err = doAdd(authorization.AddToSchemeInCoreGroup, scheme, err)
+
+	return err
+}
+
+func doAdd(addToScheme registerFunction, scheme *runtime.Scheme, err error) error {
+	callErr := addToScheme(scheme)
+	if callErr != nil {
+		logrus.Error("Error while registering Openshift types", callErr)
+	}
+
+	if err == nil {
+		return callErr
+	}
+	return err
+}
\ No newline at end of file


[camel-k] 02/05: Add license headers

Posted by nf...@apache.org.
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 bfca1f2d2e87b18c69c8c3f76cb6f81ebacbaec7
Author: nferraro <ni...@gmail.com>
AuthorDate: Sun Sep 2 08:35:59 2018 +0200

    Add license headers
---
 .gitignore                          |  4 +-
 LICENSE.txt => LICENSE              |  0
 NOTICE                              | 11 ++++++
 build/get_version.sh                |  2 +-
 cmd/camel-k/main.go                 | 56 ++++++++++++++++++++++++++++
 pkg/apis/camel/v1alpha1/doc.go      | 18 +++++++++
 pkg/apis/camel/v1alpha1/register.go | 17 +++++++++
 pkg/apis/camel/v1alpha1/types.go    | 35 +++++++++++++++++-
 pkg/build/build.go                  | 19 ++++++++++
 pkg/stub/action/action.go           | 30 +++++++++++++++
 pkg/stub/handler.go                 | 73 ++++++++++++++-----------------------
 version/version.go                  | 17 +++++++++
 12 files changed, 232 insertions(+), 50 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5819a19..319e113 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,8 @@
 # Binary files
-camel-k
+/camel-k
 
 # We do not stage vendor directory
-vendor
+/vendor
 
 # IDEs
 .idea
diff --git a/LICENSE.txt b/LICENSE
similarity index 100%
rename from LICENSE.txt
rename to LICENSE
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..f4f1281
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,11 @@
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Apache Camel distribution.                    ==
+   =========================================================================
+
+   This product includes software developed by
+   The Apache Software Foundation (http://www.apache.org/).
+
+   Please read the different LICENSE files present in the licenses directory of
+   this distribution.
\ No newline at end of file
diff --git a/build/get_version.sh b/build/get_version.sh
index 0b78013..1357527 100755
--- a/build/get_version.sh
+++ b/build/get_version.sh
@@ -1,4 +1,4 @@
 #!/bin/sh
 
 location=$(dirname $0)
-cat $location/../version/version.go | grep "Version" | awk '{print $NF}' | tr -d '"'
\ No newline at end of file
+cat $location/../version/version.go | grep "Version" | grep "=" | awk '{print $NF}' | tr -d '"'
\ No newline at end of file
diff --git a/cmd/camel-k/main.go b/cmd/camel-k/main.go
new file mode 100644
index 0000000..63c4fc0
--- /dev/null
+++ b/cmd/camel-k/main.go
@@ -0,0 +1,56 @@
+/*
+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 main
+
+import (
+	"context"
+	"runtime"
+	"time"
+
+	stub "github.com/apache/camel-k/pkg/stub"
+	sdk "github.com/operator-framework/operator-sdk/pkg/sdk"
+	k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
+	sdkVersion "github.com/operator-framework/operator-sdk/version"
+
+	"github.com/sirupsen/logrus"
+	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
+)
+
+func printVersion() {
+	logrus.Infof("Go Version: %s", runtime.Version())
+	logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)
+	logrus.Infof("operator-sdk Version: %v", sdkVersion.Version)
+}
+
+func main() {
+	printVersion()
+
+	sdk.ExposeMetricsPort()
+
+	resource := "camel.apache.org/v1alpha1"
+	kind := "Integration"
+	namespace, err := k8sutil.GetWatchNamespace()
+	if err != nil {
+		logrus.Fatalf("failed to get watch namespace: %v", err)
+	}
+	resyncPeriod := time.Duration(5) * time.Second
+	logrus.Infof("Watching %s, %s, %s, %d", resource, kind, namespace, resyncPeriod)
+	sdk.Watch(resource, kind, namespace, resyncPeriod)
+	sdk.Handle(stub.NewHandler())
+	sdk.Run(context.TODO())
+}
diff --git a/pkg/apis/camel/v1alpha1/doc.go b/pkg/apis/camel/v1alpha1/doc.go
index 0745fff..c937536 100644
--- a/pkg/apis/camel/v1alpha1/doc.go
+++ b/pkg/apis/camel/v1alpha1/doc.go
@@ -1,3 +1,21 @@
 // +k8s:deepcopy-gen=package
 // +groupName=camel.apache.org
+
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 package v1alpha1
diff --git a/pkg/apis/camel/v1alpha1/register.go b/pkg/apis/camel/v1alpha1/register.go
index a054677..dbbdfd7 100644
--- a/pkg/apis/camel/v1alpha1/register.go
+++ b/pkg/apis/camel/v1alpha1/register.go
@@ -1,3 +1,20 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 package v1alpha1
 
 import (
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index 158e8c0..d1a4431 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -1,3 +1,20 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 package v1alpha1
 
 import (
@@ -22,8 +39,22 @@ type Integration struct {
 }
 
 type IntegrationSpec struct {
-	// Fill me
+	Replicas   *int32       `json:"replicas,omitempty"`
+	Source     SourceSpec   `json:"source,omitempty"`
+}
+
+type SourceSpec struct {
+    Code    *string `json:"code,omitempty"`
 }
+
 type IntegrationStatus struct {
-	// Fill me
+	Phase   IntegrationPhase    `json:"phase,omitempty"`
 }
+
+type IntegrationPhase string
+
+const (
+    IntegrationPhaseBuilding    IntegrationPhase = "Building"
+    IntegrationPhaseRunning     IntegrationPhase = "Running"
+    IntegrationPhaseError       IntegrationPhase = "Error"
+)
\ No newline at end of file
diff --git a/pkg/build/build.go b/pkg/build/build.go
new file mode 100644
index 0000000..88d762c
--- /dev/null
+++ b/pkg/build/build.go
@@ -0,0 +1,19 @@
+/*
+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 build
+
diff --git a/pkg/stub/action/action.go b/pkg/stub/action/action.go
new file mode 100644
index 0000000..185105f
--- /dev/null
+++ b/pkg/stub/action/action.go
@@ -0,0 +1,30 @@
+/*
+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 action
+
+import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+)
+
+type Action interface {
+
+	CanExecute(integration *v1alpha1.Integration) bool
+
+	Execute(syndesis *v1alpha1.Integration) error
+
+}
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
index 749d304..8f6fd16 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/handler.go
@@ -1,3 +1,20 @@
+/*
+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 stub
 
 import (
@@ -6,63 +23,29 @@ import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/sirupsen/logrus"
-	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime/schema"
+	"github.com/apache/camel-k/pkg/stub/action"
 )
 
 func NewHandler() sdk.Handler {
-	return &Handler{}
+	return &Handler{
+		actionPool: []action.Action{},
+	}
 }
 
 type Handler struct {
-	// Fill me
+	actionPool	[]action.Action
 }
 
 func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
 	switch o := event.Object.(type) {
 	case *v1alpha1.Integration:
-		err := sdk.Create(newbusyBoxPod(o))
-		if err != nil && !errors.IsAlreadyExists(err) {
-			logrus.Errorf("failed to create busybox pod : %v", err)
-			return err
+		for _, a := range h.actionPool {
+			if a.CanExecute(o) {
+				if err := a.Execute(o); err != nil {
+					return err
+				}
+			}
 		}
 	}
 	return nil
 }
-
-// newbusyBoxPod demonstrates how to create a busybox pod
-func newbusyBoxPod(cr *v1alpha1.Integration) *corev1.Pod {
-	labels := map[string]string{
-		"app": "busy-box",
-	}
-	return &corev1.Pod{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "Pod",
-			APIVersion: "v1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "busy-box",
-			Namespace: cr.Namespace,
-			OwnerReferences: []metav1.OwnerReference{
-				*metav1.NewControllerRef(cr, schema.GroupVersionKind{
-					Group:   v1alpha1.SchemeGroupVersion.Group,
-					Version: v1alpha1.SchemeGroupVersion.Version,
-					Kind:    "Integration",
-				}),
-			},
-			Labels: labels,
-		},
-		Spec: corev1.PodSpec{
-			Containers: []corev1.Container{
-				{
-					Name:    "busybox",
-					Image:   "busybox",
-					Command: []string{"sleep", "3600"},
-				},
-			},
-		},
-	}
-}
diff --git a/version/version.go b/version/version.go
index 74ac223..6d8b94b 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,3 +1,20 @@
+/*
+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 version
 
 var (


[camel-k] 05/05: Detect changes with digest and redeploy

Posted by nf...@apache.org.
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 f42dedff26826e3e68a565fbc965bc9593b3a053
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Sep 6 13:04:19 2018 +0200

    Detect changes with digest and redeploy
---
 pkg/apis/camel/v1alpha1/types.go                  |  6 +--
 pkg/build/api/types.go                            |  7 ++-
 pkg/build/build_manager.go                        | 11 ++---
 pkg/build/build_manager_integration_test.go       | 18 +++++---
 pkg/build/local/local_builder.go                  | 10 ++---
 pkg/stub/action/build.go                          |  9 ++--
 pkg/stub/action/initialize.go                     |  5 +--
 pkg/stub/{handler.go => action/monitor.go}        | 52 +++++++++++------------
 pkg/stub/handler.go                               |  1 +
 pkg/{build/api/types.go => util/digest/digest.go} | 44 +++++++++----------
 10 files changed, 86 insertions(+), 77 deletions(-)

diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index 0f58ff9..ec30eb9 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -48,9 +48,9 @@ type SourceSpec struct {
 }
 
 type IntegrationStatus struct {
-	Phase IntegrationPhase `json:"phase,omitempty"`
-	Hash  string           `json:"hash,omitempty"`
-	Image string           `json:"image,omitempty"`
+	Phase  IntegrationPhase `json:"phase,omitempty"`
+	Digest string           `json:"digest,omitempty"`
+	Image  string           `json:"image,omitempty"`
 }
 
 type IntegrationPhase string
diff --git a/pkg/build/api/types.go b/pkg/build/api/types.go
index 4500937..3358a6c 100644
--- a/pkg/build/api/types.go
+++ b/pkg/build/api/types.go
@@ -19,10 +19,15 @@ package api
 
 // a request to build a specific code
 type BuildSource struct {
-	Identifier	string
+	Identifier	BuildIdentifier
 	Code		string
 }
 
+type BuildIdentifier struct {
+	Name	string
+	Digest	string
+}
+
 // represents the result of a build
 type BuildResult struct {
 	Source		*BuildSource
diff --git a/pkg/build/build_manager.go b/pkg/build/build_manager.go
index fee2315..170a5e6 100644
--- a/pkg/build/build_manager.go
+++ b/pkg/build/build_manager.go
@@ -26,19 +26,19 @@ import (
 
 // main facade to the image build system
 type BuildManager struct {
-	builds	map[string]*api.BuildResult
+	builds	map[api.BuildIdentifier]*api.BuildResult
 	mutex	sync.Mutex
 	builder	api.Builder
 }
 
 func NewBuildManager(ctx context.Context, namespace string) *BuildManager {
 	return &BuildManager{
-		builds: make(map[string]*api.BuildResult),
+		builds: make(map[api.BuildIdentifier]*api.BuildResult),
 		builder: local.NewLocalBuilder(ctx, namespace),
 	}
 }
 
-func (m *BuildManager) Get(identifier string) api.BuildResult {
+func (m *BuildManager) Get(identifier api.BuildIdentifier) api.BuildResult {
 	m.mutex.Lock()
 	defer m.mutex.Unlock()
 
@@ -53,7 +53,7 @@ func (m *BuildManager) Start(source api.BuildSource) {
 	m.mutex.Lock()
 	defer m.mutex.Unlock()
 
-	initialBuildInfo := initialBuildInfo()
+	initialBuildInfo := initialBuildInfo(&source)
 	m.builds[source.Identifier] = &initialBuildInfo
 
 	resChannel := m.builder.Build(source)
@@ -72,8 +72,9 @@ func noBuildInfo() api.BuildResult {
 	}
 }
 
-func initialBuildInfo() api.BuildResult {
+func initialBuildInfo(source *api.BuildSource) api.BuildResult {
 	return api.BuildResult{
+		Source: source,
 		Status: api.BuildStatusStarted,
 	}
 }
\ No newline at end of file
diff --git a/pkg/build/build_manager_integration_test.go b/pkg/build/build_manager_integration_test.go
index f74fd4e..dcf7c9e 100644
--- a/pkg/build/build_manager_integration_test.go
+++ b/pkg/build/build_manager_integration_test.go
@@ -31,16 +31,19 @@ import (
 func TestBuild(t *testing.T) {
 	ctx := context.TODO()
 	buildManager := NewBuildManager(ctx, test.GetTargetNamespace())
-
+	identifier := build.BuildIdentifier{
+		Name: "example",
+		Digest: "sadsadasdsadasdafwefwef",
+	}
 	buildManager.Start(build.BuildSource{
-		Identifier: "1",
+		Identifier: identifier,
 		Code: code(),
 	})
 
 	deadline := time.Now().Add(5 * time.Minute)
 	var result build.BuildResult
 	for time.Now().Before(deadline) {
-		result = buildManager.Get("1")
+		result = buildManager.Get(identifier)
 		if result.Status == build.BuildStatusCompleted || result.Status == build.BuildStatusError {
 			break
 		}
@@ -56,16 +59,19 @@ func TestFailedBuild(t *testing.T) {
 
 	ctx := context.TODO()
 	buildManager := NewBuildManager(ctx, test.GetTargetNamespace())
-
+	identifier := build.BuildIdentifier{
+		Name: "example",
+		Digest: "545454",
+	}
 	buildManager.Start(build.BuildSource{
-		Identifier: "1",
+		Identifier: identifier,
 		Code: code() + "XX",
 	})
 
 	deadline := time.Now().Add(5 * time.Minute)
 	var result build.BuildResult
 	for time.Now().Before(deadline) {
-		result = buildManager.Get("1")
+		result = buildManager.Get(identifier)
 		if result.Status == build.BuildStatusCompleted || result.Status == build.BuildStatusError {
 			break
 		}
diff --git a/pkg/build/local/local_builder.go b/pkg/build/local/local_builder.go
index 9874434..02bacf1 100644
--- a/pkg/build/local/local_builder.go
+++ b/pkg/build/local/local_builder.go
@@ -139,7 +139,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string
 			Kind: "BuildConfig",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Name: "kamel",
+			Name: "kamel-" + source.Identifier.Name,
 			Namespace: b.namespace,
 		},
 		Spec: buildv1.BuildConfigSpec{
@@ -158,7 +158,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string
 				Output: buildv1.BuildOutput{
 					To: &v1.ObjectReference{
 						Kind: "ImageStreamTag",
-						Name: "kamel:latest",
+						Name: "kamel-" + source.Identifier.Name + ":" + source.Identifier.Digest,
 					},
 				},
 			},
@@ -177,7 +177,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string
 			Kind: "ImageStream",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Name: "kamel",
+			Name: "kamel-" + source.Identifier.Name,
 			Namespace: b.namespace,
 		},
 		Spec: imagev1.ImageStreamSpec{
@@ -224,7 +224,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string
 		Namespace(b.namespace).
 		Body(resource).
 		Resource("buildconfigs").
-		Name("kamel").
+		Name("kamel-" + source.Identifier.Name).
 		SubResource("instantiatebinary").
 		Do()
 
@@ -269,7 +269,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string
 	if is.Status.DockerImageRepository == "" {
 		return "", errors.New("dockerImageRepository not available in ImageStream")
 	}
-	return is.Status.DockerImageRepository + ":latest", nil
+	return is.Status.DockerImageRepository + ":" + source.Identifier.Digest, nil
 }
 
 func (b *localBuilder) createTar(buildDir string, source build.BuildSource) (string, error) {
diff --git a/pkg/stub/action/build.go b/pkg/stub/action/build.go
index 3b7b942..62517b0 100644
--- a/pkg/stub/action/build.go
+++ b/pkg/stub/action/build.go
@@ -45,11 +45,14 @@ func (b *BuildAction) CanHandle(integration *v1alpha1.Integration) bool {
 }
 
 func (b *BuildAction) Handle(integration *v1alpha1.Integration) error {
-
-	buildResult := b.buildManager.Get(integration.Status.Hash)
+	buildIdentifier := api.BuildIdentifier{
+		Name: integration.Name,
+		Digest: integration.Status.Digest,
+	}
+	buildResult := b.buildManager.Get(buildIdentifier)
 	if buildResult.Status == api.BuildStatusNotRequested {
 		b.buildManager.Start(api.BuildSource{
-			Identifier: integration.Status.Hash,
+			Identifier: buildIdentifier,
 			Code: *integration.Spec.Source.Code, // FIXME possible panic
 		})
 		logrus.Info("Build started")
diff --git a/pkg/stub/action/initialize.go b/pkg/stub/action/initialize.go
index 52d8db9..8b5a445 100644
--- a/pkg/stub/action/initialize.go
+++ b/pkg/stub/action/initialize.go
@@ -20,8 +20,7 @@ package action
 import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"math/rand"
-	"strconv"
+	"github.com/apache/camel-k/pkg/util/digest"
 )
 
 // initializes the integration status to trigger the deployment
@@ -50,6 +49,6 @@ func (b *InitializeAction) Handle(integration *v1alpha1.Integration) error {
 	}
 	// update the status
 	target.Status.Phase = v1alpha1.IntegrationPhaseBuilding
-	target.Status.Hash = strconv.Itoa(rand.Int()) // TODO replace with hash
+	target.Status.Digest = digest.Compute(integration)
 	return sdk.Update(target)
 }
diff --git a/pkg/stub/handler.go b/pkg/stub/action/monitor.go
similarity index 52%
copy from pkg/stub/handler.go
copy to pkg/stub/action/monitor.go
index ca46e54..deb30d6 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/action/monitor.go
@@ -15,43 +15,43 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package stub
+package action
 
 import (
-	"context"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/apache/camel-k/pkg/stub/action"
+	"github.com/apache/camel-k/pkg/util/digest"
 	"github.com/sirupsen/logrus"
 )
 
-func NewHandler(ctx context.Context, namespace string) sdk.Handler {
-	return &Handler{
-		actionPool: []action.Action{
-			action.NewInitializeAction(),
-			action.NewBuildAction(ctx, namespace),
-			action.NewDeployAction(),
-		},
-	}
+type MonitorAction struct {
+}
+
+func NewMonitorAction() *MonitorAction {
+	return &MonitorAction{}
 }
 
-type Handler struct {
-	actionPool	[]action.Action
+func (b *MonitorAction) Name() string {
+	return "monitor"
 }
 
-func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
-	switch o := event.Object.(type) {
-	case *v1alpha1.Integration:
-		for _, a := range h.actionPool {
-			if a.CanHandle(o) {
-				logrus.Info("Invoking action ", a.Name(), " on integration ", o.Name)
-				if err := a.Handle(o); err != nil {
-					return err
-				}
-			}
-		}
+func (a *MonitorAction) CanHandle(integration *v1alpha1.Integration) bool {
+	return integration.Status.Phase == v1alpha1.IntegrationPhaseRunning ||
+		integration.Status.Phase == v1alpha1.IntegrationPhaseError
+}
+
+func (a *MonitorAction) Handle(integration *v1alpha1.Integration) error {
+
+	hash := digest.Compute(integration)
+	if hash != integration.Status.Digest {
+		logrus.Info("Integration ", integration.Name, " needs a rebuild")
+
+		target := integration.DeepCopy()
+		target.Status.Digest=hash
+		target.Status.Phase=v1alpha1.IntegrationPhaseBuilding
+		return sdk.Update(target)
 	}
+
+	// TODO check also if deployment matches (e.g. replicas)
 	return nil
 }
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
index ca46e54..a46c16f 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/handler.go
@@ -33,6 +33,7 @@ func NewHandler(ctx context.Context, namespace string) sdk.Handler {
 			action.NewInitializeAction(),
 			action.NewBuildAction(ctx, namespace),
 			action.NewDeployAction(),
+			action.NewMonitorAction(),
 		},
 	}
 }
diff --git a/pkg/build/api/types.go b/pkg/util/digest/digest.go
similarity index 52%
copy from pkg/build/api/types.go
copy to pkg/util/digest/digest.go
index 4500937..c29c018 100644
--- a/pkg/build/api/types.go
+++ b/pkg/util/digest/digest.go
@@ -15,31 +15,25 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package api
+package digest
 
-// a request to build a specific code
-type BuildSource struct {
-	Identifier	string
-	Code		string
-}
-
-// represents the result of a build
-type BuildResult struct {
-	Source		*BuildSource
-	Status		BuildStatus
-	Image		string
-	Error		error
-}
+import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"crypto/sha256"
+	"github.com/apache/camel-k/version"
+	"encoding/base64"
+)
 
-// supertype of all builders
-type Builder interface {
-	Build(BuildSource) <- chan BuildResult
+// Compute a digest of the fields that are relevant for the deployment
+// Produces a digest that can be used as docker image tag
+func Compute(integration *v1alpha1.Integration) string {
+	hash := sha256.New()
+	// Operator version is relevant
+	hash.Write([]byte(version.Version))
+	// Integration relevant fields
+	if integration.Spec.Source.Code != nil {
+		hash.Write([]byte(*integration.Spec.Source.Code))
+	}
+	// Add a letter at the beginning and use URL safe encoding
+	return "v" + base64.RawURLEncoding.EncodeToString(hash.Sum(nil))
 }
-
-type BuildStatus int
-const (
-	BuildStatusNotRequested		BuildStatus = iota
-	BuildStatusStarted
-	BuildStatusCompleted
-	BuildStatusError
-)
\ No newline at end of file


[camel-k] 01/05: Scaffolding project structure

Posted by nf...@apache.org.
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 62b435477c879e3da63e6884219367582afab34d
Author: nferraro <ni...@gmail.com>
AuthorDate: Sun Sep 2 07:38:14 2018 +0200

    Scaffolding project structure
---
 .gitignore                                       | 120 +++++
 Gopkg.lock                                       | 543 +++++++++++++++++++++++
 Gopkg.toml                                       |  58 +++
 Makefile                                         |   1 +
 build/Makefile                                   |   7 +
 build/get_version.sh                             |   4 +
 config/config.yaml                               |   3 +
 deploy/cr.yaml                                   |   4 +
 deploy/crd.yaml                                  |  13 +
 deploy/operator.yaml                             |  30 ++
 deploy/rbac.yaml                                 |  46 ++
 pkg/apis/camel/v1alpha1/doc.go                   |   3 +
 pkg/apis/camel/v1alpha1/register.go              |  35 ++
 pkg/apis/camel/v1alpha1/types.go                 |  29 ++
 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go | 102 +++++
 pkg/stub/handler.go                              |  68 +++
 tmp/build/Dockerfile                             |   6 +
 tmp/build/build.sh                               |  18 +
 tmp/build/docker_build.sh                        |  11 +
 tmp/codegen/boilerplate.go.txt                   |   1 +
 tmp/codegen/update-generated.sh                  |  12 +
 version/version.go                               |   5 +
 22 files changed, 1119 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5819a19
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,120 @@
+# Binary files
+camel-k
+
+# We do not stage vendor directory
+vendor
+
+# IDEs
+.idea
+*.iml
+.project
+.metadata
+
+# Temporary Build Files
+tmp/_output
+tmp/_test
+
+
+# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
+
+### Emacs ###
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
+projectile-bookmarks.eld
+
+# directory configuration
+.dir-locals.el
+
+# saveplace
+places
+
+# url cache
+url/cache/
+
+# cedet
+ede-projects.el
+
+# smex
+smex-items
+
+# company-statistics
+company-statistics-cache.el
+
+# anaconda-mode
+anaconda-mode/
+
+### Go ###
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with 'go test -c'
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+### Vim ###
+# swap
+.sw[a-p]
+.*.sw[a-p]
+# session
+Session.vim
+# temporary
+.netrwhist
+# auto-generated tag files
+tags
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history
+
+
+# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
diff --git a/Gopkg.lock b/Gopkg.lock
new file mode 100644
index 0000000..c662f8a
--- /dev/null
+++ b/Gopkg.lock
@@ -0,0 +1,543 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+  name = "cloud.google.com/go"
+  packages = ["compute/metadata"]
+  revision = "64a2037ec6be8a4b0c1d1f706ed35b428b989239"
+  version = "v0.26.0"
+
+[[projects]]
+  name = "github.com/PuerkitoBio/purell"
+  packages = ["."]
+  revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
+  version = "v1.1.0"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/PuerkitoBio/urlesc"
+  packages = ["."]
+  revision = "de5bf2ad457846296e2031421a34e2568e304e35"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/beorn7/perks"
+  packages = ["quantile"]
+  revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
+
+[[projects]]
+  name = "github.com/davecgh/go-spew"
+  packages = ["spew"]
+  revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
+  version = "v1.1.1"
+
+[[projects]]
+  name = "github.com/emicklei/go-restful"
+  packages = [
+    ".",
+    "log"
+  ]
+  revision = "3eb9738c1697594ea6e71a7156a9bb32ed216cf0"
+  version = "v2.8.0"
+
+[[projects]]
+  name = "github.com/ghodss/yaml"
+  packages = ["."]
+  revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
+  version = "v1.0.0"
+
+[[projects]]
+  name = "github.com/go-openapi/jsonpointer"
+  packages = ["."]
+  revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
+  version = "0.16.0"
+
+[[projects]]
+  name = "github.com/go-openapi/jsonreference"
+  packages = ["."]
+  revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
+  version = "0.16.0"
+
+[[projects]]
+  name = "github.com/go-openapi/spec"
+  packages = ["."]
+  revision = "384415f06ee238aae1df5caad877de6ceac3a5c4"
+  version = "0.16.0"
+
+[[projects]]
+  name = "github.com/go-openapi/swag"
+  packages = ["."]
+  revision = "becd2f08beafcca035645a8a101e0e3e18140458"
+  version = "0.16.0"
+
+[[projects]]
+  name = "github.com/gogo/protobuf"
+  packages = [
+    "proto",
+    "sortkeys"
+  ]
+  revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
+  version = "v1.1.1"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/golang/glog"
+  packages = ["."]
+  revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
+
+[[projects]]
+  name = "github.com/golang/protobuf"
+  packages = [
+    "proto",
+    "ptypes",
+    "ptypes/any",
+    "ptypes/duration",
+    "ptypes/timestamp"
+  ]
+  revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
+  version = "v1.2.0"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/google/gofuzz"
+  packages = ["."]
+  revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
+
+[[projects]]
+  name = "github.com/googleapis/gnostic"
+  packages = [
+    "OpenAPIv2",
+    "compiler",
+    "extensions"
+  ]
+  revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
+  version = "v0.2.0"
+
+[[projects]]
+  name = "github.com/hashicorp/golang-lru"
+  packages = [
+    ".",
+    "simplelru"
+  ]
+  revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768"
+  version = "v0.5.0"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/howeyc/gopass"
+  packages = ["."]
+  revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
+
+[[projects]]
+  name = "github.com/imdario/mergo"
+  packages = ["."]
+  revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4"
+  version = "v0.3.6"
+
+[[projects]]
+  name = "github.com/json-iterator/go"
+  packages = ["."]
+  revision = "1624edc4454b8682399def8740d46db5e4362ba4"
+  version = "v1.1.5"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/mailru/easyjson"
+  packages = [
+    "buffer",
+    "jlexer",
+    "jwriter"
+  ]
+  revision = "60711f1a8329503b04e1c88535f419d0bb440bff"
+
+[[projects]]
+  name = "github.com/matttproud/golang_protobuf_extensions"
+  packages = ["pbutil"]
+  revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
+  version = "v1.0.1"
+
+[[projects]]
+  name = "github.com/modern-go/concurrent"
+  packages = ["."]
+  revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
+  version = "1.0.3"
+
+[[projects]]
+  name = "github.com/modern-go/reflect2"
+  packages = ["."]
+  revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
+  version = "1.0.1"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/operator-framework/operator-sdk"
+  packages = [
+    "pkg/k8sclient",
+    "pkg/sdk",
+    "pkg/sdk/internal/metrics",
+    "pkg/util/k8sutil",
+    "version"
+  ]
+  revision = "0f60da86a138f7e79f275d539c18615bc2727014"
+
+[[projects]]
+  name = "github.com/prometheus/client_golang"
+  packages = [
+    "prometheus",
+    "prometheus/promhttp"
+  ]
+  revision = "c5b7fccd204277076155f10851dad72b76a49317"
+  version = "v0.8.0"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/prometheus/client_model"
+  packages = ["go"]
+  revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/prometheus/common"
+  packages = [
+    "expfmt",
+    "internal/bitbucket.org/ww/goautoneg",
+    "model"
+  ]
+  revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/prometheus/procfs"
+  packages = [
+    ".",
+    "internal/util",
+    "nfs",
+    "xfs"
+  ]
+  revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
+
+[[projects]]
+  name = "github.com/sirupsen/logrus"
+  packages = ["."]
+  revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
+  version = "v1.0.6"
+
+[[projects]]
+  name = "github.com/spf13/pflag"
+  packages = ["."]
+  revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
+  version = "v1.0.2"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/crypto"
+  packages = ["ssh/terminal"]
+  revision = "182538f80094b6a8efaade63a8fd8e0d9d5843dd"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/net"
+  packages = [
+    "context",
+    "context/ctxhttp",
+    "http/httpguts",
+    "http2",
+    "http2/hpack",
+    "idna"
+  ]
+  revision = "8a410e7b638dca158bf9e766925842f6651ff828"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/oauth2"
+  packages = [
+    ".",
+    "google",
+    "internal",
+    "jws",
+    "jwt"
+  ]
+  revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/sys"
+  packages = [
+    "unix",
+    "windows"
+  ]
+  revision = "fa5fdf94c78965f1aa8423f0cc50b8b8d728b05a"
+
+[[projects]]
+  name = "golang.org/x/text"
+  packages = [
+    "collate",
+    "collate/build",
+    "internal/colltab",
+    "internal/gen",
+    "internal/tag",
+    "internal/triegen",
+    "internal/ucd",
+    "language",
+    "secure/bidirule",
+    "transform",
+    "unicode/bidi",
+    "unicode/cldr",
+    "unicode/norm",
+    "unicode/rangetable",
+    "width"
+  ]
+  revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
+  version = "v0.3.0"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/time"
+  packages = ["rate"]
+  revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/tools"
+  packages = [
+    "go/ast/astutil",
+    "imports",
+    "internal/fastwalk"
+  ]
+  revision = "7ca132754999accbaa5c1735eda29e7ce0f3bf03"
+
+[[projects]]
+  name = "google.golang.org/appengine"
+  packages = [
+    ".",
+    "internal",
+    "internal/app_identity",
+    "internal/base",
+    "internal/datastore",
+    "internal/log",
+    "internal/modules",
+    "internal/remote_api",
+    "internal/urlfetch",
+    "urlfetch"
+  ]
+  revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
+  version = "v1.1.0"
+
+[[projects]]
+  name = "gopkg.in/inf.v0"
+  packages = ["."]
+  revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
+  version = "v0.9.1"
+
+[[projects]]
+  name = "gopkg.in/yaml.v2"
+  packages = ["."]
+  revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
+  version = "v2.2.1"
+
+[[projects]]
+  name = "k8s.io/api"
+  packages = [
+    "admissionregistration/v1alpha1",
+    "admissionregistration/v1beta1",
+    "apps/v1",
+    "apps/v1beta1",
+    "apps/v1beta2",
+    "authentication/v1",
+    "authentication/v1beta1",
+    "authorization/v1",
+    "authorization/v1beta1",
+    "autoscaling/v1",
+    "autoscaling/v2beta1",
+    "batch/v1",
+    "batch/v1beta1",
+    "batch/v2alpha1",
+    "certificates/v1beta1",
+    "core/v1",
+    "events/v1beta1",
+    "extensions/v1beta1",
+    "networking/v1",
+    "policy/v1beta1",
+    "rbac/v1",
+    "rbac/v1alpha1",
+    "rbac/v1beta1",
+    "scheduling/v1alpha1",
+    "settings/v1alpha1",
+    "storage/v1",
+    "storage/v1alpha1",
+    "storage/v1beta1"
+  ]
+  revision = "73d903622b7391f3312dcbac6483fed484e185f8"
+
+[[projects]]
+  name = "k8s.io/apimachinery"
+  packages = [
+    "pkg/api/errors",
+    "pkg/api/meta",
+    "pkg/api/resource",
+    "pkg/apis/meta/internalversion",
+    "pkg/apis/meta/v1",
+    "pkg/apis/meta/v1/unstructured",
+    "pkg/apis/meta/v1beta1",
+    "pkg/conversion",
+    "pkg/conversion/queryparams",
+    "pkg/fields",
+    "pkg/labels",
+    "pkg/runtime",
+    "pkg/runtime/schema",
+    "pkg/runtime/serializer",
+    "pkg/runtime/serializer/json",
+    "pkg/runtime/serializer/protobuf",
+    "pkg/runtime/serializer/recognizer",
+    "pkg/runtime/serializer/streaming",
+    "pkg/runtime/serializer/versioning",
+    "pkg/selection",
+    "pkg/types",
+    "pkg/util/cache",
+    "pkg/util/clock",
+    "pkg/util/diff",
+    "pkg/util/errors",
+    "pkg/util/framer",
+    "pkg/util/intstr",
+    "pkg/util/json",
+    "pkg/util/net",
+    "pkg/util/runtime",
+    "pkg/util/sets",
+    "pkg/util/validation",
+    "pkg/util/validation/field",
+    "pkg/util/wait",
+    "pkg/util/yaml",
+    "pkg/version",
+    "pkg/watch",
+    "third_party/forked/golang/reflect"
+  ]
+  revision = "302974c03f7e50f16561ba237db776ab93594ef6"
+
+[[projects]]
+  name = "k8s.io/client-go"
+  packages = [
+    "discovery",
+    "discovery/cached",
+    "dynamic",
+    "kubernetes",
+    "kubernetes/scheme",
+    "kubernetes/typed/admissionregistration/v1alpha1",
+    "kubernetes/typed/admissionregistration/v1beta1",
+    "kubernetes/typed/apps/v1",
+    "kubernetes/typed/apps/v1beta1",
+    "kubernetes/typed/apps/v1beta2",
+    "kubernetes/typed/authentication/v1",
+    "kubernetes/typed/authentication/v1beta1",
+    "kubernetes/typed/authorization/v1",
+    "kubernetes/typed/authorization/v1beta1",
+    "kubernetes/typed/autoscaling/v1",
+    "kubernetes/typed/autoscaling/v2beta1",
+    "kubernetes/typed/batch/v1",
+    "kubernetes/typed/batch/v1beta1",
+    "kubernetes/typed/batch/v2alpha1",
+    "kubernetes/typed/certificates/v1beta1",
+    "kubernetes/typed/core/v1",
+    "kubernetes/typed/events/v1beta1",
+    "kubernetes/typed/extensions/v1beta1",
+    "kubernetes/typed/networking/v1",
+    "kubernetes/typed/policy/v1beta1",
+    "kubernetes/typed/rbac/v1",
+    "kubernetes/typed/rbac/v1alpha1",
+    "kubernetes/typed/rbac/v1beta1",
+    "kubernetes/typed/scheduling/v1alpha1",
+    "kubernetes/typed/settings/v1alpha1",
+    "kubernetes/typed/storage/v1",
+    "kubernetes/typed/storage/v1alpha1",
+    "kubernetes/typed/storage/v1beta1",
+    "pkg/apis/clientauthentication",
+    "pkg/apis/clientauthentication/v1alpha1",
+    "pkg/version",
+    "plugin/pkg/client/auth/exec",
+    "plugin/pkg/client/auth/gcp",
+    "rest",
+    "rest/watch",
+    "third_party/forked/golang/template",
+    "tools/auth",
+    "tools/cache",
+    "tools/clientcmd",
+    "tools/clientcmd/api",
+    "tools/clientcmd/api/latest",
+    "tools/clientcmd/api/v1",
+    "tools/metrics",
+    "tools/pager",
+    "tools/reference",
+    "transport",
+    "util/buffer",
+    "util/cert",
+    "util/flowcontrol",
+    "util/homedir",
+    "util/integer",
+    "util/jsonpath",
+    "util/retry",
+    "util/workqueue"
+  ]
+  revision = "989be4278f353e42f26c416c53757d16fcff77db"
+
+[[projects]]
+  name = "k8s.io/code-generator"
+  packages = [
+    "cmd/client-gen",
+    "cmd/client-gen/args",
+    "cmd/client-gen/generators",
+    "cmd/client-gen/generators/fake",
+    "cmd/client-gen/generators/scheme",
+    "cmd/client-gen/generators/util",
+    "cmd/client-gen/path",
+    "cmd/client-gen/types",
+    "cmd/conversion-gen",
+    "cmd/conversion-gen/args",
+    "cmd/conversion-gen/generators",
+    "cmd/deepcopy-gen",
+    "cmd/deepcopy-gen/args",
+    "cmd/defaulter-gen",
+    "cmd/defaulter-gen/args",
+    "cmd/informer-gen",
+    "cmd/informer-gen/args",
+    "cmd/informer-gen/generators",
+    "cmd/lister-gen",
+    "cmd/lister-gen/args",
+    "cmd/lister-gen/generators",
+    "cmd/openapi-gen",
+    "cmd/openapi-gen/args",
+    "pkg/util"
+  ]
+  revision = "7ead8f38b01cf8653249f5af80ce7b2c8aba12e2"
+
+[[projects]]
+  branch = "master"
+  name = "k8s.io/gengo"
+  packages = [
+    "args",
+    "examples/deepcopy-gen/generators",
+    "examples/defaulter-gen/generators",
+    "examples/set-gen/sets",
+    "generator",
+    "namer",
+    "parser",
+    "types"
+  ]
+  revision = "4242d8e6c5dba56827bb7bcf14ad11cda38f3991"
+
+[[projects]]
+  branch = "master"
+  name = "k8s.io/kube-openapi"
+  packages = [
+    "cmd/openapi-gen/args",
+    "pkg/common",
+    "pkg/generators",
+    "pkg/generators/rules",
+    "pkg/util/sets"
+  ]
+  revision = "e3762e86a74c878ffed47484592986685639c2cd"
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  inputs-digest = "22a3131aff0c3b6f0a97bc22324996cfa5dabe1f626a90facb0daf29bbe0d52a"
+  solver-name = "gps-cdcl"
+  solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
new file mode 100644
index 0000000..4eee1cb
--- /dev/null
+++ b/Gopkg.toml
@@ -0,0 +1,58 @@
+
+# Force dep to vendor the code generators, which aren't imported just used at dev time.
+# Picking a subpackage with Go code won't be necessary once https://github.com/golang/dep/pull/1545 is merged.
+required = [
+  "k8s.io/code-generator/cmd/defaulter-gen",
+  "k8s.io/code-generator/cmd/deepcopy-gen",
+  "k8s.io/code-generator/cmd/conversion-gen",
+  "k8s.io/code-generator/cmd/client-gen",
+  "k8s.io/code-generator/cmd/lister-gen",
+  "k8s.io/code-generator/cmd/informer-gen",
+  "k8s.io/code-generator/cmd/openapi-gen",
+  "k8s.io/gengo/args",
+]
+
+[[override]]
+  name = "k8s.io/code-generator"
+  # revision for tag "kubernetes-1.10.1"
+  revision = "7ead8f38b01cf8653249f5af80ce7b2c8aba12e2"
+
+[[override]]
+  name = "k8s.io/api"
+  # revision for tag "kubernetes-1.10.1"
+  revision = "73d903622b7391f3312dcbac6483fed484e185f8"
+
+[[override]]
+  name = "k8s.io/apiextensions-apiserver"
+  # revision for tag "kubernetes-1.10.1"
+  revision = "4347b330d0ff094db860f2f75fa725b4f4b53618"
+
+[[override]]
+  name = "k8s.io/apimachinery"
+  # revision for tag "kubernetes-1.10.1"
+  revision = "302974c03f7e50f16561ba237db776ab93594ef6"
+
+[[override]]
+  name = "k8s.io/client-go"
+  # revision for tag "kubernetes-1.10.1"
+  revision = "989be4278f353e42f26c416c53757d16fcff77db"
+
+[[override]]
+  name = "sigs.k8s.io/controller-runtime"
+  revision = "60bb251ad86f9b313653618aad0c2c53f41a6625"
+
+[prune]
+  go-tests = true
+  non-go = true
+  unused-packages = true
+
+  [[prune.project]]
+    name = "k8s.io/code-generator"
+    non-go = false
+    unused-packages = false
+
+[[constraint]]
+  name = "github.com/operator-framework/operator-sdk"
+  # The version rule is used for a specific release and the master branch for in between releases.
+  branch = "master"
+  # version = "=v0.0.5"
diff --git a/Makefile b/Makefile
new file mode 120000
index 0000000..2206d42
--- /dev/null
+++ b/Makefile
@@ -0,0 +1 @@
+./build/Makefile
\ No newline at end of file
diff --git a/build/Makefile b/build/Makefile
new file mode 100644
index 0000000..a818a4b
--- /dev/null
+++ b/build/Makefile
@@ -0,0 +1,7 @@
+VERSION := $(shell ./build/get_version.sh)
+
+compile:
+	go build -o camel-k ./cmd/camel-k/*.go
+
+images:
+	operator-sdk build docker.io/apache/camel-k:$(VERSION)
\ No newline at end of file
diff --git a/build/get_version.sh b/build/get_version.sh
new file mode 100755
index 0000000..0b78013
--- /dev/null
+++ b/build/get_version.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+location=$(dirname $0)
+cat $location/../version/version.go | grep "Version" | awk '{print $NF}' | tr -d '"'
\ No newline at end of file
diff --git a/config/config.yaml b/config/config.yaml
new file mode 100644
index 0000000..45de913
--- /dev/null
+++ b/config/config.yaml
@@ -0,0 +1,3 @@
+apiVersion: camel.apache.org/v1alpha1
+kind: Integration
+projectName: camel-k
diff --git a/deploy/cr.yaml b/deploy/cr.yaml
new file mode 100644
index 0000000..7a208db
--- /dev/null
+++ b/deploy/cr.yaml
@@ -0,0 +1,4 @@
+apiVersion: "camel.apache.org/v1alpha1"
+kind: "Integration"
+metadata:
+  name: "example"
diff --git a/deploy/crd.yaml b/deploy/crd.yaml
new file mode 100644
index 0000000..53d24d7
--- /dev/null
+++ b/deploy/crd.yaml
@@ -0,0 +1,13 @@
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: integrations.camel.apache.org
+spec:
+  group: camel.apache.org
+  names:
+    kind: Integration
+    listKind: IntegrationList
+    plural: integrations
+    singular: integration
+  scope: Namespaced
+  version: v1alpha1
diff --git a/deploy/operator.yaml b/deploy/operator.yaml
new file mode 100644
index 0000000..9d0fc83
--- /dev/null
+++ b/deploy/operator.yaml
@@ -0,0 +1,30 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: camel-k
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      name: camel-k
+  template:
+    metadata:
+      labels:
+        name: camel-k
+    spec:
+      containers:
+        - name: camel-k
+          image: docker.io/apache/camel-k:0.0.1-SNAPSHOT
+          ports:
+          - containerPort: 60000
+            name: metrics
+          command:
+          - camel-k
+          imagePullPolicy: Always
+          env:
+            - name: WATCH_NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+            - name: OPERATOR_NAME
+              value: "camel-k"
diff --git a/deploy/rbac.yaml b/deploy/rbac.yaml
new file mode 100644
index 0000000..e1cb15f
--- /dev/null
+++ b/deploy/rbac.yaml
@@ -0,0 +1,46 @@
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: camel-k
+rules:
+- apiGroups:
+  - camel.apache.org
+  resources:
+  - "*"
+  verbs:
+  - "*"
+- apiGroups:
+  - ""
+  resources:
+  - pods
+  - services
+  - endpoints
+  - persistentvolumeclaims
+  - events
+  - configmaps
+  - secrets
+  verbs:
+  - "*"
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  - daemonsets
+  - replicasets
+  - statefulsets
+  verbs:
+  - "*"
+
+---
+
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: default-account-camel-k
+subjects:
+- kind: ServiceAccount
+  name: default
+roleRef:
+  kind: Role
+  name: camel-k
+  apiGroup: rbac.authorization.k8s.io
diff --git a/pkg/apis/camel/v1alpha1/doc.go b/pkg/apis/camel/v1alpha1/doc.go
new file mode 100644
index 0000000..0745fff
--- /dev/null
+++ b/pkg/apis/camel/v1alpha1/doc.go
@@ -0,0 +1,3 @@
+// +k8s:deepcopy-gen=package
+// +groupName=camel.apache.org
+package v1alpha1
diff --git a/pkg/apis/camel/v1alpha1/register.go b/pkg/apis/camel/v1alpha1/register.go
new file mode 100644
index 0000000..a054677
--- /dev/null
+++ b/pkg/apis/camel/v1alpha1/register.go
@@ -0,0 +1,35 @@
+package v1alpha1
+
+import (
+	sdkK8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+const (
+	version   = "v1alpha1"
+	groupName = "camel.apache.org"
+)
+
+var (
+	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
+	AddToScheme   = SchemeBuilder.AddToScheme
+	// SchemeGroupVersion is the group version used to register these objects.
+	SchemeGroupVersion = schema.GroupVersion{Group: groupName, Version: version}
+)
+
+func init() {
+	sdkK8sutil.AddToSDKScheme(AddToScheme)
+}
+
+// addKnownTypes adds the set of types defined in this package to the supplied scheme.
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(SchemeGroupVersion,
+		&Integration{},
+		&IntegrationList{},
+	)
+	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
+	return nil
+}
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
new file mode 100644
index 0000000..158e8c0
--- /dev/null
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -0,0 +1,29 @@
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+type IntegrationList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata"`
+	Items           []Integration `json:"items"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+type Integration struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata"`
+	Spec              IntegrationSpec   `json:"spec"`
+	Status            IntegrationStatus `json:"status,omitempty"`
+}
+
+type IntegrationSpec struct {
+	// Fill me
+}
+type IntegrationStatus struct {
+	// Fill me
+}
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
new file mode 100644
index 0000000..8b2935d
--- /dev/null
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,102 @@
+// +build !ignore_autogenerated
+
+// Code generated by deepcopy-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Integration) DeepCopyInto(out *Integration) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	out.Spec = in.Spec
+	out.Status = in.Status
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Integration.
+func (in *Integration) DeepCopy() *Integration {
+	if in == nil {
+		return nil
+	}
+	out := new(Integration)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Integration) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *IntegrationList) DeepCopyInto(out *IntegrationList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	out.ListMeta = in.ListMeta
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Integration, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationList.
+func (in *IntegrationList) DeepCopy() *IntegrationList {
+	if in == nil {
+		return nil
+	}
+	out := new(IntegrationList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *IntegrationList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationSpec.
+func (in *IntegrationSpec) DeepCopy() *IntegrationSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(IntegrationSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *IntegrationStatus) DeepCopyInto(out *IntegrationStatus) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationStatus.
+func (in *IntegrationStatus) DeepCopy() *IntegrationStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(IntegrationStatus)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
new file mode 100644
index 0000000..749d304
--- /dev/null
+++ b/pkg/stub/handler.go
@@ -0,0 +1,68 @@
+package stub
+
+import (
+	"context"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"github.com/sirupsen/logrus"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+func NewHandler() sdk.Handler {
+	return &Handler{}
+}
+
+type Handler struct {
+	// Fill me
+}
+
+func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
+	switch o := event.Object.(type) {
+	case *v1alpha1.Integration:
+		err := sdk.Create(newbusyBoxPod(o))
+		if err != nil && !errors.IsAlreadyExists(err) {
+			logrus.Errorf("failed to create busybox pod : %v", err)
+			return err
+		}
+	}
+	return nil
+}
+
+// newbusyBoxPod demonstrates how to create a busybox pod
+func newbusyBoxPod(cr *v1alpha1.Integration) *corev1.Pod {
+	labels := map[string]string{
+		"app": "busy-box",
+	}
+	return &corev1.Pod{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Pod",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "busy-box",
+			Namespace: cr.Namespace,
+			OwnerReferences: []metav1.OwnerReference{
+				*metav1.NewControllerRef(cr, schema.GroupVersionKind{
+					Group:   v1alpha1.SchemeGroupVersion.Group,
+					Version: v1alpha1.SchemeGroupVersion.Version,
+					Kind:    "Integration",
+				}),
+			},
+			Labels: labels,
+		},
+		Spec: corev1.PodSpec{
+			Containers: []corev1.Container{
+				{
+					Name:    "busybox",
+					Image:   "busybox",
+					Command: []string{"sleep", "3600"},
+				},
+			},
+		},
+	}
+}
diff --git a/tmp/build/Dockerfile b/tmp/build/Dockerfile
new file mode 100644
index 0000000..09b528e
--- /dev/null
+++ b/tmp/build/Dockerfile
@@ -0,0 +1,6 @@
+FROM alpine:3.6
+
+RUN adduser -D camel-k
+USER camel-k
+
+ADD tmp/_output/bin/camel-k /usr/local/bin/camel-k
diff --git a/tmp/build/build.sh b/tmp/build/build.sh
new file mode 100755
index 0000000..6755c89
--- /dev/null
+++ b/tmp/build/build.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+if ! which go > /dev/null; then
+	echo "golang needs to be installed"
+	exit 1
+fi
+
+BIN_DIR="$(pwd)/tmp/_output/bin"
+mkdir -p ${BIN_DIR}
+PROJECT_NAME="camel-k"
+REPO_PATH="github.com/apache/camel-k"
+BUILD_PATH="${REPO_PATH}/cmd/${PROJECT_NAME}"
+echo "building "${PROJECT_NAME}"..."
+GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${BIN_DIR}/${PROJECT_NAME} $BUILD_PATH
diff --git a/tmp/build/docker_build.sh b/tmp/build/docker_build.sh
new file mode 100755
index 0000000..da98858
--- /dev/null
+++ b/tmp/build/docker_build.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+if ! which docker > /dev/null; then
+	echo "docker needs to be installed"
+	exit 1
+fi
+
+: ${IMAGE:?"Need to set IMAGE, e.g. gcr.io/<repo>/<your>-operator"}
+
+echo "building container ${IMAGE}..."
+docker build -t "${IMAGE}" -f tmp/build/Dockerfile .
diff --git a/tmp/codegen/boilerplate.go.txt b/tmp/codegen/boilerplate.go.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tmp/codegen/boilerplate.go.txt
@@ -0,0 +1 @@
+
diff --git a/tmp/codegen/update-generated.sh b/tmp/codegen/update-generated.sh
new file mode 100755
index 0000000..060b628
--- /dev/null
+++ b/tmp/codegen/update-generated.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+vendor/k8s.io/code-generator/generate-groups.sh \
+deepcopy \
+github.com/apache/camel-k/pkg/generated \
+github.com/apache/camel-k/pkg/apis \
+camel:v1alpha1 \
+--go-header-file "./tmp/codegen/boilerplate.go.txt"
diff --git a/version/version.go b/version/version.go
new file mode 100644
index 0000000..74ac223
--- /dev/null
+++ b/version/version.go
@@ -0,0 +1,5 @@
+package version
+
+var (
+	Version = "0.0.1-SNAPSHOT"
+)


[camel-k] 04/05: First e2e working version

Posted by nf...@apache.org.
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 c3d39e634d7462e16b89b58ee567322918a7bc95
Author: nferraro <ni...@gmail.com>
AuthorDate: Thu Sep 6 10:41:48 2018 +0200

    First e2e working version
---
 build/Makefile                                     |  27 ++++-
 build/minishift_add_role.sh                        |   7 ++
 cmd/camel-k/main.go                                |   6 +-
 deploy/cr.yaml                                     |  18 ++++
 pkg/apis/camel/v1alpha1/types.go                   |   5 +-
 pkg/build/build_manager.go                         |  12 +++
 ...r_test.go => build_manager_integration_test.go} |  71 +++++++------
 pkg/build/local/local_builder.go                   |  85 ++++++++++++----
 ...r_test.go => local_builder_integration_test.go} |   8 +-
 pkg/stub/action/action.go                          |   3 +
 pkg/stub/action/build.go                           |   9 +-
 pkg/stub/action/deploy.go                          | 111 +++++++++++++++++++++
 pkg/stub/action/initialize.go                      |  12 ++-
 pkg/stub/handler.go                                |   3 +
 .../action/action.go => util/test/testing_env.go}  |  18 ++--
 15 files changed, 319 insertions(+), 76 deletions(-)

diff --git a/build/Makefile b/build/Makefile
index a818a4b..4d9c017 100644
--- a/build/Makefile
+++ b/build/Makefile
@@ -1,7 +1,30 @@
 VERSION := $(shell ./build/get_version.sh)
 
-compile:
+install:
 	go build -o camel-k ./cmd/camel-k/*.go
 
 images:
-	operator-sdk build docker.io/apache/camel-k:$(VERSION)
\ No newline at end of file
+	operator-sdk build docker.io/apache/camel-k:$(VERSION)
+
+test: check
+check:
+	go test ./...
+
+test-integration: check-integration
+check-integration: require-kubeconfig require-namespace
+	go test ./... -tags=integration
+
+ifndef WATCH_NAMESPACE
+require-namespace:
+	$(error WATCH_NAMESPACE not set)
+else
+require-namespace:
+	@echo "WATCH_NAMESPACE set"
+endif
+ifndef KUBERNETES_CONFIG
+require-kubeconfig:
+	$(error KUBERNETES_CONFIG not set)
+else
+require-kubeconfig:
+	@echo "KUBERNETES_CONFIG set"
+endif
\ No newline at end of file
diff --git a/build/minishift_add_role.sh b/build/minishift_add_role.sh
new file mode 100755
index 0000000..4d69ca4
--- /dev/null
+++ b/build/minishift_add_role.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+user=$(oc whoami)
+
+oc login -u system:admin
+oc policy add-role-to-user --role-namespace=$(oc project -q) camel-k $user
+oc login -u $user
diff --git a/cmd/camel-k/main.go b/cmd/camel-k/main.go
index 8fdaa53..7cd1f72 100644
--- a/cmd/camel-k/main.go
+++ b/cmd/camel-k/main.go
@@ -22,9 +22,9 @@ import (
 	"runtime"
 	"time"
 
-	stub "github.com/apache/camel-k/pkg/stub"
-	sdk "github.com/operator-framework/operator-sdk/pkg/sdk"
-	k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
+	"github.com/apache/camel-k/pkg/stub"
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
 	sdkVersion "github.com/operator-framework/operator-sdk/version"
 
 	"github.com/sirupsen/logrus"
diff --git a/deploy/cr.yaml b/deploy/cr.yaml
index 7a208db..ae0ee37 100644
--- a/deploy/cr.yaml
+++ b/deploy/cr.yaml
@@ -2,3 +2,21 @@ apiVersion: "camel.apache.org/v1alpha1"
 kind: "Integration"
 metadata:
   name: "example"
+spec:
+  replicas: 1
+  source:
+    code: |-
+      package kamel;
+
+      import org.apache.camel.builder.RouteBuilder;
+
+      public class Routes extends RouteBuilder {
+
+          @Override
+          public void configure() throws Exception {
+              from("timer:tick")
+                .setBody(constant("Hello World!!!"))
+                .to("log:info");
+          }
+
+      }
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index b8ed15a..0f58ff9 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -48,8 +48,9 @@ type SourceSpec struct {
 }
 
 type IntegrationStatus struct {
-	Phase   	IntegrationPhase    `json:"phase,omitempty"`
-	Identifier	string				`json:"identifier,omitempty"`
+	Phase IntegrationPhase `json:"phase,omitempty"`
+	Hash  string           `json:"hash,omitempty"`
+	Image string           `json:"image,omitempty"`
 }
 
 type IntegrationPhase string
diff --git a/pkg/build/build_manager.go b/pkg/build/build_manager.go
index 7ddd03a..fee2315 100644
--- a/pkg/build/build_manager.go
+++ b/pkg/build/build_manager.go
@@ -50,6 +50,12 @@ func (m *BuildManager) Get(identifier string) api.BuildResult {
 }
 
 func (m *BuildManager) Start(source api.BuildSource) {
+	m.mutex.Lock()
+	defer m.mutex.Unlock()
+
+	initialBuildInfo := initialBuildInfo()
+	m.builds[source.Identifier] = &initialBuildInfo
+
 	resChannel := m.builder.Build(source)
 	go func() {
 		res := <-resChannel
@@ -64,4 +70,10 @@ func noBuildInfo() api.BuildResult {
 	return api.BuildResult{
 		Status: api.BuildStatusNotRequested,
 	}
+}
+
+func initialBuildInfo() api.BuildResult {
+	return api.BuildResult{
+		Status: api.BuildStatusStarted,
+	}
 }
\ No newline at end of file
diff --git a/pkg/build/local/local_builder_test.go b/pkg/build/build_manager_integration_test.go
similarity index 51%
copy from pkg/build/local/local_builder_test.go
copy to pkg/build/build_manager_integration_test.go
index f179682..f74fd4e 100644
--- a/pkg/build/local/local_builder_test.go
+++ b/pkg/build/build_manager_integration_test.go
@@ -1,3 +1,5 @@
+// +build integration
+
 /*
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
@@ -15,61 +17,64 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package local
+package build
 
 import (
 	"testing"
 	"context"
 	"github.com/stretchr/testify/assert"
 	build "github.com/apache/camel-k/pkg/build/api"
+	"time"
+	"github.com/apache/camel-k/pkg/util/test"
 )
 
 func TestBuild(t *testing.T) {
-
-	ctx := context.TODO()
-	builder := NewLocalBuilder(ctx, "test")
-
-	execution := builder.Build(build.BuildSource{
-		Code: code(),
-	})
-
-	res := <- execution
-
-	assert.Nil(t, res.Error, "Build failed")
-}
-
-func TestDoubleBuild(t *testing.T) {
-
 	ctx := context.TODO()
-	builder := NewLocalBuilder(ctx, "test")
-
-	execution1 := builder.Build(build.BuildSource{
-		Code: code(),
-	})
+	buildManager := NewBuildManager(ctx, test.GetTargetNamespace())
 
-	execution2 := builder.Build(build.BuildSource{
+	buildManager.Start(build.BuildSource{
+		Identifier: "1",
 		Code: code(),
 	})
 
-	res1 := <- execution1
-	res2 := <- execution2
-
-	assert.Nil(t, res1.Error, "Build failed")
-	assert.Nil(t, res2.Error, "Build failed")
+	deadline := time.Now().Add(5 * time.Minute)
+	var result build.BuildResult
+	for time.Now().Before(deadline) {
+		result = buildManager.Get("1")
+		if result.Status == build.BuildStatusCompleted || result.Status == build.BuildStatusError {
+			break
+		}
+		time.Sleep(2 * time.Second)
+	}
+
+	assert.NotEqual(t, build.BuildStatusError, result.Status)
+	assert.Equal(t, build.BuildStatusCompleted, result.Status)
+	assert.Regexp(t, ".*/.*/.*:.*", result.Image)
 }
 
 func TestFailedBuild(t *testing.T) {
 
 	ctx := context.TODO()
-	builder := NewLocalBuilder(ctx, "test")
+	buildManager := NewBuildManager(ctx, test.GetTargetNamespace())
 
-	execution := builder.Build(build.BuildSource{
-		Code: code() + "-",
+	buildManager.Start(build.BuildSource{
+		Identifier: "1",
+		Code: code() + "XX",
 	})
 
-	res := <- execution
-
-	assert.NotNil(t, res.Error, "Build should fail")
+	deadline := time.Now().Add(5 * time.Minute)
+	var result build.BuildResult
+	for time.Now().Before(deadline) {
+		result = buildManager.Get("1")
+		if result.Status == build.BuildStatusCompleted || result.Status == build.BuildStatusError {
+			break
+		}
+		time.Sleep(2 * time.Second)
+	}
+
+	assert.Equal(t, build.BuildStatusError, result.Status)
+	assert.NotEqual(t, build.BuildStatusCompleted, result.Status)
+	assert.Empty(t, result.Image)
 }
 
 func code() string {
diff --git a/pkg/build/local/local_builder.go b/pkg/build/local/local_builder.go
index bdbb3d6..9874434 100644
--- a/pkg/build/local/local_builder.go
+++ b/pkg/build/local/local_builder.go
@@ -82,7 +82,7 @@ func (b *localBuilder) buildCycle(ctx context.Context) {
 		case op := <- b.buffer:
 			now := time.Now()
 			logrus.Info("Starting new build")
-			err := b.execute(op.source)
+			image, err := b.execute(op.source)
 			elapsed := time.Now().Sub(now)
 			if err != nil {
 				logrus.Error("Error during build (total time ", elapsed.Seconds(), " seconds): ", err)
@@ -100,7 +100,7 @@ func (b *localBuilder) buildCycle(ctx context.Context) {
 				op.output <- build.BuildResult{
 					Source: &op.source,
 					Status: build.BuildStatusCompleted,
-					Image: "kamel:latest",
+					Image: image,
 				}
 			}
 
@@ -108,10 +108,10 @@ func (b *localBuilder) buildCycle(ctx context.Context) {
 	}
 }
 
-func (b *localBuilder) execute(source build.BuildSource) error {
+func (b *localBuilder) execute(source build.BuildSource) (string, error) {
 	buildDir, err := ioutil.TempDir("", "kamel-")
 	if err != nil {
-		return errors.Wrap(err, "could not create temporary dir for maven artifacts")
+		return "", errors.Wrap(err, "could not create temporary dir for maven artifacts")
 	}
 	//defer os.RemoveAll(buildDir)
 
@@ -119,19 +119,19 @@ func (b *localBuilder) execute(source build.BuildSource) error {
 
 	tarFileName, err := b.createTar(buildDir, source)
 	if err != nil {
-		return err
+		return "", err
 	}
 	logrus.Info("Created tar file ", tarFileName)
 
-	err = b.publish(tarFileName, source)
+	image, err := b.publish(tarFileName, source)
 	if err != nil {
-		return errors.Wrap(err, "could not publish docker image")
+		return "", errors.Wrap(err, "could not publish docker image")
 	}
 
-	return nil
+	return image, nil
 }
 
-func (b *localBuilder) publish(tarFile string, source build.BuildSource) error {
+func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string, error) {
 
 	bc := buildv1.BuildConfig{
 		TypeMeta: metav1.TypeMeta{
@@ -168,7 +168,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) error {
 	sdk.Delete(&bc)
 	err := sdk.Create(&bc)
 	if err != nil {
-		return errors.Wrap(err, "cannot create build config")
+		return "", errors.Wrap(err, "cannot create build config")
 	}
 
 	is := imagev1.ImageStream{
@@ -190,7 +190,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) error {
 	sdk.Delete(&is)
 	err = sdk.Create(&is)
 	if err != nil {
-		return errors.Wrap(err, "cannot create image stream")
+		return "", errors.Wrap(err, "cannot create image stream")
 	}
 
 	inConfig := k8sclient.GetKubeConfig()
@@ -211,12 +211,12 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) error {
 
 	restClient, err := rest.RESTClientFor(config)
 	if err != nil {
-		return err
+		return "", err
 	}
 
 	resource, err := ioutil.ReadFile(tarFile)
 	if err != nil {
-		return errors.Wrap(err, "cannot fully read tar file " + tarFile)
+		return "", errors.Wrap(err, "cannot fully read tar file " + tarFile)
 	}
 
 	result := restClient.
@@ -229,25 +229,24 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) error {
 		Do()
 
 	if result.Error() != nil {
-		return errors.Wrap(result.Error(), "cannot instantiate binary")
+		return "", errors.Wrap(result.Error(), "cannot instantiate binary")
 	}
 
 	data, err := result.Raw()
 	if err != nil {
-		return errors.Wrap(err, "no raw data retrieved")
+		return "", errors.Wrap(err, "no raw data retrieved")
 	}
 
 	u := unstructured.Unstructured{}
 	err = u.UnmarshalJSON(data)
 	if err != nil {
-		return errors.Wrap(err, "cannot unmarshal instantiate binary response")
+		return "", errors.Wrap(err, "cannot unmarshal instantiate binary response")
 	}
 
 	ocbuild, err := k8sutil.RuntimeObjectFromUnstructured(&u)
 	if err != nil {
-		return err
+		return "", err
 	}
-	logrus.Info(ocbuild)
 
 	err = kubernetes.WaitCondition(ocbuild, func(obj interface{})(bool, error) {
 		if val, ok := obj.(*buildv1.Build); ok {
@@ -262,7 +261,15 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) error {
 		return false, nil
 	}, 5 * time.Minute)
 
-	return err
+	err = sdk.Get(&is)
+	if err != nil {
+		return "", err
+	}
+
+	if is.Status.DockerImageRepository == "" {
+		return "", errors.New("dockerImageRepository not available in ImageStream")
+	}
+	return is.Status.DockerImageRepository + ":latest", nil
 }
 
 func (b *localBuilder) createTar(buildDir string, source build.BuildSource) (string, error) {
@@ -385,6 +392,22 @@ func (b *localBuilder) createMavenStructure(buildDir string, source build.BuildS
 		return "", err
 	}
 
+	resourcesDir := path.Join(buildDir, "src", "main", "resources")
+	err = os.MkdirAll(resourcesDir, 0777)
+	if err != nil {
+		return "", err
+	}
+	log4jFileName := path.Join(resourcesDir, "log4j2.properties")
+	log4jFile, err := os.Create(log4jFileName)
+	if err != nil {
+		return "", err
+	}
+	defer log4jFile.Close()
+
+	_, err = log4jFile.WriteString(b.log4jFile())
+	if err != nil {
+		return "", err
+	}
 
 	envFileName := path.Join(buildDir, "environment")
 	envFile, err := os.Create(envFileName)
@@ -426,8 +449,32 @@ func (b *localBuilder) createPom() string {
       <artifactId>camel-java-runtime</artifactId>
       <version>1.0-SNAPSHOT</version>
     </dependency>
+	<dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <version>2.11.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>2.11.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <version>2.11.1</version>
+    </dependency>
   </dependencies>
 
 </project>
 `
 }
+
+func (b *localBuilder) log4jFile() string {
+	return `appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.out.ref = out`
+}
\ No newline at end of file
diff --git a/pkg/build/local/local_builder_test.go b/pkg/build/local/local_builder_integration_test.go
similarity index 90%
rename from pkg/build/local/local_builder_test.go
rename to pkg/build/local/local_builder_integration_test.go
index f179682..ff14663 100644
--- a/pkg/build/local/local_builder_test.go
+++ b/pkg/build/local/local_builder_integration_test.go
@@ -1,3 +1,5 @@
+// +build integration
+
 /*
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
@@ -27,7 +29,7 @@ import (
 func TestBuild(t *testing.T) {
 
 	ctx := context.TODO()
-	builder := NewLocalBuilder(ctx, "test")
+	builder := NewLocalBuilder(ctx, test.GetTargetNamespace())
 
 	execution := builder.Build(build.BuildSource{
 		Code: code(),
@@ -41,7 +43,7 @@ func TestBuild(t *testing.T) {
 func TestDoubleBuild(t *testing.T) {
 
 	ctx := context.TODO()
-	builder := NewLocalBuilder(ctx, "test")
+	builder := NewLocalBuilder(ctx, test.GetTargetNamespace())
 
 	execution1 := builder.Build(build.BuildSource{
 		Code: code(),
@@ -61,7 +63,7 @@ func TestDoubleBuild(t *testing.T) {
 func TestFailedBuild(t *testing.T) {
 
 	ctx := context.TODO()
-	builder := NewLocalBuilder(ctx, "test")
+	builder := NewLocalBuilder(ctx, test.GetTargetNamespace())
 
 	execution := builder.Build(build.BuildSource{
 		Code: code() + "-",
diff --git a/pkg/stub/action/action.go b/pkg/stub/action/action.go
index d7558ce..c6d5cc2 100644
--- a/pkg/stub/action/action.go
+++ b/pkg/stub/action/action.go
@@ -23,6 +23,9 @@ import (
 
 type Action interface {
 
+	// a user friendly name for the action
+	Name() string
+
 	// returns true if the action can handle the integration
 	CanHandle(integration *v1alpha1.Integration) bool
 
diff --git a/pkg/stub/action/build.go b/pkg/stub/action/build.go
index ccae198..3b7b942 100644
--- a/pkg/stub/action/build.go
+++ b/pkg/stub/action/build.go
@@ -36,16 +36,20 @@ func NewBuildAction(ctx context.Context, namespace string) *BuildAction {
 	}
 }
 
+func (b *BuildAction) Name() string {
+	return "build"
+}
+
 func (b *BuildAction) CanHandle(integration *v1alpha1.Integration) bool {
 	return integration.Status.Phase == v1alpha1.IntegrationPhaseBuilding
 }
 
 func (b *BuildAction) Handle(integration *v1alpha1.Integration) error {
 
-	buildResult := b.buildManager.Get(integration.Status.Identifier)
+	buildResult := b.buildManager.Get(integration.Status.Hash)
 	if buildResult.Status == api.BuildStatusNotRequested {
 		b.buildManager.Start(api.BuildSource{
-			Identifier: integration.Status.Identifier,
+			Identifier: integration.Status.Hash,
 			Code: *integration.Spec.Source.Code, // FIXME possible panic
 		})
 		logrus.Info("Build started")
@@ -55,6 +59,7 @@ func (b *BuildAction) Handle(integration *v1alpha1.Integration) error {
 		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)
 	}
diff --git a/pkg/stub/action/deploy.go b/pkg/stub/action/deploy.go
new file mode 100644
index 0000000..aa9f0df
--- /dev/null
+++ b/pkg/stub/action/deploy.go
@@ -0,0 +1,111 @@
+/*
+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 action
+
+import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"k8s.io/api/apps/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	corev1 "k8s.io/api/core/v1"
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	"github.com/pkg/errors"
+)
+
+type DeployAction struct {
+}
+
+func NewDeployAction() *DeployAction {
+	return &DeployAction{}
+}
+
+func (b *DeployAction) Name() string {
+	return "deploy"
+}
+
+func (a *DeployAction) CanHandle(integration *v1alpha1.Integration) bool {
+	return integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying
+}
+
+func (a *DeployAction) Handle(integration *v1alpha1.Integration) error {
+
+	deployment := a.getDeploymentFor(integration)
+	err := sdk.Create(deployment)
+	if err != nil && k8serrors.IsAlreadyExists(err) {
+		err = sdk.Update(deployment)
+	}
+
+	if err != nil {
+		return errors.Wrap(err, "could not create or replace deployment for integration " + integration.Name)
+	}
+
+	target := integration.DeepCopy()
+	target.Status.Phase = v1alpha1.IntegrationPhaseRunning
+	return sdk.Update(target)
+}
+
+func (*DeployAction) getDeploymentFor(integration *v1alpha1.Integration) *v1.Deployment {
+	controller := true
+	blockOwnerDeletion := true
+	labels := map[string]string{
+		"camel.apache.org/integration": integration.Name,
+	}
+	deployment := v1.Deployment{
+		TypeMeta: metav1.TypeMeta{
+			Kind: "Deployment",
+			APIVersion: v1.SchemeGroupVersion.String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name: integration.Name,
+			Namespace: integration.Namespace,
+			Labels: integration.Labels,
+			Annotations: integration.Annotations,
+			OwnerReferences: []metav1.OwnerReference{
+				{
+					APIVersion: integration.APIVersion,
+					Kind: integration.Kind,
+					Name: integration.Name,
+					Controller: &controller,
+					BlockOwnerDeletion: &blockOwnerDeletion,
+					UID: integration.UID,
+				},
+			},
+		},
+		Spec: v1.DeploymentSpec{
+			Replicas: integration.Spec.Replicas,
+			Selector: &metav1.LabelSelector{
+				MatchLabels: labels,
+			},
+			Template: corev1.PodTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Labels: labels,
+				},
+				Spec: corev1.PodSpec{
+					Containers: []corev1.Container{
+						{
+							Name: integration.Name,
+							Image: integration.Status.Image,
+						},
+					},
+				},
+			},
+		},
+	}
+
+	return &deployment
+}
diff --git a/pkg/stub/action/initialize.go b/pkg/stub/action/initialize.go
index 60f8dc6..52d8db9 100644
--- a/pkg/stub/action/initialize.go
+++ b/pkg/stub/action/initialize.go
@@ -33,13 +33,23 @@ func NewInitializeAction() *InitializeAction {
 	return &InitializeAction{}
 }
 
+func (b *InitializeAction) Name() string {
+	return "initialize"
+}
+
 func (b *InitializeAction) CanHandle(integration *v1alpha1.Integration) bool {
 	return integration.Status.Phase == ""
 }
 
 func (b *InitializeAction) Handle(integration *v1alpha1.Integration) error {
 	target := integration.DeepCopy()
+	// set default values
+	var defaultReplicas int32 = 1
+	if target.Spec.Replicas == nil {
+		target.Spec.Replicas = &defaultReplicas
+	}
+	// update the status
 	target.Status.Phase = v1alpha1.IntegrationPhaseBuilding
-	target.Status.Identifier = strconv.Itoa(rand.Int()) // TODO replace with hash
+	target.Status.Hash = strconv.Itoa(rand.Int()) // TODO replace with hash
 	return sdk.Update(target)
 }
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
index 7c52247..ca46e54 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/handler.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
 	"github.com/apache/camel-k/pkg/stub/action"
+	"github.com/sirupsen/logrus"
 )
 
 func NewHandler(ctx context.Context, namespace string) sdk.Handler {
@@ -31,6 +32,7 @@ func NewHandler(ctx context.Context, namespace string) sdk.Handler {
 		actionPool: []action.Action{
 			action.NewInitializeAction(),
 			action.NewBuildAction(ctx, namespace),
+			action.NewDeployAction(),
 		},
 	}
 }
@@ -44,6 +46,7 @@ func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
 	case *v1alpha1.Integration:
 		for _, a := range h.actionPool {
 			if a.CanHandle(o) {
+				logrus.Info("Invoking action ", a.Name(), " on integration ", o.Name)
 				if err := a.Handle(o); err != nil {
 					return err
 				}
diff --git a/pkg/stub/action/action.go b/pkg/util/test/testing_env.go
similarity index 72%
copy from pkg/stub/action/action.go
copy to pkg/util/test/testing_env.go
index d7558ce..16e7e13 100644
--- a/pkg/stub/action/action.go
+++ b/pkg/util/test/testing_env.go
@@ -15,18 +15,14 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package action
+package test
 
-import (
-	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-)
-
-type Action interface {
+import "os"
 
-	// returns true if the action can handle the integration
-	CanHandle(integration *v1alpha1.Integration) bool
-
-	// executes the handling function
-	Handle(integration *v1alpha1.Integration) error
+const (
+	EnvWatchNamespace = "WATCH_NAMESPACE"
+)
 
+func GetTargetNamespace() string {
+	return os.Getenv(EnvWatchNamespace)
 }