feat: finish app run command
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
decentral1se 2021-08-29 21:20:21 +02:00
parent 8cc691ab52
commit 440911f983
No known key found for this signature in database
GPG Key ID: 5E2EF5A63E3718CC
6 changed files with 44 additions and 114 deletions

View File

@ -24,7 +24,7 @@
- [x] `ps` - [x] `ps`
- [x] `restore` - [x] `restore`
- [x] `rm` - [x] `rm`
- [ ] `run` (WIP: decentral1se) - [x] `run`
- [ ] `rollback` - [ ] `rollback`
- [ ] `secret` - [ ] `secret`
- [ ] `generate` - [ ] `generate`

View File

@ -7,7 +7,9 @@ import (
"coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/client" "coopcloud.tech/abra/client"
"coopcloud.tech/abra/client/container"
"coopcloud.tech/abra/config" "coopcloud.tech/abra/config"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -77,13 +79,14 @@ var appRunCommand = &cli.Command{
cmd := c.Args().Slice()[2:] cmd := c.Args().Slice()[2:]
execCreateOpts := types.ExecConfig{ execCreateOpts := types.ExecConfig{
//AttachStderr: true, AttachStderr: true,
AttachStdin: true, AttachStdin: true,
//AttachStdout: true, AttachStdout: true,
Cmd: cmd, Cmd: cmd,
Detach: false, Detach: false,
Tty: true, Tty: true,
} }
if user != "" { if user != "" {
execCreateOpts.User = user execCreateOpts.User = user
} }
@ -91,40 +94,19 @@ var appRunCommand = &cli.Command{
execCreateOpts.Tty = false execCreateOpts.Tty = false
} }
// container := containers[0] // FIXME: an absolutely monumental hack to instantiate another command-line
// idResp, err := cl.ContainerExecCreate(ctx, container.ID, execCreateOpts) // client withing our command-line client so that we pass something down
// if err != nil { // the tubes that satisfies the necessary interface requirements. We should
// logrus.Fatal(err) // refactor our vendored container code to not require all this cruft. For
// } // now, It Works.
dcli, err := command.NewDockerCli()
if err != nil {
logrus.Fatal(err)
}
// execAttachOpts := types.ExecStartCheck{Detach: false, Tty: true} if err := container.RunExec(dcli, cl, containers[0].ID, &execCreateOpts); err != nil {
// hResp, err := cl.ContainerExecAttach(ctx, idResp.ID, execAttachOpts) logrus.Fatal(err)
// if err != nil { }
// logrus.Fatal(err)
// }
// defer hResp.Close()
// var outBuf, errBuf bytes.Buffer
// outputDone := make(chan error)
// go func() {
// _, err = stdcopy.StdCopy(&outBuf, &errBuf, hResp.Reader)
// outputDone <- err
// }()
// select {
// case err := <-outputDone:
// if err != nil {
// logrus.Fatal(err)
// }
// break
// case <-ctx.Done():
// break
// }
// iresp, err := cl.ContainerExecInspect(ctx, idResp.ID)
// if err != nil {
// logrus.Fatal(err)
// }
return nil return nil
}, },

View File

@ -8,41 +8,19 @@ import (
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/cliconfig/configfile"
apiclient "github.com/docker/docker/client" apiclient "github.com/docker/docker/client"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type execOptions struct { func RunExec(dockerCli command.Cli, client *apiclient.Client, containerID string, execConfig *types.ExecConfig) error {
detachKeys string
interactive bool
tty bool
detach bool
user string
privileged bool
env opts.ListOpts
workdir string
container string
command []string
envFile opts.ListOpts
}
func RunExec(dockerCli command.Cli, options execOptions) error {
execConfig, err := parseExec(options, dockerCli.ConfigFile())
if err != nil {
return err
}
ctx := context.Background() ctx := context.Background()
client := dockerCli.Client()
// We need to check the tty _before_ we do the ContainerExecCreate, because // We need to check the tty _before_ we do the ContainerExecCreate, because
// otherwise if we error out we will leak execIDs on the server (and // otherwise if we error out we will leak execIDs on the server (and
// there's no easy way to clean those up). But also in order to make "not // there's no easy way to clean those up). But also in order to make "not
// exist" errors take precedence we do a dummy inspect first. // exist" errors take precedence we do a dummy inspect first.
if _, err := client.ContainerInspect(ctx, options.container); err != nil { if _, err := client.ContainerInspect(ctx, containerID); err != nil {
return err return err
} }
if !execConfig.Detach { if !execConfig.Detach {
@ -51,7 +29,7 @@ func RunExec(dockerCli command.Cli, options execOptions) error {
} }
} }
response, err := client.ContainerExecCreate(ctx, options.container, *execConfig) response, err := client.ContainerExecCreate(ctx, containerID, *execConfig)
if err != nil { if err != nil {
return err return err
} }
@ -68,10 +46,11 @@ func RunExec(dockerCli command.Cli, options execOptions) error {
} }
return client.ContainerExecStart(ctx, execID, execStartCheck) return client.ContainerExecStart(ctx, execID, execStartCheck)
} }
return interactiveExec(ctx, dockerCli, execConfig, execID) return interactiveExec(ctx, dockerCli, client, execConfig, execID)
} }
func interactiveExec(ctx context.Context, dockerCli command.Cli, execConfig *types.ExecConfig, execID string) error { func interactiveExec(ctx context.Context, dockerCli command.Cli, client *apiclient.Client,
execConfig *types.ExecConfig, execID string) error {
// Interactive exec requested. // Interactive exec requested.
var ( var (
out, stderr io.Writer out, stderr io.Writer
@ -92,7 +71,6 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execConfig *typ
} }
} }
client := dockerCli.Client()
execStartCheck := types.ExecStartCheck{ execStartCheck := types.ExecStartCheck{
Tty: execConfig.Tty, Tty: execConfig.Tty,
} }
@ -122,7 +100,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execConfig *typ
}() }()
if execConfig.Tty && dockerCli.In().IsTerminal() { if execConfig.Tty && dockerCli.In().IsTerminal() {
if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil { if err := MonitorTtySize(ctx, client, dockerCli, execID, true); err != nil {
fmt.Fprintln(dockerCli.Err(), "Error monitoring TTY size:", err) fmt.Fprintln(dockerCli.Err(), "Error monitoring TTY size:", err)
} }
} }
@ -150,38 +128,3 @@ func getExecExitStatus(ctx context.Context, client apiclient.ContainerAPIClient,
} }
return nil return nil
} }
// parseExec parses the specified args for the specified command and generates
// an ExecConfig from it.
func parseExec(execOpts execOptions, configFile *configfile.ConfigFile) (*types.ExecConfig, error) {
execConfig := &types.ExecConfig{
User: execOpts.user,
Privileged: execOpts.privileged,
Tty: execOpts.tty,
Cmd: execOpts.command,
Detach: execOpts.detach,
WorkingDir: execOpts.workdir,
}
// collect all the environment variables for the container
var err error
if execConfig.Env, err = opts.ReadKVEnvStrings(execOpts.envFile.GetAll(), execOpts.env.GetAll()); err != nil {
return nil, err
}
// If -d is not set, attach to everything by default
if !execOpts.detach {
execConfig.AttachStdout = true
execConfig.AttachStderr = true
if execOpts.interactive {
execConfig.AttachStdin = true
}
}
if execOpts.detachKeys != "" {
execConfig.DetachKeys = execOpts.detachKeys
} else {
execConfig.DetachKeys = configFile.DetachKeys
}
return execConfig, nil
}

View File

@ -11,6 +11,7 @@ import (
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/client" "github.com/docker/docker/client"
apiclient "github.com/docker/docker/client"
"github.com/moby/sys/signal" "github.com/moby/sys/signal"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -40,23 +41,23 @@ func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id strin
} }
// resizeTty is to resize the tty with cli out's tty size // resizeTty is to resize the tty with cli out's tty size
func resizeTty(ctx context.Context, cli command.Cli, id string, isExec bool) error { func resizeTty(ctx context.Context, client *apiclient.Client, cli command.Cli, id string, isExec bool) error {
height, width := cli.Out().GetTtySize() height, width := cli.Out().GetTtySize()
return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) return resizeTtyTo(ctx, client, id, height, width, isExec)
} }
// initTtySize is to init the tty's size to the same as the window, if there is an error, it will retry 5 times. // initTtySize is to init the tty's size to the same as the window, if there is an error, it will retry 5 times.
func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, resizeTtyFunc func(ctx context.Context, cli command.Cli, id string, isExec bool) error) { func initTtySize(ctx context.Context, client *apiclient.Client, cli command.Cli, id string, isExec bool, resizeTtyFunc func(ctx context.Context, client *apiclient.Client, cli command.Cli, id string, isExec bool) error) {
rttyFunc := resizeTtyFunc rttyFunc := resizeTtyFunc
if rttyFunc == nil { if rttyFunc == nil {
rttyFunc = resizeTty rttyFunc = resizeTty
} }
if err := rttyFunc(ctx, cli, id, isExec); err != nil { if err := rttyFunc(ctx, client, cli, id, isExec); err != nil {
go func() { go func() {
var err error var err error
for retry := 0; retry < 5; retry++ { for retry := 0; retry < 5; retry++ {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
if err = rttyFunc(ctx, cli, id, isExec); err == nil { if err = rttyFunc(ctx, client, cli, id, isExec); err == nil {
break break
} }
} }
@ -68,8 +69,8 @@ func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, r
} }
// MonitorTtySize updates the container tty size when the terminal tty changes size // MonitorTtySize updates the container tty size when the terminal tty changes size
func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool) error { func MonitorTtySize(ctx context.Context, client *apiclient.Client, cli command.Cli, id string, isExec bool) error {
initTtySize(ctx, cli, id, isExec, resizeTty) initTtySize(ctx, client, cli, id, isExec, resizeTty)
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
go func() { go func() {
prevH, prevW := cli.Out().GetTtySize() prevH, prevW := cli.Out().GetTtySize()
@ -78,7 +79,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool
h, w := cli.Out().GetTtySize() h, w := cli.Out().GetTtySize()
if prevW != w || prevH != h { if prevW != w || prevH != h {
resizeTty(ctx, cli, id, isExec) resizeTty(ctx, client, cli, id, isExec)
} }
prevH = h prevH = h
prevW = w prevW = w
@ -89,7 +90,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool
gosignal.Notify(sigchan, signal.SIGWINCH) gosignal.Notify(sigchan, signal.SIGWINCH)
go func() { go func() {
for range sigchan { for range sigchan {
resizeTty(ctx, cli, id, isExec) resizeTty(ctx, client, cli, id, isExec)
} }
}() }()
} }

1
go.mod
View File

@ -17,6 +17,7 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/hetznercloud/hcloud-go v1.28.0 github.com/hetznercloud/hcloud-go v1.28.0
github.com/moby/sys/mount v0.2.0 // indirect github.com/moby/sys/mount v0.2.0 // indirect
github.com/moby/sys/signal v0.5.0
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5

5
go.sum
View File

@ -538,6 +538,8 @@ github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7s
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/signal v0.5.0 h1:MzpEFrMxugDynb1gkTIThU1O3wEmrAkOY+G9dHcHnCc=
github.com/moby/sys/signal v0.5.0/go.mod h1:JwObcMnOrUy2VTP5swPKWwywH0Mbgk8Y5qua9iwtIRM=
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
@ -929,8 +931,9 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=