| package main |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "reflect" |
| |
| ocs "github.com/opencontainers/specs/specs-go" |
| ) |
| |
| var ( |
| bundlesDir = filepath.Join("test-artifacts", "oci-bundles") |
| refOciSpecsPath = filepath.Join(bundlesDir, "config.json") |
| ) |
| |
| 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(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(bundlesDir, 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(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) |
| } |