From 27fb892208132227afcdf22e5f49af3d8ec6cab7 Mon Sep 17 00:00:00 2001 From: p4u1 Date: Tue, 14 Nov 2023 18:51:50 +0100 Subject: [PATCH] implement copy directory and file to container --- cli/app/cp.go | 39 +++++++++++++++++++-- tests/integration/app_cp.bats | 66 ++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/cli/app/cp.go b/cli/app/cp.go index 8a250efe..bc1b568d 100644 --- a/cli/app/cp.go +++ b/cli/app/cp.go @@ -6,13 +6,16 @@ import ( "fmt" "log" "os" + "path" "strings" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" - "coopcloud.tech/abra/pkg/container" + containerPkg "coopcloud.tech/abra/pkg/container" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/upstream/container" + "github.com/docker/cli/cli/command" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" dockerClient "github.com/docker/docker/client" @@ -116,24 +119,54 @@ func findContainer(cl *dockerClient.Client, stack, service string) (types.Contai filters := filters.NewArgs() filters.Add("name", fmt.Sprintf("^%s_%s", stack, service)) - container, err := container.GetContainer(context.Background(), cl, filters, internal.NoInput) + container, err := containerPkg.GetContainer(context.Background(), cl, filters, internal.NoInput) if err != nil { return types.Container{}, err } return container, nil } +// copyToContainer works with one of the following: +// / -> = / +// / -> / = / (overrides the file) +// -> = / (creates the dst_dir if it does not exist) func copyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath string) error { - toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip} + toTarOpts := &archive.TarOptions{IncludeSourceDir: true, NoOverwriteDirNonDir: true, Compression: archive.Gzip} content, err := archive.TarWithOptions(srcPath, toTarOpts) if err != nil { return err } + srcStat, _ := os.Stat(srcPath) + stat, _ := cl.ContainerStatPath(context.Background(), containerID, dstPath) + if !stat.Mode.IsDir() { + if srcStat.IsDir() { + // Make sure the dst directory exits, when copying a directory. + dcli, err := command.NewDockerCli() + if err != nil { + return err + } + if err := container.RunExec(dcli, cl, containerID, &types.ExecConfig{ + AttachStderr: true, + AttachStdin: true, + AttachStdout: true, + Cmd: []string{"mkdir", "-p", dstPath}, + Detach: false, + Tty: true, + }); err != nil { + return fmt.Errorf("create remote directory: %s", err) + } + } else { + // Remove the file component from the path, since docker can only copy to a directoy. + dstPath, _ = path.Split(dstPath) + } + } + copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false} if err := cl.CopyToContainer(context.Background(), containerID, dstPath, content, copyOpts); err != nil { return err } + return nil } diff --git a/tests/integration/app_cp.bats b/tests/integration/app_cp.bats index 2687e9d4..112635e1 100644 --- a/tests/integration/app_cp.bats +++ b/tests/integration/app_cp.bats @@ -76,7 +76,7 @@ teardown(){ @test "error if service doesn't exist" { _deploy_app - run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt" + run bash -c "echo foo > $BATS_TMPDIR/myfile.txt" assert_success run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" doesnt_exist:/ @@ -90,15 +90,19 @@ teardown(){ } # bats test_tags=slow -@test "copy to container" { +@test "copy local file to container directory" { _deploy_app - run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt" + run bash -c "echo foo > $BATS_TMPDIR/myfile.txt" assert_success run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc assert_success + run $ABRA app run "$TEST_APP_DOMAIN" app cat /etc/myfile.txt + assert_success + assert_output --partial "foo" + run rm -rf "$BATS_TMPDIR/myfile.txt" assert_success @@ -106,7 +110,61 @@ teardown(){ } # bats test_tags=slow -@test "copy from container" { +@test "copy local file to container file (override on remote)" { + _deploy_app + + run bash -c "echo 'foo' > $BATS_TMPDIR/myfile.txt" + assert_success + + run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc + assert_success + + run $ABRA app run "$TEST_APP_DOMAIN" app cat /etc/myfile.txt + assert_success + assert_output --partial "foo" + + run bash -c "echo 'bar' > $BATS_TMPDIR/myfile.txt" + assert_success + + run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc/myfile.txt + assert_success + + run $ABRA app run "$TEST_APP_DOMAIN" app cat /etc/myfile.txt + assert_success + assert_output --partial "bar" + + run rm -rf "$BATS_TMPDIR/myfile.txt" + assert_success + + _undeploy_app +} + +# bats test_tags=slow +@test "copy local directory to container directory (and creates missing directory)" { + _deploy_app + + run bash -c "mkdir -p $BATS_TMPDIR/mydir" + assert_success + + run bash -c "echo 'foo' > $BATS_TMPDIR/mydir/myfile.txt" + assert_success + + run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/mydir" app:/etc/mydir + assert_success + + run $ABRA app run "$TEST_APP_DOMAIN" app ls /etc/mydir + assert_success + assert_output --partial "myfile.txt" + + + run rm -rf "$BATS_TMPDIR/mydir" + assert_success + + _undeploy_app +} + +# bats test_tags=slow +@test "copy container file to local directory" { _deploy_app run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt"