package main import ( "encoding/json" "fmt" "os" "os/exec" "path/filepath" "reflect" utils "github.com/docker/containerd/testutils" ocs "github.com/opencontainers/runtime-spec/specs-go" ) 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 if f, err := os.Open(utils.RefOciSpecsPath); err != nil { 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) } bundlePath := filepath.Join(utils.BundlesRoot, name) 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) if err := untarRootfs(filepath.Join(utils.ArchivesDir, source+".tar"), bundlePath); err != nil { 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 } func GetBundle(name string) *Bundle { bundle, ok := bundleMap[name] if !ok { return nil } return &bundle } func CreateBusyboxBundle(name string, args []string) error { return CreateBundleWithFilter("busybox", name, args, nil) }