-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathido.go
125 lines (99 loc) · 2.68 KB
/
ido.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
// Package ido implements containers operation.
package ido
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// Create creates an image directory.
func Create(image string) (tempDir string, err error) {
// via https://github.com/opencontainers/runc/blob/6cccc1760d57d9e1bc856b96eeb7ee02b7b8101d/README.md#using-runc
tempDir, err = ioutil.TempDir("", "")
if err != nil {
return "", err
}
rootfsDir, err := mkRootfsDir(tempDir)
if err != nil {
return "", err
}
d := newDocker()
container, err := d.create(image)
if err != nil {
return "", err
}
tempFilePath := filepath.Join(tempDir, "temp.tar")
if err = d.export(tempFilePath, container); err != nil {
return "", err
}
if err = d.rm(container); err != nil {
return "", err
}
if err = tarX(rootfsDir, tempFilePath); err != nil {
return "", err
}
if err = os.Remove(tempFilePath); err != nil {
return "", err
}
return tempDir, nil
}
// Run runs a container.
func Run(dir string, cmd string, volumes []string) error {
// via https://ericchiang.github.io/post/containers-from-scratch/#creating-namespaces-with-unshare
rootfsDir := filepath.Join(dir, "rootfs")
for _, volume := range volumes {
vDirs := strings.SplitN(volume, ":", 2)
if len(vDirs) != 2 {
return fmt.Errorf("inavlid volume: %s", volume)
}
hostDir, err := filepath.Abs(vDirs[0])
if err != nil {
return err
}
containerDir := filepath.Join(rootfsDir, vDirs[1])
if err = os.MkdirAll(containerDir, 0750); err != nil {
return err
}
mount := newShell("mount", "--bind", "-o", "ro", hostDir, containerDir)
if _, err = mount.result(); err != nil {
return err
}
umount := newShell("umount", containerDir)
defer umount.run() // nolint: errcheck
}
err := unshareChroot(rootfsDir, cmd)
if err != nil {
return err
}
return nil
}
func mkRootfsDir(dir string) (rootfsDir string, err error) {
rootfsDir = filepath.Join(dir, "rootfs")
if err = os.Mkdir(rootfsDir, 0750); err != nil {
return "", err
}
return rootfsDir, nil
}
func tarX(dir string, file string) (err error) {
sh := newShell("tar", "-C", dir, "-xvf", file)
if _, err = sh.result(); err != nil {
return err
}
return nil
}
func unshareChroot(dir string, cmd string) (err error) {
procDir := filepath.Join(dir, "proc")
// via https://github.com/karelzak/util-linux/issues/648#issuecomment-404066455
mount := newShell("mount", "--types", "proc", "proc", procDir)
if _, err = mount.result(); err != nil {
return err
}
umount := newShell("umount", procDir)
defer umount.run() // nolint: errcheck
uc := newShell("unshare", "--pid", "--fork", "--mount-proc="+procDir, "chroot", dir, cmd)
if err = uc.run(); err != nil {
return err
}
return nil
}