Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit 8ecb368

Browse files
authored
Merge pull request #537 from laijs/cli-refactor
runv cli refactor
2 parents 4332d61 + b29bbfe commit 8ecb368

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+4804
-6024
lines changed

Godeps/Godeps.json

Lines changed: 15 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ install-exec-local:
2222
$(INSTALL_PROGRAM) runv $(bindir)
2323

2424
build-runv:
25-
go build -tags "static_build $(HYPER_BULD_TAGS)" -ldflags ${GOLDFLAGS} -o runv .
25+
go build -tags "static_build $(HYPER_BULD_TAGS)" -ldflags ${GOLDFLAGS} -o runv ./cli/
2626
test-integration:
2727
cd integration-test/test_data && make
2828
cd integration-test && go test -check.v -test.timeout=120m ${TESTFLAGS} .

cli/container.go

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"syscall"
11+
"time"
12+
13+
"github.com/golang/glog"
14+
"github.com/hyperhq/runv/api"
15+
"github.com/hyperhq/runv/hypervisor"
16+
"github.com/hyperhq/runv/lib/linuxsignal"
17+
"github.com/opencontainers/runtime-spec/specs-go"
18+
)
19+
20+
func startContainer(vm *hypervisor.Vm, container string, spec *specs.Spec, state *specs.State) error {
21+
err := vm.StartContainer(container)
22+
if err != nil {
23+
glog.V(1).Infof("Start Container fail: fail to start container with err: %#v\n", err)
24+
return err
25+
}
26+
27+
err = syscall.Kill(state.Pid, syscall.SIGUSR1)
28+
if err != nil {
29+
glog.V(1).Infof("failed to notify the shim to work", err.Error())
30+
return err
31+
}
32+
33+
err = execPoststartHooks(spec, state)
34+
if err != nil {
35+
glog.V(1).Infof("execute Poststart hooks failed %s\n", err.Error())
36+
}
37+
38+
return err
39+
}
40+
41+
func createContainer(options runvOptions, vm *hypervisor.Vm, container, bundle, stateRoot string, spec *specs.Spec) (shim *os.Process, err error) {
42+
if err = setupContainerFs(vm, bundle, container, spec); err != nil {
43+
return nil, err
44+
}
45+
defer func() {
46+
if err != nil {
47+
removeContainerFs(vm, container)
48+
}
49+
}()
50+
51+
glog.V(3).Infof("vm.AddContainer()")
52+
config := api.ContainerDescriptionFromOCF(container, spec)
53+
r := vm.AddContainer(config)
54+
if !r.IsSuccess() {
55+
return nil, fmt.Errorf("add container %s failed: %s", container, r.Message())
56+
}
57+
defer func() {
58+
if err != nil {
59+
vm.RemoveContainer(container)
60+
}
61+
}()
62+
63+
// Prepare container state directory
64+
stateDir := filepath.Join(stateRoot, container)
65+
_, err = os.Stat(stateDir)
66+
if err == nil {
67+
glog.Errorf("Container %s exists\n", container)
68+
return nil, fmt.Errorf("Container %s exists", container)
69+
}
70+
err = os.MkdirAll(stateDir, 0644)
71+
if err != nil {
72+
glog.V(1).Infof("%s\n", err.Error())
73+
return nil, err
74+
}
75+
defer func() {
76+
if err != nil {
77+
os.RemoveAll(stateDir)
78+
}
79+
}()
80+
81+
// Create sandbox dir symbol link in container root dir
82+
vmRootLinkPath := filepath.Join(stateDir, "sandbox")
83+
vmRootPath := sandboxPath(vm)
84+
if err = os.Symlink(vmRootPath, vmRootLinkPath); err != nil {
85+
return nil, fmt.Errorf("failed to create symbol link %q: %v", vmRootLinkPath, err)
86+
}
87+
88+
// create shim and save the state
89+
shim, err = createShim(options, container, "init")
90+
if err != nil {
91+
return nil, err
92+
}
93+
defer func() {
94+
if err != nil {
95+
shim.Kill()
96+
}
97+
}()
98+
99+
state := &specs.State{
100+
Version: spec.Version,
101+
ID: container,
102+
Pid: shim.Pid,
103+
Bundle: bundle,
104+
}
105+
glog.V(3).Infof("save state id %s, boundle %s", container, bundle)
106+
if err = saveStateFile(filepath.Join(stateDir, "state.json"), state); err != nil {
107+
return nil, err
108+
}
109+
110+
err = execPrestartHooks(spec, state)
111+
if err != nil {
112+
// cli refator todo stop container
113+
glog.V(1).Infof("execute Prestart hooks failed, %s\n", err.Error())
114+
return nil, err
115+
}
116+
117+
// If runv is launched via docker/containerd, we start netlistener to watch/collect network changes.
118+
// TODO: if runv is launched by cni compatible tools, the cni script can use `runv cni` cmdline to update the network.
119+
// Create the listener process which will enters into the netns of the shim
120+
options.withContainer = state
121+
if err = startNsListener(options, vm); err != nil {
122+
// cli refator todo stop container
123+
glog.Errorf("start ns listener fail: %v", err)
124+
return nil, err
125+
}
126+
127+
return shim, nil
128+
}
129+
130+
func deleteContainer(vm *hypervisor.Vm, root, container string, force bool, spec *specs.Spec, state *specs.State) error {
131+
132+
// todo: check the container from vm.ContainerList()
133+
// todo: check the process of state.Pid in case it is a new unrelated process
134+
135+
// non-force killing can only be performed when at least one of the realProcess and shimProcess exited
136+
exitedVM := vm.SignalProcess(container, "init", syscall.Signal(0)) != nil // todo: is this check reliable?
137+
exitedHost := syscall.Kill(state.Pid, syscall.Signal(0)) != nil
138+
if !exitedVM && !exitedHost && !force {
139+
// don't perform deleting
140+
return fmt.Errorf("the container %s is still alive, use -f to force kill it?", container)
141+
}
142+
143+
if !exitedVM { // force kill the real init process inside the vm
144+
for i := 0; i < 100; i++ {
145+
vm.SignalProcess(container, "init", linuxsignal.SIGKILL)
146+
time.Sleep(100 * time.Millisecond)
147+
if vm.SignalProcess(container, "init", syscall.Signal(0)) != nil {
148+
break
149+
}
150+
}
151+
}
152+
153+
if !exitedHost { // force kill the shim process in the host
154+
time.Sleep(200 * time.Millisecond) // the shim might be going to exit, wait it
155+
for i := 0; i < 100; i++ {
156+
syscall.Kill(state.Pid, syscall.SIGKILL)
157+
time.Sleep(100 * time.Millisecond)
158+
if syscall.Kill(state.Pid, syscall.Signal(0)) != nil {
159+
break
160+
}
161+
}
162+
}
163+
164+
vm.RemoveContainer(container)
165+
err := execPoststopHooks(spec, state)
166+
if err != nil {
167+
glog.V(1).Infof("execute Poststop hooks failed %s\n", err.Error())
168+
removeContainerFs(vm, container)
169+
os.RemoveAll(filepath.Join(root, container))
170+
return err // return err of the hooks
171+
}
172+
173+
removeContainerFs(vm, container)
174+
return os.RemoveAll(filepath.Join(root, container))
175+
}
176+
177+
func addProcess(options runvOptions, vm *hypervisor.Vm, container, process string, spec *specs.Process) (shim *os.Process, err error) {
178+
err = vm.AddProcess(&api.Process{
179+
Container: container,
180+
Id: process,
181+
Terminal: spec.Terminal,
182+
Args: spec.Args,
183+
Envs: spec.Env,
184+
Workdir: spec.Cwd}, nil)
185+
186+
if err != nil {
187+
glog.V(1).Infof("add process to container failed: %v\n", err)
188+
return nil, err
189+
}
190+
defer func() {
191+
if err != nil {
192+
vm.SignalProcess(container, process, linuxsignal.SIGKILL)
193+
}
194+
}()
195+
196+
shim, err = createShim(options, container, process)
197+
if err != nil {
198+
return nil, err
199+
}
200+
defer func() {
201+
if err != nil {
202+
shim.Kill()
203+
}
204+
}()
205+
206+
// cli refactor todo (for the purpose of 'runv ps` command) save <container, process, shim-pid, spec> to persist file.
207+
208+
return shim, nil
209+
}
210+
211+
func execHook(hook specs.Hook, state *specs.State) error {
212+
b, err := json.Marshal(state)
213+
if err != nil {
214+
return err
215+
}
216+
cmd := exec.Cmd{
217+
Path: hook.Path,
218+
Args: hook.Args,
219+
Env: hook.Env,
220+
Stdin: bytes.NewReader(b),
221+
}
222+
return cmd.Run()
223+
}
224+
225+
func execPrestartHooks(rt *specs.Spec, state *specs.State) error {
226+
if rt.Hooks == nil {
227+
return nil
228+
}
229+
for _, hook := range rt.Hooks.Prestart {
230+
err := execHook(hook, state)
231+
if err != nil {
232+
return err
233+
}
234+
}
235+
236+
return nil
237+
}
238+
239+
func execPoststartHooks(rt *specs.Spec, state *specs.State) error {
240+
if rt.Hooks == nil {
241+
return nil
242+
}
243+
for _, hook := range rt.Hooks.Poststart {
244+
err := execHook(hook, state)
245+
if err != nil {
246+
glog.V(1).Infof("exec Poststart hook %s failed %s", hook.Path, err.Error())
247+
}
248+
}
249+
250+
return nil
251+
}
252+
253+
func execPoststopHooks(rt *specs.Spec, state *specs.State) error {
254+
if rt.Hooks == nil {
255+
return nil
256+
}
257+
for _, hook := range rt.Hooks.Poststop {
258+
err := execHook(hook, state)
259+
if err != nil {
260+
glog.V(1).Infof("exec Poststop hook %s failed %s", hook.Path, err.Error())
261+
}
262+
}
263+
264+
return nil
265+
}

0 commit comments

Comments
 (0)