forked from toolshed/abra
.gitea
cli
app
app.go
backup.go
check.go
cmd.go
cmd_test.go
config.go
cp.go
deploy.go
errors.go
list.go
logs.go
new.go
ps.go
remove.go
restart.go
restore.go
rollback.go
run.go
secret.go
services.go
undeploy.go
upgrade.go
version.go
volume.go
catalogue
internal
recipe
server
updater
cli.go
cmd
pkg
scripts
tests
.dockerignore
.drone.yml
.envrc.sample
.gitignore
.goreleaser.yml
AUTHORS.md
Dockerfile
LICENSE
Makefile
README.md
go.mod
go.sum
renovate.json
150 lines
3.9 KiB
Go
150 lines
3.9 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"coopcloud.tech/abra/cli/internal"
|
|
"coopcloud.tech/abra/pkg/autocomplete"
|
|
"coopcloud.tech/abra/pkg/client"
|
|
"coopcloud.tech/abra/pkg/config"
|
|
"coopcloud.tech/abra/pkg/container"
|
|
"coopcloud.tech/abra/pkg/formatter"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
dockerClient "github.com/docker/docker/client"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var appCpCommand = cli.Command{
|
|
Name: "cp",
|
|
Aliases: []string{"c"},
|
|
ArgsUsage: "<domain> <src> <dst>",
|
|
Flags: []cli.Flag{
|
|
internal.DebugFlag,
|
|
internal.NoInputFlag,
|
|
},
|
|
Before: internal.SubCommandBefore,
|
|
Usage: "Copy files to/from a deployed app service",
|
|
Description: `
|
|
Copy files to and from any app service file system.
|
|
|
|
If you want to copy a myfile.txt to the root of the app service:
|
|
|
|
abra app cp <domain> myfile.txt app:/
|
|
|
|
And if you want to copy that file back to your current working directory locally:
|
|
|
|
abra app cp <domain> app:/myfile.txt .
|
|
`,
|
|
BashComplete: autocomplete.AppNameComplete,
|
|
Action: func(c *cli.Context) error {
|
|
app := internal.ValidateApp(c)
|
|
|
|
src := c.Args().Get(1)
|
|
dst := c.Args().Get(2)
|
|
if src == "" {
|
|
logrus.Fatal("missing <src> argument")
|
|
} else if dst == "" {
|
|
logrus.Fatal("missing <dest> argument")
|
|
}
|
|
|
|
parsedSrc := strings.SplitN(src, ":", 2)
|
|
parsedDst := strings.SplitN(dst, ":", 2)
|
|
errorMsg := "one of <src>/<dest> arguments must take $SERVICE:$PATH form"
|
|
if len(parsedSrc) == 2 && len(parsedDst) == 2 {
|
|
logrus.Fatal(errorMsg)
|
|
} else if len(parsedSrc) != 2 {
|
|
if len(parsedDst) != 2 {
|
|
logrus.Fatal(errorMsg)
|
|
}
|
|
} else if len(parsedDst) != 2 {
|
|
if len(parsedSrc) != 2 {
|
|
logrus.Fatal(errorMsg)
|
|
}
|
|
}
|
|
|
|
var service string
|
|
var srcPath string
|
|
var dstPath string
|
|
isToContainer := false // <container:src> <dst>
|
|
if len(parsedSrc) == 2 {
|
|
service = parsedSrc[0]
|
|
srcPath = parsedSrc[1]
|
|
dstPath = dst
|
|
logrus.Debugf("assuming transfer is coming FROM the container")
|
|
} else if len(parsedDst) == 2 {
|
|
service = parsedDst[0]
|
|
dstPath = parsedDst[1]
|
|
srcPath = src
|
|
isToContainer = true // <src> <container:dst>
|
|
logrus.Debugf("assuming transfer is going TO the container")
|
|
}
|
|
|
|
if isToContainer {
|
|
if _, err := os.Stat(srcPath); os.IsNotExist(err) {
|
|
logrus.Fatalf("%s does not exist locally?", srcPath)
|
|
}
|
|
}
|
|
|
|
cl, err := client.New(app.Server)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := configureAndCp(c, cl, app, srcPath, dstPath, service, isToContainer); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func configureAndCp(
|
|
c *cli.Context,
|
|
cl *dockerClient.Client,
|
|
app config.App,
|
|
srcPath string,
|
|
dstPath string,
|
|
service string,
|
|
isToContainer bool) error {
|
|
filters := filters.NewArgs()
|
|
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), service))
|
|
|
|
container, err := container.GetContainer(context.Background(), cl, filters, internal.NoInput)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
|
|
|
|
if isToContainer {
|
|
toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
|
|
content, err := archive.TarWithOptions(srcPath, toTarOpts)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
|
|
if err := cl.CopyToContainer(context.Background(), container.ID, dstPath, content, copyOpts); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
} else {
|
|
content, _, err := cl.CopyFromContainer(context.Background(), container.ID, srcPath)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
defer content.Close()
|
|
fromTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
|
|
if err := archive.Untar(content, dstPath, fromTarOpts); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|