forked from hyperhq/runv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstart.go
211 lines (194 loc) · 6.48 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/codegangsta/cli"
"github.com/docker/containerd/api/grpc/types"
"github.com/hyperhq/runv/lib/term"
"github.com/hyperhq/runv/lib/utils"
"github.com/kardianos/osext"
"github.com/opencontainers/runtime-spec/specs-go"
netcontext "golang.org/x/net/context"
)
func firstExistingFile(candidates []string) string {
for _, file := range candidates {
if _, err := os.Stat(file); err == nil {
return file
}
}
return ""
}
var startCommand = cli.Command{
Name: "start",
Usage: "create and run a container",
ArgsUsage: `<container-id>
Where "<container-id>" is your name for the instance of the container that you
are starting. The name you provide for the container instance must be unique on
your host.`,
Description: `The start command creates an instance of a container for a bundle. The bundle
is a directory with a specification file named "` + specConfig + `" and a root
filesystem.
The specification file includes an args parameter. The args parameter is used
to specify command(s) that get run when the container is started. To change the
command(s) that get executed on start, edit the args parameter of the spec. See
"runv spec --help" for more explanation.`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "bundle, b",
Usage: "path to the root of the bundle directory",
},
},
Action: func(context *cli.Context) {
root := context.GlobalString("root")
bundle := context.String("bundle")
container := context.Args().First()
ocffile := filepath.Join(bundle, specConfig)
spec, err := loadSpec(ocffile)
if err != nil {
fmt.Printf("load config failed %v\n", err)
os.Exit(-1)
}
if os.Geteuid() != 0 {
fmt.Printf("runv should be run as root\n")
os.Exit(-1)
}
_, err = os.Stat(filepath.Join(root, container))
if err == nil {
fmt.Printf("Container %s exists\n", container)
os.Exit(-1)
}
var sharedContainer string
for _, ns := range spec.Linux.Namespaces {
if ns.Path != "" {
if strings.Contains(ns.Path, "/") {
fmt.Printf("Runv doesn't support path to namespace file, it supports containers name as shared namespaces only\n")
os.Exit(-1)
}
if ns.Type == "mount" {
// TODO support it!
fmt.Printf("Runv doesn't support shared mount namespace currently\n")
os.Exit(-1)
}
sharedContainer = ns.Path
_, err = os.Stat(filepath.Join(root, sharedContainer, stateJson))
if err != nil {
fmt.Printf("The container %s is not existing or not ready\n", sharedContainer)
os.Exit(-1)
}
_, err = os.Stat(filepath.Join(root, sharedContainer, "namespace"))
if err != nil {
fmt.Printf("The container %s is not ready\n", sharedContainer)
os.Exit(-1)
}
}
}
driver := context.GlobalString("driver")
kernel := context.GlobalString("kernel")
initrd := context.GlobalString("initrd")
// only set the default kernel/initrd when it is the first container(sharedContainer == "")
if kernel == "" && sharedContainer == "" {
kernel = firstExistingFile([]string{
filepath.Join(bundle, spec.Root.Path, "boot/vmlinuz"),
filepath.Join(bundle, "boot/vmlinuz"),
filepath.Join(bundle, "vmlinuz"),
"/var/lib/hyper/kernel",
})
}
if initrd == "" && sharedContainer == "" {
initrd = firstExistingFile([]string{
filepath.Join(bundle, spec.Root.Path, "boot/initrd.img"),
filepath.Join(bundle, "boot/initrd.img"),
filepath.Join(bundle, "initrd.img"),
"/var/lib/hyper/hyper-initrd.img",
})
}
// convert the paths to abs
kernel, err = filepath.Abs(kernel)
if err != nil {
fmt.Printf("Cannot get abs path for kernel: %s\n", err.Error())
os.Exit(-1)
}
initrd, err = filepath.Abs(initrd)
if err != nil {
fmt.Printf("Cannot get abs path for initrd: %s\n", err.Error())
os.Exit(-1)
}
var address string
if sharedContainer != "" {
address = filepath.Join(root, container, "namespace/namespaced.sock")
} else {
path, err := osext.Executable()
if err != nil {
fmt.Printf("cannot find self executable path for %s: %v\n", os.Args[0], err)
os.Exit(-1)
}
os.MkdirAll(context.String("log_dir"), 0755)
namespace, err := ioutil.TempDir("/run", "runv-namespace-")
if err != nil {
fmt.Printf("Failed to create runv namespace path: %v", err)
os.Exit(-1)
}
args := []string{
"runv-namespaced",
"--namespace", namespace,
"--state", root,
"--driver", driver,
"--kernel", kernel,
"--initrd", initrd,
}
if context.GlobalBool("debug") {
args = append(args, "-v", "3", "--log_dir", context.GlobalString("log_dir"))
}
_, err = utils.ExecInDaemon(path, args)
if err != nil {
fmt.Printf("failed to launch runv daemon, error:%v\n", err)
os.Exit(-1)
}
address = filepath.Join(namespace, "namespaced.sock")
}
status := startContainer(bundle, container, address, spec)
os.Exit(status)
},
}
// Shared namespaces multiple containers suppurt
// The runv supports pod-style shared namespaces currently.
// (More fine grain shared namespaces style (docker/runc style) is under implementation)
//
// Pod-style shared namespaces:
// * if two containers share at least one type of namespace, they share all kinds of namespaces except the mount namespace
// * mount namespace can't be shared, each container has its own mount namespace
//
// Implementation detail:
// * Shared namespaces is configured in Spec.Linux.Namespaces, the namespace Path should be existing container name.
// * In runv, shared namespaces multiple containers are located in the same VM which is managed by a runv-daemon.
// * Any running container can exit in any arbitrary order, the runv-daemon and the VM are existed until the last container of the VM is existed
func startContainer(bundle, container, address string, config *specs.Spec) int {
pid := os.Getpid()
r := &types.CreateContainerRequest{
Id: container,
BundlePath: bundle,
Stdin: fmt.Sprintf("/proc/%d/fd/0", pid),
Stdout: fmt.Sprintf("/proc/%d/fd/1", pid),
Stderr: fmt.Sprintf("/proc/%d/fd/2", pid),
}
c := getClient(address)
timestamp := uint64(time.Now().Unix())
if _, err := c.CreateContainer(netcontext.Background(), r); err != nil {
fmt.Printf("error %v\n", err)
return -1
}
if config.Process.Terminal {
s, err := term.SetRawTerminal(os.Stdin.Fd())
if err != nil {
fmt.Printf("error %v\n", err)
return -1
}
defer term.RestoreTerminal(os.Stdin.Fd(), s)
monitorTtySize(c, container, "init")
}
return waitForExit(c, timestamp, container, "init")
}