Permalink
Browse files

Add runtime.envFromFiles

Signed-off-by: Ilya Dmitrichenko <errordeveloper@gmail.com>
  • Loading branch information...
errordeveloper committed Dec 29, 2018
1 parent 1616b18 commit b7268fae5a2bac56d82a01dce71977a6cdcbb790
Showing with 59 additions and 29 deletions.
  1. +1 −0 docs/yaml.md
  2. +4 −3 pkg/init/cmd/service/cmd.go
  3. +42 −18 pkg/init/cmd/service/prepare.go
  4. +10 −7 src/cmd/linuxkit/moby/config.go
  5. +2 −1 src/cmd/linuxkit/moby/schema.go
@@ -208,6 +208,7 @@ which specifies some actions to take place when the container is being started.
- `bindNS` specifies a namespace type and a path where the namespace from the container being created will be bound. This allows a namespace to be set up in an `onboot` container, and then
using `net: path` for a `service` container to use that network namespace later.
- `namespace` overrides the LinuxKit default containerd namespace to put the container in; only applicable to services.
- `envFromFiles` specifies environment variables that need to be set based on content of files, e.g. for use when metadata needs to be used to set environment variables.

An example of using the `runtime` config to configure a network namespace with `wireguard` and then run `nginx` in that namespace is shown below:
```
@@ -156,7 +156,7 @@ func start(ctx context.Context, service, sock, basePath, dumpSpec string) (strin

rootfs := filepath.Join(path, "rootfs")

if err := prepareFilesystem(path, runtimeConfig); err != nil {
if err := prepareFilesystem(rootfs, runtimeConfig); err != nil {
return "", 0, "preparing filesystem", err
}

@@ -174,7 +174,9 @@ func start(ctx context.Context, service, sock, basePath, dumpSpec string) (strin
return "", 0, "failed to parse service spec", err
}

spec.Root.Path = rootfs
if err := prepareSpec(rootfs, runtimeConfig, spec); err != nil {
return "", 0, "preparing environment variables", err
}

if dumpSpec != "" {
d, err := os.Create(dumpSpec)
@@ -186,7 +188,6 @@ func start(ctx context.Context, service, sock, basePath, dumpSpec string) (strin
if err := enc.Encode(&spec); err != nil {
return "", 0, "failed to write spec dump", err
}

}

if runtimeConfig.Namespace != "" {
@@ -8,7 +8,7 @@ import (
"path/filepath"
"strings"

"github.com/opencontainers/runtime-spec/specs-go"
specs "github.com/opencontainers/runtime-spec/specs-go"
log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
@@ -18,12 +18,13 @@ import (

// Runtime is the type of config processed at runtime, not used to build the OCI spec
type Runtime struct {
Cgroups []string `yaml:"cgroups" json:"cgroups,omitempty"`
Mounts []specs.Mount `yaml:"mounts" json:"mounts,omitempty"`
Mkdir []string `yaml:"mkdir" json:"mkdir,omitempty"`
Interfaces []Interface `yaml:"interfaces" json:"interfaces,omitempty"`
BindNS Namespaces `yaml:"bindNS" json:"bindNS,omitempty"`
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
Cgroups []string `yaml:"cgroups" json:"cgroups,omitempty"`
Mounts []specs.Mount `yaml:"mounts" json:"mounts,omitempty"`
Mkdir []string `yaml:"mkdir" json:"mkdir,omitempty"`
Interfaces []Interface `yaml:"interfaces" json:"interfaces,omitempty"`
BindNS Namespaces `yaml:"bindNS" json:"bindNS,omitempty"`
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
EnvFromFiles map[string]string `yaml:"envFromFiles,omitempty" json:"envFromFiles,omitempty"`
}

// Namespaces is the type for configuring paths to bind namespaces
@@ -139,23 +140,23 @@ func newCgroup(cgroup string) error {
return nil
}

func makeAbsolute(rootfs, dir string) string {
if filepath.IsAbs(dir) {
return dir
}
// relative paths are relative to rootfs of container
return filepath.Join(rootfs, dir)
}

// prepareFilesystem sets up the mounts and cgroups, before the container is created
func prepareFilesystem(path string, runtime Runtime) error {
func prepareFilesystem(rootfs string, runtime Runtime) error {
// execute the runtime config that should be done up front
// we execute Mounts before Mkdir so you can make a directory under a mount
// but we do mkdir of the destination path in case missing
rootfs := filepath.Join(path, "rootfs")
makeAbsolute := func(dir string) string {
if filepath.IsAbs(dir) {
return dir
}
// relative paths are relative to rootfs of container
return filepath.Join(rootfs, dir)
}

for _, mount := range runtime.Mounts {
const mode os.FileMode = 0755
dir := makeAbsolute(mount.Destination)
dir := makeAbsolute(rootfs, mount.Destination)
err := os.MkdirAll(dir, mode)
if err != nil {
return fmt.Errorf("Cannot create directory for mount destination %s: %v", dir, err)
@@ -178,7 +179,7 @@ func prepareFilesystem(path string, runtime Runtime) error {
for _, dir := range runtime.Mkdir {
// in future we may need to change the structure to set mode, ownership
const mode os.FileMode = 0755
dir = makeAbsolute(dir)
dir = makeAbsolute(rootfs, dir)
err := os.MkdirAll(dir, mode)
if err != nil {
return fmt.Errorf("Cannot create directory %s: %v", dir, err)
@@ -195,6 +196,29 @@ func prepareFilesystem(path string, runtime Runtime) error {
return nil
}

// prepareSpec appends spec based on runtime config;
// NOTE: these changes are runtime-only, and config.json is read-only on disk
// so you won't see any of these changes in config.json
func prepareSpec(rootfs string, runtime Runtime, spec *specs.Spec) error {
spec.Root.Path = rootfs

if spec.Process == nil {
spec.Process = &specs.Process{}
}

for envKey, filePath := range runtime.EnvFromFiles {
filePath = makeAbsolute(rootfs, filePath)
data, err := ioutil.ReadFile(filePath)
if err != nil {
return fmt.Errorf("Cannot read file %s: %v", filePath, err)
}
envVar := fmt.Sprintf("%s=%s", envKey, string(data))
spec.Process.Env = append(spec.Process.Env, envVar)
}

return nil
}

// bind mount a namespace file
func bindNS(ns string, path string, pid int) error {
if path == "" {
@@ -110,12 +110,13 @@ type ImageConfig struct {

// Runtime is the type of config processed at runtime, not used to build the OCI spec
type Runtime struct {
Cgroups *[]string `yaml:"cgroups,omitempty" json:"cgroups,omitempty"`
Mounts *[]specs.Mount `yaml:"mounts,omitempty" json:"mounts,omitempty"`
Mkdir *[]string `yaml:"mkdir,omitempty" json:"mkdir,omitempty"`
Interfaces *[]Interface `yaml:"interfaces,omitempty,omitempty" json:"interfaces,omitempty"`
BindNS Namespaces `yaml:"bindNS,omitempty" json:"bindNS,omitempty"`
Namespace *string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
Cgroups *[]string `yaml:"cgroups,omitempty" json:"cgroups,omitempty"`
Mounts *[]specs.Mount `yaml:"mounts,omitempty" json:"mounts,omitempty"`
Mkdir *[]string `yaml:"mkdir,omitempty" json:"mkdir,omitempty"`
Interfaces *[]Interface `yaml:"interfaces,omitempty,omitempty" json:"interfaces,omitempty"`
BindNS Namespaces `yaml:"bindNS,omitempty" json:"bindNS,omitempty"`
Namespace *string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
EnvFromFiles *map[string]string `yaml:"envFromFiles,omitempty" json:"envFromFiles,omitempty"`
}

// Namespaces is the type for configuring paths to bind namespaces
@@ -590,6 +591,7 @@ func assignRuntime(v1, v2 *Runtime) Runtime {
runtimeMkdir := assignStrings(v1.Mkdir, v2.Mkdir)
runtimeInterfaces := assignRuntimeInterfaceArray(v1.Interfaces, v2.Interfaces)
runtimeNamespace := assignString(v1.Namespace, v2.Namespace)
runtimeEnvFromFiles := assignMaps(v1.EnvFromFiles, v2.EnvFromFiles)
runtime := Runtime{
Cgroups: &runtimeCgroups,
Mounts: &runtimeMounts,
@@ -604,7 +606,8 @@ func assignRuntime(v1, v2 *Runtime) Runtime {
User: assignStringPtr(v1.BindNS.User, v2.BindNS.User),
Uts: assignStringPtr(v1.BindNS.Uts, v2.BindNS.Uts),
},
Namespace: &runtimeNamespace,
Namespace: &runtimeNamespace,
EnvFromFiles: &runtimeEnvFromFiles,
}
return runtime
}
@@ -249,7 +249,8 @@ var schema = string(`
"mkdir": {"$ref": "#/definitions/strings"},
"interfaces": {"$ref": "#/definitions/interfaces"},
"bindNS": {"$ref": "#/definitions/namespaces"},
"namespace": {"type": "string"}
"namespace": {"type": "string"},
"envFromFiles": {"$ref": "#/definitions/mapstring"}
}
},
"image": {

0 comments on commit b7268fa

Please sign in to comment.