Merge pull request #3511 from deitch/append-binds

support merge yaml flags
This commit is contained in:
Justin Cormack 2020-05-18 20:18:00 +01:00 committed by GitHub
commit 9d5a22d44a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 4 deletions

View File

@ -13,7 +13,7 @@ so it can be tested reliably for continuous delivery.
Components are specified as Docker images which are pulled from a registry during build if they Components are specified as Docker images which are pulled from a registry during build if they
are not available locally. The Docker images are optionally verified with Docker Content Trust. are not available locally. The Docker images are optionally verified with Docker Content Trust.
For private registries or private repositories on a registry credentials provided via For private registries or private repositories on a registry credentials provided via
`docker login` are re-used. `docker login` are re-used.
The configuration file is processed in the order `kernel`, `init`, `onboot`, `onshutdown`, The configuration file is processed in the order `kernel`, `init`, `onboot`, `onshutdown`,
`services`, `files`. Each section adds files to the root file system. Sections may be omitted. `services`, `files`. Each section adds files to the root file system. Sections may be omitted.
@ -144,7 +144,9 @@ options. Default values may be specified using the `org.mobyproject.config` imag
For more details see the [OCI specification](https://github.com/opencontainers/runtime-spec/blob/master/spec.md). For more details see the [OCI specification](https://github.com/opencontainers/runtime-spec/blob/master/spec.md).
If the `org.mobylinux.config` label is set in the image, that specifies default values for these fields if they If the `org.mobylinux.config` label is set in the image, that specifies default values for these fields if they
are not set in the yaml file. You can override the label by setting the value, or setting it to be empty to remove are not set in the yaml file. While most fields are _replaced_ if they are specified in the yaml file,
some support _add_ via the format `<field>.add`; see below.
You can override the label entirely by setting the value, or setting it to be empty to remove
the specification for that value in the label. the specification for that value in the label.
If you need an OCI option that is not specified here please open an issue or pull request as the list is not yet If you need an OCI option that is not specified here please open an issue or pull request as the list is not yet
@ -159,6 +161,7 @@ bind mounted into a container.
extracted from this so they need not be filled in. extracted from this so they need not be filled in.
- `capabilities` the Linux capabilities required, for example `CAP_SYS_ADMIN`. If there is a single - `capabilities` the Linux capabilities required, for example `CAP_SYS_ADMIN`. If there is a single
capability `all` then all capabilities are added. capability `all` then all capabilities are added.
- `capabilities.add` the Linux capabilities required, but these are added to the defaults, rather than overriding them.
- `ambient` the Linux ambient capabilities (capabilities passed to non root users) that are required. - `ambient` the Linux ambient capabilities (capabilities passed to non root users) that are required.
- `mounts` is the full form for specifying a mount, which requires `type`, `source`, `destination` - `mounts` is the full form for specifying a mount, which requires `type`, `source`, `destination`
and a list of `options`. If any fields are omitted, sensible defaults are used if possible, for example and a list of `options`. If any fields are omitted, sensible defaults are used if possible, for example
@ -166,6 +169,7 @@ bind mounted into a container.
can be replaced by specifying a mount with new options here at the same mount point. can be replaced by specifying a mount with new options here at the same mount point.
- `binds` is a simpler interface to specify bind mounts, accepting a string like `/src:/dest:opt1,opt2` - `binds` is a simpler interface to specify bind mounts, accepting a string like `/src:/dest:opt1,opt2`
similar to the `-v` option for bind mounts in Docker. similar to the `-v` option for bind mounts in Docker.
- `binds.add` is a simpler interface to specify bind mounts, but these are added to the defaults, rather than overriding them.
- `tmpfs` is a simpler interface to mount a `tmpfs`, like `--tmpfs` in Docker, taking `/dest:opt1,opt2`. - `tmpfs` is a simpler interface to mount a `tmpfs`, like `--tmpfs` in Docker, taking `/dest:opt1,opt2`.
- `command` will override the command and entrypoint in the image with a new list of commands. - `command` will override the command and entrypoint in the image with a new list of commands.
- `env` will override the environment in the image with a new environment list. Specify variables as `VAR=value`. - `env` will override the environment in the image with a new environment list. Specify variables as `VAR=value`.

29
examples/addbinds.yml Normal file
View File

@ -0,0 +1,29 @@
kernel:
image: linuxkit/kernel:5.4.30
cmdline: "console=tty0 console=ttyS0 console=ttyAMA0 console=ttysclp0"
init:
- linuxkit/init:7195dc244cd92af01fd0895fd204249a6114c5e2
- linuxkit/runc:f79954950022fea76b8b6f10de58cb48e4fb3878
- linuxkit/containerd:8ee7a0d636fff9df7e13076f5492d06274e5f644
- linuxkit/ca-certificates:abfc6701b9ca17e34ac9439ce5946a247e720ff5
onboot:
- name: sysctl
image: linuxkit/sysctl:541f60fe3676611328e89e8bac251fc636b1a6aa
- name: dhcpcd
image: linuxkit/dhcpcd:2f8a9b670aa6e96a09db56ec45c9f07ef2a811ee
command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
services:
- name: getty
image: linuxkit/getty:48f66df198981e692084bf70ab72b9fe2be0f880
binds.add:
# this will keep all of the existing ones as well
- /var/tmp:/var/tmp
- name: rngd
image: linuxkit/rngd:7fab61cca793113280397dcee8159f35dc37adcb
files:
- path: etc/getty.shadow
# sample sets password for root to "abcdefgh" (without quotes)
contents: 'root:$6$6tPd2uhHrecCEKug$8mKfcgfwguP7f.BLdZsT1Wz7WIIJOBY1oUFHzIv9/O71M2J0EPdtFqFGTxB1UK5ejqQxRFQ.ZSG9YXR0SNsc11:17322:0:::::'
trust:
org:
- linuxkit

View File

@ -71,9 +71,11 @@ type Image struct {
// Everything except Runtime and ref is used to build the OCI spec // Everything except Runtime and ref is used to build the OCI spec
type ImageConfig struct { type ImageConfig struct {
Capabilities *[]string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` Capabilities *[]string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
CapabilitiesAdd *[]string `yaml:"capabilities.add,omitempty" json:"capabilities.add,omitempty"`
Ambient *[]string `yaml:"ambient,omitempty" json:"ambient,omitempty"` Ambient *[]string `yaml:"ambient,omitempty" json:"ambient,omitempty"`
Mounts *[]specs.Mount `yaml:"mounts,omitempty" json:"mounts,omitempty"` Mounts *[]specs.Mount `yaml:"mounts,omitempty" json:"mounts,omitempty"`
Binds *[]string `yaml:"binds,omitempty" json:"binds,omitempty"` Binds *[]string `yaml:"binds,omitempty" json:"binds,omitempty"`
BindsAdd *[]string `yaml:"binds.add,omitempty" json:"binds.add,omitempty"`
Tmpfs *[]string `yaml:"tmpfs,omitempty" json:"tmpfs,omitempty"` Tmpfs *[]string `yaml:"tmpfs,omitempty" json:"tmpfs,omitempty"`
Command *[]string `yaml:"command,omitempty" json:"command,omitempty"` Command *[]string `yaml:"command,omitempty" json:"command,omitempty"`
Env *[]string `yaml:"env,omitempty" json:"env,omitempty"` Env *[]string `yaml:"env,omitempty" json:"env,omitempty"`
@ -505,6 +507,34 @@ func assignStrings3(v1 []string, v2, v3 *[]string) []string {
return v1 return v1
} }
// mergeStrings does ordered unique merge between JSON string array pointers
func mergeStrings(v1, v2 *[]string) *[]string {
switch {
case v2 == nil && v1 == nil:
return &[]string{}
case v2 == nil:
return v1
case v1 == nil:
return v2
}
// merge the two uniquely
ret := []string{}
m := make(map[string]bool)
for _, s := range *v1 {
if m[s] {
continue
}
ret = append(ret, s)
}
for _, s := range *v2 {
if m[s] {
continue
}
ret = append(ret, s)
}
return &ret
}
// assignMaps does ordered overrides from JSON string map pointers // assignMaps does ordered overrides from JSON string map pointers
func assignMaps(v1, v2 *map[string]string) map[string]string { func assignMaps(v1, v2 *map[string]string) map[string]string {
if v2 != nil { if v2 != nil {
@ -770,7 +800,7 @@ func ConfigInspectToOCI(yaml *Image, inspect types.ImageInspect, idMap map[strin
} }
mounts[dest] = specs.Mount{Destination: dest, Type: "tmpfs", Source: "tmpfs", Options: opts} mounts[dest] = specs.Mount{Destination: dest, Type: "tmpfs", Source: "tmpfs", Options: opts}
} }
for _, b := range assignStrings(label.Binds, yaml.Binds) { for _, b := range assignStrings(mergeStrings(label.Binds, yaml.BindsAdd), yaml.Binds) {
parts := strings.Split(b, ":") parts := strings.Split(b, ":")
if len(parts) < 2 { if len(parts) < 2 {
return oci, runtime, fmt.Errorf("Cannot parse bind, missing ':': %s", b) return oci, runtime, fmt.Errorf("Cannot parse bind, missing ':': %s", b)
@ -880,7 +910,7 @@ func ConfigInspectToOCI(yaml *Image, inspect types.ImageInspect, idMap map[strin
capCheck[capability] = true capCheck[capability] = true
} }
boundingSet := map[string]bool{} boundingSet := map[string]bool{}
caps := assignStrings(label.Capabilities, yaml.Capabilities) caps := assignStrings(mergeStrings(label.Capabilities, yaml.CapabilitiesAdd), yaml.Capabilities)
if len(caps) == 1 { if len(caps) == 1 {
switch cap := strings.ToLower(caps[0]); cap { switch cap := strings.ToLower(caps[0]); cap {
case "none": case "none":

View File

@ -260,9 +260,11 @@ var schema = string(`
"name": {"type": "string"}, "name": {"type": "string"},
"image": {"type": "string"}, "image": {"type": "string"},
"capabilities": { "$ref": "#/definitions/strings" }, "capabilities": { "$ref": "#/definitions/strings" },
"capabilities.add": { "$ref": "#/definitions/strings" },
"ambient": { "$ref": "#/definitions/strings" }, "ambient": { "$ref": "#/definitions/strings" },
"mounts": { "$ref": "#/definitions/mounts" }, "mounts": { "$ref": "#/definitions/mounts" },
"binds": { "$ref": "#/definitions/strings" }, "binds": { "$ref": "#/definitions/strings" },
"binds.add": { "$ref": "#/definitions/strings" },
"tmpfs": { "$ref": "#/definitions/strings" }, "tmpfs": { "$ref": "#/definitions/strings" },
"command": { "$ref": "#/definitions/strings" }, "command": { "$ref": "#/definitions/strings" },
"env": { "$ref": "#/definitions/strings" }, "env": { "$ref": "#/definitions/strings" },

View File

@ -0,0 +1,15 @@
#!/bin/sh
set -x
function failed {
printf "bindmerge test suite FAILED\n" >&1
exit 1
}
# the very fact that this is running means that the bind worked, so just need to check that the defaults also
# are there
[ -d /dev/mapper ] || failed
[ -d /hostroot ] || failed
printf "bindmerge test suite PASSED\n" >&1

View File

@ -0,0 +1,27 @@
#!/bin/sh
# SUMMARY: Check that the userdata is found and read when on a cidata partition
# LABELS:
# REPEAT:
set -ex
# Source libraries. Uncomment if needed/defined
#. "${RT_LIB}"
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
NAME=bindtest
DISK=disk.img
clean_up() {
rm -rf ${NAME}-* ${DISK}
}
trap clean_up EXIT
# Test code goes here
linuxkit build -format kernel+initrd -name ${NAME} test.yml
RESULT="$(linuxkit run -disk file=${DISK},size=32M ${NAME})"
echo "${RESULT}"
echo "${RESULT}" | grep -q "suite PASSED"
exit 0

View File

@ -0,0 +1,28 @@
kernel:
image: linuxkit/kernel:5.4.30
cmdline: "console=ttyS0 console=ttyAMA0"
init:
- linuxkit/init:7195dc244cd92af01fd0895fd204249a6114c5e2
- linuxkit/runc:f79954950022fea76b8b6f10de58cb48e4fb3878
onboot:
- name: mount
image: linuxkit/mount:19fa297189166206ac97261679c3e31fb140d48f
binds.add:
- /check.sh:/check.sh
- /var/tmp:/var/tmp
# default binds from linuxkit/mount
# - /dev:/dev
# - /var:/var:rshared,rbind
# - /:/hostroot
command: ["sh", "-c", "/check.sh"]
- name: poweroff
image: linuxkit/poweroff:06dd4e46c62fbe79123a028835c921f80e4855d3
command: ["/bin/sh", "/poweroff.sh", "10"]
files:
- path: check.sh
source: ./check.sh
mode: "0700"
trust:
org:
- linuxkit
- library