Browse code

Update nsinit to be nicer to work with and test Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/06/05 08:46:30
Showing 6 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,74 @@
0
+package main
1
+
2
+import (
3
+	"fmt"
4
+	"log"
5
+	"os"
6
+	"os/exec"
7
+	"os/signal"
8
+
9
+	"github.com/codegangsta/cli"
10
+	"github.com/dotcloud/docker/pkg/libcontainer"
11
+	"github.com/dotcloud/docker/pkg/libcontainer/namespaces"
12
+)
13
+
14
+var execCommand = cli.Command{
15
+	Name:   "exec",
16
+	Usage:  "execute a new command inside a container",
17
+	Action: execAction,
18
+}
19
+
20
+func execAction(context *cli.Context) {
21
+	var (
22
+		err             error
23
+		nspid, exitCode int
24
+	)
25
+
26
+	if nspid, err = readPid(); err != nil && !os.IsNotExist(err) {
27
+		log.Fatalf("unable to read pid: %s", err)
28
+	}
29
+
30
+	if nspid > 0 {
31
+		exitCode, err = namespaces.ExecIn(container, nspid, []string(context.Args()))
32
+	} else {
33
+		term := namespaces.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
34
+		exitCode, err = startContainer(container, term, dataPath, []string(context.Args()))
35
+	}
36
+
37
+	if err != nil {
38
+		log.Fatalf("failed to exec: %s", err)
39
+	}
40
+
41
+	os.Exit(exitCode)
42
+}
43
+
44
+// startContainer starts the container. Returns the exit status or -1 and an
45
+// error.
46
+//
47
+// Signals sent to the current process will be forwarded to container.
48
+func startContainer(container *libcontainer.Container, term namespaces.Terminal, dataPath string, args []string) (int, error) {
49
+	var (
50
+		cmd  *exec.Cmd
51
+		sigc = make(chan os.Signal, 10)
52
+	)
53
+
54
+	signal.Notify(sigc)
55
+
56
+	createCommand := func(container *libcontainer.Container, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd {
57
+		cmd = namespaces.DefaultCreateCommand(container, console, rootfs, dataPath, init, pipe, args)
58
+		if logPath != "" {
59
+			cmd.Env = append(cmd.Env, fmt.Sprintf("log=%s", logPath))
60
+		}
61
+		return cmd
62
+	}
63
+
64
+	startCallback := func() {
65
+		go func() {
66
+			for sig := range sigc {
67
+				cmd.Process.Signal(sig)
68
+			}
69
+		}()
70
+	}
71
+
72
+	return namespaces.Exec(container, term, "", dataPath, args, createCommand, startCallback)
73
+}
0 74
new file mode 100644
... ...
@@ -0,0 +1,43 @@
0
+package main
1
+
2
+import (
3
+	"log"
4
+	"os"
5
+	"strconv"
6
+
7
+	"github.com/codegangsta/cli"
8
+	"github.com/dotcloud/docker/pkg/libcontainer/namespaces"
9
+)
10
+
11
+var (
12
+	dataPath  = os.Getenv("data_path")
13
+	console   = os.Getenv("console")
14
+	rawPipeFd = os.Getenv("pipe")
15
+
16
+	initCommand = cli.Command{
17
+		Name:   "init",
18
+		Usage:  "runs the init process inside the namespace",
19
+		Action: initAction,
20
+	}
21
+)
22
+
23
+func initAction(context *cli.Context) {
24
+	rootfs, err := os.Getwd()
25
+	if err != nil {
26
+		log.Fatal(err)
27
+	}
28
+
29
+	pipeFd, err := strconv.Atoi(rawPipeFd)
30
+	if err != nil {
31
+		log.Fatal(err)
32
+	}
33
+
34
+	syncPipe, err := namespaces.NewSyncPipeFromFd(0, uintptr(pipeFd))
35
+	if err != nil {
36
+		log.Fatalf("unable to create sync pipe: %s", err)
37
+	}
38
+
39
+	if err := namespaces.Init(container, rootfs, console, syncPipe, []string(context.Args())); err != nil {
40
+		log.Fatalf("unable to initialize for container: %s", err)
41
+	}
42
+}
... ...
@@ -1,220 +1,45 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"io/ioutil"
7 4
 	"log"
8 5
 	"os"
9
-	"os/exec"
10
-	"os/signal"
11
-	"path/filepath"
12
-	"strconv"
13 6
 
7
+	"github.com/codegangsta/cli"
14 8
 	"github.com/dotcloud/docker/pkg/libcontainer"
15
-	"github.com/dotcloud/docker/pkg/libcontainer/cgroups/fs"
16
-	"github.com/dotcloud/docker/pkg/libcontainer/namespaces"
17 9
 )
18 10
 
19 11
 var (
20
-	dataPath  = os.Getenv("data_path")
21
-	console   = os.Getenv("console")
22
-	rawPipeFd = os.Getenv("pipe")
12
+	container *libcontainer.Container
13
+	logPath   = os.Getenv("log")
23 14
 )
24 15
 
25
-func main() {
26
-	if len(os.Args) < 2 {
27
-		log.Fatalf("invalid number of arguments %d", len(os.Args))
28
-	}
29
-
30
-	switch os.Args[1] {
31
-	case "exec": // this is executed outside of the namespace in the cwd
32
-		container, err := loadContainer()
33
-		if err != nil {
34
-			log.Fatalf("unable to load container: %s", err)
35
-		}
36
-
37
-		var nspid, exitCode int
38
-		if nspid, err = readPid(); err != nil && !os.IsNotExist(err) {
39
-			log.Fatalf("unable to read pid: %s", err)
40
-		}
41
-
42
-		if nspid > 0 {
43
-			err = namespaces.ExecIn(container, nspid, os.Args[2:])
44
-		} else {
45
-			term := namespaces.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
46
-			exitCode, err = startContainer(container, term, dataPath, os.Args[2:])
47
-		}
48
-
49
-		if err != nil {
50
-			log.Fatalf("failed to exec: %s", err)
51
-		}
52
-		os.Exit(exitCode)
53
-	case "nsenter": // this is executed inside the namespace.
54
-		// nsinit nsenter <pid> <process label> <container JSON> <cmd>...
55
-		if len(os.Args) < 6 {
56
-			log.Fatalf("incorrect usage: nsinit nsenter <pid> <process label> <container JSON> <cmd>...")
57
-		}
58
-
59
-		container, err := loadContainerFromJson(os.Args[4])
60
-		if err != nil {
61
-			log.Fatalf("unable to load container: %s", err)
62
-		}
63
-
64
-		nspid, err := strconv.Atoi(os.Args[2])
65
-		if err != nil {
66
-			log.Fatalf("unable to read pid: %s from %q", err, os.Args[2])
67
-		}
68
-
69
-		if nspid <= 0 {
70
-			log.Fatalf("cannot enter into namespaces without valid pid: %q", nspid)
71
-		}
72
-
73
-		err = namespaces.NsEnter(container, os.Args[3], nspid, os.Args[5:])
74
-		if err != nil {
75
-			log.Fatalf("failed to nsenter: %s", err)
76
-		}
77
-	case "init": // this is executed inside of the namespace to setup the container
78
-		container, err := loadContainer()
79
-		if err != nil {
80
-			log.Fatalf("unable to load container: %s", err)
81
-		}
82
-
83
-		// by default our current dir is always our rootfs
84
-		rootfs, err := os.Getwd()
85
-		if err != nil {
86
-			log.Fatal(err)
87
-		}
88
-
89
-		pipeFd, err := strconv.Atoi(rawPipeFd)
90
-		if err != nil {
91
-			log.Fatal(err)
92
-		}
93
-		syncPipe, err := namespaces.NewSyncPipeFromFd(0, uintptr(pipeFd))
94
-		if err != nil {
95
-			log.Fatalf("unable to create sync pipe: %s", err)
96
-		}
97
-
98
-		if err := namespaces.Init(container, rootfs, console, syncPipe, os.Args[2:]); err != nil {
99
-			log.Fatalf("unable to initialize for container: %s", err)
100
-		}
101
-	case "stats":
102
-		container, err := loadContainer()
103
-		if err != nil {
104
-			log.Fatalf("unable to load container: %s", err)
105
-		}
106
-
107
-		// returns the stats of the current container.
108
-		stats, err := getContainerStats(container)
109
-		if err != nil {
110
-			log.Printf("Failed to get stats - %v\n", err)
111
-			os.Exit(1)
112
-		}
113
-		fmt.Printf("Stats:\n%v\n", stats)
114
-		os.Exit(0)
115
-
116
-	case "spec":
117
-		container, err := loadContainer()
118
-		if err != nil {
119
-			log.Fatalf("unable to load container: %s", err)
120
-		}
121
-
122
-		// returns the spec of the current container.
123
-		spec, err := getContainerSpec(container)
124
-		if err != nil {
125
-			log.Printf("Failed to get spec - %v\n", err)
126
-			os.Exit(1)
127
-		}
128
-		fmt.Printf("Spec:\n%v\n", spec)
129
-		os.Exit(0)
130
-
131
-	default:
132
-		log.Fatalf("command not supported for nsinit %s", os.Args[1])
133
-	}
134
-}
135
-
136
-func loadContainer() (*libcontainer.Container, error) {
137
-	f, err := os.Open(filepath.Join(dataPath, "container.json"))
16
+func preload(context *cli.Context) (err error) {
17
+	container, err = loadContainer()
138 18
 	if err != nil {
139
-		log.Printf("Path: %q", filepath.Join(dataPath, "container.json"))
140
-		return nil, err
141
-	}
142
-	defer f.Close()
143
-
144
-	var container *libcontainer.Container
145
-	if err := json.NewDecoder(f).Decode(&container); err != nil {
146
-		return nil, err
19
+		return err
147 20
 	}
148
-	return container, nil
149
-}
150 21
 
151
-func loadContainerFromJson(rawData string) (*libcontainer.Container, error) {
152
-	container := &libcontainer.Container{}
153
-	err := json.Unmarshal([]byte(rawData), container)
154
-	if err != nil {
155
-		return nil, err
22
+	if logPath != "" {
156 23
 	}
157
-	return container, nil
158
-}
159 24
 
160
-func readPid() (int, error) {
161
-	data, err := ioutil.ReadFile(filepath.Join(dataPath, "pid"))
162
-	if err != nil {
163
-		return -1, err
164
-	}
165
-	pid, err := strconv.Atoi(string(data))
166
-	if err != nil {
167
-		return -1, err
168
-	}
169
-	return pid, nil
25
+	return nil
170 26
 }
171 27
 
172
-// startContainer starts the container. Returns the exit status or -1 and an
173
-// error.
174
-//
175
-// Signals sent to the current process will be forwarded to container.
176
-func startContainer(container *libcontainer.Container, term namespaces.Terminal, dataPath string, args []string) (int, error) {
177
-	var (
178
-		cmd  *exec.Cmd
179
-		sigc = make(chan os.Signal, 10)
180
-	)
181
-
182
-	signal.Notify(sigc)
183
-
184
-	createCommand := func(container *libcontainer.Container, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd {
185
-		cmd = namespaces.DefaultCreateCommand(container, console, rootfs, dataPath, init, pipe, args)
186
-		return cmd
187
-	}
188
-
189
-	startCallback := func() {
190
-		go func() {
191
-			for sig := range sigc {
192
-				cmd.Process.Signal(sig)
193
-			}
194
-		}()
195
-	}
196
-
197
-	return namespaces.Exec(container, term, "", dataPath, args, createCommand, startCallback)
198
-}
28
+func main() {
29
+	app := cli.NewApp()
30
+	app.Name = "nsinit"
31
+	app.Version = "0.1"
32
+	app.Author = "libcontainer maintainers"
199 33
 
200
-// returns the container stats in json format.
201
-func getContainerStats(container *libcontainer.Container) (string, error) {
202
-	stats, err := fs.GetStats(container.Cgroups)
203
-	if err != nil {
204
-		return "", err
205
-	}
206
-	out, err := json.MarshalIndent(stats, "", "\t")
207
-	if err != nil {
208
-		return "", err
34
+	app.Before = preload
35
+	app.Commands = []cli.Command{
36
+		execCommand,
37
+		initCommand,
38
+		statsCommand,
39
+		specCommand,
209 40
 	}
210
-	return string(out), nil
211
-}
212 41
 
213
-// returns the container spec in json format.
214
-func getContainerSpec(container *libcontainer.Container) (string, error) {
215
-	spec, err := json.MarshalIndent(container, "", "\t")
216
-	if err != nil {
217
-		return "", err
42
+	if err := app.Run(os.Args); err != nil {
43
+		log.Fatal(err)
218 44
 	}
219
-	return string(spec), nil
220 45
 }
221 46
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+package main
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"log"
6
+	"os"
7
+
8
+	"github.com/codegangsta/cli"
9
+	"github.com/dotcloud/docker/pkg/libcontainer"
10
+)
11
+
12
+var specCommand = cli.Command{
13
+	Name:   "spec",
14
+	Usage:  "display the container specification",
15
+	Action: specAction,
16
+}
17
+
18
+func specAction(context *cli.Context) {
19
+	// returns the spec of the current container.
20
+	spec, err := getContainerSpec(container)
21
+	if err != nil {
22
+		log.Printf("Failed to get spec - %v\n", err)
23
+		os.Exit(1)
24
+	}
25
+	fmt.Printf("Spec:\n%v\n", spec)
26
+	os.Exit(0)
27
+
28
+}
29
+
30
+// returns the container spec in json format.
31
+func getContainerSpec(container *libcontainer.Container) (string, error) {
32
+	spec, err := json.MarshalIndent(container, "", "\t")
33
+	if err != nil {
34
+		return "", err
35
+	}
36
+	return string(spec), nil
37
+}
0 38
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+package main
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"log"
6
+	"os"
7
+
8
+	"github.com/codegangsta/cli"
9
+	"github.com/dotcloud/docker/pkg/libcontainer"
10
+	"github.com/dotcloud/docker/pkg/libcontainer/cgroups/fs"
11
+)
12
+
13
+var statsCommand = cli.Command{
14
+	Name:   "stats",
15
+	Usage:  "display statistics for the container",
16
+	Action: statsAction,
17
+}
18
+
19
+func statsAction(context *cli.Context) {
20
+	// returns the stats of the current container.
21
+	stats, err := getContainerStats(container)
22
+	if err != nil {
23
+		log.Printf("Failed to get stats - %v\n", err)
24
+		os.Exit(1)
25
+	}
26
+	fmt.Printf("Stats:\n%v\n", stats)
27
+	os.Exit(0)
28
+}
29
+
30
+// returns the container stats in json format.
31
+func getContainerStats(container *libcontainer.Container) (string, error) {
32
+	stats, err := fs.GetStats(container.Cgroups)
33
+	if err != nil {
34
+		return "", err
35
+	}
36
+	out, err := json.MarshalIndent(stats, "", "\t")
37
+	if err != nil {
38
+		return "", err
39
+	}
40
+	return string(out), nil
41
+}
0 42
new file mode 100644
... ...
@@ -0,0 +1,52 @@
0
+package main
1
+
2
+import (
3
+	"encoding/json"
4
+	"io/ioutil"
5
+	"log"
6
+	"os"
7
+	"path/filepath"
8
+	"strconv"
9
+
10
+	"github.com/dotcloud/docker/pkg/libcontainer"
11
+)
12
+
13
+func loadContainer() (*libcontainer.Container, error) {
14
+	f, err := os.Open(filepath.Join(dataPath, "container.json"))
15
+	if err != nil {
16
+		return nil, err
17
+	}
18
+	defer f.Close()
19
+
20
+	var container *libcontainer.Container
21
+	if err := json.NewDecoder(f).Decode(&container); err != nil {
22
+		return nil, err
23
+	}
24
+
25
+	return container, nil
26
+}
27
+
28
+func readPid() (int, error) {
29
+	data, err := ioutil.ReadFile(filepath.Join(dataPath, "pid"))
30
+	if err != nil {
31
+		return -1, err
32
+	}
33
+
34
+	pid, err := strconv.Atoi(string(data))
35
+	if err != nil {
36
+		return -1, err
37
+	}
38
+
39
+	return pid, nil
40
+}
41
+
42
+func openLog(name string) error {
43
+	f, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
44
+	if err != nil {
45
+		return err
46
+	}
47
+
48
+	log.SetOutput(f)
49
+
50
+	return nil
51
+}