spec.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // +build linux
  2. package runc
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "runtime"
  9. "github.com/codegangsta/cli"
  10. "github.com/opencontainers/runc/libcontainer/configs"
  11. "github.com/opencontainers/runtime-spec/specs-go"
  12. )
  13. var specCommand = cli.Command{
  14. Name: "spec",
  15. Usage: "create a new specification file",
  16. ArgsUsage: "",
  17. Description: `The spec command creates the new specification file named "` + specConfig + `" for
  18. the bundle.
  19. The spec generated is just a starter file. Editing of the spec is required to
  20. achieve desired results. For example, the newly generated spec includes an args
  21. parameter that is initially set to call the "sh" command when the container is
  22. started. Calling "sh" may work for an ubuntu container or busybox, but will not
  23. work for containers that do not include the "sh" program.
  24. EXAMPLE:
  25. To run docker's hello-world container one needs to set the args parameter
  26. in the spec to call hello. This can be done using the sed command or a text
  27. editor. The following commands create a bundle for hello-world, change the
  28. default args parameter in the spec from "sh" to "/hello", then run the hello
  29. command in a new hello-world container named container1:
  30. mkdir hello
  31. cd hello
  32. docker pull hello-world
  33. docker export $(docker create hello-world) > hello-world.tar
  34. mkdir rootfs
  35. tar -C rootfs -xf hello-world.tar
  36. runc spec
  37. sed -i 's;"sh";"/hello";' ` + specConfig + `
  38. runc start container1
  39. In the start command above, "container1" is the name for the instance of the
  40. container that you are starting. The name you provide for the container instance
  41. must be unique on your host.
  42. When starting a container through runc, runc needs root privilege. If not
  43. already running as root, you can use sudo to give runc root privilege. For
  44. example: "sudo runc start container1" will give runc root privilege to start the
  45. container on your host.`,
  46. Flags: []cli.Flag{
  47. cli.StringFlag{
  48. Name: "bundle, b",
  49. Value: "",
  50. Usage: "path to the root of the bundle directory",
  51. },
  52. },
  53. Action: func(context *cli.Context) {
  54. spec := specs.Spec{
  55. Version: specs.Version,
  56. Platform: specs.Platform{
  57. OS: runtime.GOOS,
  58. Arch: runtime.GOARCH,
  59. },
  60. Root: specs.Root{
  61. Path: "rootfs",
  62. Readonly: true,
  63. },
  64. Process: specs.Process{
  65. Terminal: true,
  66. User: specs.User{},
  67. Args: []string{
  68. "sh",
  69. },
  70. Env: []string{
  71. "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  72. "TERM=xterm",
  73. },
  74. Cwd: "/",
  75. NoNewPrivileges: true,
  76. Capabilities: []string{
  77. "CAP_AUDIT_WRITE",
  78. "CAP_KILL",
  79. "CAP_NET_BIND_SERVICE",
  80. },
  81. Rlimits: []specs.Rlimit{
  82. {
  83. Type: "RLIMIT_NOFILE",
  84. Hard: uint64(1024),
  85. Soft: uint64(1024),
  86. },
  87. },
  88. },
  89. Hostname: "runc",
  90. Mounts: []specs.Mount{
  91. {
  92. Destination: "/proc",
  93. Type: "proc",
  94. Source: "proc",
  95. Options: nil,
  96. },
  97. {
  98. Destination: "/dev",
  99. Type: "tmpfs",
  100. Source: "tmpfs",
  101. Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
  102. },
  103. {
  104. Destination: "/dev/pts",
  105. Type: "devpts",
  106. Source: "devpts",
  107. Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
  108. },
  109. {
  110. Destination: "/dev/shm",
  111. Type: "tmpfs",
  112. Source: "shm",
  113. Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
  114. },
  115. {
  116. Destination: "/dev/mqueue",
  117. Type: "mqueue",
  118. Source: "mqueue",
  119. Options: []string{"nosuid", "noexec", "nodev"},
  120. },
  121. {
  122. Destination: "/sys",
  123. Type: "sysfs",
  124. Source: "sysfs",
  125. Options: []string{"nosuid", "noexec", "nodev", "ro"},
  126. },
  127. {
  128. Destination: "/sys/fs/cgroup",
  129. Type: "cgroup",
  130. Source: "cgroup",
  131. Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
  132. },
  133. },
  134. Linux: specs.Linux{
  135. MaskedPaths: []string{
  136. "/proc/kcore",
  137. "/proc/latency_stats",
  138. "/proc/timer_stats",
  139. "/proc/sched_debug",
  140. },
  141. ReadonlyPaths: []string{
  142. "/proc/asound",
  143. "/proc/bus",
  144. "/proc/fs",
  145. "/proc/irq",
  146. "/proc/sys",
  147. "/proc/sysrq-trigger",
  148. },
  149. Resources: &specs.Resources{
  150. Devices: []specs.DeviceCgroup{
  151. {
  152. Allow: false,
  153. Access: sPtr("rwm"),
  154. },
  155. },
  156. },
  157. Namespaces: []specs.Namespace{
  158. {
  159. Type: "pid",
  160. },
  161. {
  162. Type: "network",
  163. },
  164. {
  165. Type: "ipc",
  166. },
  167. {
  168. Type: "uts",
  169. },
  170. {
  171. Type: "mount",
  172. },
  173. },
  174. },
  175. }
  176. checkNoFile := func(name string) error {
  177. _, err := os.Stat(name)
  178. if err == nil {
  179. return fmt.Errorf("File %s exists. Remove it first", name)
  180. }
  181. if !os.IsNotExist(err) {
  182. return err
  183. }
  184. return nil
  185. }
  186. bundle := context.String("bundle")
  187. if bundle != "" {
  188. if err := os.Chdir(bundle); err != nil {
  189. fatal(err)
  190. }
  191. }
  192. if err := checkNoFile(specConfig); err != nil {
  193. fatal(err)
  194. }
  195. data, err := json.MarshalIndent(&spec, "", "\t")
  196. if err != nil {
  197. fatal(err)
  198. }
  199. if err := ioutil.WriteFile(specConfig, data, 0666); err != nil {
  200. fatal(err)
  201. }
  202. },
  203. }
  204. func sPtr(s string) *string { return &s }
  205. func rPtr(r rune) *rune { return &r }
  206. func iPtr(i int64) *int64 { return &i }
  207. func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
  208. func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
  209. // loadSpec loads the specification from the provided path.
  210. // If the path is empty then the default path will be "config.json"
  211. func loadSpec(cPath string) (spec *specs.Spec, err error) {
  212. cf, err := os.Open(cPath)
  213. if err != nil {
  214. if os.IsNotExist(err) {
  215. return nil, fmt.Errorf("JSON specification file %s not found", cPath)
  216. }
  217. return nil, err
  218. }
  219. defer cf.Close()
  220. if err = json.NewDecoder(cf).Decode(&spec); err != nil {
  221. return nil, err
  222. }
  223. return spec, validateProcessSpec(&spec.Process)
  224. }
  225. func createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) {
  226. rl, err := strToRlimit(rlimit.Type)
  227. if err != nil {
  228. return configs.Rlimit{}, err
  229. }
  230. return configs.Rlimit{
  231. Type: rl,
  232. Hard: uint64(rlimit.Hard),
  233. Soft: uint64(rlimit.Soft),
  234. }, nil
  235. }