mirror of
https://github.com/opencontainers/runc.git
synced 2025-09-26 19:41:35 +08:00
Initial commit of runc binary
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
bundles
|
bundles
|
||||||
nsinit/nsinit
|
nsinit/nsinit
|
||||||
vendor/pkg
|
vendor/pkg
|
||||||
|
runc
|
||||||
|
191
LICENSE
Normal file
191
LICENSE
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2014 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
9
Makefile
Normal file
9
Makefile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
all:
|
||||||
|
go build -o runc .
|
||||||
|
|
||||||
|
install:
|
||||||
|
cp runc /usr/local/bin/runc
|
||||||
|
rm runc
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm runc
|
145
README.md
Normal file
145
README.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
## runc
|
||||||
|
|
||||||
|
`runc` is a CLI tool for spawning and running containers according to the OCF specification.
|
||||||
|
|
||||||
|
### Building:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/opencontainers/runc
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using:
|
||||||
|
|
||||||
|
To run a container that you received just execute `runc run` with the JSON format at the argument or have a
|
||||||
|
`container.json` file in the current working directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
runc
|
||||||
|
/ $ ps
|
||||||
|
PID USER COMMAND
|
||||||
|
1 daemon sh
|
||||||
|
5 daemon sh
|
||||||
|
/ $
|
||||||
|
```
|
||||||
|
|
||||||
|
### OCF Container JSON Format;
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.1",
|
||||||
|
"os": "linux",
|
||||||
|
"arch": "amd64",
|
||||||
|
"processes": [
|
||||||
|
{
|
||||||
|
"tty": true,
|
||||||
|
"user": "daemon",
|
||||||
|
"args": [
|
||||||
|
"sh"
|
||||||
|
],
|
||||||
|
"env": [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm"
|
||||||
|
],
|
||||||
|
"cwd": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"root": {
|
||||||
|
"path": "rootfs",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
"cpus": 1.1,
|
||||||
|
"memory": 1024,
|
||||||
|
"hostname": "shell",
|
||||||
|
"namespaces": [
|
||||||
|
{
|
||||||
|
"type": "process"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "network"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "mount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ipc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "uts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"capabilities": [
|
||||||
|
"AUDIT_WRITE",
|
||||||
|
"KILL",
|
||||||
|
"NET_BIND_SERVICE"
|
||||||
|
],
|
||||||
|
"devices": [
|
||||||
|
"null",
|
||||||
|
"random",
|
||||||
|
"full",
|
||||||
|
"tty",
|
||||||
|
"zero",
|
||||||
|
"urandom"
|
||||||
|
],
|
||||||
|
"mounts": [
|
||||||
|
{
|
||||||
|
"type": "proc",
|
||||||
|
"source": "proc",
|
||||||
|
"destination": "/proc",
|
||||||
|
"options": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tmpfs",
|
||||||
|
"source": "tmpfs",
|
||||||
|
"destination": "/dev",
|
||||||
|
"options": "nosuid,strictatime,mode=755,size=65536k"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "devpts",
|
||||||
|
"source": "devpts",
|
||||||
|
"destination": "/dev/pts",
|
||||||
|
"options": "nosuid,noexec,newinstance,ptmxmode=0666,mode=0620,gid=5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tmpfs",
|
||||||
|
"source": "shm",
|
||||||
|
"destination": "/dev/shm",
|
||||||
|
"options": "nosuid,noexec,nodev,mode=1777,size=65536k"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "mqueue",
|
||||||
|
"source": "mqueue",
|
||||||
|
"destination": "/dev/mqueue",
|
||||||
|
"options": "nosuid,noexec,nodev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sysfs",
|
||||||
|
"source": "sysfs",
|
||||||
|
"destination": "/sys",
|
||||||
|
"options": "nosuid,noexec,nodev"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
|
||||||
|
#### Using runc with systemd
|
||||||
|
|
||||||
|
```service
|
||||||
|
[Unit]
|
||||||
|
Description=Minecraft Build Server
|
||||||
|
Documentation=http://minecraft.net
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
CPUQuota=200%
|
||||||
|
MemoryLimit=1536M
|
||||||
|
ExecStart=/usr/local/bin/runc
|
||||||
|
Restart=on-failure
|
||||||
|
WorkingDirectory=/containers/minecraftbuild
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
63
checkpoint.go
Normal file
63
checkpoint.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var checkpointCommand = cli.Command{
|
||||||
|
Name: "checkpoint",
|
||||||
|
Usage: "checkpoint a running container",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
|
||||||
|
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||||||
|
cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
|
||||||
|
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
||||||
|
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
||||||
|
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
||||||
|
cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
options := criuOptions(context)
|
||||||
|
// these are the mandatory criu options for a container
|
||||||
|
setPageServer(context, options)
|
||||||
|
if err := container.Checkpoint(options); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCheckpointImagePath(context *cli.Context) string {
|
||||||
|
imagePath := context.String("image-path")
|
||||||
|
if imagePath == "" {
|
||||||
|
imagePath = getDefaultImagePath(context)
|
||||||
|
}
|
||||||
|
return imagePath
|
||||||
|
}
|
||||||
|
|
||||||
|
func setPageServer(context *cli.Context, options *libcontainer.CriuOpts) {
|
||||||
|
// xxx following criu opts are optional
|
||||||
|
// The dump image can be sent to a criu page server
|
||||||
|
if psOpt := context.String("page-server"); psOpt != "" {
|
||||||
|
addressPort := strings.Split(psOpt, ":")
|
||||||
|
if len(addressPort) != 2 {
|
||||||
|
fatal(fmt.Errorf("Use --page-server ADDRESS:PORT to specify page server"))
|
||||||
|
}
|
||||||
|
port_int, err := strconv.Atoi(addressPort[1])
|
||||||
|
if err != nil {
|
||||||
|
fatal(fmt.Errorf("Invalid port number"))
|
||||||
|
}
|
||||||
|
options.PageServer = libcontainer.CriuPageServerInfo{
|
||||||
|
Address: addressPort[0],
|
||||||
|
Port: int32(port_int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
events.go
Normal file
93
events.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// event struct for encoding the event data to json.
|
||||||
|
type event struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Data interface{} `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventsCommand = cli.Command{
|
||||||
|
Name: "events",
|
||||||
|
Usage: "display container events such as OOM notifications and cpu, memeory, IO, and network stats",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.DurationFlag{Name: "interval", Value: 5 * time.Second, Usage: "set the stats collection interval"},
|
||||||
|
cli.BoolFlag{Name: "stats", Usage: "display the container's stats then exit"},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
stats = make(chan *libcontainer.Stats, 1)
|
||||||
|
events = make(chan *event, 1024)
|
||||||
|
group = &sync.WaitGroup{}
|
||||||
|
)
|
||||||
|
group.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer group.Done()
|
||||||
|
enc := json.NewEncoder(os.Stdout)
|
||||||
|
for e := range events {
|
||||||
|
if err := enc.Encode(e); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if context.Bool("stats") {
|
||||||
|
s, err := container.Stats()
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
events <- &event{Type: "stats", ID: container.ID(), Data: s}
|
||||||
|
close(events)
|
||||||
|
group.Wait()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for _ = range time.Tick(context.Duration("interval")) {
|
||||||
|
s, err := container.Stats()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stats <- s
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
n, err := container.NotifyOOM()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case _, ok := <-n:
|
||||||
|
if ok {
|
||||||
|
// this means an oom event was received, if it is !ok then
|
||||||
|
// the channel was closed because the container stopped and
|
||||||
|
// the cgroups no longer exist.
|
||||||
|
events <- &event{Type: "oom", ID: container.ID()}
|
||||||
|
} else {
|
||||||
|
n = nil
|
||||||
|
}
|
||||||
|
case s := <-stats:
|
||||||
|
events <- &event{Type: "stats", ID: container.ID(), Data: s}
|
||||||
|
}
|
||||||
|
if n == nil {
|
||||||
|
close(events)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.Wait()
|
||||||
|
},
|
||||||
|
}
|
99
main.go
Normal file
99
main.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
version = "0.1"
|
||||||
|
usage = `open container runtime
|
||||||
|
|
||||||
|
runc integrates well with existing process supervisors to provide a production container runtime environment for
|
||||||
|
applications. It can be used with your existing process monitoring tools and the container will be spawned as direct
|
||||||
|
child of the process supervisor. nsinit can be used to manage the lifetime of a single container.
|
||||||
|
|
||||||
|
Execute a simple container in your shell by running:
|
||||||
|
|
||||||
|
cd /mycontainer
|
||||||
|
runc
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if len(os.Args) > 1 && os.Args[1] == "init" {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
runtime.LockOSThread()
|
||||||
|
factory, _ := libcontainer.New("")
|
||||||
|
if err := factory.StartInitialization(); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
panic("--this line should never been executed, congradulations--")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "runc"
|
||||||
|
app.Usage = usage
|
||||||
|
app.Version = version
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "id",
|
||||||
|
Value: getDefaultID(),
|
||||||
|
Usage: "specify the ID to be used for the container",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
Usage: "enable debug output for logging",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "root",
|
||||||
|
Value: "/var/run/ocf",
|
||||||
|
Usage: "root directory for storage of container state (this should be located in tmpfs)",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "criu",
|
||||||
|
Value: "criu",
|
||||||
|
Usage: "path to the criu binary used for checkpoint and restore",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
checkpointCommand,
|
||||||
|
eventsCommand,
|
||||||
|
restoreCommand,
|
||||||
|
specCommand,
|
||||||
|
}
|
||||||
|
app.Before = func(context *cli.Context) error {
|
||||||
|
if context.GlobalBool("debug") {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// default action is to execute a container
|
||||||
|
app.Action = func(context *cli.Context) {
|
||||||
|
if os.Geteuid() != 0 {
|
||||||
|
cli.ShowAppHelp(context)
|
||||||
|
logrus.Fatal("runc should be run as root")
|
||||||
|
}
|
||||||
|
spec, err := loadSpec(context.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
status, err := execContainer(context, spec)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
// exit with the container's exit status so any external supervisor is
|
||||||
|
// notified of the exit with the correct exit status.
|
||||||
|
os.Exit(status)
|
||||||
|
}
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
129
restore.go
Normal file
129
restore.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var restoreCommand = cli.Command{
|
||||||
|
Name: "restore",
|
||||||
|
Usage: "restore a container from a previous checkpoint",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{Name: "image-path", Value: "", Usage: "path to criu image files for restoring"},
|
||||||
|
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||||||
|
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
||||||
|
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
||||||
|
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
imagePath := context.String("image-path")
|
||||||
|
if imagePath == "" {
|
||||||
|
imagePath = getDefaultImagePath(context)
|
||||||
|
}
|
||||||
|
spec, err := loadSpec(context.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
config, err := createLibcontainerConfig(spec)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
status, err := restoreContainer(context, spec, config, imagePath)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
os.Exit(status)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreContainer(context *cli.Context, spec *LinuxSpec, config *configs.Config, imagePath string) (code int, err error) {
|
||||||
|
rootuid := 0
|
||||||
|
factory, err := loadFactory(context)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
container, err := factory.Load(context.GlobalString("id"))
|
||||||
|
if err != nil {
|
||||||
|
container, err = factory.Create(context.GlobalString("id"), config)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options := criuOptions(context)
|
||||||
|
// ensure that the container is always removed if we were the process
|
||||||
|
// that created it.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status, err := container.Status()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
if status != libcontainer.Checkpointed {
|
||||||
|
if err := container.Destroy(); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
if err := os.RemoveAll(options.ImagesDirectory); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
process := &libcontainer.Process{
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}
|
||||||
|
tty, err := newTty(spec.Processes[0].TTY, process, rootuid)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
defer tty.Close()
|
||||||
|
go handleSignals(process, tty)
|
||||||
|
if err := container.Restore(process, options); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
status, err := process.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return utils.ExitStatus(status.Sys().(syscall.WaitStatus)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func criuOptions(context *cli.Context) *libcontainer.CriuOpts {
|
||||||
|
imagePath := getCheckpointImagePath(context)
|
||||||
|
if err := os.MkdirAll(imagePath, 0655); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
return &libcontainer.CriuOpts{
|
||||||
|
ImagesDirectory: imagePath,
|
||||||
|
WorkDirectory: context.String("work-path"),
|
||||||
|
LeaveRunning: context.Bool("leave-running"),
|
||||||
|
TcpEstablished: true, // context.Bool("tcp-established"),
|
||||||
|
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
||||||
|
ShellJob: context.Bool("shell-job"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have to use this type of signal handler because there is a memory leak if we
|
||||||
|
// wait and reap with SICHLD.
|
||||||
|
func handleSignals(process *libcontainer.Process, tty *tty) {
|
||||||
|
sigc := make(chan os.Signal, 10)
|
||||||
|
signal.Notify(sigc)
|
||||||
|
tty.resize()
|
||||||
|
for sig := range sigc {
|
||||||
|
switch sig {
|
||||||
|
case syscall.SIGWINCH:
|
||||||
|
tty.resize()
|
||||||
|
default:
|
||||||
|
process.Signal(sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
run.go
Normal file
57
run.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func execContainer(context *cli.Context, spec *LinuxSpec) (int, error) {
|
||||||
|
if len(spec.Processes) != 1 {
|
||||||
|
return -1, fmt.Errorf("runc only supports one(1) process for the container")
|
||||||
|
}
|
||||||
|
config, err := createLibcontainerConfig(spec)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
rootuid, err := config.HostUID()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
factory, err := loadFactory(context)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
container, err := factory.Create(context.GlobalString("id"), config)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
// ensure that the container is always removed if we were the process
|
||||||
|
// that created it.
|
||||||
|
defer destroy(container)
|
||||||
|
process := newProcess(spec.Processes[0])
|
||||||
|
tty, err := newTty(spec.Processes[0].TTY, process, rootuid)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
handler := newSignalHandler(tty)
|
||||||
|
defer handler.Close()
|
||||||
|
if err := container.Start(process); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return handler.forward(process)
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroy(container libcontainer.Container) {
|
||||||
|
status, err := container.Status()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
if status != libcontainer.Checkpointed {
|
||||||
|
if err := container.Destroy(); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
signals.go
Normal file
108
signals.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const signalBufferSize = 2048
|
||||||
|
|
||||||
|
// newSignalHandler returns a signal handler for processing SIGCHLD and SIGWINCH signals
|
||||||
|
// while still forwarding all other signals to the process.
|
||||||
|
func newSignalHandler(tty *tty) *signalHandler {
|
||||||
|
// ensure that we have a large buffer size so that we do not miss any signals
|
||||||
|
// incase we are not processing them fast enough.
|
||||||
|
s := make(chan os.Signal, signalBufferSize)
|
||||||
|
// handle all signals for the process.
|
||||||
|
signal.Notify(s)
|
||||||
|
return &signalHandler{
|
||||||
|
tty: tty,
|
||||||
|
signals: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit models a process exit status with the pid and
|
||||||
|
// exit status.
|
||||||
|
type exit struct {
|
||||||
|
pid int
|
||||||
|
status int
|
||||||
|
}
|
||||||
|
|
||||||
|
type signalHandler struct {
|
||||||
|
signals chan os.Signal
|
||||||
|
tty *tty
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward handles the main signal event loop forwarding, resizing, or reaping depeding
|
||||||
|
// on the signal received.
|
||||||
|
func (h *signalHandler) forward(process *libcontainer.Process) (int, error) {
|
||||||
|
// make sure we know the pid of our main process so that we can return
|
||||||
|
// after it dies.
|
||||||
|
pid1, err := process.Pid()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
// perform the initial tty resize.
|
||||||
|
h.tty.resize()
|
||||||
|
for s := range h.signals {
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGWINCH:
|
||||||
|
h.tty.resize()
|
||||||
|
case syscall.SIGCHLD:
|
||||||
|
exits, err := h.reap()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
for _, e := range exits {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"pid": e.pid,
|
||||||
|
"status": e.status,
|
||||||
|
}).Debug("process exited")
|
||||||
|
if e.pid == pid1 {
|
||||||
|
// call Wait() on the process even though we already have the exit
|
||||||
|
// status because we must ensure that any of the go specific process
|
||||||
|
// fun such as flushing pipes are complete before we return.
|
||||||
|
process.Wait()
|
||||||
|
return e.status, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logrus.Debugf("sending signal to process %s", s)
|
||||||
|
if err := syscall.Kill(pid1, s.(syscall.Signal)); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reap runs wait4 in a loop until we have finished processing any existing exits
|
||||||
|
// then returns all exits to the main event loop for further processing.
|
||||||
|
func (h *signalHandler) reap() (exits []exit, err error) {
|
||||||
|
var (
|
||||||
|
ws syscall.WaitStatus
|
||||||
|
rus syscall.Rusage
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
pid, err := syscall.Wait4(-1, &ws, syscall.WNOHANG, &rus)
|
||||||
|
if err != nil {
|
||||||
|
if err == syscall.ECHILD {
|
||||||
|
return exits, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exits = append(exits, exit{
|
||||||
|
pid: pid,
|
||||||
|
status: utils.ExitStatus(ws),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *signalHandler) Close() error {
|
||||||
|
return h.tty.Close()
|
||||||
|
}
|
146
spec.go
Normal file
146
spec.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
const cpuQuotaMultiplyer = 100000
|
||||||
|
|
||||||
|
type Mount struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Destination string `json:"destination"`
|
||||||
|
Options string `json:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
TTY bool `json:"tty"`
|
||||||
|
User string `json:"user"`
|
||||||
|
Args []string `json:"args"`
|
||||||
|
Env []string `json:"env"`
|
||||||
|
Cwd string `json:"cwd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Root struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Readonly bool `json:"readonly"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Namespace struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PortableSpec struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
OS string `json:"os"`
|
||||||
|
Arch string `json:"arch"`
|
||||||
|
Processes []*Process `json:"processes"`
|
||||||
|
Root Root `json:"root"`
|
||||||
|
Cpus float64 `json:"cpus"` // in 1.1 for 110% cpus
|
||||||
|
Memory int64 `json:"memory"` // in mb; 1024m
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Namespaces []Namespace `json:"namespaces"`
|
||||||
|
Capabilities []string `json:"capabilities"`
|
||||||
|
Devices []string `json:"devices"`
|
||||||
|
Mounts []Mount `json:"mounts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var specCommand = cli.Command{
|
||||||
|
Name: "spec",
|
||||||
|
Usage: "create a new specification file",
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
spec := PortableSpec{
|
||||||
|
Version: version,
|
||||||
|
OS: runtime.GOOS,
|
||||||
|
Arch: runtime.GOARCH,
|
||||||
|
Root: Root{
|
||||||
|
Path: "rootfs",
|
||||||
|
Readonly: true,
|
||||||
|
},
|
||||||
|
Processes: []*Process{
|
||||||
|
{
|
||||||
|
TTY: true,
|
||||||
|
User: "daemon",
|
||||||
|
Args: []string{
|
||||||
|
"sh",
|
||||||
|
},
|
||||||
|
Env: []string{
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Cpus: 1.1,
|
||||||
|
Memory: 1024,
|
||||||
|
Hostname: "shell",
|
||||||
|
Capabilities: []string{
|
||||||
|
"AUDIT_WRITE",
|
||||||
|
"KILL",
|
||||||
|
"NET_BIND_SERVICE",
|
||||||
|
},
|
||||||
|
Devices: []string{
|
||||||
|
"null",
|
||||||
|
"random",
|
||||||
|
"full",
|
||||||
|
"tty",
|
||||||
|
"zero",
|
||||||
|
"urandom",
|
||||||
|
},
|
||||||
|
Namespaces: []Namespace{
|
||||||
|
{Type: "process"},
|
||||||
|
{Type: "network"},
|
||||||
|
{Type: "mount"},
|
||||||
|
{Type: "ipc"},
|
||||||
|
{Type: "uts"},
|
||||||
|
},
|
||||||
|
Mounts: []Mount{
|
||||||
|
{
|
||||||
|
Type: "proc",
|
||||||
|
Source: "proc",
|
||||||
|
Destination: "/proc",
|
||||||
|
Options: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "tmpfs",
|
||||||
|
Source: "tmpfs",
|
||||||
|
Destination: "/dev",
|
||||||
|
Options: "nosuid,strictatime,mode=755,size=65536k",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "devpts",
|
||||||
|
Source: "devpts",
|
||||||
|
Destination: "/dev/pts",
|
||||||
|
Options: "nosuid,noexec,newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "tmpfs",
|
||||||
|
Source: "shm",
|
||||||
|
Destination: "/dev/shm",
|
||||||
|
Options: "nosuid,noexec,nodev,mode=1777,size=65536k",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "mqueue",
|
||||||
|
Source: "mqueue",
|
||||||
|
Destination: "/dev/mqueue",
|
||||||
|
Options: "nosuid,noexec,nodev",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "sysfs",
|
||||||
|
Source: "sysfs",
|
||||||
|
Destination: "/sys",
|
||||||
|
Options: "nosuid,noexec,nodev",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
data, err := json.MarshalIndent(&spec, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s", data)
|
||||||
|
},
|
||||||
|
}
|
344
spec_linux.go
Normal file
344
spec_linux.go
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/devices"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserMapping struct {
|
||||||
|
From int `json:"from"`
|
||||||
|
To int `json:"to"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rlimit struct {
|
||||||
|
Type int `json:"type"`
|
||||||
|
Hard uint64 `json:"hard"`
|
||||||
|
Soft uint64 `json:"soft"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HugepageLimit struct {
|
||||||
|
Pagesize string `json:"pageSize"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IfPrioMap struct {
|
||||||
|
Interface string `json:"interface"`
|
||||||
|
Priority int64 `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resources struct {
|
||||||
|
// Memory reservation or soft_limit (in bytes)
|
||||||
|
MemoryReservation int64 `json:"memoryReservation"`
|
||||||
|
// Total memory usage (memory + swap); set `-1' to disable swap
|
||||||
|
MemorySwap int64 `json:"memorySwap"`
|
||||||
|
// Kernel memory limit (in bytes)
|
||||||
|
KernelMemory int64 `json:"kernelMemory"`
|
||||||
|
// CPU shares (relative weight vs. other containers)
|
||||||
|
CpuShares int64 `json:"cpuShares"`
|
||||||
|
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
|
||||||
|
CpuQuota int64 `json:"cpuQuota"`
|
||||||
|
// CPU period to be used for hardcapping (in usecs). 0 to use system default.
|
||||||
|
CpuPeriod int64 `json:"cpuPeriod"`
|
||||||
|
// How many time CPU will use in realtime scheduling (in usecs).
|
||||||
|
CpuRtRuntime int64 `json:"cpuQuota"`
|
||||||
|
// CPU period to be used for realtime scheduling (in usecs).
|
||||||
|
CpuRtPeriod int64 `json:"cpuPeriod"`
|
||||||
|
// CPU to use
|
||||||
|
CpusetCpus string `json:"cpusetCpus"`
|
||||||
|
// MEM to use
|
||||||
|
CpusetMems string `json:"cpusetMems"`
|
||||||
|
// IO read rate limit per cgroup per device, bytes per second.
|
||||||
|
BlkioThrottleReadBpsDevice string `json:"blkioThrottleReadBpsDevice"`
|
||||||
|
// IO write rate limit per cgroup per divice, bytes per second.
|
||||||
|
BlkioThrottleWriteBpsDevice string `json:"blkioThrottleWriteBpsDevice"`
|
||||||
|
// IO read rate limit per cgroup per device, IO per second.
|
||||||
|
BlkioThrottleReadIOpsDevice string `json:"blkioThrottleReadIopsDevice"`
|
||||||
|
// IO write rate limit per cgroup per device, IO per second.
|
||||||
|
BlkioThrottleWriteIOpsDevice string `json:"blkioThrottleWriteIopsDevice"`
|
||||||
|
// Specifies per cgroup weight, range is from 10 to 1000.
|
||||||
|
BlkioWeight int64 `json:"blkioWeight"`
|
||||||
|
// Weight per cgroup per device, can override BlkioWeight.
|
||||||
|
BlkioWeightDevice string `json:"blkioWeightDevice"`
|
||||||
|
// Hugetlb limit (in bytes)
|
||||||
|
HugetlbLimit []*HugepageLimit `json:"hugetlbLimit"`
|
||||||
|
// Whether to disable OOM Killer
|
||||||
|
DisableOOMKiller bool `json:"disableOOMKiller"`
|
||||||
|
// Set priority of network traffic for container
|
||||||
|
NetPrioIfpriomap []*IfPrioMap `json:"netPrioIfpriomap"`
|
||||||
|
// Set class identifier for container's network packets
|
||||||
|
NetClsClassid string `json:"netClsClassid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinuxSpec struct {
|
||||||
|
PortableSpec
|
||||||
|
UserMapping map[string]UserMapping `json:"userMapping"`
|
||||||
|
Rlimits []Rlimit `json:"rlimits"`
|
||||||
|
SystemProperties map[string]string `json:"systemProperties"`
|
||||||
|
Resources *Resources `json:"resources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var namespaceMapping = map[string]configs.NamespaceType{
|
||||||
|
"process": configs.NEWPID,
|
||||||
|
"network": configs.NEWNET,
|
||||||
|
"mount": configs.NEWNS,
|
||||||
|
"user": configs.NEWUSER,
|
||||||
|
"ipc": configs.NEWIPC,
|
||||||
|
"uts": configs.NEWUTS,
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadSpec loads the specification from the provided path.
|
||||||
|
// If the path is empty then the default path will be "container.json"
|
||||||
|
func loadSpec(path string) (*LinuxSpec, error) {
|
||||||
|
if path == "" {
|
||||||
|
path = "container.json"
|
||||||
|
}
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("JSON specification file for %s not found", path)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var s *LinuxSpec
|
||||||
|
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createLibcontainerConfig(spec *LinuxSpec) (*configs.Config, error) {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config := &configs.Config{
|
||||||
|
Capabilities: spec.Capabilities,
|
||||||
|
Rootfs: filepath.Join(cwd, spec.Root.Path),
|
||||||
|
Readonlyfs: spec.Root.Readonly,
|
||||||
|
Hostname: spec.Hostname,
|
||||||
|
Privatefs: true,
|
||||||
|
}
|
||||||
|
for _, ns := range spec.Namespaces {
|
||||||
|
t, exists := namespaceMapping[ns.Type]
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("namespace %q does not exist", ns)
|
||||||
|
}
|
||||||
|
config.Namespaces.Add(t, ns.Path)
|
||||||
|
}
|
||||||
|
for _, m := range spec.Mounts {
|
||||||
|
config.Mounts = append(config.Mounts, createLibcontianerMount(cwd, m))
|
||||||
|
}
|
||||||
|
if err := createDevices(spec, config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := setupUserNamespace(spec, config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := createCgroupConfig(spec, config.Devices)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Cgroups = c
|
||||||
|
if config.Readonlyfs {
|
||||||
|
setReadonly(config)
|
||||||
|
config.MaskPaths = []string{
|
||||||
|
"/proc/kcore",
|
||||||
|
}
|
||||||
|
config.ReadonlyPaths = []string{
|
||||||
|
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createLibcontianerMount(cwd string, m Mount) *configs.Mount {
|
||||||
|
flags, data := parseMountOptions(m.Options)
|
||||||
|
source := m.Source
|
||||||
|
if m.Type == "bind" {
|
||||||
|
if !filepath.IsAbs(source) {
|
||||||
|
source = filepath.Join(cwd, m.Source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &configs.Mount{
|
||||||
|
Device: m.Type,
|
||||||
|
Source: source,
|
||||||
|
Destination: m.Destination,
|
||||||
|
Data: data,
|
||||||
|
Flags: flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createCgroupConfig(spec *LinuxSpec, devices []*configs.Device) (*configs.Cgroup, error) {
|
||||||
|
myCgroupPath, err := cgroups.GetThisCgroupDir("devices")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c := &configs.Cgroup{
|
||||||
|
Name: getDefaultID(),
|
||||||
|
Parent: myCgroupPath,
|
||||||
|
AllowedDevices: append(devices, allowedDevices...),
|
||||||
|
CpuQuota: getCPUQuota(spec.Cpus),
|
||||||
|
Memory: spec.Memory * 1024 * 1024,
|
||||||
|
MemorySwap: -1,
|
||||||
|
}
|
||||||
|
if r := spec.Resources; r != nil {
|
||||||
|
c.MemoryReservation = r.MemoryReservation
|
||||||
|
c.MemorySwap = r.MemorySwap
|
||||||
|
c.KernelMemory = r.KernelMemory
|
||||||
|
c.CpuShares = r.CpuShares
|
||||||
|
c.CpuQuota = r.CpuQuota
|
||||||
|
c.CpuPeriod = r.CpuPeriod
|
||||||
|
c.CpuRtRuntime = r.CpuRtRuntime
|
||||||
|
c.CpuRtPeriod = r.CpuRtPeriod
|
||||||
|
c.CpusetCpus = r.CpusetCpus
|
||||||
|
c.CpusetMems = r.CpusetMems
|
||||||
|
c.BlkioThrottleReadBpsDevice = r.BlkioThrottleReadBpsDevice
|
||||||
|
c.BlkioThrottleWriteBpsDevice = r.BlkioThrottleWriteBpsDevice
|
||||||
|
c.BlkioThrottleReadIOpsDevice = r.BlkioThrottleReadIOpsDevice
|
||||||
|
c.BlkioThrottleWriteIOpsDevice = r.BlkioThrottleWriteIOpsDevice
|
||||||
|
c.BlkioWeight = r.BlkioWeight
|
||||||
|
c.BlkioWeightDevice = r.BlkioWeightDevice
|
||||||
|
for _, l := range r.HugetlbLimit {
|
||||||
|
c.HugetlbLimit = append(c.HugetlbLimit, &configs.HugepageLimit{
|
||||||
|
Pagesize: l.Pagesize,
|
||||||
|
Limit: l.Limit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
c.OomKillDisable = r.DisableOOMKiller
|
||||||
|
for _, m := range r.NetPrioIfpriomap {
|
||||||
|
c.NetPrioIfpriomap = append(c.NetPrioIfpriomap, &configs.IfPrioMap{
|
||||||
|
Interface: m.Interface,
|
||||||
|
Priority: m.Priority,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
c.NetClsClassid = r.NetClsClassid
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDevices(spec *LinuxSpec, config *configs.Config) error {
|
||||||
|
for _, name := range spec.Devices {
|
||||||
|
d, err := devices.DeviceFromPath(filepath.Join("/dev", name), "rwm")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.Devices = append(config.Devices, d)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setReadonly(config *configs.Config) {
|
||||||
|
for _, m := range config.Mounts {
|
||||||
|
if m.Device == "sysfs" {
|
||||||
|
m.Flags |= syscall.MS_RDONLY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCPUQuota(cpus float64) int64 {
|
||||||
|
return int64(cpus * cpuQuotaMultiplyer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupUserNamespace(spec *LinuxSpec, config *configs.Config) error {
|
||||||
|
if len(spec.UserMapping) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
config.Namespaces.Add(configs.NEWUSER, "")
|
||||||
|
mappings := make(map[string][]configs.IDMap)
|
||||||
|
for k, v := range spec.UserMapping {
|
||||||
|
mappings[k] = append(mappings[k], configs.IDMap{
|
||||||
|
ContainerID: v.From,
|
||||||
|
HostID: v.To,
|
||||||
|
Size: v.Count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
config.UidMappings = mappings["uid"]
|
||||||
|
config.GidMappings = mappings["gid"]
|
||||||
|
rootUid, err := config.HostUID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rootGid, err := config.HostGID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, node := range config.Devices {
|
||||||
|
node.Uid = uint32(rootUid)
|
||||||
|
node.Gid = uint32(rootGid)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMountOptions parses the string and returns the flags and any mount data that
|
||||||
|
// it contains.
|
||||||
|
func parseMountOptions(options string) (int, string) {
|
||||||
|
var (
|
||||||
|
flag int
|
||||||
|
data []string
|
||||||
|
)
|
||||||
|
flags := map[string]struct {
|
||||||
|
clear bool
|
||||||
|
flag int
|
||||||
|
}{
|
||||||
|
"defaults": {false, 0},
|
||||||
|
"ro": {false, syscall.MS_RDONLY},
|
||||||
|
"rw": {true, syscall.MS_RDONLY},
|
||||||
|
"suid": {true, syscall.MS_NOSUID},
|
||||||
|
"nosuid": {false, syscall.MS_NOSUID},
|
||||||
|
"dev": {true, syscall.MS_NODEV},
|
||||||
|
"nodev": {false, syscall.MS_NODEV},
|
||||||
|
"exec": {true, syscall.MS_NOEXEC},
|
||||||
|
"noexec": {false, syscall.MS_NOEXEC},
|
||||||
|
"sync": {false, syscall.MS_SYNCHRONOUS},
|
||||||
|
"async": {true, syscall.MS_SYNCHRONOUS},
|
||||||
|
"dirsync": {false, syscall.MS_DIRSYNC},
|
||||||
|
"remount": {false, syscall.MS_REMOUNT},
|
||||||
|
"mand": {false, syscall.MS_MANDLOCK},
|
||||||
|
"nomand": {true, syscall.MS_MANDLOCK},
|
||||||
|
"atime": {true, syscall.MS_NOATIME},
|
||||||
|
"noatime": {false, syscall.MS_NOATIME},
|
||||||
|
"diratime": {true, syscall.MS_NODIRATIME},
|
||||||
|
"nodiratime": {false, syscall.MS_NODIRATIME},
|
||||||
|
"bind": {false, syscall.MS_BIND},
|
||||||
|
"rbind": {false, syscall.MS_BIND | syscall.MS_REC},
|
||||||
|
"unbindable": {false, syscall.MS_UNBINDABLE},
|
||||||
|
"runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC},
|
||||||
|
"private": {false, syscall.MS_PRIVATE},
|
||||||
|
"rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC},
|
||||||
|
"shared": {false, syscall.MS_SHARED},
|
||||||
|
"rshared": {false, syscall.MS_SHARED | syscall.MS_REC},
|
||||||
|
"slave": {false, syscall.MS_SLAVE},
|
||||||
|
"rslave": {false, syscall.MS_SLAVE | syscall.MS_REC},
|
||||||
|
"relatime": {false, syscall.MS_RELATIME},
|
||||||
|
"norelatime": {true, syscall.MS_RELATIME},
|
||||||
|
"strictatime": {false, syscall.MS_STRICTATIME},
|
||||||
|
"nostrictatime": {true, syscall.MS_STRICTATIME},
|
||||||
|
}
|
||||||
|
for _, o := range strings.Split(options, ",") {
|
||||||
|
// If the option does not exist in the flags table or the flag
|
||||||
|
// is not supported on the platform,
|
||||||
|
// then it is a data value for a specific fs type
|
||||||
|
if f, exists := flags[o]; exists && f.flag != 0 {
|
||||||
|
if f.clear {
|
||||||
|
flag &= ^f.flag
|
||||||
|
} else {
|
||||||
|
flag |= f.flag
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = append(data, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag, strings.Join(data, ",")
|
||||||
|
}
|
97
tty.go
Normal file
97
tty.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newTty creates a new pty for use with the container. If a tty is not to be
|
||||||
|
// created for the process, pipes are created so that the TTY of the parent
|
||||||
|
// process are not inherited by the container.
|
||||||
|
func newTty(create bool, p *libcontainer.Process, rootuid int) (*tty, error) {
|
||||||
|
if create {
|
||||||
|
return createTty(p, rootuid)
|
||||||
|
}
|
||||||
|
return createStdioPipes(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup standard pipes so that the TTY of the calling nsinit process
|
||||||
|
// is not inherited by the container.
|
||||||
|
func createStdioPipes(p *libcontainer.Process) (*tty, error) {
|
||||||
|
t := &tty{}
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go io.Copy(w, os.Stdin)
|
||||||
|
t.closers = append(t.closers, w)
|
||||||
|
p.Stdin = r
|
||||||
|
if r, w, err = os.Pipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go io.Copy(os.Stdout, r)
|
||||||
|
p.Stdout = w
|
||||||
|
t.closers = append(t.closers, r)
|
||||||
|
if r, w, err = os.Pipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go io.Copy(os.Stderr, r)
|
||||||
|
p.Stderr = w
|
||||||
|
t.closers = append(t.closers, r)
|
||||||
|
return t, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTty(p *libcontainer.Process, rootuid int) (*tty, error) {
|
||||||
|
console, err := p.NewConsole(rootuid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go io.Copy(console, os.Stdin)
|
||||||
|
go io.Copy(os.Stdout, console)
|
||||||
|
state, err := term.SetRawTerminal(os.Stdin.Fd())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := &tty{
|
||||||
|
console: console,
|
||||||
|
state: state,
|
||||||
|
closers: []io.Closer{
|
||||||
|
console,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p.Stderr = nil
|
||||||
|
p.Stdout = nil
|
||||||
|
p.Stdin = nil
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tty struct {
|
||||||
|
console libcontainer.Console
|
||||||
|
state *term.State
|
||||||
|
closers []io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tty) Close() error {
|
||||||
|
for _, c := range t.closers {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
if t.state != nil {
|
||||||
|
term.RestoreTerminal(os.Stdin.Fd(), t.state)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tty) resize() error {
|
||||||
|
if t.console == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ws, err := term.GetWinsize(os.Stdin.Fd())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return term.SetWinsize(t.console.Fd(), ws)
|
||||||
|
}
|
173
utils.go
Normal file
173
utils.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const wildcard = -1
|
||||||
|
|
||||||
|
var allowedDevices = []*configs.Device{
|
||||||
|
// allow mknod for any device
|
||||||
|
{
|
||||||
|
Type: 'c',
|
||||||
|
Major: wildcard,
|
||||||
|
Minor: wildcard,
|
||||||
|
Permissions: "m",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: 'b',
|
||||||
|
Major: wildcard,
|
||||||
|
Minor: wildcard,
|
||||||
|
Permissions: "m",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dev/console",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 5,
|
||||||
|
Minor: 1,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dev/tty0",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 4,
|
||||||
|
Minor: 0,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dev/tty1",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 4,
|
||||||
|
Minor: 1,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
// /dev/pts/ - pts namespaces are "coming soon"
|
||||||
|
{
|
||||||
|
Path: "",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 136,
|
||||||
|
Minor: wildcard,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 5,
|
||||||
|
Minor: 2,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
// tuntap
|
||||||
|
{
|
||||||
|
Path: "",
|
||||||
|
Type: 'c',
|
||||||
|
Major: 10,
|
||||||
|
Minor: 200,
|
||||||
|
Permissions: "rwm",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var container libcontainer.Container
|
||||||
|
|
||||||
|
func containerPreload(context *cli.Context) error {
|
||||||
|
c, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
container = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var factory libcontainer.Factory
|
||||||
|
|
||||||
|
func factoryPreload(context *cli.Context) error {
|
||||||
|
f, err := loadFactory(context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
factory = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadFactory returns the configured factory instance for execing containers.
|
||||||
|
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
||||||
|
root := context.GlobalString("root")
|
||||||
|
abs, err := filepath.Abs(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return libcontainer.New(abs, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
|
||||||
|
l.CriuPath = context.GlobalString("criu")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContainer returns the specified container instance by loading it from state
|
||||||
|
// with the default factory.
|
||||||
|
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
||||||
|
factory, err := loadFactory(context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
container, err := factory.Load(context.GlobalString("id"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fatal prints the error's details if it is a libcontainer specific error type
|
||||||
|
// then exists the program with an exit status of 1.
|
||||||
|
func fatal(err error) {
|
||||||
|
if lerr, ok := err.(libcontainer.Error); ok {
|
||||||
|
lerr.Detail(os.Stderr)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fatalf formats the errror string with the specified template then exits the
|
||||||
|
// program with an exit status of 1.
|
||||||
|
func fatalf(t string, v ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, t, v...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultID returns a string to be used as the container id based on the
|
||||||
|
// current working directory of the nsinit process. This function panics
|
||||||
|
// if the cwd is unable to be found based on a system error.
|
||||||
|
func getDefaultID() string {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return filepath.Base(cwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultImagePath(context *cli.Context) string {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return filepath.Join(cwd, "checkpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
// newProcess returns a new libcontainer Process with the arguments from the
|
||||||
|
// spec and stdio from the current process.
|
||||||
|
func newProcess(p *Process) *libcontainer.Process {
|
||||||
|
return &libcontainer.Process{
|
||||||
|
Args: p.Args,
|
||||||
|
Env: p.Env,
|
||||||
|
User: p.User,
|
||||||
|
Cwd: p.Cwd,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user