2016-03-21 19:06:39 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
|
|
|
|
2016-05-27 17:35:42 +00:00
|
|
|
utils "github.com/docker/containerd/testutils"
|
2016-04-26 20:38:36 +00:00
|
|
|
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
2016-03-21 19:06:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type OciProcessArgs struct {
|
|
|
|
Cmd string
|
|
|
|
Args []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Bundle struct {
|
|
|
|
Source string
|
|
|
|
Name string
|
|
|
|
Spec ocs.Spec
|
|
|
|
Path string
|
|
|
|
}
|
|
|
|
|
|
|
|
var bundleMap map[string]Bundle
|
|
|
|
|
|
|
|
// untarRootfs untars the given `source` tarPath into `destination/rootfs`
|
|
|
|
func untarRootfs(source string, destination string) error {
|
|
|
|
destination = filepath.Join(destination, "rootfs")
|
|
|
|
if err := os.MkdirAll(destination, 0755); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
tar := exec.Command("tar", "-C", destination, "-xf", source)
|
|
|
|
return tar.Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateBundleWithFilter generate a new oci-bundle named `name` from
|
|
|
|
// the provide `source` rootfs. It starts from the default spec
|
|
|
|
// generated by `runc spec`, overrides the `spec.Process.Args` value
|
|
|
|
// with `args` and set `spec.Process.Terminal` to false. It then apply
|
|
|
|
// `filter()` to the resulting spec if it is provided.
|
|
|
|
func CreateBundleWithFilter(source, name string, args []string, filter func(spec *ocs.Spec)) error {
|
|
|
|
// Generate the spec
|
|
|
|
var spec ocs.Spec
|
2016-05-27 17:35:42 +00:00
|
|
|
if f, err := os.Open(utils.RefOciSpecsPath); err != nil {
|
2016-03-21 19:06:39 +00:00
|
|
|
return fmt.Errorf("Failed to open default spec: %v", err)
|
|
|
|
} else {
|
|
|
|
if err := json.NewDecoder(f).Decode(&spec); err != nil {
|
|
|
|
return fmt.Errorf("Failed to load default spec: %v", err)
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
spec.Process.Args = args
|
|
|
|
spec.Process.Terminal = false
|
|
|
|
if filter != nil {
|
|
|
|
filter(&spec)
|
|
|
|
}
|
|
|
|
|
2016-05-27 17:35:42 +00:00
|
|
|
bundlePath := filepath.Join(utils.BundlesRoot, name)
|
2016-03-21 19:06:39 +00:00
|
|
|
nb := Bundle{source, name, spec, bundlePath}
|
|
|
|
|
|
|
|
// Check that we don't already have such a bundle
|
|
|
|
if b, ok := bundleMap[name]; ok {
|
|
|
|
if reflect.DeepEqual(b, nb) == false {
|
|
|
|
return fmt.Errorf("A bundle name named '%s' already exist but with different properties! %#v != %#v",
|
|
|
|
name, b, nb)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing should be there, but just in case
|
|
|
|
os.RemoveAll(bundlePath)
|
|
|
|
|
2016-05-27 17:35:42 +00:00
|
|
|
if err := untarRootfs(filepath.Join(utils.ArchivesDir, source+".tar"), bundlePath); err != nil {
|
2016-03-21 19:06:39 +00:00
|
|
|
return fmt.Errorf("Failed to untar %s.tar: %v", source, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a place for the io fifo
|
|
|
|
if err := os.Mkdir(filepath.Join(bundlePath, "io"), 0755); err != nil {
|
|
|
|
return fmt.Errorf("Failed to create bundle io directory: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the updated spec to the right location
|
|
|
|
config, e := os.Create(filepath.Join(bundlePath, "config.json"))
|
|
|
|
if e != nil {
|
|
|
|
return fmt.Errorf("Failed to create oci spec: %v", e)
|
|
|
|
}
|
|
|
|
defer config.Close()
|
|
|
|
|
|
|
|
if err := json.NewEncoder(config).Encode(&spec); err != nil {
|
|
|
|
return fmt.Errorf("Failed to encore oci spec: %v", e)
|
|
|
|
}
|
|
|
|
|
|
|
|
bundleMap[name] = nb
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-28 23:22:17 +00:00
|
|
|
func GetBundle(name string) *Bundle {
|
|
|
|
bundle, ok := bundleMap[name]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &bundle
|
|
|
|
}
|
|
|
|
|
2016-03-21 19:06:39 +00:00
|
|
|
func CreateBusyboxBundle(name string, args []string) error {
|
|
|
|
return CreateBundleWithFilter("busybox", name, args, nil)
|
|
|
|
}
|