From 06510a95a7cdfe7a798c230575b5b6ae6ad92a9f Mon Sep 17 00:00:00 2001 From: Emily Casey Date: Thu, 4 Oct 2018 16:13:21 -0400 Subject: [PATCH] Introduce --buildpack flag [buildpack/pack#25] Signed-off-by: Jacques Chester --- acceptance/acceptance_test.go | 73 +++++++++++++++++ acceptance/testdata/maven_app/.gitignore | 10 +++ acceptance/testdata/maven_app/pom.xml | 51 ++++++++++++ .../java/io/buildpacks/pack/testdata/App.java | 26 ++++++ acceptance/testdata/mock_app/detect_first | 0 acceptance/testdata/mock_app/detect_second | 0 acceptance/testdata/mock_app/detect_third | 0 acceptance/testdata/mock_app/run | 7 ++ .../testdata/mock_buildpacks/builder.toml | 14 ++++ .../testdata/mock_buildpacks/first/bin/build | 17 ++++ .../testdata/mock_buildpacks/first/bin/detect | 10 +++ .../mock_buildpacks/first/buildpack.toml | 7 ++ .../testdata/mock_buildpacks/second/bin/build | 16 ++++ .../mock_buildpacks/second/bin/detect | 9 +++ .../mock_buildpacks/second/buildpack.toml | 7 ++ .../testdata/mock_buildpacks/third/bin/build | 16 ++++ .../testdata/mock_buildpacks/third/bin/detect | 9 +++ .../mock_buildpacks/third/buildpack.toml | 7 ++ build.go | 81 +++++++++++++++---- cmd/pack/main.go | 1 + 20 files changed, 344 insertions(+), 17 deletions(-) create mode 100644 acceptance/testdata/maven_app/.gitignore create mode 100644 acceptance/testdata/maven_app/pom.xml create mode 100644 acceptance/testdata/maven_app/src/main/java/io/buildpacks/pack/testdata/App.java create mode 100644 acceptance/testdata/mock_app/detect_first create mode 100644 acceptance/testdata/mock_app/detect_second create mode 100644 acceptance/testdata/mock_app/detect_third create mode 100644 acceptance/testdata/mock_app/run create mode 100644 acceptance/testdata/mock_buildpacks/builder.toml create mode 100755 acceptance/testdata/mock_buildpacks/first/bin/build create mode 100755 acceptance/testdata/mock_buildpacks/first/bin/detect create mode 100644 acceptance/testdata/mock_buildpacks/first/buildpack.toml create mode 100755 acceptance/testdata/mock_buildpacks/second/bin/build create mode 100755 acceptance/testdata/mock_buildpacks/second/bin/detect create mode 100644 acceptance/testdata/mock_buildpacks/second/buildpack.toml create mode 100755 acceptance/testdata/mock_buildpacks/third/bin/build create mode 100755 acceptance/testdata/mock_buildpacks/third/bin/detect create mode 100644 acceptance/testdata/mock_buildpacks/third/buildpack.toml diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index b2b6df7e80..074d41c337 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -132,6 +132,79 @@ func testPack(t *testing.T, when spec.G, it spec.S) { }) }, spec.Parallel(), spec.Report(report.Terminal{})) + when("a custom group is specified by providing multiple '--buildpack' params", func() { + javaBpId := "io.buildpacks.samples.java" + it.Before(func() { + var err error + sourceCodePath, err = ioutil.TempDir("", "pack.build.maven_app.") + if err != nil { + t.Fatal(err) + } + exec.Command("cp", "-r", "testdata/maven_app/.", sourceCodePath).Run() + }) + + it("builds and exports an image", func() { + cmd := exec.Command(pack, "build", repoName, "--buildpack", javaBpId, "-p", sourceCodePath) + cmd.Env = append(os.Environ(), "HOME="+homeDir) + buildOutput := run(t, cmd) + + t.Log(buildOutput) + assertEq(t, strings.Contains(buildOutput, "DETECTING WITH MANUALLY-PROVIDED GROUP:"), true) + if strings.Contains(buildOutput, "Node.js Buildpack") { + t.Fatalf("should have skipped Node.js buildpack because --buildpack flag was provided") + } + assertEq(t, strings.Contains(buildOutput, "Sample Java Buildpack: pass"), true) + + run(t, exec.Command("docker", "run", "--name="+containerName, "--rm=true", "-d", "-e", "PORT=8080", "-p", ":8080", repoName)) + launchPort := fetchHostPort(t, containerName) + + time.Sleep(2 * time.Second) + assertEq(t, fetch(t, "http://localhost:"+launchPort), "Maven buildpack worked!") + }) + }) + + when("a custom group is specified by providing multiple '--buildpack' params", func() { + var ( + builderRepoName string + builderTOML string + ) + it.Before(func() { + builderRepoName = "some-org/" + randString(10) + builderTOML = filepath.Join("testdata", "mock_buildpacks", "builder.toml") + }) + + it.After(func() { + docker.Kill(containerName) + docker.RemoveImage(builderRepoName) + }) + + it("builds and exports an image", func() { + t.Log("create builder image") + cmd := exec.Command(pack, "create-builder", builderRepoName, "-b", builderTOML) + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("create-builder command failed: %s: %s", output, err) + } + + //cmd := exec.Command(pack, "build", repoName, "--buildpack", javaBpId, "-p", sourceCodePath) + //cmd.Env = append(os.Environ(), "HOME="+homeDir) + //buildOutput := run(t, cmd) + // + //t.Log(buildOutput) + //assertEq(t, strings.Contains(buildOutput, "DETECTING WITH MANUALLY-PROVIDED GROUP:"), true) + //if strings.Contains(buildOutput, "Node.js Buildpack") { + // t.Fatalf("should have skipped Node.js buildpack because --buildpack flag was provided") + //} + //assertEq(t, strings.Contains(buildOutput, "Sample Java Buildpack: pass"), true) + // + //run(t, exec.Command("docker", "run", "--name="+containerName, "--rm=true", "-d", "-e", "PORT=8080", "-p", ":8080", repoName)) + //launchPort := fetchHostPort(t, containerName) + // + //time.Sleep(2 * time.Second) + //assertEq(t, fetch(t, "http://localhost:"+launchPort), "Maven buildpack worked!") + }) + }) + when("'--publish' flag is specified", func() { it("builds and exports an image", func() { runPackBuild := func() string { diff --git a/acceptance/testdata/maven_app/.gitignore b/acceptance/testdata/maven_app/.gitignore new file mode 100644 index 0000000000..9c01d043c2 --- /dev/null +++ b/acceptance/testdata/maven_app/.gitignore @@ -0,0 +1,10 @@ +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar \ No newline at end of file diff --git a/acceptance/testdata/maven_app/pom.xml b/acceptance/testdata/maven_app/pom.xml new file mode 100644 index 0000000000..ce65390f5a --- /dev/null +++ b/acceptance/testdata/maven_app/pom.xml @@ -0,0 +1,51 @@ + + + + 1.8 + 1.8 + + + 4.0.0 + pack.buildpacks.io + testdata-sample-app + 1.0-SNAPSHOT + + + + org.nanohttpd + nanohttpd + 2.3.1 + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + + true + io.buildpacks.pack.testdata.App + + + + + + assemble-all + package + + single + + + + + + + + diff --git a/acceptance/testdata/maven_app/src/main/java/io/buildpacks/pack/testdata/App.java b/acceptance/testdata/maven_app/src/main/java/io/buildpacks/pack/testdata/App.java new file mode 100644 index 0000000000..6ff06e3896 --- /dev/null +++ b/acceptance/testdata/maven_app/src/main/java/io/buildpacks/pack/testdata/App.java @@ -0,0 +1,26 @@ +package io.buildpacks.pack.testdata; + +import java.io.IOException; +import java.util.Map; +import fi.iki.elonen.NanoHTTPD; + +public class App extends NanoHTTPD { + public App() throws IOException { + super(8080); + start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); + System.out.println("\nRunning at http://localhost:8080/ \n"); + } + + public static void main(String[] args) { + try { + new App(); + } catch (IOException ioe) { + System.err.println("Couldn't start server:\n" + ioe); + } + } + + @Override + public Response serve(IHTTPSession session) { + return newFixedLengthResponse("Maven buildpack worked!"); + } +} diff --git a/acceptance/testdata/mock_app/detect_first b/acceptance/testdata/mock_app/detect_first new file mode 100644 index 0000000000..e69de29bb2 diff --git a/acceptance/testdata/mock_app/detect_second b/acceptance/testdata/mock_app/detect_second new file mode 100644 index 0000000000..e69de29bb2 diff --git a/acceptance/testdata/mock_app/detect_third b/acceptance/testdata/mock_app/detect_third new file mode 100644 index 0000000000..e69de29bb2 diff --git a/acceptance/testdata/mock_app/run b/acceptance/testdata/mock_app/run new file mode 100644 index 0000000000..db243facb4 --- /dev/null +++ b/acceptance/testdata/mock_app/run @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +echo Here are the deps: + +cat *-dep + +echo "I hope they're what you wanted" diff --git a/acceptance/testdata/mock_buildpacks/builder.toml b/acceptance/testdata/mock_buildpacks/builder.toml new file mode 100644 index 0000000000..48b5b9f997 --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/builder.toml @@ -0,0 +1,14 @@ +[[buildpacks]] +id = "mock.bp.first" +uri = "file://first" + +[[buildpacks]] +id = "mock.bp.second" +uri = "file://second" + +[[buildpacks]] +id = "mock.bp.third" +uri = "file://third" + +[[groups]] +buildpacks = [{ id = "mock.bp.first", version = "1.2.3" }] diff --git a/acceptance/testdata/mock_buildpacks/first/bin/build b/acceptance/testdata/mock_buildpacks/first/bin/build new file mode 100755 index 0000000000..02108c47a1 --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/first/bin/build @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +echo "---> First mock buildpack" + +set -o errexit +set -o nounset +set -o pipefail + +launch_dir=$3 + +mkdir "$launch_dir/first-layer" +echo "First Dep Contents" > "$launch_dir/first-layer/first-dep" +ln -snf "$launch_dir/first-layer/first-dep" first-dep + +echo 'processes = [{ type = "web", command = "run"}]' > "$launch_dir/launch.toml" + +echo "---> Done" diff --git a/acceptance/testdata/mock_buildpacks/first/bin/detect b/acceptance/testdata/mock_buildpacks/first/bin/detect new file mode 100755 index 0000000000..60ab5a4c06 --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/first/bin/detect @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + + +if [[ ! -f detect_first ]]; then + exit 100 +fi \ No newline at end of file diff --git a/acceptance/testdata/mock_buildpacks/first/buildpack.toml b/acceptance/testdata/mock_buildpacks/first/buildpack.toml new file mode 100644 index 0000000000..f4e8a66139 --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/first/buildpack.toml @@ -0,0 +1,7 @@ +[buildpack] +id = "mock.bp.first" +version = "0.0.1-mock" +name = "First Mock Buildpack" + +[[stacks]] +id = "io.buildpacks.stacks.bionic" \ No newline at end of file diff --git a/acceptance/testdata/mock_buildpacks/second/bin/build b/acceptance/testdata/mock_buildpacks/second/bin/build new file mode 100755 index 0000000000..3b4bd69e90 --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/second/bin/build @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +echo "---> Second mock buildpack" + +set -o errexit +set -o nounset +set -o pipefail + +launch_dir=$3 + +mkdir "$launch_dir/second-layer" +echo "Second Dep Contents" > "$launch_dir/second-layer/second-dep" +ln -snf "$launch_dir/second-layer/second-dep" second-dep + +echo 'processes = [{ type = "web", command = "run"}]' > "$launch_dir/launch.toml" + +echo "---> Done" \ No newline at end of file diff --git a/acceptance/testdata/mock_buildpacks/second/bin/detect b/acceptance/testdata/mock_buildpacks/second/bin/detect new file mode 100755 index 0000000000..5e31e04bc4 --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/second/bin/detect @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +if [[ ! -f detect_second ]]; then + exit 100 +fi \ No newline at end of file diff --git a/acceptance/testdata/mock_buildpacks/second/buildpack.toml b/acceptance/testdata/mock_buildpacks/second/buildpack.toml new file mode 100644 index 0000000000..5a0de3af1e --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/second/buildpack.toml @@ -0,0 +1,7 @@ +[buildpack] +id = "mock.bp.second" +version = "0.0.1-mock" +name = "Second Mock Buildpack" + +[[stacks]] +id = "io.buildpacks.stacks.bionic" \ No newline at end of file diff --git a/acceptance/testdata/mock_buildpacks/third/bin/build b/acceptance/testdata/mock_buildpacks/third/bin/build new file mode 100755 index 0000000000..a10c3932af --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/third/bin/build @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +echo "---> Third mock buildpack" + +set -o errexit +set -o nounset +set -o pipefail + +launch_dir=$3 + +mkdir "$launch_dir/third-layer" +echo "Third Dep Contents" > "$launch_dir/third-layer/third-dep" +ln -snf "$launch_dir/third-layer/third-dep" third-dep + +echo 'processes = [{ type = "web", command = "run"}]' > "$launch_dir/launch.toml" + +echo "---> Done" diff --git a/acceptance/testdata/mock_buildpacks/third/bin/detect b/acceptance/testdata/mock_buildpacks/third/bin/detect new file mode 100755 index 0000000000..d6417e7fab --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/third/bin/detect @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +if [[ ! -f detect_third ]]; then + exit 100 +fi \ No newline at end of file diff --git a/acceptance/testdata/mock_buildpacks/third/buildpack.toml b/acceptance/testdata/mock_buildpacks/third/buildpack.toml new file mode 100644 index 0000000000..a08d7b6a34 --- /dev/null +++ b/acceptance/testdata/mock_buildpacks/third/buildpack.toml @@ -0,0 +1,7 @@ +[buildpack] +id = "mock.bp.third" +version = "0.0.1-mock" +name = "Third Mock Buildpack" + +[[stacks]] +id = "io.buildpacks.stacks.bionic" \ No newline at end of file diff --git a/build.go b/build.go index 81cd0b40de..012d794fef 100644 --- a/build.go +++ b/build.go @@ -41,20 +41,22 @@ type BuildFactory struct { } type BuildFlags struct { - AppDir string - Builder string - RunImage string - RepoName string - Publish bool - NoPull bool + AppDir string + Builder string + RunImage string + RepoName string + Publish bool + NoPull bool + Buildpack string } type BuildConfig struct { - AppDir string - Builder string - RunImage string - RepoName string - Publish bool + AppDir string + Builder string + RunImage string + RepoName string + Publish bool + Buildpack string // Above are copied from BuildFlags are set by init Cli Docker Stdout io.Writer @@ -104,10 +106,11 @@ func (bf *BuildFactory) BuildConfigFromFlags(f *BuildFlags) (*BuildConfig, error } b := &BuildConfig{ - AppDir: appDir, - Builder: f.Builder, + AppDir: appDir, + Builder: f.Builder, RepoName: f.RepoName, Publish: f.Publish, + Buildpack: f.Buildpack, Cli: bf.Cli, Stdout: bf.Stdout, Stderr: bf.Stderr, @@ -185,7 +188,6 @@ func Build(appDir, buildImage, runImage, repoName string, publish bool) error { func (b *BuildConfig) Run() error { defer b.Cli.VolumeRemove(context.Background(), b.WorkspaceVolume, true) - fmt.Println("*** DETECTING:") group, err := b.Detect() if err != nil { return err @@ -210,10 +212,45 @@ func (b *BuildConfig) Run() error { } func (b *BuildConfig) Detect() (*lifecycle.BuildpackGroup, error) { + var orderToml string + if b.Buildpack == "" { + fmt.Println("*** DETECTING:") + orderToml = "" // use order toml already in image + } else { + fmt.Println("*** DETECTING WITH MANUALLY-PROVIDED GROUP:") + + var order struct { + Groups lifecycle.BuildpackOrder `toml:"groups"` + } + + order.Groups = lifecycle.BuildpackOrder{ + lifecycle.BuildpackGroup{ + Buildpacks: []*lifecycle.Buildpack{ + {ID: b.Buildpack, Version: "0.0.1", Optional: false}, + }, + }, + } + + tomlBuilder := &strings.Builder{} + err := toml.NewEncoder(tomlBuilder).Encode(order) + if err != nil { + return nil, errors.Wrapf(err, "encoding order.toml: %#v", order) + } + + orderToml = tomlBuilder.String() + } + + var cmd []string + if orderToml != "" { + cmd = []string{"/lifecycle/detector", "-order", "/workspace/app/pack-order.toml"} + } else { + cmd = []string{"/lifecycle/detector"} + } + ctx := context.Background() ctr, err := b.Cli.ContainerCreate(ctx, &container.Config{ Image: b.Builder, - Cmd: []string{"/lifecycle/detector"}, + Cmd: cmd, }, &container.HostConfig{ Binds: []string{ b.WorkspaceVolume + ":/workspace", @@ -241,6 +278,16 @@ func (b *BuildConfig) Detect() (*lifecycle.BuildpackGroup, error) { return nil, errors.Wrap(err, "chown app to workspace volume") } + if orderToml != "" { + ftr, err := b.FS.CreateSingleFileTar("/workspace/app/pack-order.toml", orderToml) + if err != nil { + return nil, errors.Wrap(err, "converting order TOML to tar reader") + } + if err := b.Cli.CopyToContainer(ctx, ctr.ID, "/", ftr, dockertypes.CopyToContainerOptions{}); err != nil { + return nil, errors.Wrap(err, "creating /workspace/app/pack-order.toml") + } + } + if err := b.Cli.RunContainer(ctx, ctr.ID, b.Stdout, b.Stderr); err != nil { return nil, errors.Wrap(err, "run detect container") } @@ -256,11 +303,11 @@ func (b *BuildConfig) groupToml(ctrID string) (*lifecycle.BuildpackGroup, error) tr := tar.NewReader(trc) _, err = tr.Next() if err != nil { - return nil, errors.Wrap(err, "reading group.toml from container") + return nil, errors.Wrap(err, "extracting group.toml from tar") } var group lifecycle.BuildpackGroup if _, err := toml.DecodeReader(tr, &group); err != nil { - return nil, errors.Wrap(err, "reading group.toml from container") + return nil, errors.Wrap(err, "decoding group.toml") } return &group, nil } diff --git a/cmd/pack/main.go b/cmd/pack/main.go index cd1ad71d65..478c11ee4e 100644 --- a/cmd/pack/main.go +++ b/cmd/pack/main.go @@ -57,6 +57,7 @@ func buildCommand() *cobra.Command { buildCommand.Flags().StringVar(&buildFlags.RunImage, "run-image", "", "run image") buildCommand.Flags().BoolVar(&buildFlags.Publish, "publish", false, "publish to registry") buildCommand.Flags().BoolVar(&buildFlags.NoPull, "no-pull", false, "don't pull images before use") + buildCommand.Flags().StringVar(&buildFlags.Buildpack, "buildpack", "", "buildpack ID to skip detection") return buildCommand }