Add args and netns flag to runsc spec

Adds a netns flag to runsc spec that allows users to specify a network
namespace path when creating a sample config.json file. Also, adds the ability
to specify the command arguments used when running the container.

This will make it easier for new users to create sample OCI bundles without
having to edit the config.json by hand.

PiperOrigin-RevId: 320486267
This commit is contained in:
Ian Lewis
2020-07-09 15:26:52 -07:00
committed by gVisor bot
parent 8d2910a04d
commit e506fcd931
3 changed files with 129 additions and 115 deletions
+4 -6
View File
@@ -15,8 +15,8 @@ mkdir bundle
cd bundle
```
Create a root file system for the container. We will use the Docker hello-world
image as the basis for our container.
Create a root file system for the container. We will use the Docker
`hello-world` image as the basis for our container.
```bash
mkdir rootfs
@@ -24,12 +24,10 @@ docker export $(docker create hello-world) | tar -xf - -C rootfs
```
Next, create an specification file called `config.json` that contains our
container specification. We will update the default command it runs to `/hello`
in the `hello-world` container.
container specification. We tell the container to run the `/hello` program.
```bash
runsc spec
sed -i 's;"sh";"/hello";' config.json
runsc spec -- /hello
```
Finally run the container.
+8 -6
View File
@@ -128,12 +128,14 @@ sudo mkdir -p rootfs/var/www/html
sudo sh -c 'echo "Hello World!" > rootfs/var/www/html/index.html'
```
Next create the `config.json` specifying the network namespace. `sudo
/usr/local/bin/runsc spec sudo sed -i 's;"sh";"python", "-m", "http.server";'
config.json sudo sed -i "s;\"cwd\": \"/\";\"cwd\": \"/var/www/html\";"
config.json sudo sed -i "s;\"type\": \"network\";\"type\":
\"network\",\n\t\t\t\t\"path\": \"/var/run/netns/${CNI_CONTAINERID}\";"
config.json`
Next create the `config.json` specifying the network namespace.
```
sudo /usr/local/bin/runsc spec \
--cwd /var/www/html \
--netns /var/run/netns/${CNI_CONTAINERID} \
-- python -m http.server
```
## Run the Container
+117 -103
View File
@@ -16,124 +16,122 @@ package cmd
import (
"context"
"fmt"
"io/ioutil"
"encoding/json"
"io"
"os"
"path/filepath"
"github.com/google/subcommands"
specs "github.com/opencontainers/runtime-spec/specs-go"
"gvisor.dev/gvisor/runsc/flag"
)
func genSpec(cwd string) []byte {
var template = fmt.Sprintf(`{
"ociVersion": "1.0.0",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
func writeSpec(w io.Writer, cwd string, netns string, args []string) error {
spec := &specs.Spec{
Version: "1.0.0",
Process: &specs.Process{
Terminal: true,
User: specs.User{
UID: 0,
GID: 0,
},
Args: args,
Env: []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm",
},
Cwd: cwd,
Capabilities: &specs.LinuxCapabilities{
Bounding: []string{
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE",
},
Effective: []string{
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE",
},
Inheritable: []string{
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE",
},
Permitted: []string{
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE",
},
// TODO(gvisor.dev/issue/3166): support ambient capabilities
},
Rlimits: []specs.POSIXRlimit{
{
Type: "RLIMIT_NOFILE",
Hard: 1024,
Soft: 1024,
},
},
},
"args": [
"sh"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "%s",
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"inheritable": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"permitted": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"ambient": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
]
Root: &specs.Root{
Path: "rootfs",
Readonly: true,
},
"rlimits": [
Hostname: "runsc",
Mounts: []specs.Mount{
{
"type": "RLIMIT_NOFILE",
"hard": 1024,
"soft": 1024
}
]
},
"root": {
"path": "rootfs",
"readonly": true
},
"hostname": "runsc",
"mounts": [
{
"destination": "/proc",
"type": "proc",
"source": "proc"
},
{
"destination": "/dev",
"type": "tmpfs",
"source": "tmpfs",
"options": []
},
{
"destination": "/sys",
"type": "sysfs",
"source": "sysfs",
"options": [
"nosuid",
"noexec",
"nodev",
"ro"
]
}
],
"linux": {
"namespaces": [
{
"type": "pid"
Destination: "/proc",
Type: "proc",
Source: "proc",
},
{
"type": "network"
Destination: "/dev",
Type: "tmpfs",
Source: "tmpfs",
},
{
"type": "ipc"
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{
"nosuid",
"noexec",
"nodev",
"ro",
},
},
{
"type": "uts"
},
Linux: &specs.Linux{
Namespaces: []specs.LinuxNamespace{
{
Type: "pid",
},
{
Type: "network",
Path: netns,
},
{
Type: "ipc",
},
{
Type: "uts",
},
{
Type: "mount",
},
},
{
"type": "mount"
}
]
},
}
}`, cwd)
return []byte(template)
e := json.NewEncoder(w)
e.SetIndent("", " ")
return e.Encode(spec)
}
// Spec implements subcommands.Command for the "spec" command.
type Spec struct {
bundle string
cwd string
netns string
}
// Name implements subcommands.Command.Name.
@@ -148,21 +146,26 @@ func (*Spec) Synopsis() string {
// Usage implements subcommands.Command.Usage.
func (*Spec) Usage() string {
return `spec [options] - create a new OCI bundle specification file.
return `spec [options] [-- args...] - create a new OCI bundle specification file.
The spec command creates a new specification file (config.json) for a new OCI bundle.
The spec command creates a new specification file (config.json) for a new OCI
bundle.
The specification file is a starter file that runs the "sh" command in the container. You
should edit the file to suit your needs. You can find out more about the format of the
specification file by visiting the OCI runtime spec repository:
The specification file is a starter file that runs the command specified by
'args' in the container. If 'args' is not specified the default is to run the
'sh' program.
While a number of flags are provided to change values in the specification, you
can examine the file and edit it to suit your needs after this command runs.
You can find out more about the format of the specification file by visiting
the OCI runtime spec repository:
https://github.com/opencontainers/runtime-spec/
EXAMPLE:
$ mkdir -p bundle/rootfs
$ cd bundle
$ runsc spec
$ runsc spec -- /hello
$ docker export $(docker create hello-world) | tar -xf - -C rootfs
$ sed -i 's;"sh";"/hello";' config.json
$ sudo runsc run hello
`
@@ -173,18 +176,29 @@ func (s *Spec) SetFlags(f *flag.FlagSet) {
f.StringVar(&s.bundle, "bundle", ".", "path to the root of the OCI bundle")
f.StringVar(&s.cwd, "cwd", "/", "working directory that will be set for the executable, "+
"this value MUST be an absolute path")
f.StringVar(&s.netns, "netns", "", "network namespace path")
}
// Execute implements subcommands.Command.Execute.
func (s *Spec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
// Grab the arguments.
containerArgs := f.Args()
if len(containerArgs) == 0 {
containerArgs = []string{"sh"}
}
confPath := filepath.Join(s.bundle, "config.json")
if _, err := os.Stat(confPath); !os.IsNotExist(err) {
Fatalf("file %q already exists", confPath)
}
var spec = genSpec(s.cwd)
configFile, err := os.OpenFile(confPath, os.O_WRONLY|os.O_CREATE, 0664)
if err != nil {
Fatalf("opening file %q: %v", confPath, err)
}
if err := ioutil.WriteFile(confPath, spec, 0664); err != nil {
err = writeSpec(configFile, s.cwd, s.netns, containerArgs)
if err != nil {
Fatalf("writing to %q: %v", confPath, err)
}