forked from toolshed/abra
		
	
		
			
				
	
	
		
			155 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package app
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"coopcloud.tech/abra/cli/formatter"
 | |
| 	"coopcloud.tech/abra/cli/internal"
 | |
| 	"coopcloud.tech/abra/pkg/client"
 | |
| 	"coopcloud.tech/abra/pkg/config"
 | |
| 	"github.com/docker/docker/api/types"
 | |
| 	"github.com/docker/docker/api/types/filters"
 | |
| 	"github.com/docker/docker/pkg/archive"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| 	"github.com/urfave/cli/v2"
 | |
| )
 | |
| 
 | |
| var appCpCommand = &cli.Command{
 | |
| 	Name:      "cp",
 | |
| 	Aliases:   []string{"c"},
 | |
| 	ArgsUsage: "<src> <dst>",
 | |
| 	Usage:     "Copy files to/from a running app service",
 | |
| 	Description: `
 | |
| This command supports copying 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 <app> myfile.txt app:/
 | |
| 
 | |
| And if you want to copy that file back to your current working directory locally:
 | |
| 
 | |
| 		abra app cp <app> app:/myfile.txt .
 | |
| 
 | |
| `,
 | |
| 	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(dstPath); os.IsNotExist(err) {
 | |
| 				logrus.Fatalf("%s does not exist locally?", dstPath)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		appFiles, err := config.LoadAppFiles("")
 | |
| 		if err != nil {
 | |
| 			logrus.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		appEnv, err := config.GetApp(appFiles, app.Name)
 | |
| 		if err != nil {
 | |
| 			logrus.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		cl, err := client.New(app.Server)
 | |
| 		if err != nil {
 | |
| 			logrus.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		filters := filters.NewArgs()
 | |
| 		filters.Add("name", fmt.Sprintf("%s_%s", appEnv.StackName(), service))
 | |
| 		containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
 | |
| 		if err != nil {
 | |
| 			logrus.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		if len(containers) != 1 {
 | |
| 			logrus.Fatalf("expected 1 container but got %v", len(containers))
 | |
| 		}
 | |
| 		container := containers[0]
 | |
| 
 | |
| 		logrus.Debugf("retrieved '%s' as target container on '%s'", formatter.ShortenID(container.ID), app.Server)
 | |
| 
 | |
| 		if isToContainer {
 | |
| 			if _, err := os.Stat(srcPath); err != nil {
 | |
| 				logrus.Fatalf("'%s' does not exist?", srcPath)
 | |
| 			}
 | |
| 
 | |
| 			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(c.Context, container.ID, dstPath, content, copyOpts); err != nil {
 | |
| 				logrus.Fatal(err)
 | |
| 			}
 | |
| 		} else {
 | |
| 			content, _, err := cl.CopyFromContainer(c.Context, 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
 | |
| 	},
 | |
| 	BashComplete: func(c *cli.Context) {
 | |
| 		appNames, err := config.GetAppNames()
 | |
| 		if err != nil {
 | |
| 			logrus.Warn(err)
 | |
| 		}
 | |
| 		if c.NArg() > 0 {
 | |
| 			return
 | |
| 		}
 | |
| 		for _, a := range appNames {
 | |
| 			fmt.Println(a)
 | |
| 		}
 | |
| 	},
 | |
| }
 |