Skip to content

Commit

Permalink
add lifecycle args & permissions changes so that application source c…
Browse files Browse the repository at this point in the history
…ode can be added at places other than workspace

Signed-off-by: dwillist <dthornton@vmware.com>
  • Loading branch information
dwillist committed Apr 29, 2021
1 parent 5d39613 commit 35ed268
Show file tree
Hide file tree
Showing 14 changed files with 192 additions and 50 deletions.
2 changes: 1 addition & 1 deletion acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2763,7 +2763,7 @@ func createStackImage(dockerCli client.CommonAPIClient, repoName string, dir str
defaultFilterFunc := func(file string) bool { return true }

ctx := context.Background()
buildContext := archive.ReadDirAsTar(dir, "/", 0, 0, -1, true, defaultFilterFunc)
buildContext := archive.ReadDirAsTar(dir, "/", 0, 0, -1, true, false, defaultFilterFunc)

res, err := dockerCli.ImageBuild(ctx, buildContext, dockertypes.ImageBuildOptions{
Tags: []string{repoName},
Expand Down
1 change: 1 addition & 0 deletions acceptance/buildpacks/archive_buildpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (a archiveBuildpack) createTgz(sourceDir string) (string, error) {
defaultGid,
defaultMode,
true,
false,
nil,
)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion inspect_buildpack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,5 +652,5 @@ func writeBuildpackArchive(buildpackPath, tmpDir string, assert h.AssertionManag
tw := tar.NewWriter(buildpackWriter)
defer tw.Close()

assert.Nil(archive.WriteDirToTar(tw, layoutDir, "/", 0, 0, 0755, true, nil))
assert.Nil(archive.WriteDirToTar(tw, layoutDir, "/", 0, 0, 0755, true, false, nil))
}
2 changes: 1 addition & 1 deletion internal/blob/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (b blob) Open() (r io.ReadCloser, err error) {
return nil, errors.Wrapf(err, "read blob at path '%s'", b.path)
}
if fi.IsDir() {
return archive.ReadDirAsTar(b.path, ".", 0, 0, -1, true, nil), nil
return archive.ReadDirAsTar(b.path, ".", 0, 0, -1, true, false, nil), nil
}

fh, err := os.Open(b.path)
Expand Down
9 changes: 5 additions & 4 deletions internal/build/container_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ import (
type ContainerOperation func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error

// CopyDir copies a local directory (src) to the destination on the container while filtering files and changing it's UID/GID.
func CopyDir(src, dst string, uid, gid int, os string, fileFilter func(string) bool) ContainerOperation {
// 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 {
return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error {
tarPath := dst
if os == "windows" {
tarPath = paths.WindowsToSlash(dst)
}

reader, err := createReader(src, tarPath, uid, gid, fileFilter)
reader, err := createReader(src, tarPath, uid, gid, includeRoot, fileFilter)
if err != nil {
return errors.Wrapf(err, "create tar archive from '%s'", src)
}
Expand Down Expand Up @@ -166,7 +167,7 @@ func WriteStackToml(dstPath string, stack builder.StackMetadata, os string) Cont
}
}

func createReader(src, dst string, uid, gid int, fileFilter func(string) bool) (io.ReadCloser, error) {
func createReader(src, dst string, uid, gid int, includeRoot bool, fileFilter func(string) bool) (io.ReadCloser, error) {
fi, err := os.Stat(src)
if err != nil {
return nil, err
Expand All @@ -178,7 +179,7 @@ func createReader(src, dst string, uid, gid int, fileFilter func(string) bool) (
mode = 0777
}

return archive.ReadDirAsTar(src, dst, uid, gid, mode, false, fileFilter), nil
return archive.ReadDirAsTar(src, dst, uid, gid, mode, false, includeRoot, fileFilter), nil
}

return archive.ReadZipAsTar(src, dst, uid, gid, -1, false, fileFilter), nil
Expand Down
54 changes: 51 additions & 3 deletions internal/build/container_ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func testContainerOps(t *testing.T, when spec.G, it spec.S) {
h.AssertNil(t, err)
defer cleanupContainer(ctx, ctr.ID)

copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app"), containerDir, 123, 456, osType, nil)
copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app"), containerDir, 123, 456, osType, false, nil)

var outBuf, errBuf bytes.Buffer
err = copyDirOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf)
Expand Down Expand Up @@ -124,6 +124,54 @@ lrwxrwxrwx 1 123 456 (.*) fake-app-symlink -> fake-app-file
}
})

when("includeRoot", func() {
it("copies root dir with new GID, UID and permissions", func() {
containerDir := "/some-vol"
if osType == "windows" {
containerDir = `c:\some-vol`
}

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

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, true, nil)

var outBuf, errBuf bytes.Buffer
err = copyDirOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf)
h.AssertNil(t, err)

err = container.Run(ctx, ctrClient, ctr.ID, &outBuf, &errBuf)
h.AssertNil(t, err)

h.AssertEq(t, errBuf.String(), "")
if osType == "windows" {
// Expected WCOW results
h.AssertContainsMatch(t, strings.ReplaceAll(outBuf.String(), "\r", ""), `
(.*) <DIR> ... some-vol
`)
} else {
if runtime.GOOS == "windows" {
// Expected LCOW results
h.AssertContainsMatch(t, outBuf.String(), `
drwxrwxrwx 2 123 456 (.*) some-vol
`)
} else {
// Expected results
h.AssertContainsMatch(t, outBuf.String(), `
drwsrwsrwt 2 123 456 (.*) some-vol
`)
}
}
})
})

it("writes contents ignoring from file filter", func() {
containerDir := "/some-vol"
if osType == "windows" {
Expand All @@ -140,7 +188,7 @@ lrwxrwxrwx 1 123 456 (.*) fake-app-symlink -> fake-app-file
h.AssertNil(t, err)
defer cleanupContainer(ctx, ctr.ID)

copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app"), containerDir, 123, 456, osType, func(filename string) bool {
copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app"), containerDir, 123, 456, osType, false, func(filename string) bool {
return filepath.Base(filename) != "file-to-ignore"
})

Expand Down Expand Up @@ -172,7 +220,7 @@ lrwxrwxrwx 1 123 456 (.*) fake-app-symlink -> fake-app-file
h.AssertNil(t, err)
defer cleanupContainer(ctx, ctr.ID)

copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app.zip"), containerDir, 123, 456, osType, nil)
copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app.zip"), containerDir, 123, 456, osType, false, nil)

var outBuf, errBuf bytes.Buffer
err = copyDirOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf)
Expand Down
10 changes: 8 additions & 2 deletions internal/build/lifecycle_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ func (l *LifecycleExecution) Cleanup() error {

func (l *LifecycleExecution) Create(ctx context.Context, publish bool, dockerHost string, clearCache bool, runImage, repoName, networkMode string, buildCache, launchCache Cache, additionalTags, volumes []string, phaseFactory PhaseFactory) error {
flags := addTags([]string{
"-app", l.mountPaths.appDir(),
"-cache-dir", l.mountPaths.cacheDir(),
"-run-image", runImage,
}, additionalTags)
Expand Down Expand Up @@ -200,7 +201,7 @@ func (l *LifecycleExecution) Create(ctx context.Context, publish bool, dockerHos
WithArgs(repoName),
WithNetwork(networkMode),
cacheOpts,
WithContainerOperations(CopyDir(l.opts.AppPath, l.mountPaths.appDir(), l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.opts.FileFilter)),
WithContainerOperations(CopyDir(l.opts.AppPath, l.mountPaths.appDir(), l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, true, l.opts.FileFilter)),
}

if publish {
Expand All @@ -224,6 +225,7 @@ func (l *LifecycleExecution) Create(ctx context.Context, publish bool, dockerHos
}

func (l *LifecycleExecution) Detect(ctx context.Context, networkMode string, volumes []string, phaseFactory PhaseFactory) error {
flags := []string{"-app", l.mountPaths.appDir()}
configProvider := NewPhaseConfigProvider(
"detector",
l,
Expand All @@ -235,8 +237,9 @@ func (l *LifecycleExecution) Detect(ctx context.Context, networkMode string, vol
WithBinds(volumes...),
WithContainerOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyDir(l.opts.AppPath, l.mountPaths.appDir(), l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.opts.FileFilter),
CopyDir(l.opts.AppPath, l.mountPaths.appDir(), l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, true, l.opts.FileFilter),
),
WithFlags(flags...),
)

detect := phaseFactory.New(configProvider)
Expand Down Expand Up @@ -357,13 +360,15 @@ func (l *LifecycleExecution) newAnalyze(repoName, networkMode string, publish bo
}

func (l *LifecycleExecution) Build(ctx context.Context, networkMode string, volumes []string, phaseFactory PhaseFactory) error {
flags := []string{"-app", l.mountPaths.appDir()}
configProvider := NewPhaseConfigProvider(
"builder",
l,
WithLogPrefix("builder"),
WithArgs(l.withLogLevel()...),
WithNetwork(networkMode),
WithBinds(volumes...),
WithFlags(flags...),
)

build := phaseFactory.New(configProvider)
Expand All @@ -382,6 +387,7 @@ func determineDefaultProcessType(platformAPI *api.Version, providedValue string)

func (l *LifecycleExecution) newExport(repoName, runImage string, publish bool, dockerHost, networkMode string, buildCache, launchCache Cache, additionalTags []string, phaseFactory PhaseFactory) (RunnerCleaner, error) {
flags := []string{
"-app", l.mountPaths.appDir(),
"-cache-dir", l.mountPaths.cacheDir(),
"-stack", l.mountPaths.stackPath(),
"-run-image", runImage,
Expand Down
80 changes: 61 additions & 19 deletions internal/build/lifecycle_execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,37 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
}
}
})
when("Run with workspace dir", func() {
it("succeeds", func() {
opts := build.LifecycleOptions{
Publish: false,
ClearCache: false,
RunImage: "test",
Image: imageName,
Builder: fakeBuilder,
TrustBuilder: true,
Workspace: "app",
UseCreator: true,
}

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
h.AssertNil(t, err)

err = lifecycle.Run(context.Background(), func(execution *build.LifecycleExecution) build.PhaseFactory {
return fakePhaseFactory
})
h.AssertNil(t, err)

h.AssertEq(t, len(fakePhaseFactory.NewCalledWithProvider), 1)

for _, entry := range fakePhaseFactory.NewCalledWithProvider {
if entry.Name() == "creator" {
h.AssertSliceContainsInOrder(t, entry.ContainerConfig().Cmd, "-app", "/app")
h.AssertSliceContains(t, entry.ContainerConfig().Cmd, "/some/image")
}
}
})
})
})
when("Run without using creator", func() {
it("succeeds", func() {
Expand Down Expand Up @@ -183,29 +214,40 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
}
}
})
})
when("Run with workspace dir", func() {
it("succeeds", func() {
opts := build.LifecycleOptions{
Publish: false,
ClearCache: false,
RunImage: "test",
Image: imageName,
Builder: fakeBuilder,
TrustBuilder: false,
Workspace: "app",
UseCreator: false,
}

when("Run with workspace dir", func() {
it("succeeds", func() {
opts := build.LifecycleOptions{
Publish: false,
ClearCache: false,
RunImage: "test",
Image: imageName,
Builder: fakeBuilder,
TrustBuilder: false,
Workspace: "app",
UseCreator: true,
}
lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
h.AssertNil(t, err)
h.AssertEq(t, filepath.Base(lifecycle.AppDir()), "app")

lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
h.AssertNil(t, err)
h.AssertEq(t, filepath.Base(lifecycle.AppDir()), "app")
err = lifecycle.Run(context.Background(), func(execution *build.LifecycleExecution) build.PhaseFactory {
return fakePhaseFactory
})
h.AssertNil(t, err)

err = lifecycle.Run(context.Background(), func(execution *build.LifecycleExecution) build.PhaseFactory {
return fakePhaseFactory
h.AssertEq(t, len(fakePhaseFactory.NewCalledWithProvider), 5)

appCount := 0
for _, entry := range fakePhaseFactory.NewCalledWithProvider {
switch entry.Name() {
case "detector", "builder", "exporter":
h.AssertSliceContainsInOrder(t, entry.ContainerConfig().Cmd, "-app", "/app")
appCount++
}
}
h.AssertEq(t, appCount, 3)
})
h.AssertNil(t, err)
})
})

Expand Down
5 changes: 3 additions & 2 deletions internal/build/phase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ func testPhase(t *testing.T, when spec.G, it spec.S) {
lifecycleExec.Builder().UID(),
lifecycleExec.Builder().GID(),
osType,
false,
nil,
),
),
Expand Down Expand Up @@ -225,7 +226,7 @@ func testPhase(t *testing.T, when spec.G, it spec.S) {
lifecycleExec,
build.WithArgs("read", "/workspace/fake-app-file"),
build.WithContainerOperations(
build.CopyDir(lifecycleExec.AppPath(), "/workspace", 0, 0, osType, nil),
build.CopyDir(lifecycleExec.AppPath(), "/workspace", 0, 0, osType, false, nil),
),
))
h.AssertNil(t, err)
Expand Down Expand Up @@ -437,7 +438,7 @@ func assertAppModTimePreserved(t *testing.T, lifecycle *build.LifecycleExecution
lifecycle,
build.WithArgs("read", "/workspace/fake-app-file"),
build.WithContainerOperations(
build.CopyDir(lifecycle.AppPath(), "/workspace", 0, 0, osType, nil),
build.CopyDir(lifecycle.AppPath(), "/workspace", 0, 0, osType, false, nil),
),
))
assertRunSucceeds(t, readPhase, outBuf, errBuf)
Expand Down
2 changes: 1 addition & 1 deletion internal/builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {

lifecycleTarReader := archive.ReadDirAsTar(
filepath.Join("testdata", "lifecycle", "platform-0.4"),
".", 0, 0, 0755, true, nil,
".", 0, 0, 0755, true, false, nil,
)

descriptorContents, err := ioutil.ReadFile(filepath.Join("testdata", "lifecycle", "platform-0.4", "lifecycle.toml"))
Expand Down
2 changes: 1 addition & 1 deletion internal/buildpackage/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func (b *PackageBuilder) SaveAsFile(path, imageOS string) error {
tw := tar.NewWriter(outputFile)
defer tw.Close()

return archive.WriteDirToTar(tw, layoutDir, "/", 0, 0, 0755, true, nil)
return archive.WriteDirToTar(tw, layoutDir, "/", 0, 0, 0755, true, false, nil)
}

func (b *PackageBuilder) SaveAsImage(repoName string, publish bool, imageOS string) (imgutil.Image, error) {
Expand Down
20 changes: 16 additions & 4 deletions pkg/archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ func (defaultTarWriterFactory) NewWriter(w io.Writer) TarWriter {
return tar.NewWriter(w)
}

func ReadDirAsTar(srcDir, basePath string, uid, gid int, mode int64, normalizeModTime bool, fileFilter func(string) bool) io.ReadCloser {
func ReadDirAsTar(srcDir, basePath string, uid, gid int, mode int64, normalizeModTime, includeRoot bool, fileFilter func(string) bool) io.ReadCloser {
return GenerateTar(func(tw TarWriter) error {
return WriteDirToTar(tw, srcDir, basePath, uid, gid, mode, normalizeModTime, fileFilter)
return WriteDirToTar(tw, srcDir, basePath, uid, gid, mode, normalizeModTime, includeRoot, fileFilter)
})
}

Expand Down Expand Up @@ -164,8 +164,20 @@ func ReadTarEntry(rc io.Reader, entryPath string) (*tar.Header, []byte, error) {
}

// WriteDirToTar writes the contents of a directory to a tar writer. `basePath` is the "location" in the tar the
// contents will be placed.
func WriteDirToTar(tw TarWriter, srcDir, basePath string, uid, gid int, mode int64, normalizeModTime bool, fileFilter func(string) bool) error {
// contents will be placed. The includeRoot param sets the permissions and metadata on the root file.
func WriteDirToTar(tw TarWriter, srcDir, basePath string, uid, gid int, mode int64, normalizeModTime, includeRoot bool, fileFilter func(string) bool) error {
if includeRoot {
rootHeader := &tar.Header{
Typeflag: tar.TypeDir,
Name: basePath,
Mode: mode,
}
finalizeHeader(rootHeader, uid, gid, mode, normalizeModTime)
if err := tw.WriteHeader(rootHeader); err != nil {
return err
}
}

return filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error {
var relPath string
if fileFilter != nil {
Expand Down
Loading

0 comments on commit 35ed268

Please sign in to comment.