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 b045295a159709b7db20180cf4a2c004760bd0ba Author: nferraro <ni.ferr...@gmail.com> AuthorDate: Fri Sep 7 17:52:49 2018 +0200 Refactor maven builder into its own module --- Sample.java | 2 +- build/Makefile | 7 +- pkg/build/local/local_builder.go | 235 ++++++----------------------------- pkg/client/cmd/run/run.go | 14 ++- pkg/util/maven/maven.go | 256 +++++++++++++++++++++++++++++++++++++++ pkg/util/maven/types.go | 34 ++++++ 6 files changed, 349 insertions(+), 199 deletions(-) diff --git a/Sample.java b/Sample.java index 47f59e9..2d73b92 100644 --- a/Sample.java +++ b/Sample.java @@ -7,7 +7,7 @@ public class Routes extends RouteBuilder { @Override public void configure() throws Exception { from("timer:tick") - .setBody(constant("Hello Kamel!!!")) + .setBody(constant("Hello! Camel K rocks!!!")) .to("log:info"); } diff --git a/build/Makefile b/build/Makefile index 2d2aee8..d8d2b7a 100644 --- a/build/Makefile +++ b/build/Makefile @@ -14,9 +14,14 @@ build-runtime: codegen: ./tmp/codegen/update-generated.sh -images: +images: images-build + +images-build: operator-sdk build docker.io/apache/camel-k:$(VERSION) +images-push: + docker push docker.io/apache/camel-k:$(VERSION) + test: check check: go test ./... diff --git a/pkg/build/local/local_builder.go b/pkg/build/local/local_builder.go index 478a810..e5f85ba 100644 --- a/pkg/build/local/local_builder.go +++ b/pkg/build/local/local_builder.go @@ -22,12 +22,7 @@ import ( "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" @@ -43,28 +38,29 @@ import ( build "github.com/apache/camel-k/pkg/build/api" "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/apache/camel-k/version" + "github.com/apache/camel-k/pkg/util/maven" ) type localBuilder struct { - buffer chan buildOperation - namespace string + buffer chan buildOperation + namespace string } type buildOperation struct { - source build.BuildSource - output chan build.BuildResult + source build.BuildSource + output chan build.BuildResult } func NewLocalBuilder(ctx context.Context, namespace string) build.Builder { builder := localBuilder{ - buffer: make(chan buildOperation, 100), + buffer: make(chan buildOperation, 100), namespace: namespace, } go builder.buildCycle(ctx) return &builder } -func (b *localBuilder) Build(source build.BuildSource) <- chan build.BuildResult { +func (b *localBuilder) Build(source build.BuildSource) <-chan build.BuildResult { res := make(chan build.BuildResult, 1) op := buildOperation{ source: source, @@ -77,10 +73,10 @@ func (b *localBuilder) Build(source build.BuildSource) <- chan build.BuildResult func (b *localBuilder) buildCycle(ctx context.Context) { for { select { - case <- ctx.Done(): + case <-ctx.Done(): b.buffer = nil return - case op := <- b.buffer: + case op := <-b.buffer: now := time.Now() logrus.Info("Starting new build") image, err := b.execute(op.source) @@ -95,13 +91,13 @@ func (b *localBuilder) buildCycle(ctx context.Context) { op.output <- build.BuildResult{ Source: &op.source, Status: build.BuildStatusError, - Error: err, + Error: err, } } else { op.output <- build.BuildResult{ Source: &op.source, Status: build.BuildStatusCompleted, - Image: image, + Image: image, } } @@ -110,15 +106,28 @@ func (b *localBuilder) buildCycle(ctx context.Context) { } func (b *localBuilder) execute(source build.BuildSource) (string, error) { - buildDir, err := ioutil.TempDir("", "camel-k-") - 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) + project := maven.Project{ + GroupId: "org.apache.camel.k.integration", + ArtifactId: "camel-k-integration", + Version: "1.0.0", + JavaSources: map[string]string{ + "kamel/Routes.java": source.Code, + }, + Env: map[string]string{ + "JAVA_MAIN_CLASS": "org.apache.camel.k.jvm.Application", + "KAMEL_CLASS": "kamel.Routes", + }, + Dependencies: []maven.Dependency{ + { + GroupId: "org.apache.camel.k", + ArtifactId: "camel-k-runtime-jvm", + Version: version.Version, + }, + }, + } - tarFileName, err := b.createTar(buildDir, source) + tarFileName, err := maven.Build(project) if err != nil { return "", err } @@ -137,10 +146,10 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string bc := buildv1.BuildConfig{ TypeMeta: metav1.TypeMeta{ APIVersion: buildv1.SchemeGroupVersion.String(), - Kind: "BuildConfig", + Kind: "BuildConfig", }, ObjectMeta: metav1.ObjectMeta{ - Name: "camel-k-" + source.Identifier.Name, + Name: "camel-k-" + source.Identifier.Name, Namespace: b.namespace, }, Spec: buildv1.BuildConfigSpec{ @@ -175,10 +184,10 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string is := imagev1.ImageStream{ TypeMeta: metav1.TypeMeta{ APIVersion: imagev1.SchemeGroupVersion.String(), - Kind: "ImageStream", + Kind: "ImageStream", }, ObjectMeta: metav1.ObjectMeta{ - Name: "camel-k-" + source.Identifier.Name, + Name: "camel-k-" + source.Identifier.Name, Namespace: b.namespace, }, Spec: imagev1.ImageStreamSpec{ @@ -197,7 +206,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string inConfig := k8sclient.GetKubeConfig() config := rest.CopyConfig(inConfig) config.GroupVersion = &schema.GroupVersion{ - Group: "build.openshift.io", + Group: "build.openshift.io", Version: "v1", } config.APIPath = "/apis" @@ -217,7 +226,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string resource, err := ioutil.ReadFile(tarFile) if err != nil { - return "", errors.Wrap(err, "cannot fully read tar file " + tarFile) + return "", errors.Wrap(err, "cannot fully read tar file "+tarFile) } result := restClient. @@ -249,7 +258,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string return "", err } - err = kubernetes.WaitCondition(ocbuild, func(obj interface{})(bool, error) { + 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 @@ -260,7 +269,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string } } return false, nil - }, 5 * time.Minute) + }, 5*time.Minute) err = sdk.Get(&is) if err != nil { @@ -272,169 +281,3 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string } return is.Status.DockerImageRepository + ":" + source.Identifier.Digest, nil } - -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, "camel-k-integration.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", "camel-k-integration-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=org.apache.camel.k.jvm.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>org.apache.camel.k.integration</groupId> - <artifactId>camel-k-integration</artifactId> - <version>1.0.0</version> - - <dependencies> - <dependency> - <groupId>org.apache.camel.k</groupId> - <artifactId>camel-k-runtime-jvm</artifactId> - <version>` + version.Version + `</version> - </dependency> - </dependencies> - -</project> -` -} \ No newline at end of file diff --git a/pkg/client/cmd/run/run.go b/pkg/client/cmd/run/run.go index 3237f80..fd6df53 100644 --- a/pkg/client/cmd/run/run.go +++ b/pkg/client/cmd/run/run.go @@ -28,6 +28,7 @@ import ( "io/ioutil" k8serrors "k8s.io/apimachinery/pkg/api/errors" "github.com/apache/camel-k/pkg/util/kubernetes" + "fmt" ) type runCmdFlags struct { @@ -89,8 +90,10 @@ func run(cmd *cobra.Command, args []string) error { }, } + existed := false err = sdk.Create(&integration) if err != nil && k8serrors.IsAlreadyExists(err) { + existed = true clone := integration.DeepCopy() err = sdk.Get(clone) if err != nil { @@ -100,7 +103,16 @@ func run(cmd *cobra.Command, args []string) error { err = sdk.Update(&integration) } - return err + if err != nil { + return err + } + + if !existed { + fmt.Printf("integration \"%s\" created\n", name) + } else { + fmt.Printf("integration \"%s\" updated\n", name) + } + return nil } func loadCode(fileName string) (string, error) { diff --git a/pkg/util/maven/maven.go b/pkg/util/maven/maven.go new file mode 100644 index 0000000..08864ac --- /dev/null +++ b/pkg/util/maven/maven.go @@ -0,0 +1,256 @@ +/* +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 maven + +import ( + "io/ioutil" + "os" + "archive/tar" + "github.com/sirupsen/logrus" + "io" + "github.com/pkg/errors" + "path" + "strings" + "os/exec" +) + + +const ( + buildDirPrefix = "maven-" + artifactDirPrefix = "maven-bin-" +) + +// Takes a project description and returns a binary tar with the built artifacts +func Build(project Project) (string, error) { + buildDir, err := ioutil.TempDir("", buildDirPrefix) + if err != nil { + return "", errors.Wrap(err, "could not create temporary dir for maven source files") + } + defer os.RemoveAll(buildDir) + + err = createMavenStructure(buildDir, project) + if err != nil { + return "", errors.Wrap(err, "could not write maven source files") + } + err = runMavenBuild(buildDir) + if err != nil { + return "", err + } + tarfile, err := createTar(buildDir, project) + if err != nil { + return "", err + } + return tarfile, nil +} + +func runMavenBuild(buildDir string) error { + mavenBuild := exec.Command("mvn", "clean", "install", "-DskipTests") + mavenBuild.Dir = buildDir + 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 = buildDir + 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") + return nil +} + +func createTar(buildDir string, project Project) (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) + err = appendToTar(path.Join(buildDir, "target", project.ArtifactId + "-" + project.Version + ".jar"), "", writer) + if err != nil { + return "", err + } + + // Environment variables + if project.Env != nil { + err = writeFile(buildDir, "run-env.sh", envFileContent(project.Env)) + if err != nil { + return "", err + } + err = appendToTar(path.Join(buildDir, "run-env.sh"), "", 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 = appendToTar(path.Join(dependenciesDir, dep.Name()), "", writer) + if err != nil { + return "", err + } + } + + writer.Close() + + return tarFileName, nil +} + +func 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 createMavenStructure(buildDir string, project Project) error { + err := writeFile(buildDir, "pom.xml", pomFileContent(project)) + if err != nil { + return err + } + err = writeFiles(path.Join(buildDir, "src", "main", "java"), project.JavaSources) + if err != nil { + return err + } + err = writeFiles(path.Join(buildDir, "src", "main", "resources"), project.Resources) + if err != nil { + return err + } + + return nil +} + +func writeFiles(buildDir string, files map[string]string) error { + if files == nil { + return nil + } + for fileName, fileContent := range files { + err := writeFile(buildDir, fileName, fileContent) + if err != nil { + return err + } + } + return nil +} + +func writeFile(buildDir string, relativePath string, content string) error { + filePath := path.Join(buildDir, relativePath) + fileDir := path.Dir(filePath) + // Create dir if not present + err := os.MkdirAll(fileDir, 0777) + if err != nil { + return errors.Wrap(err, "could not create dir for file " + relativePath) + } + // Create file + file, err := os.Create(filePath) + if err != nil { + return errors.Wrap(err, "could not create file " + relativePath) + } + defer file.Close() + + _, err = file.WriteString(content) + if err != nil { + errors.Wrap(err, "could not write to file " + relativePath) + } + return nil +} + + +func envFileContent(env map[string]string) string { + if env == nil { + return "" + } + content := "" + for k,v := range env { + content = content + "export " + k + "=" + v + "\n" + } + return content +} + + +func pomFileContent(project Project) string { + basePom := `<?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>` + project.GroupId + `</groupId> + <artifactId>` + project.ArtifactId + `</artifactId> + <version>` + project.Version + `</version> + + <dependencies> + #dependencies# + </dependencies> + +</project> +` + depStr := "" + for _, dep := range project.Dependencies { + depStr += "\t\t<dependency>" + depStr += "\t\t\t<groupId>" + dep.GroupId + "</groupId>" + depStr += "\t\t\t<artifactId>" + dep.ArtifactId + "</artifactId>" + if dep.Version != "" { + depStr += "\t\t\t<version>" + dep.Version + "</version>" + } + depStr += "\t\t</dependency>" + depStr += "\n" + } + + pom := strings.Replace(basePom, "#dependencies#", depStr, 1) + return pom +} \ No newline at end of file diff --git a/pkg/util/maven/types.go b/pkg/util/maven/types.go new file mode 100644 index 0000000..c34bb84 --- /dev/null +++ b/pkg/util/maven/types.go @@ -0,0 +1,34 @@ +/* +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 maven + +type Project struct { + GroupId string + ArtifactId string + Version string + Dependencies []Dependency + JavaSources map[string]string + Resources map[string]string + Env map[string]string +} + +type Dependency struct { + GroupId string + ArtifactId string + Version string +} \ No newline at end of file