forked from toolshed/abra
		
	feat: finish app run command
This commit is contained in:
		
							
								
								
									
										2
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								TODO.md
									
									
									
									
									
								
							| @ -24,7 +24,7 @@ | ||||
|     - [x] `ps` | ||||
|     - [x] `restore` | ||||
|     - [x] `rm` | ||||
|     - [ ] `run` (WIP: decentral1se) | ||||
|     - [x] `run` | ||||
|     - [ ] `rollback` | ||||
|     - [ ] `secret` | ||||
|       - [ ] `generate` | ||||
|  | ||||
| @ -7,7 +7,9 @@ import ( | ||||
|  | ||||
| 	"coopcloud.tech/abra/cli/internal" | ||||
| 	"coopcloud.tech/abra/client" | ||||
| 	"coopcloud.tech/abra/client/container" | ||||
| 	"coopcloud.tech/abra/config" | ||||
| 	"github.com/docker/cli/cli/command" | ||||
| 	"github.com/docker/docker/api/types" | ||||
| 	"github.com/docker/docker/api/types/filters" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| @ -77,13 +79,14 @@ var appRunCommand = &cli.Command{ | ||||
|  | ||||
| 		cmd := c.Args().Slice()[2:] | ||||
| 		execCreateOpts := types.ExecConfig{ | ||||
| 			//AttachStderr: true, | ||||
| 			AttachStderr: true, | ||||
| 			AttachStdin:  true, | ||||
| 			//AttachStdout: true, | ||||
| 			AttachStdout: true, | ||||
| 			Cmd:          cmd, | ||||
| 			Detach:       false, | ||||
| 			Tty:          true, | ||||
| 		} | ||||
|  | ||||
| 		if user != "" { | ||||
| 			execCreateOpts.User = user | ||||
| 		} | ||||
| @ -91,40 +94,19 @@ var appRunCommand = &cli.Command{ | ||||
| 			execCreateOpts.Tty = false | ||||
| 		} | ||||
|  | ||||
| 		// container := containers[0] | ||||
| 		// idResp, err := cl.ContainerExecCreate(ctx, container.ID, execCreateOpts) | ||||
| 		// if err != nil { | ||||
| 		// 	logrus.Fatal(err) | ||||
| 		// } | ||||
| 		// FIXME: an absolutely monumental hack to instantiate another command-line | ||||
| 		// client withing our command-line client so that we pass something down | ||||
| 		// the tubes that satisfies the necessary interface requirements. We should | ||||
| 		// 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} | ||||
| 		// hResp, err := cl.ContainerExecAttach(ctx, idResp.ID, execAttachOpts) | ||||
| 		// 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) | ||||
| 		// } | ||||
| 		if err := container.RunExec(dcli, cl, containers[0].ID, &execCreateOpts); err != nil { | ||||
| 			logrus.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}, | ||||
|  | ||||
| @ -8,41 +8,19 @@ import ( | ||||
|  | ||||
| 	"github.com/docker/cli/cli" | ||||
| 	"github.com/docker/cli/cli/command" | ||||
| 	"github.com/docker/cli/opts" | ||||
| 	"github.com/docker/docker/api/types" | ||||
| 	"github.com/docker/docker/cliconfig/configfile" | ||||
| 	apiclient "github.com/docker/docker/client" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| type execOptions struct { | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| func RunExec(dockerCli command.Cli, client *apiclient.Client, containerID string, execConfig *types.ExecConfig) error { | ||||
| 	ctx := context.Background() | ||||
| 	client := dockerCli.Client() | ||||
|  | ||||
| 	// 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 | ||||
| 	// 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. | ||||
| 	if _, err := client.ContainerInspect(ctx, options.container); err != nil { | ||||
| 	if _, err := client.ContainerInspect(ctx, containerID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	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 { | ||||
| 		return err | ||||
| 	} | ||||
| @ -68,10 +46,11 @@ func RunExec(dockerCli command.Cli, options execOptions) error { | ||||
| 		} | ||||
| 		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. | ||||
| 	var ( | ||||
| 		out, stderr io.Writer | ||||
| @ -92,7 +71,6 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execConfig *typ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	client := dockerCli.Client() | ||||
| 	execStartCheck := types.ExecStartCheck{ | ||||
| 		Tty: execConfig.Tty, | ||||
| 	} | ||||
| @ -122,7 +100,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execConfig *typ | ||||
| 	}() | ||||
|  | ||||
| 	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) | ||||
| 		} | ||||
| 	} | ||||
| @ -150,38 +128,3 @@ func getExecExitStatus(ctx context.Context, client apiclient.ContainerAPIClient, | ||||
| 	} | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| @ -11,6 +11,7 @@ import ( | ||||
| 	"github.com/docker/cli/cli/command" | ||||
| 	"github.com/docker/docker/api/types" | ||||
| 	"github.com/docker/docker/client" | ||||
| 	apiclient "github.com/docker/docker/client" | ||||
| 	"github.com/moby/sys/signal" | ||||
| 	"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 | ||||
| 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() | ||||
| 	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. | ||||
| 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 | ||||
| 	if rttyFunc == nil { | ||||
| 		rttyFunc = resizeTty | ||||
| 	} | ||||
| 	if err := rttyFunc(ctx, cli, id, isExec); err != nil { | ||||
| 	if err := rttyFunc(ctx, client, cli, id, isExec); err != nil { | ||||
| 		go func() { | ||||
| 			var err error | ||||
| 			for retry := 0; retry < 5; retry++ { | ||||
| 				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 | ||||
| 				} | ||||
| 			} | ||||
| @ -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 | ||||
| func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool) error { | ||||
| 	initTtySize(ctx, cli, id, isExec, resizeTty) | ||||
| func MonitorTtySize(ctx context.Context, client *apiclient.Client, cli command.Cli, id string, isExec bool) error { | ||||
| 	initTtySize(ctx, client, cli, id, isExec, resizeTty) | ||||
| 	if runtime.GOOS == "windows" { | ||||
| 		go func() { | ||||
| 			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() | ||||
|  | ||||
| 				if prevW != w || prevH != h { | ||||
| 					resizeTty(ctx, cli, id, isExec) | ||||
| 					resizeTty(ctx, client, cli, id, isExec) | ||||
| 				} | ||||
| 				prevH = h | ||||
| 				prevW = w | ||||
| @ -89,7 +90,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool | ||||
| 		gosignal.Notify(sigchan, signal.SIGWINCH) | ||||
| 		go func() { | ||||
| 			for range sigchan { | ||||
| 				resizeTty(ctx, cli, id, isExec) | ||||
| 				resizeTty(ctx, client, cli, id, isExec) | ||||
| 			} | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @ -17,6 +17,7 @@ require ( | ||||
| 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect | ||||
| 	github.com/hetznercloud/hcloud-go v1.28.0 | ||||
| 	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/morikuni/aec v1.0.0 // indirect | ||||
| 	github.com/olekukonko/tablewriter v0.0.5 | ||||
|  | ||||
							
								
								
									
										5
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								go.sum
									
									
									
									
									
								
							| @ -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.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/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/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= | ||||
| 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-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-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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= | ||||
|  | ||||
		Reference in New Issue
	
	Block a user