mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-11-04 08:07:48 +00:00 
			
		
		
		
	Use Go code to extract rootfs from system containers
- this removes the use of riddler to extract the rootfs, use code we were using for rootfs. riddler now just geenrates the config, next stage is to generate this ourselves - change the naming of the daemons so no longer include number as we do not guarantee ordering as they start up simultaneously Signed-off-by: Justin Cormack <justin.cormack@docker.com>
This commit is contained in:
		@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"archive/tar"
 | 
						"archive/tar"
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
@@ -108,7 +109,7 @@ func build(name string, args []string) {
 | 
				
			|||||||
	containers = append(containers, ktar)
 | 
						containers = append(containers, ktar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// convert init image to tarball
 | 
						// convert init image to tarball
 | 
				
			||||||
	init, err := imageExtract(m.Init)
 | 
						init, err := ImageExtract(m.Init, "")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("Failed to build init tarball: %v", err)
 | 
							log.Fatalf("Failed to build init tarball: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -116,20 +117,29 @@ func build(name string, args []string) {
 | 
				
			|||||||
	containers = append(containers, buffer)
 | 
						containers = append(containers, buffer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i, image := range m.System {
 | 
						for i, image := range m.System {
 | 
				
			||||||
		args := ConfigToRun(i, "system", &image)
 | 
							config, err := ConfigToOCI(&image)
 | 
				
			||||||
		out, err := dockerRun(args...)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatalf("Failed to build container tarball: %v", err)
 | 
								log.Fatalf("Failed to run riddler to get config.json for %s: %v", image.Image, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							so := fmt.Sprintf("%03d", i)
 | 
				
			||||||
 | 
							path := "containers/system/" + so + "-" + image.Name
 | 
				
			||||||
 | 
							out, err := ImageBundle(path, image.Image, config)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatalf("Failed to extract root filesystem for %s: %v", image.Image, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		buffer := bytes.NewBuffer(out)
 | 
							buffer := bytes.NewBuffer(out)
 | 
				
			||||||
		containers = append(containers, buffer)
 | 
							containers = append(containers, buffer)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i, image := range m.Daemon {
 | 
						for _, image := range m.Daemon {
 | 
				
			||||||
		args := ConfigToRun(i, "daemon", &image)
 | 
							config, err := ConfigToOCI(&image)
 | 
				
			||||||
		out, err := dockerRun(args...)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatalf("Failed to build container tarball: %v", err)
 | 
								log.Fatalf("Failed to run riddler to get config.json for %s: %v", image.Image, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							path := "containers/daemon/" + image.Name
 | 
				
			||||||
 | 
							out, err := ImageBundle(path, image.Image, config)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatalf("Failed to extract root filesystem for %s: %v", image.Image, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		buffer := bytes.NewBuffer(out)
 | 
							buffer := bytes.NewBuffer(out)
 | 
				
			||||||
		containers = append(containers, buffer)
 | 
							containers = append(containers, buffer)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,7 @@ type MobyImage struct {
 | 
				
			|||||||
	ReadOnly     bool `yaml:"read_only"`
 | 
						ReadOnly     bool `yaml:"read_only"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const riddler = "mobylinux/riddler:c23ab4b6e2a2a4ebd4dd51a059cef7f270da72cb@sha256:7e7744b2f554518411633200db98e599782b120e323348495f43f540de26f7b6"
 | 
					const riddler = "mobylinux/riddler:2b4051422b155f659019f9e3fef8cca04e153f5c@sha256:f4bb0c39f1e5c636ed52ebd3ed8ec447ca6c0dc554ffb5784cbeff423ac70d34"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewConfig parses a config file
 | 
					// NewConfig parses a config file
 | 
				
			||||||
func NewConfig(config []byte) (*Moby, error) {
 | 
					func NewConfig(config []byte) (*Moby, error) {
 | 
				
			||||||
@@ -64,11 +64,10 @@ func NewConfig(config []byte) (*Moby, error) {
 | 
				
			|||||||
	return &m, nil
 | 
						return &m, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConfigToRun converts a config to a series of arguments for docker run
 | 
					// ConfigToOCI converts a config specification to an OCI config file
 | 
				
			||||||
func ConfigToRun(order int, path string, image *MobyImage) []string {
 | 
					func ConfigToOCI(image *MobyImage) (string, error) {
 | 
				
			||||||
	// riddler arguments
 | 
						// riddler arguments
 | 
				
			||||||
	so := fmt.Sprintf("%03d", order)
 | 
						args := []string{"-v", "/var/run/docker.sock:/var/run/docker.sock", riddler, image.Image}
 | 
				
			||||||
	args := []string{"-v", "/var/run/docker.sock:/var/run/docker.sock", riddler, image.Image, "/containers/" + path + "/" + so + "-" + image.Name}
 | 
					 | 
				
			||||||
	// docker arguments
 | 
						// docker arguments
 | 
				
			||||||
	args = append(args, "--cap-drop", "all")
 | 
						args = append(args, "--cap-drop", "all")
 | 
				
			||||||
	for _, cap := range image.Capabilities {
 | 
						for _, cap := range image.Capabilities {
 | 
				
			||||||
@@ -107,7 +106,12 @@ func ConfigToRun(order int, path string, image *MobyImage) []string {
 | 
				
			|||||||
	// command
 | 
						// command
 | 
				
			||||||
	args = append(args, image.Command...)
 | 
						args = append(args, image.Command...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return args
 | 
						config, err := dockerRun(args...)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("Failed to run riddler to get config.json: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return string(config), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func filesystem(m *Moby) (*bytes.Buffer, error) {
 | 
					func filesystem(m *Moby) (*bytes.Buffer, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -164,23 +164,69 @@ nameserver 2001:4860:4860::8844
 | 
				
			|||||||
	"etc/hostname": "moby",
 | 
						"etc/hostname": "moby",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func imageExtract(image string) ([]byte, error) {
 | 
					// ImageExtract extracts the filesystem from an image and returns a tarball with the files prefixed by the given path
 | 
				
			||||||
 | 
					func ImageExtract(image, prefix string) ([]byte, error) {
 | 
				
			||||||
 | 
						out := new(bytes.Buffer)
 | 
				
			||||||
 | 
						tw := tar.NewWriter(out)
 | 
				
			||||||
 | 
						err := tarPrefix(prefix, tw)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = imageTar(image, prefix, tw)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = tw.Close()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out.Bytes(), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tarPrefix creates the leading directories for a path
 | 
				
			||||||
 | 
					func tarPrefix(path string, tw *tar.Writer) error {
 | 
				
			||||||
 | 
						if path == "" {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if path[len(path)-1] != byte('/') {
 | 
				
			||||||
 | 
							return fmt.Errorf("path does not end with /: %s", path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						path = path[:len(path)-1]
 | 
				
			||||||
 | 
						if path[0] == byte('/') {
 | 
				
			||||||
 | 
							return fmt.Errorf("path should be relative: %s", path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mkdir := ""
 | 
				
			||||||
 | 
						for _, dir := range strings.Split(path, "/") {
 | 
				
			||||||
 | 
							mkdir = mkdir + dir
 | 
				
			||||||
 | 
							hdr := &tar.Header{
 | 
				
			||||||
 | 
								Name:     mkdir,
 | 
				
			||||||
 | 
								Mode:     0755,
 | 
				
			||||||
 | 
								Typeflag: tar.TypeDir,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tw.WriteHeader(hdr)
 | 
				
			||||||
 | 
							mkdir = mkdir + "/"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func imageTar(image, prefix string, tw *tar.Writer) error {
 | 
				
			||||||
 | 
						if prefix != "" && prefix[len(prefix)-1] != byte('/') {
 | 
				
			||||||
 | 
							return fmt.Errorf("prefix does not end with /: %s", prefix)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	container, err := dockerCreate(image)
 | 
						container, err := dockerCreate(image)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return []byte{}, fmt.Errorf("Failed to docker create image %s: %v", image, err)
 | 
							return fmt.Errorf("Failed to docker create image %s: %v", image, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	contents, err := dockerExport(container)
 | 
						contents, err := dockerExport(container)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return []byte{}, fmt.Errorf("Failed to docker export container from container %s: %v", container, err)
 | 
							return fmt.Errorf("Failed to docker export container from container %s: %v", container, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = dockerRm(container)
 | 
						err = dockerRm(container)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return []byte{}, fmt.Errorf("Failed to docker rm container %s: %v", container, err)
 | 
							return fmt.Errorf("Failed to docker rm container %s: %v", container, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// now we need to filter out some files from the resulting tar archive
 | 
						// now we need to filter out some files from the resulting tar archive
 | 
				
			||||||
	out := new(bytes.Buffer)
 | 
					 | 
				
			||||||
	tw := tar.NewWriter(out)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := bytes.NewReader(contents)
 | 
						r := bytes.NewReader(contents)
 | 
				
			||||||
	tr := tar.NewReader(r)
 | 
						tr := tar.NewReader(r)
 | 
				
			||||||
@@ -191,21 +237,58 @@ func imageExtract(image string) ([]byte, error) {
 | 
				
			|||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return []byte{}, err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if exclude[hdr.Name] {
 | 
							if exclude[hdr.Name] {
 | 
				
			||||||
			io.Copy(ioutil.Discard, tr)
 | 
								io.Copy(ioutil.Discard, tr)
 | 
				
			||||||
		} else if replace[hdr.Name] != "" {
 | 
							} else if replace[hdr.Name] != "" {
 | 
				
			||||||
			hdr.Size = int64(len(replace[hdr.Name]))
 | 
								contents := replace[hdr.Name]
 | 
				
			||||||
 | 
								hdr.Size = int64(len(contents))
 | 
				
			||||||
 | 
								hdr.Name = prefix + hdr.Name
 | 
				
			||||||
			tw.WriteHeader(hdr)
 | 
								tw.WriteHeader(hdr)
 | 
				
			||||||
			buf := bytes.NewBufferString(replace[hdr.Name])
 | 
								buf := bytes.NewBufferString(contents)
 | 
				
			||||||
			io.Copy(tw, buf)
 | 
								io.Copy(tw, buf)
 | 
				
			||||||
 | 
								io.Copy(ioutil.Discard, tr)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								hdr.Name = prefix + hdr.Name
 | 
				
			||||||
			tw.WriteHeader(hdr)
 | 
								tw.WriteHeader(hdr)
 | 
				
			||||||
			io.Copy(tw, tr)
 | 
								io.Copy(tw, tr)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = tw.Close()
 | 
						err = tw.Close()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ImageBundle produces an OCI bundle at the given path in a tarball, given an image and a config.json
 | 
				
			||||||
 | 
					func ImageBundle(path, image, config string) ([]byte, error) {
 | 
				
			||||||
 | 
						out := new(bytes.Buffer)
 | 
				
			||||||
 | 
						tw := tar.NewWriter(out)
 | 
				
			||||||
 | 
						err := tarPrefix(path+"/rootfs/", tw)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						hdr := &tar.Header{
 | 
				
			||||||
 | 
							Name: path + "/" + "config.json",
 | 
				
			||||||
 | 
							Mode: 0644,
 | 
				
			||||||
 | 
							Size: int64(len(config)),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = tw.WriteHeader(hdr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						buf := bytes.NewBufferString(config)
 | 
				
			||||||
 | 
						_, err = io.Copy(tw, buf)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = imageTar(image, path+"/rootfs/", tw)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = tw.Close()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return []byte{}, err
 | 
							return []byte{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,15 +3,12 @@
 | 
				
			|||||||
set -e
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# arguments are image name, prefix, then arguments passed to Docker
 | 
					# arguments are image name, prefix, then arguments passed to Docker
 | 
				
			||||||
# eg ./riddler.sh alpine:3.4 / --read-only alpine:3.4 ls
 | 
					# eg ./riddler.sh alpine:3.4 --read-only alpine:3.4 ls
 | 
				
			||||||
# This script will output a tarball under prefix/ with rootfs and config.json
 | 
					# This script will output config.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IMAGE="$1"; shift
 | 
					IMAGE="$1"; shift
 | 
				
			||||||
PREFIX="$1"; shift
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
cd /tmp
 | 
					cd /tmp
 | 
				
			||||||
mkdir -p /tmp/$PREFIX
 | 
					 | 
				
			||||||
cd /tmp/$PREFIX
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# riddler always adds the apparmor options if this is not present
 | 
					# riddler always adds the apparmor options if this is not present
 | 
				
			||||||
EXTRA_OPTIONS="--security-opt apparmor=unconfined"
 | 
					EXTRA_OPTIONS="--security-opt apparmor=unconfined"
 | 
				
			||||||
@@ -22,7 +19,7 @@ riddler $CONTAINER > /dev/null
 | 
				
			|||||||
docker rm $CONTAINER > /dev/null
 | 
					docker rm $CONTAINER > /dev/null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# unfixed known issues
 | 
					# unfixed known issues
 | 
				
			||||||
# noNewPrivileges is always set by riddler, but that is fine for our use cases
 | 
					# noNewPrivileges is always set by riddler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# These fixes should be removed when riddler is fixed
 | 
					# These fixes should be removed when riddler is fixed
 | 
				
			||||||
# process.rlimits, just a constant at present, not useful
 | 
					# process.rlimits, just a constant at present, not useful
 | 
				
			||||||
@@ -47,16 +44,5 @@ cat config.json.orig | \
 | 
				
			|||||||
  jq 'if .root.readonly==true then .mounts = (.mounts|map(if .destination=="/dev" then .options |= .+ ["ro"] else . end)) else . end' | \
 | 
					  jq 'if .root.readonly==true then .mounts = (.mounts|map(if .destination=="/dev" then .options |= .+ ["ro"] else . end)) else . end' | \
 | 
				
			||||||
  jq '.mounts = if .process.capabilities | length != 38 then (.mounts|map(if .destination=="/sys" then .options |= .+ ["ro"] else . end)) else . end' \
 | 
					  jq '.mounts = if .process.capabilities | length != 38 then (.mounts|map(if .destination=="/sys" then .options |= .+ ["ro"] else . end)) else . end' \
 | 
				
			||||||
  > config.json
 | 
					  > config.json
 | 
				
			||||||
rm config.json.orig
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# extract rootfs
 | 
					cat config.json
 | 
				
			||||||
EXCLUDE="--exclude .dockerenv --exclude Dockerfile \
 | 
					 | 
				
			||||||
        --exclude dev/console --exclude dev/pts --exclude dev/shm \
 | 
					 | 
				
			||||||
        --exclude etc/hostname --exclude etc/hosts --exclude etc/resolv.conf"
 | 
					 | 
				
			||||||
mkdir -p rootfs
 | 
					 | 
				
			||||||
CONTAINER="$(docker create $IMAGE /dev/null)"
 | 
					 | 
				
			||||||
docker export "$CONTAINER" | tar -xf - -C rootfs $EXCLUDE
 | 
					 | 
				
			||||||
docker rm "$CONTAINER" > /dev/null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cd /tmp
 | 
					 | 
				
			||||||
tar cf - .
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user