mirror of https://github.com/buildpacks/pack.git
315 lines
11 KiB
Go
315 lines
11 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/buildpacks/imgutil/fakes"
|
|
"github.com/buildpacks/lifecycle/auth"
|
|
"github.com/heroku/color"
|
|
"github.com/sclevine/spec"
|
|
"github.com/sclevine/spec/report"
|
|
|
|
ifakes "github.com/buildpacks/pack/internal/fakes"
|
|
"github.com/buildpacks/pack/pkg/image"
|
|
"github.com/buildpacks/pack/pkg/logging"
|
|
h "github.com/buildpacks/pack/testhelpers"
|
|
)
|
|
|
|
func TestRebase(t *testing.T) {
|
|
color.Disable(true)
|
|
defer color.Disable(false)
|
|
spec.Run(t, "rebase_factory", testRebase, spec.Parallel(), spec.Report(report.Terminal{}))
|
|
}
|
|
|
|
func testRebase(t *testing.T, when spec.G, it spec.S) {
|
|
when("#Rebase", func() {
|
|
var (
|
|
fakeImageFetcher *ifakes.FakeImageFetcher
|
|
subject *Client
|
|
fakeAppImage *fakes.Image
|
|
fakeRunImage *fakes.Image
|
|
fakeRunImageMirror *fakes.Image
|
|
out bytes.Buffer
|
|
)
|
|
|
|
it.Before(func() {
|
|
fakeImageFetcher = ifakes.NewFakeImageFetcher()
|
|
|
|
fakeAppImage = fakes.NewImage("some/app", "", &fakeIdentifier{name: "app-image"})
|
|
h.AssertNil(t, fakeAppImage.SetLabel("io.buildpacks.lifecycle.metadata",
|
|
`{"stack":{"runImage":{"image":"some/run", "mirrors":["example.com/some/run"]}}}`))
|
|
h.AssertNil(t, fakeAppImage.SetLabel("io.buildpacks.stack.id", "io.buildpacks.stacks.jammy"))
|
|
fakeImageFetcher.LocalImages["some/app"] = fakeAppImage
|
|
|
|
fakeRunImage = fakes.NewImage("some/run", "run-image-top-layer-sha", &fakeIdentifier{name: "run-image-digest"})
|
|
h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.id", "io.buildpacks.stacks.jammy"))
|
|
fakeImageFetcher.LocalImages["some/run"] = fakeRunImage
|
|
|
|
fakeRunImageMirror = fakes.NewImage("example.com/some/run", "mirror-top-layer-sha", &fakeIdentifier{name: "mirror-digest"})
|
|
h.AssertNil(t, fakeRunImageMirror.SetLabel("io.buildpacks.stack.id", "io.buildpacks.stacks.jammy"))
|
|
fakeImageFetcher.LocalImages["example.com/some/run"] = fakeRunImageMirror
|
|
|
|
keychain, err := auth.DefaultKeychain("pack-test/dummy")
|
|
h.AssertNil(t, err)
|
|
|
|
fakeLogger := logging.NewLogWithWriters(&out, &out)
|
|
subject = &Client{
|
|
logger: fakeLogger,
|
|
imageFetcher: fakeImageFetcher,
|
|
keychain: keychain,
|
|
}
|
|
})
|
|
|
|
it.After(func() {
|
|
h.AssertNilE(t, fakeAppImage.Cleanup())
|
|
h.AssertNilE(t, fakeRunImage.Cleanup())
|
|
h.AssertNilE(t, fakeRunImageMirror.Cleanup())
|
|
})
|
|
|
|
when("#Rebase", func() {
|
|
when("run image is provided by the user", func() {
|
|
when("the image has a label with a run image specified", func() {
|
|
var fakeCustomRunImage *fakes.Image
|
|
|
|
it.Before(func() {
|
|
fakeCustomRunImage = fakes.NewImage("custom/run", "custom-base-top-layer-sha", &fakeIdentifier{name: "custom-base-digest"})
|
|
h.AssertNil(t, fakeCustomRunImage.SetLabel("io.buildpacks.stack.id", "io.buildpacks.stacks.jammy"))
|
|
fakeImageFetcher.LocalImages["custom/run"] = fakeCustomRunImage
|
|
})
|
|
|
|
it.After(func() {
|
|
h.AssertNilE(t, fakeCustomRunImage.Cleanup())
|
|
})
|
|
|
|
when("--force", func() {
|
|
it("uses the run image provided by the user", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(),
|
|
RebaseOptions{
|
|
RunImage: "custom/run",
|
|
RepoName: "some/app",
|
|
Force: true,
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "custom/run")
|
|
lbl, _ := fakeAppImage.Label("io.buildpacks.lifecycle.metadata")
|
|
h.AssertContains(t, lbl, `"runImage":{"topLayer":"custom-base-top-layer-sha","reference":"custom-base-digest"`)
|
|
})
|
|
})
|
|
|
|
it("errors", func() {
|
|
h.AssertError(t, subject.Rebase(context.TODO(),
|
|
RebaseOptions{
|
|
RunImage: "custom/run",
|
|
RepoName: "some/app",
|
|
}), "new base image 'custom/run' not found in existing run image metadata")
|
|
})
|
|
})
|
|
})
|
|
|
|
when("run image is NOT provided by the user", func() {
|
|
when("the image has a label with a run image specified", func() {
|
|
it("uses the run image provided in the App image label", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "some/run")
|
|
lbl, _ := fakeAppImage.Label("io.buildpacks.lifecycle.metadata")
|
|
h.AssertContains(t, lbl, `"runImage":{"topLayer":"run-image-top-layer-sha","reference":"run-image-digest"`)
|
|
})
|
|
})
|
|
|
|
when("the image has a label with a run image mirrors specified", func() {
|
|
when("there are no user provided mirrors", func() {
|
|
it.Before(func() {
|
|
fakeImageFetcher.LocalImages["example.com/some/app"] = fakeAppImage
|
|
})
|
|
|
|
it("chooses a matching mirror from the app image label", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "example.com/some/app",
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "example.com/some/run")
|
|
lbl, _ := fakeAppImage.Label("io.buildpacks.lifecycle.metadata")
|
|
h.AssertContains(t, lbl, `"runImage":{"topLayer":"mirror-top-layer-sha","reference":"mirror-digest"`)
|
|
})
|
|
})
|
|
|
|
when("there are user provided mirrors", func() {
|
|
var (
|
|
fakeLocalMirror *fakes.Image
|
|
)
|
|
it.Before(func() {
|
|
fakeImageFetcher.LocalImages["example.com/some/app"] = fakeAppImage
|
|
fakeLocalMirror = fakes.NewImage("example.com/some/local-run", "local-mirror-top-layer-sha", &fakeIdentifier{name: "local-mirror-digest"})
|
|
h.AssertNil(t, fakeLocalMirror.SetLabel("io.buildpacks.stack.id", "io.buildpacks.stacks.jammy"))
|
|
fakeImageFetcher.LocalImages["example.com/some/local-run"] = fakeLocalMirror
|
|
})
|
|
|
|
it.After(func() {
|
|
h.AssertNilE(t, fakeLocalMirror.Cleanup())
|
|
})
|
|
when("--force", func() {
|
|
it("chooses a matching local mirror first", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "example.com/some/app",
|
|
AdditionalMirrors: map[string][]string{
|
|
"some/run": {"example.com/some/local-run"},
|
|
},
|
|
Force: true,
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "example.com/some/local-run")
|
|
lbl, _ := fakeAppImage.Label("io.buildpacks.lifecycle.metadata")
|
|
h.AssertContains(t, lbl, `"runImage":{"topLayer":"local-mirror-top-layer-sha","reference":"local-mirror-digest"`)
|
|
})
|
|
})
|
|
})
|
|
when("there is a label and it has a run image and no stack", func() {
|
|
it("reads the run image from the label", func() {
|
|
h.AssertNil(t, fakeAppImage.SetLabel("io.buildpacks.lifecycle.metadata",
|
|
`{"runImage":{"image":"some/run", "mirrors":["example.com/some/run"]}}`))
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "some/run")
|
|
})
|
|
})
|
|
when("there is neither runImage nor stack", func() {
|
|
it("fails gracefully", func() {
|
|
h.AssertNil(t, fakeAppImage.SetLabel("io.buildpacks.lifecycle.metadata", `{}`))
|
|
h.AssertError(t, subject.Rebase(context.TODO(), RebaseOptions{RepoName: "some/app"}),
|
|
"run image must be specified")
|
|
})
|
|
})
|
|
})
|
|
|
|
when("the image does not have a label with a run image specified", func() {
|
|
it("returns an error", func() {
|
|
h.AssertNil(t, fakeAppImage.SetLabel("io.buildpacks.lifecycle.metadata", "{}"))
|
|
err := subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
})
|
|
h.AssertError(t, err, "run image must be specified")
|
|
})
|
|
})
|
|
})
|
|
|
|
when("publish", func() {
|
|
var (
|
|
fakeRemoteRunImage *fakes.Image
|
|
)
|
|
|
|
it.Before(func() {
|
|
fakeRemoteRunImage = fakes.NewImage("some/run", "remote-top-layer-sha", &fakeIdentifier{name: "remote-digest"})
|
|
h.AssertNil(t, fakeRemoteRunImage.SetLabel("io.buildpacks.stack.id", "io.buildpacks.stacks.jammy"))
|
|
fakeImageFetcher.RemoteImages["some/run"] = fakeRemoteRunImage
|
|
})
|
|
|
|
it.After(func() {
|
|
h.AssertNilE(t, fakeRemoteRunImage.Cleanup())
|
|
})
|
|
|
|
when("is false", func() {
|
|
when("pull policy is always", func() {
|
|
it("updates the local image", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
PullPolicy: image.PullAlways,
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "some/run")
|
|
lbl, _ := fakeAppImage.Label("io.buildpacks.lifecycle.metadata")
|
|
h.AssertContains(t, lbl, `"runImage":{"topLayer":"remote-top-layer-sha","reference":"remote-digest"`)
|
|
})
|
|
})
|
|
|
|
when("pull policy is never", func() {
|
|
it("uses local image", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
PullPolicy: image.PullNever,
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "some/run")
|
|
lbl, _ := fakeAppImage.Label("io.buildpacks.lifecycle.metadata")
|
|
h.AssertContains(t, lbl, `"runImage":{"topLayer":"run-image-top-layer-sha","reference":"run-image-digest"`)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("report directory is set", func() {
|
|
it("writes the report", func() {
|
|
tmpdir := t.TempDir()
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
ReportDestinationDir: tmpdir,
|
|
}))
|
|
_, err := os.Stat(filepath.Join(tmpdir, "report.toml"))
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
|
|
when("is true", func() {
|
|
it.Before(func() {
|
|
fakeImageFetcher.RemoteImages["some/app"] = fakeAppImage
|
|
})
|
|
|
|
when("skip pull is anything", func() {
|
|
it("uses remote image", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
Publish: true,
|
|
}))
|
|
h.AssertEq(t, fakeAppImage.Base(), "some/run")
|
|
lbl, _ := fakeAppImage.Label("io.buildpacks.lifecycle.metadata")
|
|
h.AssertContains(t, lbl, `"runImage":{"topLayer":"remote-top-layer-sha","reference":"remote-digest"`)
|
|
args := fakeImageFetcher.FetchCalls["some/run"]
|
|
h.AssertEq(t, args.Target.ValuesAsPlatform(), "linux/amd64")
|
|
})
|
|
})
|
|
})
|
|
})
|
|
when("previous image is provided", func() {
|
|
it("fetches the image using the previous image name", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "new/app",
|
|
PreviousImage: "some/app",
|
|
}))
|
|
args := fakeImageFetcher.FetchCalls["some/app"]
|
|
h.AssertNotNil(t, args)
|
|
h.AssertEq(t, args.Daemon, true)
|
|
})
|
|
})
|
|
|
|
when("previous image is set to new image name", func() {
|
|
it("returns error if Fetch function fails", func() {
|
|
err := subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
PreviousImage: "new/app",
|
|
})
|
|
h.AssertError(t, err, "image 'new/app' does not exist on the daemon: not found")
|
|
})
|
|
})
|
|
|
|
when("previous image is not provided", func() {
|
|
it("fetches the image using the repo name", func() {
|
|
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
|
|
RepoName: "some/app",
|
|
}))
|
|
args := fakeImageFetcher.FetchCalls["some/app"]
|
|
h.AssertNotNil(t, args)
|
|
h.AssertEq(t, args.Daemon, true)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
type fakeIdentifier struct {
|
|
name string
|
|
}
|
|
|
|
func (f *fakeIdentifier) String() string {
|
|
return f.name
|
|
}
|