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")
-}
|