Skip to content

Commit

Permalink
Merge pull request #1326 from aemengo/pack-interact-mode-still-going
Browse files Browse the repository at this point in the history
Add file explorer to pack-interact workflow
Signed-off-by: David Freilich <[email protected]>
  • Loading branch information
dfreilich authored Dec 6, 2021
2 parents 52a3caa + 9d648bc commit cb57fea
Show file tree
Hide file tree
Showing 30 changed files with 1,684 additions and 362 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/docker/cli v20.10.11+incompatible
github.com/docker/docker v20.10.11+incompatible
github.com/docker/go-connections v0.4.0
github.com/dustin/go-humanize v1.0.0
github.com/gdamore/tcell/v2 v2.4.0
github.com/ghodss/yaml v1.0.0
github.com/golang/mock v1.6.0
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNE
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
Expand Down
21 changes: 20 additions & 1 deletion internal/build/container_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,25 @@ import (

type ContainerOperation func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error

// CopyOut copies container directories to a handler function. The handler is responsible for closing the Reader.
func CopyOut(handler func(closer io.ReadCloser) error, srcs ...string) ContainerOperation {
return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error {
for _, src := range srcs {
reader, _, err := ctrClient.CopyFromContainer(ctx, containerID, src)
if err != nil {
return err
}

err = handler(reader)
if err != nil {
return err
}
}

return nil
}
}

// CopyDir copies a local directory (src) to the destination on the container while filtering files and changing it's UID/GID.
// if includeRoot is set the UID/GID will be set on the dst directory.
func CopyDir(src, dst string, uid, gid int, os string, includeRoot bool, fileFilter func(string) bool) ContainerOperation {
Expand Down Expand Up @@ -140,7 +159,7 @@ func findMount(info types.ContainerJSON, dst string) (types.MountPoint, error) {
return types.MountPoint{}, fmt.Errorf("no matching mount found for %s", dst)
}

//WriteProjectMetadata
// WriteProjectMetadata
func WriteProjectMetadata(p string, metadata platform.ProjectMetadata, os string) ContainerOperation {
return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error {
buf := &bytes.Buffer{}
Expand Down
61 changes: 61 additions & 0 deletions internal/build/container_ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -244,6 +247,64 @@ drwsrwsrwt 2 123 456 (.*) some-vol
})
})

when("#CopyOut", func() {
it("reads the contents of a container directory", func() {
h.SkipIf(t, osType == "windows", "copying directories out of windows containers not yet supported")

containerDir := "/some-vol"
if osType == "windows" {
containerDir = `c:\some-vol`
}

ctrCmd := []string{"ls", "-al", "/some-vol"}
if osType == "windows" {
ctrCmd = []string{"cmd", "/c", `dir /q /s c:\some-vol`}
}

ctx := context.Background()
ctr, err := createContainer(ctx, imageName, containerDir, osType, ctrCmd...)
h.AssertNil(t, err)
defer cleanupContainer(ctx, ctr.ID)

copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app"), containerDir, 123, 456, osType, false, nil)
err = copyDirOp(ctrClient, ctx, ctr.ID, ioutil.Discard, ioutil.Discard)
h.AssertNil(t, err)

tarDestination, err := ioutil.TempFile("", "pack.container.ops.test.")
h.AssertNil(t, err)
defer os.RemoveAll(tarDestination.Name())

handler := func(reader io.ReadCloser) error {
defer reader.Close()

contents, err := ioutil.ReadAll(reader)
h.AssertNil(t, err)

err = ioutil.WriteFile(tarDestination.Name(), contents, 0600)
h.AssertNil(t, err)

return nil
}

copyOutDirsOp := build.CopyOut(handler, containerDir)
err = copyOutDirsOp(ctrClient, ctx, ctr.ID, ioutil.Discard, ioutil.Discard)
h.AssertNil(t, err)

err = container.RunWithHandler(ctx, ctrClient, ctr.ID, container.DefaultHandler(ioutil.Discard, ioutil.Discard))
h.AssertNil(t, err)

separator := "/"
if osType == "windows" {
separator = `\`
}

h.AssertTarball(t, tarDestination.Name())
h.AssertTarHasFile(t, tarDestination.Name(), fmt.Sprintf("some-vol%sfake-app-file", separator))
h.AssertTarHasFile(t, tarDestination.Name(), fmt.Sprintf("some-vol%sfake-app-symlink", separator))
h.AssertTarHasFile(t, tarDestination.Name(), fmt.Sprintf("some-vol%sfile-to-ignore", separator))
})
})

when("#WriteStackToml", func() {
it("writes file", func() {
containerDir := "/layers-vol"
Expand Down
16 changes: 8 additions & 8 deletions internal/build/fakes/fake_termui.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ import (
)

type FakeTermui struct {
handler container.Handler
}

func NewFakeTermui(handler container.Handler) *FakeTermui {
return &FakeTermui{
handler: handler,
}
HandlerFunc container.Handler
ReadLayersFunc func(reader io.ReadCloser)
}

func (f *FakeTermui) Run(funk func()) error {
return nil
}

func (f *FakeTermui) Handler() container.Handler {
return f.handler
return f.HandlerFunc
}

func (f *FakeTermui) ReadLayers(reader io.ReadCloser) error {
f.ReadLayersFunc(reader)
return nil
}

func WithTermui(screen build.Termui) func(*build.LifecycleOptions) {
Expand Down
6 changes: 6 additions & 0 deletions internal/build/lifecycle_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ func (l *LifecycleExecution) Create(ctx context.Context, publish bool, dockerHos
cacheOpts,
WithContainerOperations(WriteProjectMetadata(l.mountPaths.projectPath(), l.opts.ProjectMetadata, l.os)),
WithContainerOperations(CopyDir(l.opts.AppPath, l.mountPaths.appDir(), l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, true, l.opts.FileFilter)),
If(l.opts.Interactive, WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOut(l.opts.Termui.ReadLayers, l.mountPaths.layersDir(), l.mountPaths.appDir()))),
}

if publish {
Expand Down Expand Up @@ -538,6 +541,9 @@ func (l *LifecycleExecution) newExport(repoName, runImage string, publish bool,
cacheOpt,
WithContainerOperations(WriteStackToml(l.mountPaths.stackPath(), l.opts.Builder.Stack(), l.os)),
WithContainerOperations(WriteProjectMetadata(l.mountPaths.projectPath(), l.opts.ProjectMetadata, l.os)),
If(l.opts.Interactive, WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOut(l.opts.Termui.ReadLayers, l.mountPaths.layersDir(), l.mountPaths.appDir()))),
}

if publish {
Expand Down
52 changes: 52 additions & 0 deletions internal/build/lifecycle_execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"math/rand"
"os"
Expand Down Expand Up @@ -110,13 +111,16 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
logger *logging.LogWithWriters
docker *client.Client
fakePhaseFactory *fakes.FakePhaseFactory
fakeTermui *fakes.FakeTermui
)

it.Before(func() {
var err error
imageName, err = name.NewTag("/some/image", name.WeakValidation)
h.AssertNil(t, err)

fakeTermui = &fakes.FakeTermui{}

fakeBuilder, err = fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.3")}))
h.AssertNil(t, err)
logger = logging.NewLogWithWriters(&outBuf, &outBuf)
Expand All @@ -135,6 +139,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
Builder: fakeBuilder,
TrustBuilder: false,
UseCreator: true,
Termui: fakeTermui,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
Expand Down Expand Up @@ -165,6 +170,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
TrustBuilder: true,
Workspace: "app",
UseCreator: true,
Termui: fakeTermui,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
Expand Down Expand Up @@ -197,6 +203,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
Builder: fakeBuilder,
TrustBuilder: false,
UseCreator: false,
Termui: fakeTermui,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
Expand Down Expand Up @@ -230,6 +237,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
Builder: fakeBuilder,
TrustBuilder: false,
UseCreator: false,
Termui: fakeTermui,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
Expand Down Expand Up @@ -259,6 +267,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
Builder: fakeBuilder,
TrustBuilder: false,
UseCreator: false,
Termui: fakeTermui,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
Expand Down Expand Up @@ -291,6 +300,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
TrustBuilder: false,
Workspace: "app",
UseCreator: false,
Termui: fakeTermui,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
Expand Down Expand Up @@ -329,6 +339,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
TrustBuilder: false,
UseCreator: false,
CacheImage: "%%%",
Termui: fakeTermui,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
Expand Down Expand Up @@ -997,6 +1008,26 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
})
})
})

when("interactive mode", func() {
it("provides the termui readLayersFunc as a post container operation", func() {
lifecycle := newTestLifecycleExec(t, false, func(opts *build.LifecycleOptions) {
opts.Interactive = true
opts.Termui = &fakes.FakeTermui{ReadLayersFunc: func(_ io.ReadCloser) {
// no-op
}}
})
fakePhase := &fakes.FakePhase{}
fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))

err := lifecycle.Create(context.Background(), false, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory)
h.AssertNil(t, err)

h.AssertEq(t, fakePhase.CleanupCallCount, 1)
h.AssertEq(t, fakePhase.RunCallCount, 1)
h.AssertEq(t, len(fakePhaseFactory.NewCalledWithProvider[0].PostContainerRunOps()), 2)
})
})
})

when("#Detect", func() {
Expand Down Expand Up @@ -2517,6 +2548,26 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
})
})
})

when("interactive mode", func() {
it("provides the termui readLayersFunc as a post container operation", func() {
lifecycle := newTestLifecycleExec(t, false, func(opts *build.LifecycleOptions) {
opts.Interactive = true
opts.Termui = &fakes.FakeTermui{ReadLayersFunc: func(_ io.ReadCloser) {
// no-op
}}
})

fakePhase := &fakes.FakePhase{}
fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))
err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory)
h.AssertNil(t, err)

h.AssertEq(t, fakePhase.CleanupCallCount, 1)
h.AssertEq(t, fakePhase.RunCallCount, 1)
h.AssertEq(t, len(fakePhaseFactory.NewCalledWithProvider[0].PostContainerRunOps()), 2)
})
})
})
}

Expand All @@ -2539,6 +2590,7 @@ func newTestLifecycleExecErr(t *testing.T, logVerbose bool, ops ...func(*build.L
HTTPProxy: "some-http-proxy",
HTTPSProxy: "some-https-proxy",
NoProxy: "some-no-proxy",
Termui: &fakes.FakeTermui{},
}

for _, op := range ops {
Expand Down
2 changes: 2 additions & 0 deletions internal/build/lifecycle_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package build

import (
"context"
"io"
"math/rand"
"time"

Expand Down Expand Up @@ -54,6 +55,7 @@ type Termui interface {

Run(funk func()) error
Handler() container.Handler
ReadLayers(reader io.ReadCloser) error
}

func init() {
Expand Down
41 changes: 26 additions & 15 deletions internal/build/phase.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@ import (
)

type Phase struct {
name string
infoWriter io.Writer
errorWriter io.Writer
docker client.CommonAPIClient
handler container.Handler
ctrConf *dcontainer.Config
hostConf *dcontainer.HostConfig
ctr dcontainer.ContainerCreateCreatedBody
uid, gid int
appPath string
containerOps []ContainerOperation
fileFilter func(string) bool
name string
infoWriter io.Writer
errorWriter io.Writer
docker client.CommonAPIClient
handler container.Handler
ctrConf *dcontainer.Config
hostConf *dcontainer.HostConfig
ctr dcontainer.ContainerCreateCreatedBody
uid, gid int
appPath string
containerOps []ContainerOperation
postContainerRunOps []ContainerOperation
fileFilter func(string) bool
}

func (p *Phase) Run(ctx context.Context) error {
Expand All @@ -45,12 +46,22 @@ func (p *Phase) Run(ctx context.Context) error {
handler = p.handler
}

return container.RunWithHandler(
err = container.RunWithHandler(
ctx,
p.docker,
p.ctr.ID,
handler,
)
handler)
if err != nil {
return err
}

for _, containerOp := range p.postContainerRunOps {
if err := containerOp(p.docker, ctx, p.ctr.ID, p.infoWriter, p.errorWriter); err != nil {
return err
}
}

return nil
}

func (p *Phase) Cleanup() error {
Expand Down
Loading

0 comments on commit cb57fea

Please sign in to comment.