forked from AliyunContainerService/pouch
/
start.go
143 lines (125 loc) · 3.5 KB
/
start.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package main
import (
"context"
"fmt"
"io"
"os"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
)
// startDescription is used to describe start command in detail and auto generate command doc.
var startDescription = "Start a created container object in Pouchd. " +
"When starting, the relevant resource preserved during creating period comes into use." +
"This is useful when you wish to start a container which has been created in advance." +
"The container you started will be running if no error occurs."
// StartCommand use to implement 'start' command, it start a container.
type StartCommand struct {
baseCommand
detachKeys string
attach bool
stdin bool
}
// Init initialize start command.
func (s *StartCommand) Init(c *Cli) {
s.cli = c
s.cmd = &cobra.Command{
Use: "start [OPTIONS] CONTAINER",
Short: "Start a created or stopped container",
Long: startDescription,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return s.runStart(args)
},
Example: startExample(),
}
s.addFlags()
}
// addFlags adds flags for specific command.
func (s *StartCommand) addFlags() {
flagSet := s.cmd.Flags()
flagSet.StringVar(&s.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
flagSet.BoolVarP(&s.attach, "attach", "a", false, "Attach container's STDOUT and STDERR")
flagSet.BoolVarP(&s.stdin, "interactive", "i", false, "Attach container's STDIN")
}
// runStart is the entry of start command.
func (s *StartCommand) runStart(args []string) error {
container := args[0]
// attach to io.
ctx := context.Background()
apiClient := s.cli.Client()
var wait chan struct{}
if s.attach || s.stdin {
in, out, err := setRawMode(s.stdin, false)
if err != nil {
return fmt.Errorf("failed to set raw mode")
}
defer func() {
if err := restoreMode(in, out); err != nil {
fmt.Fprintf(os.Stderr, "failed to restore term mode")
}
}()
conn, br, err := apiClient.ContainerAttach(ctx, container, s.stdin)
if err != nil {
return fmt.Errorf("failed to attach container: %v", err)
}
wait = make(chan struct{})
go func() {
io.Copy(os.Stdout, br)
close(wait)
}()
go func() {
io.Copy(conn, os.Stdin)
close(wait)
}()
}
// start container
if err := apiClient.ContainerStart(ctx, container, s.detachKeys); err != nil {
return fmt.Errorf("failed to start container %s: %v", container, err)
}
// wait the io to finish.
if s.attach || s.stdin {
<-wait
}
return nil
}
func setRawMode(stdin, stdout bool) (*terminal.State, *terminal.State, error) {
var (
in *terminal.State
out *terminal.State
err error
)
if stdin {
if in, err = terminal.MakeRaw(0); err != nil {
return nil, nil, err
}
}
if stdout {
if out, err = terminal.MakeRaw(1); err != nil {
return nil, nil, err
}
}
return in, out, nil
}
func restoreMode(in, out *terminal.State) error {
if in != nil {
if err := terminal.Restore(0, in); err != nil {
return err
}
}
if out != nil {
if err := terminal.Restore(1, out); err != nil {
return err
}
}
return nil
}
// startExample shows examples in start command, and is used in auto-generated cli docs.
func startExample() string {
return `$ pouch ps
Name ID Status Image Runtime
foo 71b9c1 Created docker.io/library/busybox:latest runc
$ pouch start foo
$ pouch ps
Name ID Status Image Runtime
foo 71b9c1 Running docker.io/library/busybox:latest runc`
}