mirror of
https://github.com/clearlinux/docker.git
synced 2026-09-05 13:11:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38b8373434 | ||
|
|
03b5f8a585 | ||
|
|
bc260f0225 | ||
|
|
45dcd1125b | ||
|
|
d2e063d9e1 | ||
|
|
567a484b66 | ||
|
|
5d4b886ad6 |
Vendored
+48
-19
@@ -1,27 +1,55 @@
|
|||||||
# -*- mode: ruby -*-
|
# -*- mode: ruby -*-
|
||||||
# vi: set ft=ruby :
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
BOX_NAME = "ubuntu"
|
def v10(config)
|
||||||
BOX_URI = "http://files.vagrantup.com/precise64.box"
|
config.vm.box = 'precise64'
|
||||||
PPA_KEY = "E61D797F63561DC6"
|
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
|
||||||
|
|
||||||
Vagrant::Config.run do |config|
|
# Install ubuntu packaging dependencies and create ubuntu packages
|
||||||
# Setup virtual machine box. This VM configuration code is always executed.
|
config.vm.provision :shell, :inline => "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list"
|
||||||
config.vm.box = BOX_NAME
|
config.vm.provision :shell, :inline => 'export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; apt-get install -qq -y --force-yes lxc-docker'
|
||||||
config.vm.box_url = BOX_URI
|
end
|
||||||
# Add docker PPA key to the local repository and install docker
|
|
||||||
pkg_cmd = "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys #{PPA_KEY}; "
|
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||||
pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list; "
|
v10(config)
|
||||||
pkg_cmd << "apt-get update -qq; apt-get install -q -y lxc-docker"
|
end
|
||||||
if ARGV.include?("--provider=aws".downcase)
|
|
||||||
# Add AUFS dependency to amazon's VM
|
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("1") do |config|
|
||||||
pkg_cmd << "; apt-get install linux-image-extra-3.2.0-40-virtual"
|
v10(config)
|
||||||
end
|
|
||||||
config.vm.provision :shell, :inline => pkg_cmd
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Providers were added on Vagrant >= 1.1.0
|
|
||||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||||
|
config.vm.provider :aws do |aws|
|
||||||
|
config.vm.box = "dummy"
|
||||||
|
config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
|
||||||
|
aws.access_key_id = ENV["AWS_ACCESS_KEY_ID"]
|
||||||
|
aws.secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
|
||||||
|
aws.keypair_name = ENV["AWS_KEYPAIR_NAME"]
|
||||||
|
aws.ssh_private_key_path = ENV["AWS_SSH_PRIVKEY"]
|
||||||
|
aws.region = "us-east-1"
|
||||||
|
aws.ami = "ami-d0f89fb9"
|
||||||
|
aws.ssh_username = "ubuntu"
|
||||||
|
aws.instance_type = "t1.micro"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.provider :rackspace do |rs|
|
||||||
|
config.vm.box = "dummy"
|
||||||
|
config.vm.box_url = "https://github.com/mitchellh/vagrant-rackspace/raw/master/dummy.box"
|
||||||
|
config.ssh.private_key_path = ENV["RS_PRIVATE_KEY"]
|
||||||
|
rs.username = ENV["RS_USERNAME"]
|
||||||
|
rs.api_key = ENV["RS_API_KEY"]
|
||||||
|
rs.public_key_path = ENV["RS_PUBLIC_KEY"]
|
||||||
|
rs.flavor = /512MB/
|
||||||
|
rs.image = /Ubuntu/
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.provider :virtualbox do |vb|
|
||||||
|
config.vm.box = 'precise64'
|
||||||
|
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Vagrant::VERSION >= "1.2.0" and Vagrant.configure("2") do |config|
|
||||||
config.vm.provider :aws do |aws, override|
|
config.vm.provider :aws do |aws, override|
|
||||||
config.vm.box = "dummy"
|
config.vm.box = "dummy"
|
||||||
config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
|
config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
|
||||||
@@ -47,7 +75,8 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
|||||||
end
|
end
|
||||||
|
|
||||||
config.vm.provider :virtualbox do |vb|
|
config.vm.provider :virtualbox do |vb|
|
||||||
config.vm.box = BOX_NAME
|
config.vm.box = 'precise64'
|
||||||
config.vm.box_url = BOX_URI
|
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
+169
@@ -0,0 +1,169 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
runtime *Runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuilder(runtime *Runtime) *Builder {
|
||||||
|
return &Builder{
|
||||||
|
runtime: runtime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder *Builder) Run(image *Image, cmd ...string) (*Container, error) {
|
||||||
|
// FIXME: pass a NopWriter instead of nil
|
||||||
|
config, err := ParseRun(append([]string{"-d", image.Id}, cmd...), nil, builder.runtime.capabilities)
|
||||||
|
if config.Image == "" {
|
||||||
|
return nil, fmt.Errorf("Image not specified")
|
||||||
|
}
|
||||||
|
if len(config.Cmd) == 0 {
|
||||||
|
return nil, fmt.Errorf("Command not specified")
|
||||||
|
}
|
||||||
|
if config.Tty {
|
||||||
|
return nil, fmt.Errorf("The tty mode is not supported within the builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new container
|
||||||
|
container, err := builder.runtime.Create(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.Start(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder *Builder) Commit(container *Container, repository, tag, comment, author string) (*Image, error) {
|
||||||
|
return builder.runtime.Commit(container.Id, repository, tag, comment, author)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
|
||||||
|
for c := range containers {
|
||||||
|
tmp := builder.runtime.Get(c)
|
||||||
|
builder.runtime.Destroy(tmp)
|
||||||
|
Debugf("Removing container %s", c)
|
||||||
|
}
|
||||||
|
for i := range images {
|
||||||
|
builder.runtime.graph.Delete(i)
|
||||||
|
Debugf("Removing image %s", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
|
||||||
|
var (
|
||||||
|
image, base *Image
|
||||||
|
tmpContainers map[string]struct{} = make(map[string]struct{})
|
||||||
|
tmpImages map[string]struct{} = make(map[string]struct{})
|
||||||
|
)
|
||||||
|
defer builder.clearTmp(tmpContainers, tmpImages)
|
||||||
|
|
||||||
|
file := bufio.NewReader(dockerfile)
|
||||||
|
for {
|
||||||
|
line, err := file.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
// Skip comments and empty line
|
||||||
|
if len(line) == 0 || line[0] == '#' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tmp := strings.SplitN(line, " ", 2)
|
||||||
|
if len(tmp) != 2 {
|
||||||
|
return fmt.Errorf("Invalid Dockerfile format")
|
||||||
|
}
|
||||||
|
switch tmp[0] {
|
||||||
|
case "from":
|
||||||
|
fmt.Fprintf(stdout, "FROM %s\n", tmp[1])
|
||||||
|
image, err = builder.runtime.repositories.LookupImage(tmp[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "run":
|
||||||
|
fmt.Fprintf(stdout, "RUN %s\n", tmp[1])
|
||||||
|
if image == nil {
|
||||||
|
return fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the container and start it
|
||||||
|
c, err := builder.Run(image, "/bin/sh", "-c", tmp[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmpContainers[c.Id] = struct{}{}
|
||||||
|
|
||||||
|
// Wait for it to finish
|
||||||
|
if result := c.Wait(); result != 0 {
|
||||||
|
return fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", tmp[1], result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit the container
|
||||||
|
base, err = builder.Commit(c, "", "", "", "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmpImages[base.Id] = struct{}{}
|
||||||
|
|
||||||
|
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
|
||||||
|
break
|
||||||
|
case "copy":
|
||||||
|
if image == nil {
|
||||||
|
return fmt.Errorf("Please provide a source image with `from` prior to copy")
|
||||||
|
}
|
||||||
|
tmp2 := strings.SplitN(tmp[1], " ", 2)
|
||||||
|
if len(tmp) != 2 {
|
||||||
|
return fmt.Errorf("Invalid COPY format")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(stdout, "COPY %s to %s in %s\n", tmp2[0], tmp2[1], base.ShortId())
|
||||||
|
|
||||||
|
file, err := Download(tmp2[0], stdout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Body.Close()
|
||||||
|
|
||||||
|
c, err := builder.Run(base, "echo", "insert", tmp2[0], tmp2[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Inject(file.Body, tmp2[1]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
base, err = builder.Commit(c, "", "", "", "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(stdout, "Skipping unknown op %s\n", tmp[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if base != nil {
|
||||||
|
// The build is successful, keep the temporary containers and images
|
||||||
|
for i := range tmpImages {
|
||||||
|
delete(tmpImages, i)
|
||||||
|
}
|
||||||
|
for i := range tmpContainers {
|
||||||
|
delete(tmpContainers, i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(stdout, "Build finished. image id: %s\n", base.ShortId())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(stdout, "An error occured during the build\n")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+77
-19
@@ -10,6 +10,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -33,6 +34,7 @@ func (srv *Server) Help() string {
|
|||||||
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
|
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
|
||||||
for _, cmd := range [][]string{
|
for _, cmd := range [][]string{
|
||||||
{"attach", "Attach to a running container"},
|
{"attach", "Attach to a running container"},
|
||||||
|
{"build", "Build a container from Dockerfile"},
|
||||||
{"commit", "Create a new image from a container's changes"},
|
{"commit", "Create a new image from a container's changes"},
|
||||||
{"diff", "Inspect changes on a container's filesystem"},
|
{"diff", "Inspect changes on a container's filesystem"},
|
||||||
{"export", "Stream the contents of a container as a tar archive"},
|
{"export", "Stream the contents of a container as a tar archive"},
|
||||||
@@ -40,6 +42,7 @@ func (srv *Server) Help() string {
|
|||||||
{"images", "List images"},
|
{"images", "List images"},
|
||||||
{"import", "Create a new filesystem image from the contents of a tarball"},
|
{"import", "Create a new filesystem image from the contents of a tarball"},
|
||||||
{"info", "Display system-wide information"},
|
{"info", "Display system-wide information"},
|
||||||
|
{"insert", "Insert a file in an image"},
|
||||||
{"inspect", "Return low-level information on a container"},
|
{"inspect", "Return low-level information on a container"},
|
||||||
{"kill", "Kill a running container"},
|
{"kill", "Kill a running container"},
|
||||||
{"login", "Register or Login to the docker registry server"},
|
{"login", "Register or Login to the docker registry server"},
|
||||||
@@ -63,6 +66,74 @@ func (srv *Server) Help() string {
|
|||||||
return help
|
return help
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *Server) CmdInsert(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
|
stdout.Flush()
|
||||||
|
cmd := rcli.Subcmd(stdout, "insert", "IMAGE URL PATH", "Insert a file from URL in the IMAGE at PATH")
|
||||||
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if cmd.NArg() != 3 {
|
||||||
|
cmd.Usage()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
imageId := cmd.Arg(0)
|
||||||
|
url := cmd.Arg(1)
|
||||||
|
path := cmd.Arg(2)
|
||||||
|
|
||||||
|
img, err := srv.runtime.repositories.LookupImage(imageId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := Download(url, stdout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Body.Close()
|
||||||
|
|
||||||
|
b := NewBuilder(srv.runtime)
|
||||||
|
c, err := b.Run(img, "echo", "insert", url, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Inject(ProgressReader(file.Body, int(file.ContentLength), stdout, "Downloading %v/%v (%v)"), path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// FIXME: Handle custom repo, tag comment, author
|
||||||
|
img, err = b.Commit(c, "", "", img.Comment, img.Author)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(stdout, "%s\n", img.Id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) CmdBuild(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
|
stdout.Flush()
|
||||||
|
cmd := rcli.Subcmd(stdout, "build", "[Dockerfile|-]", "Build a container from Dockerfile")
|
||||||
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dockerfile := cmd.Arg(0)
|
||||||
|
if dockerfile == "" {
|
||||||
|
dockerfile = "Dockerfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
var file io.Reader
|
||||||
|
|
||||||
|
if dockerfile != "-" {
|
||||||
|
f, err := os.Open(dockerfile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
file = f
|
||||||
|
} else {
|
||||||
|
file = stdin
|
||||||
|
}
|
||||||
|
return NewBuilder(srv.runtime).Build(file, stdout)
|
||||||
|
}
|
||||||
|
|
||||||
// 'docker login': login / register a user to registry service.
|
// 'docker login': login / register a user to registry service.
|
||||||
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
// Read a line on raw terminal with support for simple backspace
|
// Read a line on raw terminal with support for simple backspace
|
||||||
@@ -477,7 +548,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args .
|
|||||||
}
|
}
|
||||||
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout, "Importing %v/%v (%v)")
|
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout, "Importing %v/%v (%v)")
|
||||||
}
|
}
|
||||||
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
|
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -726,8 +797,6 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
|||||||
"Create a new image from a container's changes")
|
"Create a new image from a container's changes")
|
||||||
flComment := cmd.String("m", "", "Commit message")
|
flComment := cmd.String("m", "", "Commit message")
|
||||||
flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
|
flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
|
||||||
flConfig := cmd.String("config", "", "Config automatically applied when the image is run. This option must be the last one.")
|
|
||||||
flCommand := cmd.String("command", "", "Command to run when starting the image")
|
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -736,22 +805,7 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
|||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment, *flAuthor)
|
||||||
var config []string
|
|
||||||
if *flConfig != "" {
|
|
||||||
config = strings.Split(*flConfig, " ")
|
|
||||||
}
|
|
||||||
if *flCommand != "" {
|
|
||||||
config = append(config, "", "/bin/sh", "-c", *flCommand)
|
|
||||||
} else if *flConfig != "" {
|
|
||||||
config = append(config, "", "")
|
|
||||||
}
|
|
||||||
c, err := ParseRun(config, stdout, srv.runtime.capabilities)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment, *flAuthor, c)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -942,6 +996,10 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
|
|||||||
fmt.Fprintln(stdout, "Error: Image not specified")
|
fmt.Fprintln(stdout, "Error: Image not specified")
|
||||||
return fmt.Errorf("Image not specified")
|
return fmt.Errorf("Image not specified")
|
||||||
}
|
}
|
||||||
|
if len(config.Cmd) == 0 {
|
||||||
|
fmt.Fprintln(stdout, "Error: Command not specified")
|
||||||
|
return fmt.Errorf("Command not specified")
|
||||||
|
}
|
||||||
|
|
||||||
if config.Tty {
|
if config.Tty {
|
||||||
stdout.SetOptionRawTerminal()
|
stdout.SetOptionRawTerminal()
|
||||||
|
|||||||
@@ -168,6 +168,23 @@ func (settings *NetworkSettings) PortMappingHuman() string {
|
|||||||
return strings.Join(mapping, ", ")
|
return strings.Join(mapping, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject the io.Reader at the given path. Note: do not close the reader
|
||||||
|
func (container *Container) Inject(file io.Reader, pth string) error {
|
||||||
|
// Make sure the directory exists
|
||||||
|
if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// FIXME: Handle permissions/already existing dest
|
||||||
|
dest, err := os.Create(path.Join(container.rwPath(), pth))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(dest, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) Cmd() *exec.Cmd {
|
func (container *Container) Cmd() *exec.Cmd {
|
||||||
return container.cmd
|
return container.cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-80
@@ -193,7 +193,7 @@ func TestDiff(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "", nil)
|
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -226,84 +226,6 @@ func TestDiff(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommitAutoRun(t *testing.T) {
|
|
||||||
runtime, err := newTestRuntime()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer nuke(runtime)
|
|
||||||
container1, err := runtime.Create(
|
|
||||||
&Config{
|
|
||||||
Image: GetTestImage(runtime).Id,
|
|
||||||
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer runtime.Destroy(container1)
|
|
||||||
|
|
||||||
if container1.State.Running {
|
|
||||||
t.Errorf("Container shouldn't be running")
|
|
||||||
}
|
|
||||||
if err := container1.Run(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if container1.State.Running {
|
|
||||||
t.Errorf("Container shouldn't be running")
|
|
||||||
}
|
|
||||||
|
|
||||||
rwTar, err := container1.ExportRw()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "", &Config{Cmd: []string{"cat", "/world"}})
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
|
|
||||||
|
|
||||||
container2, err := runtime.Create(
|
|
||||||
&Config{
|
|
||||||
Image: img.Id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer runtime.Destroy(container2)
|
|
||||||
stdout, err := container2.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
stderr, err := container2.StderrPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := container2.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container2.Wait()
|
|
||||||
output, err := ioutil.ReadAll(stdout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
output2, err := ioutil.ReadAll(stderr)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stdout.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stderr.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "hello\n" {
|
|
||||||
t.Fatalf("Unexpected output. Expected %s, received: %s (err: %s)", "hello\n", output, output2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommitRun(t *testing.T) {
|
func TestCommitRun(t *testing.T) {
|
||||||
runtime, err := newTestRuntime()
|
runtime, err := newTestRuntime()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -336,7 +258,7 @@ func TestCommitRun(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "", nil)
|
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,14 +84,13 @@ func (graph *Graph) Get(name string) (*Image, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new image and registers it in the graph.
|
// Create creates a new image and registers it in the graph.
|
||||||
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
|
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string) (*Image, error) {
|
||||||
img := &Image{
|
img := &Image{
|
||||||
Id: GenerateId(),
|
Id: GenerateId(),
|
||||||
Comment: comment,
|
Comment: comment,
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
DockerVersion: VERSION,
|
DockerVersion: VERSION,
|
||||||
Author: author,
|
Author: author,
|
||||||
Config: config,
|
|
||||||
}
|
}
|
||||||
if container != nil {
|
if container != nil {
|
||||||
img.Parent = container.Image
|
img.Parent = container.Image
|
||||||
|
|||||||
+6
-6
@@ -62,7 +62,7 @@ func TestGraphCreate(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
image, err := graph.Create(archive, nil, "Testing", "", nil)
|
image, err := graph.Create(archive, nil, "Testing", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ func TestMount(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
image, err := graph.Create(archive, nil, "Testing", "", nil)
|
image, err := graph.Create(archive, nil, "Testing", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ func createTestImage(graph *Graph, t *testing.T) *Image {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
img, err := graph.Create(archive, nil, "Test image", "", nil)
|
img, err := graph.Create(archive, nil, "Test image", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ func TestDelete(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assertNImages(graph, t, 0)
|
assertNImages(graph, t, 0)
|
||||||
img, err := graph.Create(archive, nil, "Bla bla", "", nil)
|
img, err := graph.Create(archive, nil, "Bla bla", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -192,11 +192,11 @@ func TestDelete(t *testing.T) {
|
|||||||
assertNImages(graph, t, 0)
|
assertNImages(graph, t, 0)
|
||||||
|
|
||||||
// Test 2 create (same name) / 1 delete
|
// Test 2 create (same name) / 1 delete
|
||||||
img1, err := graph.Create(archive, nil, "Testing", "", nil)
|
img1, err := graph.Create(archive, nil, "Testing", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
|
if _, err = graph.Create(archive, nil, "Testing", ""); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assertNImages(graph, t, 2)
|
assertNImages(graph, t, 2)
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ type Image struct {
|
|||||||
ContainerConfig Config `json:"container_config,omitempty"`
|
ContainerConfig Config `json:"container_config,omitempty"`
|
||||||
DockerVersion string `json:"docker_version,omitempty"`
|
DockerVersion string `json:"docker_version,omitempty"`
|
||||||
Author string `json:"author,omitempty"`
|
Author string `json:"author,omitempty"`
|
||||||
Config *Config `json:"config,omitempty"`
|
|
||||||
graph *Graph
|
graph *Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
node default {
|
||||||
|
exec {
|
||||||
|
"apt_update" :
|
||||||
|
command => "/usr/bin/apt-get update"
|
||||||
|
}
|
||||||
|
|
||||||
|
Package {
|
||||||
|
require => Exec['apt_update']
|
||||||
|
}
|
||||||
|
|
||||||
|
group { "puppet":
|
||||||
|
ensure => "present"
|
||||||
|
}
|
||||||
|
|
||||||
|
include "docker"
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
class virtualbox {
|
||||||
|
Package { ensure => "installed" }
|
||||||
|
|
||||||
|
# remove some files from the base vagrant image because they're old
|
||||||
|
file { "/home/vagrant/docker-master":
|
||||||
|
ensure => absent,
|
||||||
|
recurse => true,
|
||||||
|
force => true,
|
||||||
|
purge => true,
|
||||||
|
}
|
||||||
|
file { "/usr/local/bin/dockerd":
|
||||||
|
ensure => absent,
|
||||||
|
}
|
||||||
|
file { "/usr/local/bin/docker":
|
||||||
|
ensure => absent,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up VirtualBox guest utils
|
||||||
|
package { "virtualbox-guest-utils": }
|
||||||
|
exec { "vbox-add" :
|
||||||
|
command => "/etc/init.d/vboxadd setup",
|
||||||
|
require => [
|
||||||
|
Package["virtualbox-guest-utils"],
|
||||||
|
Package["linux-headers-3.5.0-25-generic"], ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class docker {
|
||||||
|
# update this with latest go binary dist
|
||||||
|
$go_url = "http://go.googlecode.com/files/go1.0.3.linux-amd64.tar.gz"
|
||||||
|
|
||||||
|
Package { ensure => "installed" }
|
||||||
|
|
||||||
|
package { ["lxc", "debootstrap", "wget", "bsdtar", "git",
|
||||||
|
"linux-image-3.5.0-25-generic",
|
||||||
|
"linux-image-extra-3.5.0-25-generic",
|
||||||
|
"linux-headers-3.5.0-25-generic"]: }
|
||||||
|
|
||||||
|
$ec2_version = file("/etc/ec2_version", "/dev/null")
|
||||||
|
$rax_version = inline_template("<%= %x{/usr/bin/xenstore-read vm-data/provider_data/provider} %>")
|
||||||
|
|
||||||
|
if ($ec2_version) {
|
||||||
|
$vagrant_user = "ubuntu"
|
||||||
|
$vagrant_home = "/home/ubuntu"
|
||||||
|
} elsif ($rax_version) {
|
||||||
|
$vagrant_user = "root"
|
||||||
|
$vagrant_home = "/root"
|
||||||
|
} else {
|
||||||
|
# virtualbox is the vagrant default, so it should be safe to assume
|
||||||
|
$vagrant_user = "vagrant"
|
||||||
|
$vagrant_home = "/home/vagrant"
|
||||||
|
include virtualbox
|
||||||
|
}
|
||||||
|
|
||||||
|
exec { "fetch-go":
|
||||||
|
require => Package["wget"],
|
||||||
|
command => "/usr/bin/wget -O - $go_url | /bin/tar xz -C /usr/local",
|
||||||
|
creates => "/usr/local/go/bin/go",
|
||||||
|
}
|
||||||
|
|
||||||
|
file { "/etc/init/dockerd.conf":
|
||||||
|
mode => 600,
|
||||||
|
owner => "root",
|
||||||
|
group => "root",
|
||||||
|
content => template("docker/dockerd.conf"),
|
||||||
|
}
|
||||||
|
|
||||||
|
file { "/opt/go":
|
||||||
|
owner => $vagrant_user,
|
||||||
|
group => $vagrant_user,
|
||||||
|
recurse => true,
|
||||||
|
}
|
||||||
|
|
||||||
|
file { "${vagrant_home}/.profile":
|
||||||
|
mode => 644,
|
||||||
|
owner => $vagrant_user,
|
||||||
|
group => $vagrant_user,
|
||||||
|
content => template("docker/profile"),
|
||||||
|
}
|
||||||
|
|
||||||
|
exec { "build-docker" :
|
||||||
|
cwd => "/opt/go/src/github.com/dotcloud/docker",
|
||||||
|
user => $vagrant_user,
|
||||||
|
environment => "GOPATH=/opt/go",
|
||||||
|
command => "/usr/local/go/bin/go get -v ./... && /usr/local/go/bin/go install ./docker",
|
||||||
|
creates => "/opt/go/bin/docker",
|
||||||
|
logoutput => "on_failure",
|
||||||
|
require => [ Exec["fetch-go"], File["/opt/go"] ],
|
||||||
|
}
|
||||||
|
|
||||||
|
service { "dockerd" :
|
||||||
|
ensure => "running",
|
||||||
|
start => "/sbin/initctl start dockerd",
|
||||||
|
stop => "/sbin/initctl stop dockerd",
|
||||||
|
require => [ Exec["build-docker"], File["/etc/init/dockerd.conf"] ],
|
||||||
|
name => "dockerd",
|
||||||
|
provider => "base"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
description "Run dockerd"
|
||||||
|
|
||||||
|
stop on runlevel [!2345]
|
||||||
|
start on runlevel [3]
|
||||||
|
|
||||||
|
# if you want it to automatically restart if it crashes, leave the next line in
|
||||||
|
respawn
|
||||||
|
|
||||||
|
script
|
||||||
|
test -f /etc/default/locale && . /etc/default/locale || true
|
||||||
|
LANG=$LANG LC_ALL=$LANG /opt/go/bin/docker -d >> /var/log/dockerd 2>&1
|
||||||
|
end script
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# ~/.profile: executed by the command interpreter for login shells.
|
||||||
|
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
|
||||||
|
# exists.
|
||||||
|
# see /usr/share/doc/bash/examples/startup-files for examples.
|
||||||
|
# the files are located in the bash-doc package.
|
||||||
|
|
||||||
|
# the default umask is set in /etc/profile; for setting the umask
|
||||||
|
# for ssh logins, install and configure the libpam-umask package.
|
||||||
|
#umask 022
|
||||||
|
|
||||||
|
# if running bash
|
||||||
|
if [ -n "$BASH_VERSION" ]; then
|
||||||
|
# include .bashrc if it exists
|
||||||
|
if [ -f "$HOME/.bashrc" ]; then
|
||||||
|
. "$HOME/.bashrc"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set PATH so it includes user's private bin if it exists
|
||||||
|
if [ -d "$HOME/bin" ] ; then
|
||||||
|
PATH="$HOME/bin:$PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export GOPATH=/opt/go
|
||||||
|
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
|
||||||
|
|
||||||
|
docker=/opt/go/src/github.com/dotcloud/docker
|
||||||
|
if [ -d $docker ]; then
|
||||||
|
cd $docker
|
||||||
|
fi
|
||||||
+4
-52
@@ -77,59 +77,12 @@ func (runtime *Runtime) containerRoot(id string) string {
|
|||||||
return path.Join(runtime.repository, id)
|
return path.Join(runtime.repository, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) mergeConfig(userConf, imageConf *Config) {
|
|
||||||
if userConf.Hostname != "" {
|
|
||||||
userConf.Hostname = imageConf.Hostname
|
|
||||||
}
|
|
||||||
if userConf.User != "" {
|
|
||||||
userConf.User = imageConf.User
|
|
||||||
}
|
|
||||||
if userConf.Memory == 0 {
|
|
||||||
userConf.Memory = imageConf.Memory
|
|
||||||
}
|
|
||||||
if userConf.MemorySwap == 0 {
|
|
||||||
userConf.MemorySwap = imageConf.MemorySwap
|
|
||||||
}
|
|
||||||
if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
|
|
||||||
userConf.PortSpecs = imageConf.PortSpecs
|
|
||||||
}
|
|
||||||
if !userConf.Tty {
|
|
||||||
userConf.Tty = userConf.Tty
|
|
||||||
}
|
|
||||||
if !userConf.OpenStdin {
|
|
||||||
userConf.OpenStdin = imageConf.OpenStdin
|
|
||||||
}
|
|
||||||
if !userConf.StdinOnce {
|
|
||||||
userConf.StdinOnce = imageConf.StdinOnce
|
|
||||||
}
|
|
||||||
if userConf.Env == nil || len(userConf.Env) == 0 {
|
|
||||||
userConf.Env = imageConf.Env
|
|
||||||
}
|
|
||||||
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
|
|
||||||
userConf.Cmd = imageConf.Cmd
|
|
||||||
}
|
|
||||||
if userConf.Dns == nil || len(userConf.Dns) == 0 {
|
|
||||||
userConf.Dns = imageConf.Dns
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (runtime *Runtime) Create(config *Config) (*Container, error) {
|
func (runtime *Runtime) Create(config *Config) (*Container, error) {
|
||||||
|
|
||||||
// Lookup image
|
// Lookup image
|
||||||
img, err := runtime.repositories.LookupImage(config.Image)
|
img, err := runtime.repositories.LookupImage(config.Image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//runtime.mergeConfig(config, img.Config)
|
|
||||||
if img.Config != nil {
|
|
||||||
config = img.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Cmd == nil {
|
|
||||||
return nil, fmt.Errorf("No command specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate id
|
// Generate id
|
||||||
id := GenerateId()
|
id := GenerateId()
|
||||||
// Generate default hostname
|
// Generate default hostname
|
||||||
@@ -150,7 +103,6 @@ func (runtime *Runtime) Create(config *Config) (*Container, error) {
|
|||||||
// FIXME: do we need to store this in the container?
|
// FIXME: do we need to store this in the container?
|
||||||
SysInitPath: sysInitPath,
|
SysInitPath: sysInitPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
container.root = runtime.containerRoot(container.Id)
|
container.root = runtime.containerRoot(container.Id)
|
||||||
// Step 1: create the container directory.
|
// Step 1: create the container directory.
|
||||||
// This doubles as a barrier to avoid race conditions.
|
// This doubles as a barrier to avoid race conditions.
|
||||||
@@ -297,7 +249,7 @@ func (runtime *Runtime) Destroy(container *Container) error {
|
|||||||
|
|
||||||
// Commit creates a new filesystem image from the current state of a container.
|
// Commit creates a new filesystem image from the current state of a container.
|
||||||
// The image can optionally be tagged into a repository
|
// The image can optionally be tagged into a repository
|
||||||
func (runtime *Runtime) Commit(id, repository, tag, comment, author string, config *Config) (*Image, error) {
|
func (runtime *Runtime) Commit(id, repository, tag, comment, author string) (*Image, error) {
|
||||||
container := runtime.Get(id)
|
container := runtime.Get(id)
|
||||||
if container == nil {
|
if container == nil {
|
||||||
return nil, fmt.Errorf("No such container: %s", id)
|
return nil, fmt.Errorf("No such container: %s", id)
|
||||||
@@ -309,7 +261,7 @@ func (runtime *Runtime) Commit(id, repository, tag, comment, author string, conf
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create a new image from the container's base layers + a new layer from container changes
|
// Create a new image from the container's base layers + a new layer from container changes
|
||||||
img, err := runtime.graph.Create(rwTar, container, comment, author, config)
|
img, err := runtime.graph.Create(rwTar, container, comment, author)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -362,13 +314,13 @@ func NewRuntime() (*Runtime, error) {
|
|||||||
_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
|
_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
|
||||||
runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
|
runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
|
||||||
if !runtime.capabilities.MemoryLimit {
|
if !runtime.capabilities.MemoryLimit {
|
||||||
log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
|
log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
|
_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
|
||||||
runtime.capabilities.SwapLimit = err == nil
|
runtime.capabilities.SwapLimit = err == nil
|
||||||
if !runtime.capabilities.SwapLimit {
|
if !runtime.capabilities.SwapLimit {
|
||||||
log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
|
log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return runtime, nil
|
return runtime, nil
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ func init() {
|
|||||||
panic("docker tests needs to be run as root")
|
panic("docker tests needs to be run as root")
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkBridgeIface = "testdockbr0"
|
|
||||||
|
|
||||||
// Make it our Store root
|
// Make it our Store root
|
||||||
runtime, err := NewRuntimeFromDirectory(unitTestStoreBase)
|
runtime, err := NewRuntimeFromDirectory(unitTestStoreBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user