camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nferr...@apache.org
Subject [camel-k] 01/04: Refactored build module into assembler and publisher
Date Fri, 21 Sep 2018 13:56:56 GMT
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 0047930e30b2429af3b2540d70da17d99f4b09c9
Author: nferraro <ni.ferraro@gmail.com>
AuthorDate: Fri Sep 21 11:25:41 2018 +0200

    Refactored build module into assembler and publisher
---
 Gopkg.lock                                         |   9 +
 pkg/build/assemble/doc.go                          |  19 +++
 pkg/build/assemble/maven_assembler.go              | 171 +++++++++++++++++++
 .../maven_assembler_test.go}                       |   2 +-
 pkg/build/build_manager.go                         |  87 ++++++++--
 pkg/build/build_types.go                           |  25 ++-
 pkg/build/publish/doc.go                           |  19 +++
 .../local_builder.go => publish/s2i_publisher.go}  | 183 ++++++++-------------
 pkg/stub/action/context/build.go                   |   8 +-
 pkg/util/maven/maven.go                            | 112 ++-----------
 pkg/util/tar/appender.go                           | 106 ++++++++++++
 test/build_manager_integration_test.go             |  15 +-
 test/local_builder_integration_test.go             | 107 ------------
 13 files changed, 513 insertions(+), 350 deletions(-)

diff --git a/Gopkg.lock b/Gopkg.lock
index 4c8e50f..0975b14 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -493,6 +493,14 @@
   version = "v0.9.1"
 
 [[projects]]
+  branch = "v1"
+  digest = "1:52ffb9db0286de37253a5098607cfbcfcdc94e51e8c226da120513df82adab0c"
+  name = "gopkg.in/yaml.v1"
+  packages = ["."]
+  pruneopts = "NUT"
+  revision = "9f9df34309c04878acc86042b16630b0f696e1de"
+
+[[projects]]
   digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082"
   name = "gopkg.in/yaml.v2"
   packages = ["."]
@@ -733,6 +741,7 @@
     "github.com/spf13/cobra",
     "github.com/stoewer/go-strcase",
     "github.com/stretchr/testify/assert",
+    "gopkg.in/yaml.v1",
     "gopkg.in/yaml.v2",
     "k8s.io/api/apps/v1",
     "k8s.io/api/core/v1",
diff --git a/pkg/build/assemble/doc.go b/pkg/build/assemble/doc.go
new file mode 100644
index 0000000..20b1d42
--- /dev/null
+++ b/pkg/build/assemble/doc.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 assemble contains tools that convert source files and dependencies into the integration
classpath
+package assemble
diff --git a/pkg/build/assemble/maven_assembler.go b/pkg/build/assemble/maven_assembler.go
new file mode 100644
index 0000000..9094181
--- /dev/null
+++ b/pkg/build/assemble/maven_assembler.go
@@ -0,0 +1,171 @@
+/*
+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 assemble
+
+import (
+	"context"
+	"encoding/xml"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/apache/camel-k/pkg/build"
+	"github.com/apache/camel-k/pkg/util/maven"
+	"github.com/sirupsen/logrus"
+
+	// import openshift utilities
+	_ "github.com/apache/camel-k/pkg/util/openshift"
+	"github.com/apache/camel-k/version"
+)
+
+type mavenAssembler struct {
+	buffer chan assembleOperation
+}
+
+type assembleOperation struct {
+	request build.Request
+	output  chan build.AssembledOutput
+}
+
+// NewMavenAssembler create a new builder
+func NewMavenAssembler(ctx context.Context) build.Assembler {
+	assembler := mavenAssembler{
+		buffer: make(chan assembleOperation, 100),
+	}
+	go assembler.assembleCycle(ctx)
+	return &assembler
+}
+
+func (b *mavenAssembler) Assemble(request build.Request) <-chan build.AssembledOutput
{
+	res := make(chan build.AssembledOutput, 1)
+	op := assembleOperation{
+		request: request,
+		output:  res,
+	}
+	b.buffer <- op
+	return res
+}
+
+func (b *mavenAssembler) assembleCycle(ctx context.Context) {
+	for {
+		select {
+		case <-ctx.Done():
+			b.buffer = nil
+			return
+		case op := <-b.buffer:
+			now := time.Now()
+			logrus.Info("Starting new Maven build")
+			res := b.execute(&op.request)
+			elapsed := time.Now().Sub(now)
+
+			if res.Error != nil {
+				logrus.Error("Error during Maven build (total time ", elapsed.Seconds(), " seconds):
", res.Error)
+			} else {
+				logrus.Info("Maven build completed in ", elapsed.Seconds(), " seconds")
+			}
+
+			op.output <- res
+		}
+	}
+}
+
+func (b *mavenAssembler) execute(request *build.Request) build.AssembledOutput {
+	project, err := generateProject(request)
+	if err != nil {
+		return build.AssembledOutput{
+			Error: err,
+		}
+	}
+
+	res, err := maven.Process(project)
+	if err != nil {
+		return build.AssembledOutput{
+			Error: err,
+		}
+	}
+
+	output := build.AssembledOutput{
+		Classpath: make([]build.ClasspathEntry, 0, len(res.Classpath)),
+	}
+	for _, e := range res.Classpath {
+		output.Classpath = append(output.Classpath, build.ClasspathEntry{
+			ID:       e.ID,
+			Location: e.Location,
+		})
+	}
+
+	return output
+}
+
+func generateProject(source *build.Request) (maven.Project, error) {
+	project := maven.Project{
+		XMLName:           xml.Name{Local: "project"},
+		XMLNs:             "http://maven.apache.org/POM/4.0.0",
+		XMLNsXsi:          "http://www.w3.org/2001/XMLSchema-instance",
+		XsiSchemaLocation: "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd",
+		ModelVersion:      "4.0.0",
+		GroupID:           "org.apache.camel.k.integration",
+		ArtifactID:        "camel-k-integration",
+		Version:           version.Version,
+		DependencyManagement: maven.DependencyManagement{
+			Dependencies: maven.Dependencies{
+				Dependencies: []maven.Dependency{
+					{
+						//TODO: camel version should be retrieved from an external request or provided as static
version
+						GroupID:    "org.apache.camel",
+						ArtifactID: "camel-bom",
+						Version:    "2.22.1",
+						Type:       "pom",
+						Scope:      "import",
+					},
+				},
+			},
+		},
+		Dependencies: maven.Dependencies{
+			Dependencies: make([]maven.Dependency, 0),
+		},
+	}
+
+	//
+	// set-up dependencies
+	//
+
+	deps := &project.Dependencies
+	deps.AddGAV("org.apache.camel.k", "camel-k-runtime-jvm", version.Version)
+
+	for _, d := range source.Dependencies {
+		if strings.HasPrefix(d, "camel:") {
+			artifactID := strings.TrimPrefix(d, "camel:")
+
+			if !strings.HasPrefix(artifactID, "camel-") {
+				artifactID = "camel-" + artifactID
+			}
+
+			deps.AddGAV("org.apache.camel", artifactID, "")
+		} else if strings.HasPrefix(d, "mvn:") {
+			mid := strings.TrimPrefix(d, "mvn:")
+			gav := strings.Replace(mid, "/", ":", -1)
+
+			deps.AddEncodedGAV(gav)
+		} else {
+			return maven.Project{}, fmt.Errorf("unknown dependency type: %s", d)
+		}
+	}
+
+	return project, nil
+}
diff --git a/pkg/build/local/local_builder_test.go b/pkg/build/assemble/maven_assembler_test.go
similarity index 99%
rename from pkg/build/local/local_builder_test.go
rename to pkg/build/assemble/maven_assembler_test.go
index ddb928e..20de3d8 100644
--- a/pkg/build/local/local_builder_test.go
+++ b/pkg/build/assemble/maven_assembler_test.go
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package local
+package assemble
 
 import (
 	"testing"
diff --git a/pkg/build/build_manager.go b/pkg/build/build_manager.go
index 50d1187..895575b 100644
--- a/pkg/build/build_manager.go
+++ b/pkg/build/build_manager.go
@@ -19,23 +19,24 @@ package build
 
 import (
 	"context"
+	"errors"
 	"sync"
 )
 
 // Manager represent the main facade to the image build system
 type Manager struct {
-	builds    sync.Map
 	ctx       context.Context
-	namespace string
-	builder   Builder
+	builds    sync.Map
+	assembler Assembler
+	publisher Publisher
 }
 
 // NewManager creates an instance of the build manager using the given builder
-func NewManager(ctx context.Context, namespace string, builder Builder) *Manager {
+func NewManager(ctx context.Context, assembler Assembler, publisher Publisher) *Manager {
 	return &Manager{
 		ctx:       ctx,
-		namespace: namespace,
-		builder:   builder,
+		assembler: assembler,
+		publisher: publisher,
 	}
 }
 
@@ -46,18 +47,41 @@ func (m *Manager) Get(identifier Identifier) Result {
 		return noBuildInfo()
 	}
 
-	return *info.(*Result)
+	return info.(Result)
 }
 
 // Start starts a new build
-func (m *Manager) Start(source Request) {
-	initialBuildInfo := initialBuildInfo(&source)
-	m.builds.Store(source.Identifier, &initialBuildInfo)
+func (m *Manager) Start(request Request) {
+	m.builds.Store(request.Identifier, initialBuildInfo(request))
 
-	resChannel := m.builder.Build(source)
+	assembleChannel := m.assembler.Assemble(request)
 	go func() {
-		res := <-resChannel
-		m.builds.Store(res.Request.Identifier, &res)
+		var assembled AssembledOutput
+		select {
+		case <-m.ctx.Done():
+			m.builds.Store(request.Identifier, canceledBuildInfo(request))
+			return
+		case assembled = <-assembleChannel:
+			if assembled.Error != nil {
+				m.builds.Store(request.Identifier, failedAssembleBuildInfo(request, assembled))
+				return
+			}
+		}
+
+		publishChannel := m.publisher.Publish(request, assembled)
+		var published PublishedOutput
+		select {
+		case <-m.ctx.Done():
+			m.builds.Store(request.Identifier, canceledBuildInfo(request))
+			return
+		case published = <-publishChannel:
+			if published.Error != nil {
+				m.builds.Store(request.Identifier, failedPublishBuildInfo(request, published))
+				return
+			}
+		}
+
+		m.builds.Store(request.Identifier, completeResult(request, assembled, published))
 	}()
 }
 
@@ -67,9 +91,42 @@ func noBuildInfo() Result {
 	}
 }
 
-func initialBuildInfo(source *Request) Result {
+func initialBuildInfo(request Request) Result {
 	return Result{
-		Request: source,
+		Request: request,
 		Status:  StatusStarted,
 	}
 }
+
+func canceledBuildInfo(request Request) Result {
+	return Result{
+		Request: request,
+		Error:   errors.New("build canceled"),
+		Status:  StatusError,
+	}
+}
+
+func failedAssembleBuildInfo(request Request, output AssembledOutput) Result {
+	return Result{
+		Request: request,
+		Error:   output.Error,
+		Status:  StatusError,
+	}
+}
+
+func failedPublishBuildInfo(request Request, output PublishedOutput) Result {
+	return Result{
+		Request: request,
+		Error:   output.Error,
+		Status:  StatusError,
+	}
+}
+
+func completeResult(request Request, a AssembledOutput, p PublishedOutput) Result {
+	return Result{
+		Request:   request,
+		Status:    StatusCompleted,
+		Classpath: a.Classpath,
+		Image:     p.Image,
+	}
+}
diff --git a/pkg/build/build_types.go b/pkg/build/build_types.go
index dc7c787..22ff178 100644
--- a/pkg/build/build_types.go
+++ b/pkg/build/build_types.go
@@ -39,7 +39,7 @@ type Source struct {
 
 // Result represents the result of a build
 type Result struct {
-	Request   *Request
+	Request   Request
 	Status    Status
 	Image     string
 	Error     error
@@ -52,9 +52,26 @@ type ClasspathEntry struct {
 	Location string `json:"location,omitempty" yaml:"location,omitempty"`
 }
 
-// Builder is supertype of all builders
-type Builder interface {
-	Build(Request) <-chan Result
+// AssembledOutput represents the output of the assemble phase
+type AssembledOutput struct {
+	Error     error
+	Classpath []ClasspathEntry
+}
+
+// A Assembler can be used to compute the classpath of a integration context
+type Assembler interface {
+	Assemble(Request) <-chan AssembledOutput
+}
+
+// PublishedOutput is the output of the publish phase
+type PublishedOutput struct {
+	Error error
+	Image string
+}
+
+// A Publisher publishes a docker image of a build request
+type Publisher interface {
+	Publish(Request, AssembledOutput) <-chan PublishedOutput
 }
 
 // Status --
diff --git a/pkg/build/publish/doc.go b/pkg/build/publish/doc.go
new file mode 100644
index 0000000..19343a8
--- /dev/null
+++ b/pkg/build/publish/doc.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 publish contains strategies for publishing integrations into a Docker registries
+package publish
diff --git a/pkg/build/local/local_builder.go b/pkg/build/publish/s2i_publisher.go
similarity index 58%
rename from pkg/build/local/local_builder.go
rename to pkg/build/publish/s2i_publisher.go
index 232360b..5548194 100644
--- a/pkg/build/local/local_builder.go
+++ b/pkg/build/publish/s2i_publisher.go
@@ -15,67 +15,66 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package local
+package publish
 
 import (
 	"context"
-	"encoding/xml"
-	"fmt"
-	"io/ioutil"
-	"strings"
-	"time"
-
+	"github.com/apache/camel-k/pkg/build"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/kubernetes/customclient"
+	"github.com/apache/camel-k/pkg/util/maven"
+	"github.com/apache/camel-k/pkg/util/tar"
 	buildv1 "github.com/openshift/api/build/v1"
 	imagev1 "github.com/openshift/api/image/v1"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
 	"github.com/operator-framework/operator-sdk/pkg/util/k8sutil"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
+	"io/ioutil"
 	"k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"path"
+	"time"
+)
 
-	"github.com/apache/camel-k/pkg/build"
-	"github.com/apache/camel-k/pkg/util/kubernetes"
-	"github.com/apache/camel-k/pkg/util/kubernetes/customclient"
-	"github.com/apache/camel-k/pkg/util/maven"
-
-	// import openshift utilities
-	_ "github.com/apache/camel-k/pkg/util/openshift"
-	"github.com/apache/camel-k/version"
+const (
+	artifactDirPrefix = "s2i-"
 )
 
-type localBuilder struct {
-	buffer    chan buildOperation
+type s2iPublisher struct {
+	buffer    chan publishOperation
 	namespace string
 }
 
-type buildOperation struct {
-	request build.Request
-	output  chan build.Result
+type publishOperation struct {
+	request   build.Request
+	assembled build.AssembledOutput
+	output    chan build.PublishedOutput
 }
 
-// NewLocalBuilder create a new builder
-func NewLocalBuilder(ctx context.Context, namespace string) build.Builder {
-	builder := localBuilder{
-		buffer:    make(chan buildOperation, 100),
+// NewS2IPublisher creates a new publisher doing a Openshift S2I binary build
+func NewS2IPublisher(ctx context.Context, namespace string) build.Publisher {
+	publisher := s2iPublisher{
+		buffer:    make(chan publishOperation, 100),
 		namespace: namespace,
 	}
-	go builder.buildCycle(ctx)
-	return &builder
+	go publisher.publishCycle(ctx)
+	return &publisher
 }
 
-func (b *localBuilder) Build(request build.Request) <-chan build.Result {
-	res := make(chan build.Result, 1)
-	op := buildOperation{
-		request: request,
-		output:  res,
+func (b *s2iPublisher) Publish(request build.Request, assembled build.AssembledOutput) <-chan
build.PublishedOutput {
+	res := make(chan build.PublishedOutput, 1)
+	op := publishOperation{
+		request:   request,
+		assembled: assembled,
+		output:    res,
 	}
 	b.buffer <- op
 	return res
 }
 
-func (b *localBuilder) buildCycle(ctx context.Context) {
+func (b *s2iPublisher) publishCycle(ctx context.Context) {
 	for {
 		select {
 		case <-ctx.Done():
@@ -83,14 +82,14 @@ func (b *localBuilder) buildCycle(ctx context.Context) {
 			return
 		case op := <-b.buffer:
 			now := time.Now()
-			logrus.Info("Starting new build")
-			res := b.execute(&op.request)
+			logrus.Info("Starting a new image publication")
+			res := b.execute(op.request, op.assembled)
 			elapsed := time.Now().Sub(now)
 
 			if res.Error != nil {
-				logrus.Error("Error during build (total time ", elapsed.Seconds(), " seconds): ", res.Error)
+				logrus.Error("Error during publication (total time ", elapsed.Seconds(), " seconds):
", res.Error)
 			} else {
-				logrus.Info("Process completed in ", elapsed.Seconds(), " seconds")
+				logrus.Info("Publication completed in ", elapsed.Seconds(), " seconds")
 			}
 
 			op.output <- res
@@ -98,43 +97,22 @@ func (b *localBuilder) buildCycle(ctx context.Context) {
 	}
 }
 
-func (b *localBuilder) execute(request *build.Request) build.Result {
-	project, err := generateProject(request)
+func (b *s2iPublisher) execute(request build.Request, assembled build.AssembledOutput) build.PublishedOutput
{
+	tarFile, err := b.createTar(assembled)
 	if err != nil {
-		return build.Result{
-			Error:  err,
-			Status: build.StatusError,
-		}
+		return build.PublishedOutput{Error: err}
 	}
 
-	res, err := maven.Process(project)
+	image, err := b.publish(tarFile, request)
 	if err != nil {
-		return build.Result{
-			Error:  err,
-			Status: build.StatusError,
-		}
+		return build.PublishedOutput{Error: err}
 	}
 
-	logrus.Info("Created tar file ", res.TarFilePath)
-
-	image, err := b.publish(res.TarFilePath, request)
-	if err != nil {
-		return build.Result{
-			Error:  errors.Wrap(err, "could not publish docker image"),
-			Status: build.StatusError,
-		}
-	}
-
-	return build.Result{
-		Request:   request,
-		Image:     image,
-		Error:     nil,
-		Status:    build.StatusCompleted,
-		Classpath: res.Classpath,
-	}
+	return build.PublishedOutput{Image: image}
 }
 
-func (b *localBuilder) publish(tarFile string, source *build.Request) (string, error) {
+func (b *s2iPublisher) publish(tarFile string, source build.Request) (string, error) {
+
 	bc := buildv1.BuildConfig{
 		TypeMeta: metav1.TypeMeta{
 			APIVersion: buildv1.SchemeGroupVersion.String(),
@@ -257,60 +235,39 @@ func (b *localBuilder) publish(tarFile string, source *build.Request)
(string, e
 	return is.Status.DockerImageRepository + ":" + source.Identifier.Qualifier, nil
 }
 
-func generateProject(source *build.Request) (maven.Project, error) {
-	project := maven.Project{
-		XMLName:           xml.Name{Local: "project"},
-		XMLNs:             "http://maven.apache.org/POM/4.0.0",
-		XMLNsXsi:          "http://www.w3.org/2001/XMLSchema-instance",
-		XsiSchemaLocation: "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd",
-		ModelVersion:      "4.0.0",
-		GroupID:           "org.apache.camel.k.integration",
-		ArtifactID:        "camel-k-integration",
-		Version:           version.Version,
-		DependencyManagement: maven.DependencyManagement{
-			Dependencies: maven.Dependencies{
-				Dependencies: []maven.Dependency{
-					{
-						//TODO: camel version should be retrieved from an external request or provided as static
version
-						GroupID:    "org.apache.camel",
-						ArtifactID: "camel-bom",
-						Version:    "2.22.1",
-						Type:       "pom",
-						Scope:      "import",
-					},
-				},
-			},
-		},
-		Dependencies: maven.Dependencies{
-			Dependencies: make([]maven.Dependency, 0),
-		},
+func (b *s2iPublisher) createTar(assembled build.AssembledOutput) (string, error) {
+	artifactDir, err := ioutil.TempDir("", artifactDirPrefix)
+	if err != nil {
+		return "", errors.Wrap(err, "could not create temporary dir for s2i artifacts")
 	}
 
-	//
-	// set-up dependencies
-	//
-
-	deps := &project.Dependencies
-	deps.AddGAV("org.apache.camel.k", "camel-k-runtime-jvm", version.Version)
+	tarFileName := path.Join(artifactDir, "occi.tar")
+	tarAppender, err := tar.NewAppender(tarFileName)
+	if err != nil {
+		return "", err
+	}
+	defer tarAppender.Close()
 
-	for _, d := range source.Dependencies {
-		if strings.HasPrefix(d, "camel:") {
-			artifactID := strings.TrimPrefix(d, "camel:")
+	cp := ""
+	for _, entry := range assembled.Classpath {
+		gav, err := maven.ParseGAV(entry.ID)
+		if err != nil {
+			return "", nil
+		}
 
-			if !strings.HasPrefix(artifactID, "camel-") {
-				artifactID = "camel-" + artifactID
-			}
+		tarPath := path.Join("dependencies/", gav.GroupID)
+		fileName, err := tarAppender.AddFile(entry.Location, tarPath)
+		if err != nil {
+			return "", err
+		}
 
-			deps.AddGAV("org.apache.camel", artifactID, "")
-		} else if strings.HasPrefix(d, "mvn:") {
-			mid := strings.TrimPrefix(d, "mvn:")
-			gav := strings.Replace(mid, "/", ":", -1)
+		cp += fileName + "\n"
+	}
 
-			deps.AddEncodedGAV(gav)
-		} else {
-			return maven.Project{}, fmt.Errorf("unknown dependency type: %s", d)
-		}
+	err = tarAppender.AppendData([]byte(cp), "classpath")
+	if err != nil {
+		return "", err
 	}
 
-	return project, nil
+	return tarFileName, nil
 }
diff --git a/pkg/stub/action/context/build.go b/pkg/stub/action/context/build.go
index 9d5d3b1..a815c7c 100644
--- a/pkg/stub/action/context/build.go
+++ b/pkg/stub/action/context/build.go
@@ -19,6 +19,8 @@ package action
 
 import (
 	"context"
+	"github.com/apache/camel-k/pkg/build/assemble"
+	"github.com/apache/camel-k/pkg/build/publish"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -27,13 +29,13 @@ import (
 
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/build"
-	"github.com/apache/camel-k/pkg/build/local"
 )
 
 // NewIntegrationContextBuildAction creates a new build handling action for the context
 func NewIntegrationContextBuildAction(ctx context.Context, namespace string) IntegrationContextAction
{
-	builder := local.NewLocalBuilder(ctx, namespace)
-	manager := build.NewManager(ctx, namespace, builder)
+	assembler := assemble.NewMavenAssembler(ctx)
+	publisher := publish.NewS2IPublisher(ctx, namespace)
+	manager := build.NewManager(ctx, assembler, publisher)
 
 	return &integrationContextBuildAction{
 		buildManager: manager,
diff --git a/pkg/util/maven/maven.go b/pkg/util/maven/maven.go
index 1920be8..e2a9b35 100644
--- a/pkg/util/maven/maven.go
+++ b/pkg/util/maven/maven.go
@@ -18,11 +18,9 @@ limitations under the License.
 package maven
 
 import (
-	"archive/tar"
 	"bytes"
 	"encoding/xml"
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"os/exec"
@@ -32,7 +30,6 @@ import (
 
 	"github.com/apache/camel-k/version"
 
-	"github.com/apache/camel-k/pkg/build"
 	"gopkg.in/yaml.v2"
 
 	"github.com/pkg/errors"
@@ -41,13 +38,18 @@ import (
 
 const (
 	buildDirPrefix    = "maven-"
-	artifactDirPrefix = "maven-bin-"
+
 )
 
 // BuildResult --
 type BuildResult struct {
-	TarFilePath string
-	Classpath   []build.ClasspathEntry
+	Classpath []ClasspathLibrary
+}
+
+// ClasspathLibrary --
+type ClasspathLibrary struct {
+	ID       string `json:"id" yaml:"id"`
+	Location string `json:"location,omitempty" yaml:"location,omitempty"`
 }
 
 // Process takes a project description and returns a binary tar with the built artifacts
@@ -65,16 +67,7 @@ func Process(project Project) (BuildResult, error) {
 		return res, errors.Wrap(err, "could not write maven source files")
 	}
 	err = runMavenBuild(buildDir, &res)
-	if err != nil {
-		return res, err
-	}
-
-	res.TarFilePath, err = createTar(project, &res)
-	if err != nil {
-		return res, err
-	}
-
-	return res, nil
+	return res, err
 }
 
 func runMavenBuild(buildDir string, result *BuildResult) error {
@@ -95,7 +88,7 @@ func runMavenBuild(buildDir string, result *BuildResult) error {
 		return err
 	}
 
-	cp := make(map[string][]build.ClasspathEntry)
+	cp := make(map[string][]ClasspathLibrary)
 	if err := yaml.Unmarshal(content, &cp); err != nil {
 		return err
 	}
@@ -113,91 +106,6 @@ func mavenExtraOptions() string {
 	return "-Dcamel.noop=true"
 }
 
-func createTar(project Project, result *BuildResult) (string, error) {
-	artifactDir, err := ioutil.TempDir("", artifactDirPrefix)
-	if err != nil {
-		return "", errors.Wrap(err, "could not create temporary dir for maven artifacts")
-	}
-
-	tarFileName := path.Join(artifactDir, project.ArtifactID+".tar")
-	tarFile, err := os.Create(tarFileName)
-	if err != nil {
-		return "", errors.Wrap(err, "cannot create tar file "+tarFileName)
-	}
-	defer tarFile.Close()
-
-	writer := tar.NewWriter(tarFile)
-	defer writer.Close()
-
-	cp := ""
-	for _, entry := range result.Classpath {
-		gav, err := ParseGAV(entry.ID)
-		if err != nil {
-			return "", nil
-		}
-
-		tarPath := path.Join("dependencies/", gav.GroupID)
-		fileName, err := appendFileToTar(entry.Location, tarPath, writer)
-		if err != nil {
-			return "", err
-		}
-
-		cp += fileName + "\n"
-	}
-
-	err = appendDataToTar([]byte(cp), "classpath", writer)
-	if err != nil {
-		return "", err
-	}
-
-	return tarFileName, nil
-}
-
-func appendFileToTar(filePath string, tarPath string, writer *tar.Writer) (string, 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 fileName, nil
-}
-
-func appendDataToTar(data []byte, tarPath string, writer *tar.Writer) error {
-	writer.WriteHeader(&tar.Header{
-		Name: tarPath,
-		Size: int64(len(data)),
-		Mode: 0644,
-	})
-
-	_, err := writer.Write(data)
-	if err != nil {
-		return errors.Wrap(err, "cannot add data to the tar archive")
-	}
-	return nil
-}
-
 func createMavenStructure(buildDir string, project Project) error {
 	pom, err := GeneratePomFileContent(project)
 	if err != nil {
diff --git a/pkg/util/tar/appender.go b/pkg/util/tar/appender.go
new file mode 100644
index 0000000..3e4aebf
--- /dev/null
+++ b/pkg/util/tar/appender.go
@@ -0,0 +1,106 @@
+/*
+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 tar
+
+import (
+	atar "archive/tar"
+	"github.com/pkg/errors"
+	"io"
+	"os"
+	"path"
+)
+
+// Appender provides a high level abstraction over writing tar files
+type Appender struct {
+	tarFile *os.File
+	writer  *atar.Writer
+}
+
+// NewAppender creates a new tar appender
+func NewAppender(fileName string) (*Appender, error) {
+	tarFile, err := os.Create(fileName)
+	if err != nil {
+		return nil, errors.Wrap(err, "cannot create tar file "+fileName)
+	}
+
+	writer := atar.NewWriter(tarFile)
+	appender := Appender{
+		tarFile: tarFile,
+		writer:  writer,
+	}
+	return &appender, nil
+}
+
+// Close closes all handles managed by the appender
+func (t *Appender) Close() error {
+	if err := t.writer.Close(); err != nil {
+		return err
+	}
+	if err := t.tarFile.Close(); err != nil {
+		return err
+	}
+	return nil
+}
+
+// AddFile adds a file content to the tarDir, using the original file name.
+// It returns the full path of the file inside the tar.
+func (t *Appender) AddFile(filePath string, tarDir string) (string, error) {
+	info, err := os.Stat(filePath)
+	if err != nil {
+		return "", err
+	}
+	_, fileName := path.Split(filePath)
+	if tarDir != "" {
+		fileName = path.Join(tarDir, fileName)
+	}
+
+	t.writer.WriteHeader(&atar.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(t.writer, file)
+	if err != nil {
+		return "", errors.Wrap(err, "cannot add file to the tar archive")
+	}
+
+	return fileName, nil
+}
+
+// AppendData appends the given content to a file inside the tar, creating it if it does
not exist
+func (t *Appender) AppendData(data []byte, tarPath string) error {
+	t.writer.WriteHeader(&atar.Header{
+		Name: tarPath,
+		Size: int64(len(data)),
+		Mode: 0644,
+	})
+
+	_, err := t.writer.Write(data)
+	if err != nil {
+		return errors.Wrap(err, "cannot add data to the tar archive")
+	}
+	return nil
+}
diff --git a/test/build_manager_integration_test.go b/test/build_manager_integration_test.go
index 9ba22f0..fa7d8f8 100644
--- a/test/build_manager_integration_test.go
+++ b/test/build_manager_integration_test.go
@@ -23,7 +23,8 @@ package test
 
 import (
 	"context"
-	"github.com/apache/camel-k/pkg/build/local"
+	"github.com/apache/camel-k/pkg/build/assemble"
+	"github.com/apache/camel-k/pkg/build/publish"
 	"testing"
 	"time"
 
@@ -35,9 +36,11 @@ import (
 func TestBuildManagerBuild(t *testing.T) {
 	ctx := context.TODO()
 	namespace := getTargetNamespace()
-	buildManager := build.NewManager(ctx, namespace, local.NewLocalBuilder(ctx, namespace))
+	assembler := assemble.NewMavenAssembler(ctx)
+	publisher := publish.NewS2IPublisher(ctx, namespace)
+	buildManager := build.NewManager(ctx, assembler, publisher)
 	identifier := build.Identifier{
-		Name:   "man-test",
+		Name:      "man-test",
 		Qualifier: digest.Random(),
 	}
 	buildManager.Start(build.Request{
@@ -70,9 +73,11 @@ func TestBuildManagerFailedBuild(t *testing.T) {
 
 	ctx := context.TODO()
 	namespace := getTargetNamespace()
-	buildManager := build.NewManager(ctx, namespace, local.NewLocalBuilder(ctx, namespace))
+	assembler := assemble.NewMavenAssembler(ctx)
+	publisher := publish.NewS2IPublisher(ctx, namespace)
+	buildManager := build.NewManager(ctx, assembler, publisher)
 	identifier := build.Identifier{
-		Name:   "man-test-2",
+		Name:      "man-test-2",
 		Qualifier: digest.Random(),
 	}
 	buildManager.Start(build.Request{
diff --git a/test/local_builder_integration_test.go b/test/local_builder_integration_test.go
deleted file mode 100644
index c3f35a2..0000000
--- a/test/local_builder_integration_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-// +build integration
-
-// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring
& Build Tags -> Custom Tags" and add "integration"
-
-/*
-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 test
-
-import (
-	"context"
-	"testing"
-
-	"github.com/apache/camel-k/pkg/build"
-	"github.com/apache/camel-k/pkg/util/digest"
-	"github.com/stretchr/testify/assert"
-	"github.com/apache/camel-k/pkg/build/local"
-)
-
-func TestLocalBuild(t *testing.T) {
-
-	ctx := context.TODO()
-	builder := local.NewLocalBuilder(ctx, getTargetNamespace())
-
-	execution := builder.Build(build.Request{
-		Identifier: build.Identifier{
-			Name:      "test0",
-			Qualifier: digest.Random(),
-		},
-		Code: build.Source{
-			Content: createTimerToLogIntegrationCode(),
-		},
-	})
-
-	res := <-execution
-
-	assert.Nil(t, res.Error, "Build failed")
-}
-
-func TestLocalDoubleBuild(t *testing.T) {
-
-	ctx := context.TODO()
-	builder := local.NewLocalBuilder(ctx, getTargetNamespace())
-
-	execution1 := builder.Build(build.Request{
-		Identifier: build.Identifier{
-			Name:      "test1",
-			Qualifier: digest.Random(),
-		},
-		Code: build.Source{
-			Content: createTimerToLogIntegrationCode(),
-		},
-	})
-
-	execution2 := builder.Build(build.Request{
-		Identifier: build.Identifier{
-			Name:      "test2",
-			Qualifier: digest.Random(),
-		},
-		Code: build.Source{
-			Content: createTimerToLogIntegrationCode(),
-		},
-	})
-
-	res1 := <-execution1
-	res2 := <-execution2
-
-	assert.Nil(t, res1.Error, "Build failed")
-	assert.Nil(t, res2.Error, "Build failed")
-}
-
-func TestLocalFailedBuild(t *testing.T) {
-
-	ctx := context.TODO()
-	builder := local.NewLocalBuilder(ctx, getTargetNamespace())
-
-	execution := builder.Build(build.Request{
-		Identifier: build.Identifier{
-			Name:      "test3",
-			Qualifier: digest.Random(),
-		},
-		Code: build.Source{
-			Content: createTimerToLogIntegrationCode(),
-		},
-		Dependencies: []string{
-			"camel:cippalippa",
-		},
-	})
-
-	res := <-execution
-
-	assert.NotNil(t, res.Error, "Build should fail")
-}


Mime
View raw message