Skip to content

Commit

Permalink
Java Support for Gaia Pipelines
Browse files Browse the repository at this point in the history
  • Loading branch information
michelvocks committed Jul 30, 2018
1 parent 742944c commit 84690cc
Show file tree
Hide file tree
Showing 14 changed files with 532 additions and 107 deletions.
2 changes: 1 addition & 1 deletion cmd/gaia/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func main() {
pS := &plugin.Plugin{}

// Initialize scheduler
scheduler := scheduler.NewScheduler(store, pS)
scheduler := scheduler.NewScheduler(store, pS, cert)
err = scheduler.Init()
if err != nil {
gaia.Cfg.Logger.Error("cannot initialize scheduler:", "error", err.Error())
Expand Down
6 changes: 3 additions & 3 deletions frontend/client/views/pipeline/create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@
<div class="pipelinetype" title="Golang" v-tippy="{ arrow : true, animation : 'shift-away'}" v-on:click="createPipeline.pipeline.type = 'golang'" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'golang' }" data-tippy-hideOnClick="false">
<img src="~assets/golang.png" class="typeimage">
</div>
<div class="pipelinetype" title="Java" v-tippy="{ arrow : true, animation : 'shift-away'}" v-on:click="createPipeline.pipeline.type = 'java'" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'java' }" data-tippy-hideOnClick="false">
<img src="~assets/java.png" class="typeimage">
</div>
<div class="pipelinetype" title="Python (not yet supported)" v-tippy="{ arrow : true, animation : 'shift-away'}" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'python' }" data-tippy-hideOnClick="false">
<img src="~assets/python.png" class="typeimage typeimagenotyetsupported">
</div>
<div class="pipelinetype" title="Java (not yet supported)" v-tippy="{ arrow : true, animation : 'shift-away'}" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'java' }" data-tippy-hideOnClick="false">
<img src="~assets/java.png" class="typeimage typeimagenotyetsupported">
</div>
</div>
<div class="content" style="display: flex;">
<div class="pipelinetype" title="C++ (not yet supported)" v-tippy="{ arrow : true, animation : 'shift-away'}" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'cplusplus' }" data-tippy-hideOnClick="false">
Expand Down
3 changes: 3 additions & 0 deletions gaia.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const (
// PTypeGolang golang plugin type
PTypeGolang PipelineType = "golang"

// PTypeJava java plugin type
PTypeJava PipelineType = "java"

// CreatePipelineFailed status
CreatePipelineFailed CreatePipelineType = "failed"

Expand Down
44 changes: 0 additions & 44 deletions pipeline/build_golang.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package pipeline

import (
"context"
"io"
"os"
"os/exec"
"path/filepath"
Expand All @@ -16,11 +14,8 @@ import (
const (
golangBinaryName = "go"
golangFolder = "golang"
srcFolder = "src"
)

var execCommandContext = exec.CommandContext

// BuildPipelineGolang is the real implementation of BuildPipeline for golang
type BuildPipelineGolang struct {
Type gaia.PipelineType
Expand Down Expand Up @@ -90,21 +85,6 @@ func (b *BuildPipelineGolang) ExecuteBuild(p *gaia.CreatePipeline) error {
return nil
}

// executeCmd wraps a context around the command and executes it.
func executeCmd(path string, args []string, env []string, dir string) ([]byte, error) {
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), maxTimeoutMinutes*time.Minute)
defer cancel()

// Create command
cmd := execCommandContext(ctx, path, args...)
cmd.Env = env
cmd.Dir = dir

// Execute command
return cmd.CombinedOutput()
}

// CopyBinary copies the final compiled archive to the
// destination folder.
func (b *BuildPipelineGolang) CopyBinary(p *gaia.CreatePipeline) error {
Expand All @@ -131,27 +111,3 @@ func (b *BuildPipelineGolang) SavePipeline(p *gaia.Pipeline) error {
// Our pipeline is finished constructing. Save it.
return storeService.PipelinePut(p)
}

// copyFileContents copies the content from source to destination.
func copyFileContents(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return
}
defer func() {
cerr := out.Close()
if err == nil {
err = cerr
}
}()
if _, err = io.Copy(out, in); err != nil {
return
}
err = out.Sync()
return
}
57 changes: 13 additions & 44 deletions pipeline/build_golang_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ package pipeline

import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"

Expand All @@ -18,39 +15,7 @@ import (
hclog "github.com/hashicorp/go-hclog"
)

var killContext = false
var killOnBuild = false

func fakeExecCommandContext(ctx context.Context, name string, args ...string) *exec.Cmd {
if killContext {
c, cancel := context.WithTimeout(context.Background(), 0)
defer cancel()
ctx = c
}
cs := []string{"-test.run=TestExecCommandContextHelper", "--", name}
cs = append(cs, args...)
cmd := exec.CommandContext(ctx, os.Args[0], cs...)
arg := strings.Join(cs, ",")
envArgs := os.Getenv("CMD_ARGS")
if len(envArgs) != 0 {
envArgs += ":" + arg
} else {
envArgs = arg
}
os.Setenv("CMD_ARGS", envArgs)
return cmd
}

func TestExecCommandContextHelper(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
fmt.Fprintf(os.Stdout, os.Getenv("STDOUT"))
i, _ := strconv.Atoi(os.Getenv("EXIT_STATUS"))
os.Exit(i)
}

func TestPrepareEnvironment(t *testing.T) {
func TestPrepareEnvironmentGo(t *testing.T) {
tmp := os.TempDir()
gaia.Cfg = new(gaia.Config)
gaia.Cfg.HomePath = tmp
Expand All @@ -66,7 +31,7 @@ func TestPrepareEnvironment(t *testing.T) {
}
}

func TestPrepareEnvironmentInvalidPathForMkdir(t *testing.T) {
func TestPrepareEnvironmentInvalidPathForMkdirGo(t *testing.T) {
gaia.Cfg = new(gaia.Config)
gaia.Cfg.HomePath = "/notexists"
b := new(BuildPipelineGolang)
Expand All @@ -77,7 +42,7 @@ func TestPrepareEnvironmentInvalidPathForMkdir(t *testing.T) {
}
}

func TestExecuteBuild(t *testing.T) {
func TestExecuteBuildGo(t *testing.T) {
execCommandContext = fakeExecCommandContext
defer func() {
execCommandContext = exec.CommandContext
Expand All @@ -99,7 +64,7 @@ func TestExecuteBuild(t *testing.T) {
}
}

func TestExecuteBuildFailPipelineBuild(t *testing.T) {
func TestExecuteBuildFailPipelineBuildGo(t *testing.T) {
os.Mkdir("tmp", 0744)
ioutil.WriteFile(filepath.Join("tmp", "main.go"), []byte(`package main
import "os"
Expand Down Expand Up @@ -132,7 +97,7 @@ func TestExecuteBuildFailPipelineBuild(t *testing.T) {
}
}

func TestExecuteBuildContextTimeout(t *testing.T) {
func TestExecuteBuildContextTimeoutGo(t *testing.T) {
execCommandContext = fakeExecCommandContext
killContext = true
defer func() {
Expand Down Expand Up @@ -160,7 +125,7 @@ func TestExecuteBuildContextTimeout(t *testing.T) {
}
}

func TestExecuteBuildBinaryNotFoundError(t *testing.T) {
func TestExecuteBuildBinaryNotFoundErrorGo(t *testing.T) {
tmp := os.TempDir()
gaia.Cfg = new(gaia.Config)
gaia.Cfg.HomePath = tmp
Expand All @@ -185,7 +150,7 @@ func TestExecuteBuildBinaryNotFoundError(t *testing.T) {
}
}

func TestCopyBinary(t *testing.T) {
func TestCopyBinaryGo(t *testing.T) {
tmp := os.TempDir()
gaia.Cfg = new(gaia.Config)
gaia.Cfg.HomePath = tmp
Expand Down Expand Up @@ -220,7 +185,7 @@ func TestCopyBinary(t *testing.T) {
}
}

func TestCopyBinarySrcDoesNotExist(t *testing.T) {
func TestCopyBinarySrcDoesNotExistGo(t *testing.T) {
tmp := os.TempDir()
gaia.Cfg = new(gaia.Config)
gaia.Cfg.HomePath = tmp
Expand All @@ -245,7 +210,11 @@ func TestCopyBinarySrcDoesNotExist(t *testing.T) {
}
}

func TestSavePipeline(t *testing.T) {
func TestSavePipelineGo(t *testing.T) {
tmp := os.TempDir()
gaia.Cfg = new(gaia.Config)
gaia.Cfg.HomePath = tmp
defer os.Remove(tmp)
s := store.NewStore()
s.Init()
storeService = s
Expand Down
101 changes: 101 additions & 0 deletions pipeline/build_java.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package pipeline

import (
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/gaia-pipeline/gaia"
"github.com/satori/go.uuid"
)

var (
mavenBinaryName = "mvn"
)

const (
javaFolder = "java"
javaFinalJarName = "plugin-jar-with-dependencies.jar"
mavenTargetFolder = "target"
)

// BuildPipelineJava is the real implementation of BuildPipeline for java
type BuildPipelineJava struct {
Type gaia.PipelineType
}

// PrepareEnvironment prepares the environment before we start the build process.
func (b *BuildPipelineJava) PrepareEnvironment(p *gaia.CreatePipeline) error {
// create uuid for destination folder
uuid := uuid.Must(uuid.NewV4(), nil)

// Create local temp folder for clone
rootPath := filepath.Join(gaia.Cfg.HomePath, tmpFolder, javaFolder)
cloneFolder := filepath.Join(rootPath, srcFolder, uuid.String())
err := os.MkdirAll(cloneFolder, 0700)
if err != nil {
return err
}

// Set new generated path in pipeline obj for later usage
p.Pipeline.Repo.LocalDest = cloneFolder
p.Pipeline.UUID = uuid.String()
return err
}

// ExecuteBuild executes the java build process
func (b *BuildPipelineJava) ExecuteBuild(p *gaia.CreatePipeline) error {
// Look for maven executeable
path, err := exec.LookPath(mavenBinaryName)
if err != nil {
gaia.Cfg.Logger.Debug("cannot find maven executeable", "error", err.Error())
return err
}
env := os.Environ()

// Set command args for build
args := []string{
"clean",
"compile",
"assembly:single",
}

// Execute and wait until finish or timeout
output, err := executeCmd(path, args, env, p.Pipeline.Repo.LocalDest)
p.Output = string(output)
if err != nil {
gaia.Cfg.Logger.Debug("cannot build pipeline", "error", err.Error(), "output", string(output))
return err
}

return nil
}

// CopyBinary copies the final compiled archive to the
// destination folder.
func (b *BuildPipelineJava) CopyBinary(p *gaia.CreatePipeline) error {
// Define src and destination
src := filepath.Join(p.Pipeline.Repo.LocalDest, mavenTargetFolder, javaFinalJarName)
dest := filepath.Join(gaia.Cfg.PipelinePath, appendTypeToName(p.Pipeline.Name, p.Pipeline.Type))

// Copy binary
if err := copyFileContents(src, dest); err != nil {
return err
}

// Set +x (execution right) for pipeline
return os.Chmod(dest, 0766)
}

// SavePipeline saves the current pipeline configuration.
func (b *BuildPipelineJava) SavePipeline(p *gaia.Pipeline) error {
dest := filepath.Join(gaia.Cfg.PipelinePath, appendTypeToName(p.Name, p.Type))
p.ExecPath = dest
p.Type = gaia.PTypeJava
p.Name = strings.TrimSuffix(filepath.Base(dest), typeDelimiter+gaia.PTypeJava.String())
p.Created = time.Now()
// Our pipeline is finished constructing. Save it.
return storeService.PipelinePut(p)
}
Loading

0 comments on commit 84690cc

Please sign in to comment.