Skip to content

Add support for building Maven multimodule projects #1152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Oct 19, 2018
46 changes: 43 additions & 3 deletions pkg/skaffold/build/local/jib_maven.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package local
import (
"context"
"io"
"strings"

"fmt"

Expand All @@ -30,8 +31,11 @@ import (
)

func (b *Builder) buildJibMavenToDocker(ctx context.Context, out io.Writer, workspace string, a *latest.JibMavenArtifact) (string, error) {
// If this is a multi-module project, we require `package` be bound to jib:dockerBuild
if a.Module != "" {
return "", errors.New("maven multi-modules not supported yet")
if err := verifyJibPackageGoal(ctx, "dockerBuild", workspace, a); err != nil {
return "", err
}
}

skaffoldImage := generateJibImageRef(workspace, a.Module)
Expand All @@ -45,8 +49,11 @@ func (b *Builder) buildJibMavenToDocker(ctx context.Context, out io.Writer, work
}

func (b *Builder) buildJibMavenToRegistry(ctx context.Context, out io.Writer, workspace string, artifact *latest.Artifact) (string, error) {
// If this is a multi-module project, we require `package` be bound to jib:build
if artifact.JibMavenArtifact.Module != "" {
return "", errors.New("maven multi-modules not supported yet")
if err := verifyJibPackageGoal(ctx, "build", workspace, artifact.JibMavenArtifact); err != nil {
return "", err
}
}

initialTag := util.RandomID()
Expand All @@ -62,14 +69,47 @@ func (b *Builder) buildJibMavenToRegistry(ctx context.Context, out io.Writer, wo

// generateMavenArgs generates the arguments to Maven for building the project as an image called `skaffoldImage`.
func generateMavenArgs(goal string, skaffoldImage string, a *latest.JibMavenArtifact) []string {
command := []string{"prepare-package", "jib:" + goal, "-Dimage=" + skaffoldImage}
var command []string
if a.Module == "" {
// single-module project
command = []string{"--non-recursive", "prepare-package", "jib:" + goal, "-Dimage=" + skaffoldImage}
} else {
// multi-module project: we assume `package` is boujd to `jib:<goal>`
command = []string{"--projects", a.Module, "--also-make", "package", "-Dimage=" + skaffoldImage}
}
if a.Profile != "" {
command = append(command, "-P"+a.Profile)
}

return command
}

// verifyJibPackageGoal verifies that the referenced module has `package` bound to a single jib goal.
// It returns `nil` if the goal is matched, and an error if there is a mismatch.
func verifyJibPackageGoal(ctx context.Context, requiredGoal string, workspace string, a *latest.JibMavenArtifact) error {
// cannot use --non-recursive
command := []string{"--projects", a.Module, "jib:_skaffold-package-goals", "--quiet"}
if a.Profile != "" {
command = append(command, "-P"+a.Profile)
}

cmd := jib.MavenCommand.CreateCommand(ctx, workspace, command)
logrus.Debugf("Looking for jib bound package goals for %s: %s, %v", workspace, cmd.Path, cmd.Args)
stdout, err := util.RunCmdOut(cmd)
if err != nil {
return errors.Wrap(err, "could not obtain jib package goals")
}
// need to trim last newline
goals := strings.Split(strings.TrimSpace(string(stdout)), "\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also probably need to handle windows here?

logrus.Debugf("jib bound package goals for %s %s: %v (%d)", workspace, a.Module, goals, len(goals))
if len(goals) != 1 {
return errors.New("skaffold requires a single jib goal bound to 'package'")
} else if goals[0] != requiredGoal {
return errors.New(fmt.Sprintf("skaffold `push` setting requires 'package' be bound to 'jib:%s'", requiredGoal))
}
return nil
}

func runMavenCommand(ctx context.Context, out io.Writer, workspace string, args []string) error {
cmd := jib.MavenCommand.CreateCommand(ctx, workspace, args)
cmd.Stdout = out
Expand Down
40 changes: 31 additions & 9 deletions pkg/skaffold/build/local/jib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ package local

import (
"context"
"io/ioutil"
"testing"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
"github.com/GoogleContainerTools/skaffold/testutil"
)

Expand All @@ -30,8 +30,10 @@ func TestGenerateMavenArgs(t *testing.T) {
in latest.JibMavenArtifact
out []string
}{
{latest.JibMavenArtifact{}, []string{"prepare-package", "jib:goal", "-Dimage=image"}},
{latest.JibMavenArtifact{Profile: "profile"}, []string{"prepare-package", "jib:goal", "-Dimage=image", "-Pprofile"}},
{latest.JibMavenArtifact{}, []string{"--non-recursive", "prepare-package", "jib:goal", "-Dimage=image"}},
{latest.JibMavenArtifact{Profile: "profile"}, []string{"--non-recursive", "prepare-package", "jib:goal", "-Dimage=image", "-Pprofile"}},
{latest.JibMavenArtifact{Module: "module"}, []string{"--projects", "module", "--also-make", "package", "-Dimage=image"}},
{latest.JibMavenArtifact{Module: "module", Profile: "profile"}, []string{"--projects", "module", "--also-make", "package", "-Dimage=image", "-Pprofile"}},
}

for _, tt := range testCases {
Expand All @@ -41,14 +43,34 @@ func TestGenerateMavenArgs(t *testing.T) {
}
}

func TestMultiModulesNotSupported(t *testing.T) {
builder := &Builder{}
func TestMavenVerifyJibPackageGoal(t *testing.T) {
var testCases = []struct {
requiredGoal string
mavenOutput string
shouldError bool
}{
{"xxx", "", true}, // no goals should fail
{"xxx", "\n", true}, // no goals should faill; newline stripped
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: typo (s/faill/fail)

{"dockerBuild", "dockerBuild", false},
{"dockerBuild", "dockerBuild\n", false}, // newline stripped
{"dockerBuild", "build\n", true},
{"dockerBuild", "build\ndockerBuild\n", true},
}

defer func(c util.Command) { util.DefaultExecCommand = c }(util.DefaultExecCommand)
// create an empty workspace so that a maven wrapper is never found
workspace, cleanup := testutil.NewTempDir(t)
defer cleanup()

_, err := builder.buildJibMavenToDocker(context.Background(), ioutil.Discard, ".", &latest.JibMavenArtifact{
Module: "module",
})
for _, tt := range testCases {
util.DefaultExecCommand = testutil.NewFakeCmdOut("mvn --projects module jib:_skaffold-package-goals --quiet", tt.mavenOutput, nil)

err := verifyJibPackageGoal(context.TODO(), tt.requiredGoal, workspace.Root(), &latest.JibMavenArtifact{Module: "module"})
if hasError := err != nil; tt.shouldError != hasError {
t.Error("Unexpected return result")
}
}

testutil.CheckError(t, true, err)
}

func TestGenerateGradleArgs(t *testing.T) {
Expand Down