From 547f785da541494cb6ba79f372f6471156f41e98 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Sun, 29 Aug 2021 13:41:29 +0200 Subject: [PATCH] feat: add app cp command --- TODO.md | 2 +- cli/app/cp.go | 118 +++++++++++++++++++++++++++++++++++++++++++++++++- go.sum | 6 ++- 3 files changed, 122 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index f779ef54..b063927b 100644 --- a/TODO.md +++ b/TODO.md @@ -19,7 +19,7 @@ - [x] `check` - [x] `version` - [x] `config` - - [ ] `cp` (WIP: decentral1se) + - [x] `cp` - [ ] `logs` - [x] `ps` - [ ] `restore` diff --git a/cli/app/cp.go b/cli/app/cp.go index e39d26b3..1515762e 100644 --- a/cli/app/cp.go +++ b/cli/app/cp.go @@ -1,8 +1,124 @@ package app -import "github.com/urfave/cli/v2" +import ( + "context" + "errors" + "fmt" + "os" + "strings" + + "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/client" + "coopcloud.tech/abra/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", ArgsUsage: " ", + Usage: "copy files to/from running service container", + Action: func(c *cli.Context) error { + appName := c.Args().First() + if appName == "" { + internal.ShowSubcommandHelpAndError(c, errors.New("no app name provided")) + } + + src := c.Args().Get(1) + dst := c.Args().Get(2) + if src == "" { + logrus.Fatal("missing argument") + } else if dst == "" { + logrus.Fatal("missing argument") + } + + parsedSrc := strings.SplitN(src, ":", 2) + parsedDst := strings.SplitN(dst, ":", 2) + errorMsg := "one of / 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 // + if len(parsedSrc) == 2 { + service = parsedSrc[0] + srcPath = parsedSrc[1] + dstPath = dst + } else if len(parsedDst) == 2 { + service = parsedDst[0] + dstPath = parsedDst[1] + srcPath = src + isToContainer = true // + } + + appFiles, err := config.LoadAppFiles("") + if err != nil { + logrus.Fatal(err) + } + + appEnv, err := config.GetApp(appFiles, appName) + if err != nil { + logrus.Fatal(err) + } + + ctx := context.Background() + host := appFiles[appName].Server + cl, err := client.NewClientWithContext(host) + if err != nil { + logrus.Fatal(err) + } + + filters := filters.NewArgs() + filters.Add("name", fmt.Sprintf("%s_%s", appEnv.StackName(), service)) + containers, err := cl.ContainerList(ctx, 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] + + 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(ctx, container.ID, dstPath, content, copyOpts); err != nil { + logrus.Fatal(err) + } + } else { + content, _, err := cl.CopyFromContainer(ctx, container.ID, srcPath) + if err != nil { + logrus.Fatal(err) + } + fromTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip} + archive.Untar(content, dstPath, fromTarOpts) + defer content.Close() + } + + return nil + }, } diff --git a/go.sum b/go.sum index 77267680..505c3bd1 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,6 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -coopcloud.tech/tagcmp v0.0.0-20210809131701-f81a7c03b97c h1:j4y6MBImeCU/nH7vFt9vIkFKJFcbtdSHk3OST79Mfj4= -coopcloud.tech/tagcmp v0.0.0-20210809131701-f81a7c03b97c/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ= coopcloud.tech/tagcmp v0.0.0-20210813114741-44053d6bfba1 h1:C9PonNP/Y+X3GXarmk4Mn+XeAokspS8Ov1x2GtRzVtI= coopcloud.tech/tagcmp v0.0.0-20210813114741-44053d6bfba1/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -146,6 +144,7 @@ github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1 github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= @@ -359,6 +358,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -588,6 +588,7 @@ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59P github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.1 h1:G18PGckGdAm3yVQRWDVQ1rLSLntiniKJ0cNRT2Tm5gs= github.com/opencontainers/runc v1.0.1/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -758,6 +759,7 @@ go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvS go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=