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.ferr...@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

Reply via email to