mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-20 01:29:07 +00:00
Merge pull request #145 from justincormack/runtime-mount
Add support for mount in runtime config
This commit is contained in:
commit
69596e17dd
@ -183,6 +183,8 @@ permissions issues in use.
|
|||||||
|
|
||||||
In addition to the parts of the specification above used to generate the OCI spec, there is a `runtime` section in the image specification
|
In addition to the parts of the specification above used to generate the OCI spec, there is a `runtime` section in the image specification
|
||||||
which specifies some actions to take place when the container is being started.
|
which specifies some actions to take place when the container is being started.
|
||||||
|
- `mounts` takes a list of mount specifications (`source`, `destination`, `type`, `options`) and mounts them in the root namespace before the container is created. It will
|
||||||
|
try to make any missing destination directories.
|
||||||
- `mkdir` takes a list of directories to create at runtime, in the root mount namespace. These are created before the container is started, so they can be used to create
|
- `mkdir` takes a list of directories to create at runtime, in the root mount namespace. These are created before the container is started, so they can be used to create
|
||||||
directories for bind mounts, for example in `/tmp` or `/run` which would otherwise be empty.
|
directories for bind mounts, for example in `/tmp` or `/run` which would otherwise be empty.
|
||||||
- `interface` defines a list of actions to perform on a network interface:
|
- `interface` defines a list of actions to perform on a network interface:
|
||||||
|
@ -120,7 +120,7 @@ func enforceContentTrust(fullImageName string, config *TrustConfig) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputImage(image Image, section string, prefix string, m Moby, idMap map[string]uint32, pull bool, iw *tar.Writer) error {
|
func outputImage(image Image, section string, prefix string, m Moby, idMap map[string]uint32, dupMap map[string]string, pull bool, iw *tar.Writer) error {
|
||||||
log.Infof(" Create OCI config for %s", image.Image)
|
log.Infof(" Create OCI config for %s", image.Image)
|
||||||
useTrust := enforceContentTrust(image.Image, &m.Trust)
|
useTrust := enforceContentTrust(image.Image, &m.Trust)
|
||||||
oci, runtime, err := ConfigToOCI(image, useTrust, idMap)
|
oci, runtime, err := ConfigToOCI(image, useTrust, idMap)
|
||||||
@ -131,13 +131,9 @@ func outputImage(image Image, section string, prefix string, m Moby, idMap map[s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to create config for %s: %v", image.Image, err)
|
return fmt.Errorf("Failed to create config for %s: %v", image.Image, err)
|
||||||
}
|
}
|
||||||
runtimeConfig, err := json.MarshalIndent(runtime, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to create runtime config for %s: %v", image.Image, err)
|
|
||||||
}
|
|
||||||
path := path.Join("containers", section, prefix+image.Name)
|
path := path.Join("containers", section, prefix+image.Name)
|
||||||
readonly := oci.Root.Readonly
|
readonly := oci.Root.Readonly
|
||||||
err = ImageBundle(path, image.Image, config, runtimeConfig, iw, useTrust, pull, readonly)
|
err = ImageBundle(path, image.Image, config, runtime, iw, useTrust, pull, readonly, dupMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to extract root filesystem for %s: %v", image.Image, err)
|
return fmt.Errorf("Failed to extract root filesystem for %s: %v", image.Image, err)
|
||||||
}
|
}
|
||||||
@ -171,6 +167,9 @@ func Build(m Moby, w io.Writer, pull bool, tp string) error {
|
|||||||
id++
|
id++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deduplicate containers with the same image
|
||||||
|
dupMap := map[string]string{}
|
||||||
|
|
||||||
if m.Kernel.Image != "" {
|
if m.Kernel.Image != "" {
|
||||||
// get kernel and initrd tarball from container
|
// get kernel and initrd tarball from container
|
||||||
log.Infof("Extract kernel image: %s", m.Kernel.Image)
|
log.Infof("Extract kernel image: %s", m.Kernel.Image)
|
||||||
@ -202,7 +201,7 @@ func Build(m Moby, w io.Writer, pull bool, tp string) error {
|
|||||||
}
|
}
|
||||||
for i, image := range m.Onboot {
|
for i, image := range m.Onboot {
|
||||||
so := fmt.Sprintf("%03d", i)
|
so := fmt.Sprintf("%03d", i)
|
||||||
if err := outputImage(image, "onboot", so+"-", m, idMap, pull, iw); err != nil {
|
if err := outputImage(image, "onboot", so+"-", m, idMap, dupMap, pull, iw); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,7 +211,7 @@ func Build(m Moby, w io.Writer, pull bool, tp string) error {
|
|||||||
}
|
}
|
||||||
for i, image := range m.Onshutdown {
|
for i, image := range m.Onshutdown {
|
||||||
so := fmt.Sprintf("%03d", i)
|
so := fmt.Sprintf("%03d", i)
|
||||||
if err := outputImage(image, "onshutdown", so+"-", m, idMap, pull, iw); err != nil {
|
if err := outputImage(image, "onshutdown", so+"-", m, idMap, dupMap, pull, iw); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,7 +220,7 @@ func Build(m Moby, w io.Writer, pull bool, tp string) error {
|
|||||||
log.Infof("Add service containers:")
|
log.Infof("Add service containers:")
|
||||||
}
|
}
|
||||||
for _, image := range m.Services {
|
for _, image := range m.Services {
|
||||||
if err := outputImage(image, "services", "", m, idMap, pull, iw); err != nil {
|
if err := outputImage(image, "services", "", m, idMap, dupMap, pull, iw); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,9 +93,10 @@ type Image struct {
|
|||||||
|
|
||||||
// Runtime is the type of config processed at runtime, not used to build the OCI spec
|
// Runtime is the type of config processed at runtime, not used to build the OCI spec
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
Mkdir []string `yaml:"mkdir" json:"mkdir,omitempty"`
|
Mounts []specs.Mount `yaml:"mounts" json:"mounts,omitempty"`
|
||||||
Interfaces []Interface `yaml:"interfaces" json:"interfaces,omitempty"`
|
Mkdir []string `yaml:"mkdir" json:"mkdir,omitempty"`
|
||||||
BindNS *Namespaces `yaml:"bindNS" json:"bindNS,omitempty"`
|
Interfaces []Interface `yaml:"interfaces" json:"interfaces,omitempty"`
|
||||||
|
BindNS Namespaces `yaml:"bindNS" json:"bindNS,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Namespaces is the type for configuring paths to bind namespaces
|
// Namespaces is the type for configuring paths to bind namespaces
|
||||||
@ -727,7 +728,6 @@ func ConfigInspectToOCI(yaml Image, inspect types.ImageInspect, idMap map[string
|
|||||||
sort.Sort(mountList)
|
sort.Sort(mountList)
|
||||||
|
|
||||||
namespaces := []specs.LinuxNamespace{}
|
namespaces := []specs.LinuxNamespace{}
|
||||||
// to attach to an existing namespace, easiest to bind mount with nsfs in a system container
|
|
||||||
|
|
||||||
// net, ipc, and uts namespaces: default to not creating a new namespace (usually host namespace)
|
// net, ipc, and uts namespaces: default to not creating a new namespace (usually host namespace)
|
||||||
netNS := assignStringEmpty3("root", label.Net, yaml.Net)
|
netNS := assignStringEmpty3("root", label.Net, yaml.Net)
|
||||||
|
@ -3,6 +3,7 @@ package moby
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -10,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tarWriter interface {
|
type tarWriter interface {
|
||||||
@ -189,18 +191,28 @@ func ImageTar(image, prefix string, tw tarWriter, trust bool, pull bool, resolv
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ImageBundle produces an OCI bundle at the given path in a tarball, given an image and a config.json
|
// ImageBundle produces an OCI bundle at the given path in a tarball, given an image and a config.json
|
||||||
func ImageBundle(prefix string, image string, config []byte, runtimeConfig []byte, tw tarWriter, trust bool, pull bool, readonly bool) error {
|
func ImageBundle(prefix string, image string, config []byte, runtime Runtime, tw tarWriter, trust bool, pull bool, readonly bool, dupMap map[string]string) error {
|
||||||
log.Debugf("image bundle: %s %s cfg: %s runtime: %s", prefix, image, string(config), string(runtimeConfig))
|
|
||||||
|
|
||||||
// if read only, just unpack in rootfs/ but otherwise set up for overlay
|
// if read only, just unpack in rootfs/ but otherwise set up for overlay
|
||||||
rootfs := "rootfs"
|
rootExtract := "rootfs"
|
||||||
if !readonly {
|
if !readonly {
|
||||||
rootfs = "lower"
|
rootExtract = "lower"
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ImageTar(image, path.Join(prefix, rootfs)+"/", tw, trust, pull, ""); err != nil {
|
// See if we have extracted this image previously
|
||||||
return err
|
root := path.Join(prefix, rootExtract)
|
||||||
|
var foundElsewhere = dupMap[image] != ""
|
||||||
|
if !foundElsewhere {
|
||||||
|
if err := ImageTar(image, root+"/", tw, trust, pull, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dupMap[image] = root
|
||||||
|
} else {
|
||||||
|
if err := tarPrefix(prefix+"/", tw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root = dupMap[image]
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr := &tar.Header{
|
hdr := &tar.Header{
|
||||||
Name: path.Join(prefix, "config.json"),
|
Name: path.Join(prefix, "config.json"),
|
||||||
Mode: 0644,
|
Mode: 0644,
|
||||||
@ -214,26 +226,11 @@ func ImageBundle(prefix string, image string, config []byte, runtimeConfig []byt
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not write an empty runtime config
|
|
||||||
if string(runtimeConfig) != "{}" {
|
|
||||||
hdr = &tar.Header{
|
|
||||||
Name: path.Join(prefix, "runtime.json"),
|
|
||||||
Mode: 0644,
|
|
||||||
Size: int64(len(runtimeConfig)),
|
|
||||||
}
|
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf = bytes.NewBuffer(runtimeConfig)
|
|
||||||
if _, err := io.Copy(tw, buf); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !readonly {
|
if !readonly {
|
||||||
// add a tmp directory to be used as a mount point for tmpfs for upper, work
|
// add a tmp directory to be used as a mount point for tmpfs for upper, work
|
||||||
|
tmp := path.Join(prefix, "tmp")
|
||||||
hdr = &tar.Header{
|
hdr = &tar.Header{
|
||||||
Name: path.Join(prefix, "tmp"),
|
Name: tmp,
|
||||||
Mode: 0755,
|
Mode: 0755,
|
||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
}
|
}
|
||||||
@ -249,7 +246,47 @@ func ImageBundle(prefix string, image string, config []byte, runtimeConfig []byt
|
|||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
runtime.Mounts = append(runtime.Mounts, specs.Mount{Source: "tmpfs", Type: "tmpfs", Destination: "/" + tmp})
|
||||||
|
// remount private as nothing else should see the temporary layers
|
||||||
|
runtime.Mounts = append(runtime.Mounts, specs.Mount{Destination: "/" + tmp, Options: []string{"remount", "private"}})
|
||||||
|
overlayOptions := []string{"lowerdir=/" + root, "upperdir=/" + path.Join(tmp, "upper"), "workdir=/" + path.Join(tmp, "work")}
|
||||||
|
runtime.Mounts = append(runtime.Mounts, specs.Mount{Source: "overlay", Type: "overlay", Destination: "/" + path.Join(prefix, "rootfs"), Options: overlayOptions})
|
||||||
|
} else {
|
||||||
|
if foundElsewhere {
|
||||||
|
// we need to make the mountpoint at rootfs
|
||||||
|
hdr = &tar.Header{
|
||||||
|
Name: path.Join(prefix, "rootfs"),
|
||||||
|
Mode: 0755,
|
||||||
|
Typeflag: tar.TypeDir,
|
||||||
|
}
|
||||||
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// either bind from another location, or bind from self to make sure it is a mountpoint as runc prefers this
|
||||||
|
runtime.Mounts = append(runtime.Mounts, specs.Mount{Source: "/" + root, Destination: "/" + path.Join(prefix, "rootfs"), Options: []string{"bind"}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write the runtime config
|
||||||
|
runtimeConfig, err := json.MarshalIndent(runtime, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to create runtime config for %s: %v", image, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = &tar.Header{
|
||||||
|
Name: path.Join(prefix, "runtime.json"),
|
||||||
|
Mode: 0644,
|
||||||
|
Size: int64(len(runtimeConfig)),
|
||||||
|
}
|
||||||
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf = bytes.NewBuffer(runtimeConfig)
|
||||||
|
if _, err := io.Copy(tw, buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("image bundle: %s %s cfg: %s runtime: %s", prefix, image, string(config), string(runtimeConfig))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -239,6 +239,7 @@ var schema = string(`
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"mounts": {"$ref": "#/definitions/mounts"},
|
||||||
"mkdir": {"$ref": "#/definitions/strings"},
|
"mkdir": {"$ref": "#/definitions/strings"},
|
||||||
"interfaces": {"$ref": "#/definitions/interfaces"},
|
"interfaces": {"$ref": "#/definitions/interfaces"},
|
||||||
"bindNS": {"$ref": "#/definitions/namespaces"}
|
"bindNS": {"$ref": "#/definitions/namespaces"}
|
||||||
|
Loading…
Reference in New Issue
Block a user