Merge pull request #4025 from deitch/tag-sources-in-tar

include image reference as source in every tar file header
This commit is contained in:
Avi Deitcher 2024-04-18 16:34:05 +03:00 committed by GitHub
commit a610332100
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 257 additions and 68 deletions

View File

@ -8,17 +8,21 @@ import (
// apkTarWriter apk-aware tar writer that consolidates installed database, so that
// it can be called multiple times and will do the union of all such databases,
// rather than overwriting the previous one.
// Useful only for things that write to the base filesystem, i.e. init, since everything
// else is inside containers.
const apkInstalledPath = "lib/apk/db/installed"
type apkTarWriter struct {
*tar.Writer
dbs [][]byte
current *bytes.Buffer
location string
}
func newAPKTarWriter(w *tar.Writer) *apkTarWriter {
func newAPKTarWriter(w *tar.Writer, location string) *apkTarWriter {
return &apkTarWriter{
Writer: w,
location: location,
}
}
@ -67,6 +71,10 @@ func (a *apkTarWriter) WriteAPKDB() error {
Gid: 0,
Typeflag: tar.TypeReg,
Size: int64(size),
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: "LINUXKIT.apkinit",
PaxRecordLinuxkitLocation: a.location,
},
}
if err := a.Writer.WriteHeader(hdr); err != nil {
return err

View File

@ -92,25 +92,25 @@ func outputImage(image *Image, section string, prefix string, m Moby, idMap map[
}
src, err := imagePull(&ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch)
if err != nil {
return fmt.Errorf("Could not pull image %s: %v", image.Image, err)
return fmt.Errorf("could not pull image %s: %v", image.Image, err)
}
configRaw, err := src.Config()
if err != nil {
return fmt.Errorf("Failed to retrieve config for %s: %v", image.Image, err)
return fmt.Errorf("failed to retrieve config for %s: %v", image.Image, err)
}
oci, runtime, err := ConfigToOCI(image, configRaw, idMap)
if err != nil {
return fmt.Errorf("Failed to create OCI spec for %s: %v", image.Image, err)
return fmt.Errorf("failed to create OCI spec for %s: %v", image.Image, err)
}
config, err := json.MarshalIndent(oci, "", " ")
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)
}
path := path.Join("containers", section, prefix+image.Name)
readonly := oci.Root.Readonly
err = ImageBundle(path, image.ref, config, runtime, iw, readonly, dupMap, opts)
err = ImageBundle(path, section, image.ref, config, runtime, iw, readonly, dupMap, opts)
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)
}
return nil
}
@ -153,14 +153,14 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error {
if m.Kernel.ref != nil {
// get kernel and initrd tarball and ucode cpio archive from container
log.Infof("Extract kernel image: %s", m.Kernel.ref)
kf := newKernelFilter(iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel)
err := ImageTar(m.Kernel.ref, "", kf, "", opts)
kf := newKernelFilter(m.Kernel.ref, iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel)
err := ImageTar("kernel", m.Kernel.ref, "", kf, "", opts)
if err != nil {
return fmt.Errorf("Failed to extract kernel image and tarball: %v", err)
return fmt.Errorf("failed to extract kernel image and tarball: %v", err)
}
err = kf.Close()
if err != nil {
return fmt.Errorf("Close error: %v", err)
return fmt.Errorf("close error: %v", err)
}
}
@ -168,10 +168,10 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error {
if len(m.Init) != 0 {
log.Infof("Add init containers:")
}
apkTar := newAPKTarWriter(iw)
apkTar := newAPKTarWriter(iw, "init")
for _, ii := range m.initRefs {
log.Infof("Process init image: %s", ii)
err := ImageTar(ii, "", apkTar, resolvconfSymlink, opts)
err := ImageTar("init", ii, "", apkTar, resolvconfSymlink, opts)
if err != nil {
return fmt.Errorf("failed to build init tarball from %s: %v", ii, err)
}
@ -252,9 +252,10 @@ type kernelFilter struct {
foundKernel bool
foundKTar bool
foundUCode bool
ref *reference.Spec
}
func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode *string, decompressKernel bool) *kernelFilter {
func newKernelFilter(ref *reference.Spec, tw *tar.Writer, cmdline string, kernel string, tar, ucode *string, decompressKernel bool) *kernelFilter {
tarName, kernelName, ucodeName := "kernel.tar", "kernel", ""
if tar != nil {
tarName = *tar
@ -268,7 +269,7 @@ func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode *
if ucode != nil {
ucodeName = *ucode
}
return &kernelFilter{tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName, ucode: ucodeName, decompressKernel: decompressKernel}
return &kernelFilter{ref: ref, tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName, ucode: ucodeName, decompressKernel: decompressKernel}
}
func (k *kernelFilter) finishTar() error {
@ -299,7 +300,7 @@ func (k *kernelFilter) finishTar() error {
}
tr := tar.NewReader(k.buffer)
err := tarAppend(k.tw, tr)
err := tarAppend(k.ref, k.tw, tr)
k.buffer = nil
return err
}
@ -353,6 +354,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
Typeflag: tar.TypeDir,
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: hdr.PAXRecords,
}
if err := tw.WriteHeader(whdr); err != nil {
return err
@ -365,6 +367,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
Size: int64(len(k.cmdline)),
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: hdr.PAXRecords,
}
if err := tw.WriteHeader(whdr); err != nil {
return err
@ -380,6 +383,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
Size: hdr.Size,
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: hdr.PAXRecords,
}
k.buffer = new(bytes.Buffer)
case k.tar:
@ -397,6 +401,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
Typeflag: tar.TypeDir,
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: hdr.PAXRecords,
}
if err := tw.WriteHeader(whdr); err != nil {
return err
@ -408,6 +413,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
Size: hdr.Size,
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: hdr.PAXRecords,
}
if err := tw.WriteHeader(whdr); err != nil {
return err
@ -419,7 +425,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
return nil
}
func tarAppend(iw *tar.Writer, tr *tar.Reader) error {
func tarAppend(ref *reference.Spec, iw *tar.Writer, tr *tar.Reader) error {
for {
hdr, err := tr.Next()
if err == io.EOF {
@ -428,6 +434,12 @@ func tarAppend(iw *tar.Writer, tr *tar.Reader) error {
if err != nil {
return err
}
hdr.Format = tar.FormatPAX
if hdr.PAXRecords == nil {
hdr.PAXRecords = make(map[string]string)
}
hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String()
hdr.PAXRecords[PaxRecordLinuxkitLocation] = "kernel"
err = iw.WriteHeader(hdr)
if err != nil {
return err
@ -478,7 +490,7 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) {
versionMajor := int(s[versionIdx])
versionMinor := int(s[versionIdx+1])
if versionMajor < 2 && versionMinor < 8 {
return nil, fmt.Errorf("Unsupported bzImage version: %d.%d", versionMajor, versionMinor)
return nil, fmt.Errorf("unsupported bzImage version: %d.%d", versionMajor, versionMinor)
}
setupSectors := uint32(s[setupSectorsIdx])
@ -488,7 +500,7 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) {
log.Debugf("bzImage: Payload at Offset: %d Length: %d", payloadOff, payloadLen)
if len(s) < int(payloadOff+payloadLen) {
return nil, fmt.Errorf("Compressed bzImage payload exceeds size of image")
return nil, fmt.Errorf("compressed bzImage payload exceeds size of image")
}
if bytes.HasPrefix(s[payloadOff:], []byte(gzipMagic)) {
@ -496,10 +508,10 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) {
return gunzip(bytes.NewBuffer(s[payloadOff : payloadOff+payloadLen]))
}
// TODO(rn): Add more supported formats
return nil, fmt.Errorf("Unsupported bzImage payload format at offset %d", payloadOff)
return nil, fmt.Errorf("unsupported bzImage payload format at offset %d", payloadOff)
}
return nil, fmt.Errorf("No compressed kernel or no supported format found")
return nil, fmt.Errorf("no compressed kernel or no supported format found")
}
func gunzip(src *bytes.Buffer) (*bytes.Buffer, error) {
@ -529,7 +541,7 @@ func metadata(m Moby, md string) ([]byte, error) {
case "yaml":
return yaml.Marshal(m)
default:
return []byte{}, fmt.Errorf("Unsupported metadata type: %s", md)
return []byte{}, fmt.Errorf("unsupported metadata type: %s", md)
}
}
@ -540,10 +552,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
if len(m.Files) != 0 {
log.Infof("Add files:")
}
for _, f := range m.Files {
for filecount, f := range m.Files {
log.Infof(" %s", f.Path)
if f.Path == "" {
return errors.New("Did not specify path for file")
return errors.New("did not specify path for file")
}
// tar archives should not have absolute paths
if f.Path[0] == '/' {
@ -557,7 +569,7 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
var err error
mode, err = strconv.ParseInt(f.Mode, 8, 32)
if err != nil {
return fmt.Errorf("Cannot parse file mode as octal value: %v", err)
return fmt.Errorf("cannot parse file mode as octal value: %v", err)
}
}
dirMode := mode
@ -586,10 +598,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
}
if !f.Directory && f.Symlink == "" && f.Contents == nil {
if f.Source == "" && f.Metadata == "" {
return fmt.Errorf("Contents of file (%s) not specified", f.Path)
return fmt.Errorf("contents of file (%s) not specified", f.Path)
}
if f.Source != "" && f.Metadata != "" {
return fmt.Errorf("Specified Source and Metadata for file: %s", f.Path)
return fmt.Errorf("specified Source and Metadata for file: %s", f.Path)
}
if f.Source != "" {
source := f.Source
@ -600,7 +612,7 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
_, err := os.Stat(source)
if err != nil {
// skip if not found or readable
log.Debugf("Skipping file [%s] as not readable and marked optional", source)
log.Debugf("skipping file [%s] as not readable and marked optional", source)
continue
}
}
@ -617,10 +629,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
}
} else {
if f.Metadata != "" {
return fmt.Errorf("Specified Contents and Metadata for file: %s", f.Path)
return fmt.Errorf("specified Contents and Metadata for file: %s", f.Path)
}
if f.Source != "" {
return fmt.Errorf("Specified Contents and Source for file: %s", f.Path)
return fmt.Errorf("specified Contents and Source for file: %s", f.Path)
}
}
// we need all the leading directories
@ -644,6 +656,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
Uid: int(uid),
Gid: int(gid),
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: "linuxkit.files",
PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount),
},
}
err := tw.WriteHeader(hdr)
if err != nil {
@ -660,10 +676,14 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
Uid: int(uid),
Gid: int(gid),
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: "linuxkit.files",
PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount),
},
}
if f.Directory {
if f.Contents != nil {
return errors.New("Directory with contents not allowed")
return errors.New("directory with contents not allowed")
}
hdr.Typeflag = tar.TypeDir
err := tw.WriteHeader(hdr)

View File

@ -14,6 +14,14 @@ import (
log "github.com/sirupsen/logrus"
)
const (
// PaxRecordLinuxkitSource report the package source for a specific file
PaxRecordLinuxkitSource = "LINUXKIT.source"
// PaxRecordLinuxkitLocation report the location of the file in the linuxkit.yaml
// that led to this file being in this location
PaxRecordLinuxkitLocation = "LINUXKIT.location"
)
type tarWriter interface {
Close() error
Flush() error
@ -140,7 +148,8 @@ var touch = map[string]tar.Header{
}
// tarPrefix creates the leading directories for a path
func tarPrefix(path string, tw tarWriter) error {
// path is the path to prefix, location is where this appears in the linuxkit.yaml file
func tarPrefix(path, location string, ref *reference.Spec, tw tarWriter) error {
if path == "" {
return nil
}
@ -160,6 +169,10 @@ func tarPrefix(path string, tw tarWriter) error {
ModTime: defaultModTime,
Typeflag: tar.TypeDir,
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: ref.String(),
PaxRecordLinuxkitLocation: location,
},
}
if err := tw.WriteHeader(hdr); err != nil {
return err
@ -170,13 +183,14 @@ func tarPrefix(path string, tw tarWriter) error {
}
// ImageTar takes a Docker image and outputs it to a tar stream
func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, opts BuildOpts) (e error) {
// location is where it is in the linuxkit.yaml file
func ImageTar(location string, ref *reference.Spec, prefix string, tw tarWriter, resolv string, opts BuildOpts) (e error) {
log.Debugf("image tar: %s %s", ref, prefix)
if prefix != "" && prefix[len(prefix)-1] != '/' {
return fmt.Errorf("prefix does not end with /: %s", prefix)
}
err := tarPrefix(prefix, tw)
err := tarPrefix(prefix, location, ref, tw)
if err != nil {
return err
}
@ -185,12 +199,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
// If pull==true, then it always tries to pull from registry.
src, err := imagePull(ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch)
if err != nil {
return fmt.Errorf("Could not pull image %s: %v", ref, err)
return fmt.Errorf("could not pull image %s: %v", ref, err)
}
contents, err := src.TarReader()
if err != nil {
return fmt.Errorf("Could not unpack image %s: %v", ref, err)
return fmt.Errorf("could not unpack image %s: %v", ref, err)
}
defer contents.Close()
@ -214,6 +228,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
// force PAX format, since it allows for unlimited Name/Linkname
// and we move all files below prefix.
hdr.Format = tar.FormatPAX
// ensure we record the source of the file in the PAX header
if hdr.PAXRecords == nil {
hdr.PAXRecords = make(map[string]string)
}
hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String()
hdr.PAXRecords[PaxRecordLinuxkitLocation] = location
if exclude[hdr.Name] {
log.Debugf("image tar: %s %s exclude %s", ref, prefix, hdr.Name)
_, err = io.Copy(io.Discard, tr)
@ -286,6 +306,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
continue
}
hdr := touch[name]
// ensure that we record the source of the file
if hdr.PAXRecords == nil {
hdr.PAXRecords = make(map[string]string)
}
hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String()
hdr.PAXRecords[PaxRecordLinuxkitLocation] = location
origName := hdr.Name
hdr.Name = prefix + origName
hdr.Format = tar.FormatPAX
@ -329,7 +355,7 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
}
// ImageBundle produces an OCI bundle at the given path in a tarball, given an image and a config.json
func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runtime, tw tarWriter, readonly bool, dupMap map[string]string, opts BuildOpts) error { // nolint: lll
func ImageBundle(prefix, location string, ref *reference.Spec, config []byte, runtime Runtime, tw tarWriter, readonly bool, dupMap map[string]string, opts BuildOpts) error { // nolint: lll
// if read only, just unpack in rootfs/ but otherwise set up for overlay
rootExtract := "rootfs"
if !readonly {
@ -340,12 +366,12 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
root := path.Join(prefix, rootExtract)
var foundElsewhere = dupMap[ref.String()] != ""
if !foundElsewhere {
if err := ImageTar(ref, root+"/", tw, "", opts); err != nil {
if err := ImageTar(location, ref, root+"/", tw, "", opts); err != nil {
return err
}
dupMap[ref.String()] = root
} else {
if err := tarPrefix(prefix+"/", tw); err != nil {
if err := tarPrefix(prefix+"/", location, ref, tw); err != nil {
return err
}
root = dupMap[ref.String()]
@ -357,6 +383,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
Size: int64(len(config)),
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: ref.String(),
PaxRecordLinuxkitLocation: location,
},
}
if err := tw.WriteHeader(hdr); err != nil {
return err
@ -375,6 +405,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
Typeflag: tar.TypeDir,
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: ref.String(),
PaxRecordLinuxkitLocation: location,
},
}
if err := tw.WriteHeader(hdr); err != nil {
return err
@ -386,6 +420,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
Typeflag: tar.TypeDir,
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: ref.String(),
PaxRecordLinuxkitLocation: location,
},
}
if err := tw.WriteHeader(hdr); err != nil {
return err
@ -406,6 +444,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
Typeflag: tar.TypeDir,
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: ref.String(),
PaxRecordLinuxkitLocation: location,
},
}
if err := tw.WriteHeader(hdr); err != nil {
return err
@ -424,7 +466,7 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
// write the runtime config
runtimeConfig, err := json.MarshalIndent(runtime, "", " ")
if err != nil {
return fmt.Errorf("Failed to create runtime config for %s: %v", ref, err)
return fmt.Errorf("failed to create runtime config for %s: %v", ref, err)
}
hdr = &tar.Header{
@ -433,6 +475,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
Size: int64(len(runtimeConfig)),
ModTime: defaultModTime,
Format: tar.FormatPAX,
PAXRecords: map[string]string{
PaxRecordLinuxkitSource: ref.String(),
PaxRecordLinuxkitLocation: location,
},
}
if err := tw.WriteHeader(hdr); err != nil {
return err

View File

@ -0,0 +1,31 @@
import tarfile
import sys
def list_pax_headers(archive_path):
# Open the tar archive
try:
with tarfile.open(archive_path, 'r') as tar:
# Iterate over each member in the tar archive
for member in tar.getmembers():
# ignore the root directory, which just exists
if member.name == '.':
continue
# Check if there are any PAX headers
if member.pax_headers:
# Check for the specific PAX header
if 'LINUXKIT.source' not in member.pax_headers:
print(f"File: {member.name} is missing LINUXKIT.source PAX Header.")
if 'LINUXKIT.location' not in member.pax_headers:
print(f"File: {member.name} is missing LINUXKIT.source PAX Header.")
else:
print(f"File: {member.name} has No PAX Headers.")
except Exception as e:
print("Failed to read tar archive:", e)
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python list_pax_headers.py <archive.tar>")
sys.exit(1)
archive_filename = sys.argv[1]
list_pax_headers(archive_filename)

View File

@ -0,0 +1,38 @@
#!/bin/sh
# SUMMARY: Check that tar output format build contains proper headers for each file
# LABELS:
set -e
# Source libraries. Uncomment if needed/defined
#. "${RT_LIB}"
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
NAME=tarheaders
clean_up() {
rm -f ${NAME}*
}
trap clean_up EXIT
# do not include the sbom, because the SBoM unique IDs per file/package are *not* deterministic,
# (currently based upon syft), and thus will make the file non-reproducible
linuxkit build --no-sbom --format tar --name "${NAME}" ./test.yml
# Check that the tarball contains the expected headers
# see that python is installed
PYTHON=
if which python ; then PYTHON=python ; elif which python3; then PYTHON=python3 ; else
echo "Failed to find any executable python or python3"
exit 1
fi
FAILED=$(python ./tarheaders.py "${NAME}.tar")
if [ -n "${FAILED}" ]; then
echo "Failed to find linuxkit.packagesource headers for the following files:"
echo "${FAILED}"
exit 1
fi
exit 0

View File

@ -0,0 +1,46 @@
# NOTE: Images build from this file likely do not run
kernel:
image: linuxkit/kernel:6.6.13
cmdline: "console=ttyS0"
init:
- linuxkit/init:45a1ad5919f0b6acf0f0cf730e9434abfae11fe6
- linuxkit/runc:6062483d748609d505f2bcde4e52ee64a3329f5f
- linuxkit/containerd:e7a92d9f3282039eac5fb1b07cac2b8664cbf0ad
onboot:
- name: dhcpcd
image: linuxkit/dhcpcd:e9e3580f2de00e73e7b316a007186d22fea056ee
command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
# Add some random unsorted caps
capabilities:
- CAP_SETGID
- CAP_DAC_OVERRIDE
services:
- name: testservice
image: linuxkit/ip:bb250017b05de5e16ac436b1eb19a39c87b5a252
# Some environments
env:
- BENV=true
- ARANDOMENV=foobar
# Some mounts
mounts:
- type: cgroup
options: ["rw","nosuid","noexec","nodev","relatime"]
- type: overlay
source: overlay
destination: writeable-host-etc
options: ["rw", "lowerdir=/etc", "upperdir=/run/hostetc/upper", "workdir=/run/hostetc/work"]
# Some binds
binds:
- /var/run:/var/run
- /foobar:/foobar
- /etc/foobar:/etc/foobar
- /etc/aaa:/etc/aaa
# And some runtime settings
runtime:
mkdir: ["/var/lib/docker","/var/lib/aaa"]
files:
- path: etc/linuxkit-config
metadata: yaml