mirror of
https://github.com/moby/moby.git
synced 2026-01-12 11:11:44 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d42639e5c5 | ||
|
|
e7fb7f13d5 | ||
|
|
ad86dde10c | ||
|
|
a75a1b3859 | ||
|
|
08812096f5 | ||
|
|
5c30faf6f7 | ||
|
|
f7aaa06606 | ||
|
|
7ff65d40d5 | ||
|
|
a3ce90b78b | ||
|
|
03b83b3210 | ||
|
|
40ccf1d300 | ||
|
|
038ca5ee39 | ||
|
|
957c500ac9 | ||
|
|
62a595da5c | ||
|
|
d97661aa71 | ||
|
|
c6119da339 | ||
|
|
5051c20833 | ||
|
|
cdc2657ee9 | ||
|
|
76a1a7cf5b | ||
|
|
20c2a4f80f | ||
|
|
ebe157ebb5 | ||
|
|
cb431f223f | ||
|
|
ab34115b42 | ||
|
|
4b3354af3f | ||
|
|
8f81e175af | ||
|
|
636c7835d3 | ||
|
|
6d1dd8b41a | ||
|
|
ae97477284 | ||
|
|
86ad98e72a | ||
|
|
03d82922aa | ||
|
|
30d327d37e | ||
|
|
724e2d6b0a | ||
|
|
51d6228261 | ||
|
|
4db680fda4 | ||
|
|
9c7293508d | ||
|
|
9d8743a7ae | ||
|
|
50144aeb42 | ||
|
|
ee298d1420 | ||
|
|
03855b0027 | ||
|
|
2726e3649a | ||
|
|
6ebb249131 | ||
|
|
c45beabcd5 | ||
|
|
a22c78523f | ||
|
|
5a02c9ba0a |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## 0.2.1 (2012-05-01)
|
||||
+ 'docker commit -run' bundles a layer with default runtime options: command, ports etc.
|
||||
* Improve install process on Vagrant
|
||||
+ New Dockerfile operation: "maintainer"
|
||||
+ New Dockerfile operation: "expose"
|
||||
+ New Dockerfile operation: "cmd"
|
||||
+ Contrib script to build a Debian base layer
|
||||
+ 'docker -d -r': restart crashed containers at daemon startup
|
||||
* Runtime: improve test coverage
|
||||
|
||||
## 0.2.0 (2012-04-23)
|
||||
- Runtime: ghost containers can be killed and waited for
|
||||
* Documentation: update install intructions
|
||||
|
||||
73
Vagrantfile
vendored
73
Vagrantfile
vendored
@@ -1,55 +1,27 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
def v10(config)
|
||||
config.vm.box = 'precise64'
|
||||
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
|
||||
BOX_NAME = "ubuntu"
|
||||
BOX_URI = "http://files.vagrantup.com/precise64.box"
|
||||
PPA_KEY = "E61D797F63561DC6"
|
||||
|
||||
# Install ubuntu packaging dependencies and create ubuntu packages
|
||||
config.vm.provision :shell, :inline => "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list"
|
||||
config.vm.provision :shell, :inline => 'export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; apt-get install -qq -y --force-yes lxc-docker'
|
||||
end
|
||||
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
v10(config)
|
||||
end
|
||||
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("1") do |config|
|
||||
v10(config)
|
||||
Vagrant::Config.run do |config|
|
||||
# Setup virtual machine box. This VM configuration code is always executed.
|
||||
config.vm.box = BOX_NAME
|
||||
config.vm.box_url = BOX_URI
|
||||
# Add docker PPA key to the local repository and install docker
|
||||
pkg_cmd = "apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys #{PPA_KEY}; "
|
||||
pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list; "
|
||||
pkg_cmd << "apt-get update -qq; apt-get install -q -y lxc-docker"
|
||||
if ARGV.include?("--provider=aws".downcase)
|
||||
# Add AUFS dependency to amazon's VM
|
||||
pkg_cmd << "; apt-get install linux-image-extra-3.2.0-40-virtual"
|
||||
end
|
||||
config.vm.provision :shell, :inline => pkg_cmd
|
||||
end
|
||||
|
||||
# Providers were added on Vagrant >= 1.1.0
|
||||
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.box = "dummy"
|
||||
config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
|
||||
@@ -75,8 +47,15 @@ Vagrant::VERSION >= "1.2.0" and Vagrant.configure("2") do |config|
|
||||
end
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
config.vm.box = 'precise64'
|
||||
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
|
||||
config.vm.box = BOX_NAME
|
||||
config.vm.box_url = BOX_URI
|
||||
end
|
||||
|
||||
config.vm.provider :vmware_fusion do |vm|
|
||||
config.vm.box = "precise64"
|
||||
config.vm.box_url = "http://files.vagrantup.com/precise64_vmware_fusion.box"
|
||||
config.vm.provision :shell, :inline => <<-UPDATE
|
||||
apt-get install -y linux-image-extra-3.2.0-29-virtual
|
||||
UPDATE
|
||||
end
|
||||
end
|
||||
|
||||
24
commands.go
24
commands.go
@@ -18,7 +18,7 @@ import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const VERSION = "0.2.0"
|
||||
const VERSION = "0.2.1"
|
||||
|
||||
var (
|
||||
GIT_COMMIT string
|
||||
@@ -477,7 +477,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args .
|
||||
}
|
||||
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout, "Importing %v/%v (%v)")
|
||||
}
|
||||
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "")
|
||||
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -726,6 +726,7 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||
"Create a new image from a container's changes")
|
||||
flComment := cmd.String("m", "", "Commit message")
|
||||
flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
|
||||
flConfig := cmd.String("run", "", "Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`)
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -734,7 +735,16 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment, *flAuthor)
|
||||
|
||||
var config *Config
|
||||
if *flConfig != "" {
|
||||
config = &Config{}
|
||||
if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment, *flAuthor, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -925,10 +935,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
|
||||
fmt.Fprintln(stdout, "Error: 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 {
|
||||
stdout.SetOptionRawTerminal()
|
||||
@@ -993,11 +999,11 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewServer() (*Server, error) {
|
||||
func NewServer(autoRestart bool) (*Server, error) {
|
||||
if runtime.GOARCH != "amd64" {
|
||||
log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
|
||||
}
|
||||
runtime, err := NewRuntime()
|
||||
runtime, err := NewRuntime(autoRestart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -394,4 +394,5 @@ func TestAttachDisconnect(t *testing.T) {
|
||||
// Try to avoid the timeoout in destroy. Best effort, don't check error
|
||||
cStdin, _ := container.StdinPipe()
|
||||
cStdin.Close()
|
||||
container.Wait()
|
||||
}
|
||||
|
||||
@@ -22,9 +22,8 @@ func TestIdFormat(t *testing.T) {
|
||||
defer nuke(runtime)
|
||||
container1, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
|
||||
Memory: 33554432,
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -50,7 +49,6 @@ func TestMultipleAttachRestart(t *testing.T) {
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"/bin/sh", "-c",
|
||||
"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"},
|
||||
Memory: 33554432,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -116,8 +114,8 @@ func TestMultipleAttachRestart(t *testing.T) {
|
||||
if err := container.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timeout := make(chan bool)
|
||||
go func() {
|
||||
|
||||
setTimeout(t, "Timeout reading from the process", 3*time.Second, func() {
|
||||
l1, err = bufio.NewReader(stdout1).ReadString('\n')
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -139,15 +137,8 @@ func TestMultipleAttachRestart(t *testing.T) {
|
||||
if strings.Trim(l3, " \r\n") != "hello" {
|
||||
t.Fatalf("Unexpected output. Expected [%s], received [%s]", "hello", l3)
|
||||
}
|
||||
timeout <- false
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
timeout <- true
|
||||
}()
|
||||
if <-timeout {
|
||||
t.Fatalf("Timeout reading from the process")
|
||||
}
|
||||
})
|
||||
container.Wait()
|
||||
}
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
@@ -193,7 +184,7 @@ func TestDiff(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "")
|
||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -226,7 +217,7 @@ func TestDiff(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitRun(t *testing.T) {
|
||||
func TestCommitAutoRun(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -234,9 +225,8 @@ func TestCommitRun(t *testing.T) {
|
||||
defer nuke(runtime)
|
||||
container1, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
|
||||
Memory: 33554432,
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -258,7 +248,7 @@ func TestCommitRun(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "")
|
||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "", &Config{Cmd: []string{"cat", "/world"}})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -267,9 +257,86 @@ func TestCommitRun(t *testing.T) {
|
||||
|
||||
container2, err := runtime.Create(
|
||||
&Config{
|
||||
Image: img.Id,
|
||||
Memory: 33554432,
|
||||
Cmd: []string{"cat", "/world"},
|
||||
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) {
|
||||
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", "", nil)
|
||||
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,
|
||||
Cmd: []string{"cat", "/world"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -354,9 +421,8 @@ func TestRun(t *testing.T) {
|
||||
defer nuke(runtime)
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Memory: 33554432,
|
||||
Cmd: []string{"ls", "-al"},
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"ls", "-al"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
const DOCKER_PATH = "/home/creack/dotcloud/docker/docker/docker"
|
||||
var DOCKER_PATH string = path.Join(os.Getenv("DOCKERPATH"), "docker")
|
||||
|
||||
// WARNING: this crashTest will 1) crash your host, 2) remove all containers
|
||||
func runDaemon() (*exec.Cmd, error) {
|
||||
os.Remove("/var/run/docker.pid")
|
||||
exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
|
||||
cmd := exec.Command(DOCKER_PATH, "-d")
|
||||
outPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@@ -38,19 +43,43 @@ func crashTest() error {
|
||||
return err
|
||||
}
|
||||
|
||||
var endpoint string
|
||||
if ep := os.Getenv("TEST_ENDPOINT"); ep == "" {
|
||||
endpoint = "192.168.56.1:7979"
|
||||
} else {
|
||||
endpoint = ep
|
||||
}
|
||||
|
||||
c := make(chan bool)
|
||||
var conn io.Writer
|
||||
|
||||
go func() {
|
||||
conn, _ = net.Dial("tcp", endpoint)
|
||||
c <- false
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
c <- true
|
||||
}()
|
||||
<-c
|
||||
|
||||
restartCount := 0
|
||||
totalTestCount := 1
|
||||
for {
|
||||
daemon, err := runDaemon()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
restartCount++
|
||||
// time.Sleep(5000 * time.Millisecond)
|
||||
var stop bool
|
||||
go func() error {
|
||||
stop = false
|
||||
for i := 0; i < 100 && !stop; i++ {
|
||||
for i := 0; i < 100 && !stop; {
|
||||
func() error {
|
||||
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", "hello", "world")
|
||||
log.Printf("%d", i)
|
||||
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
|
||||
i++
|
||||
totalTestCount++
|
||||
outPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -62,9 +91,10 @@ func crashTest() error {
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
io.Copy(os.Stdout, outPipe)
|
||||
}()
|
||||
if conn != nil {
|
||||
go io.Copy(conn, outPipe)
|
||||
}
|
||||
|
||||
// Expecting error, do not check
|
||||
inPipe.Write([]byte("hello world!!!!!\n"))
|
||||
go inPipe.Write([]byte("hello world!!!!!\n"))
|
||||
|
||||
@@ -49,26 +49,36 @@ def docker(args, stdin=None):
|
||||
def image_exists(img):
|
||||
return docker(["inspect", img]).read().strip() != ""
|
||||
|
||||
def run_and_commit(img_in, cmd, stdin=None):
|
||||
def image_config(img):
|
||||
return json.loads(docker(["inspect", img]).read()).get("config", {})
|
||||
|
||||
def run_and_commit(img_in, cmd, stdin=None, author=None, run=None):
|
||||
run_id = docker(["run"] + (["-i", "-a", "stdin"] if stdin else ["-d"]) + [img_in, "/bin/sh", "-c", cmd], stdin=stdin).read().rstrip()
|
||||
print "---> Waiting for " + run_id
|
||||
result=int(docker(["wait", run_id]).read().rstrip())
|
||||
if result != 0:
|
||||
print "!!! '{}' return non-zero exit code '{}'. Aborting.".format(cmd, result)
|
||||
sys.exit(1)
|
||||
return docker(["commit", run_id]).read().rstrip()
|
||||
return docker(["commit"] + (["-author", author] if author else []) + (["-run", json.dumps(run)] if run is not None else []) + [run_id]).read().rstrip()
|
||||
|
||||
def insert(base, src, dst):
|
||||
def insert(base, src, dst, author=None):
|
||||
print "COPY {} to {} in {}".format(src, dst, base)
|
||||
if dst == "":
|
||||
raise Exception("Missing destination path")
|
||||
stdin = file(src)
|
||||
stdin.seek(0)
|
||||
return run_and_commit(base, "cat > {0}; chmod +x {0}".format(dst), stdin=stdin)
|
||||
|
||||
return run_and_commit(base, "cat > {0}; chmod +x {0}".format(dst), stdin=stdin, author=author)
|
||||
|
||||
def push(base, dst, author=None):
|
||||
print "PUSH to {} in {}".format(dst, base)
|
||||
if dst == "":
|
||||
raise Exception("Missing argument to push")
|
||||
tar = subprocess.Popen(["tar", "-c", "."], stdout=subprocess.PIPE).stdout
|
||||
return run_and_commit(base, "mkdir -p '{0}' && tar -C '{0}' -x".format(dst), stdin=tar, author=author)
|
||||
|
||||
def main():
|
||||
base=""
|
||||
maintainer=""
|
||||
steps = []
|
||||
try:
|
||||
for line in sys.stdin.readlines():
|
||||
@@ -77,22 +87,46 @@ def main():
|
||||
if line == "" or line[0] == "#":
|
||||
continue
|
||||
op, param = line.split(" ", 1)
|
||||
print op.upper() + " " + param
|
||||
if op == "from":
|
||||
print "FROM " + param
|
||||
base = param
|
||||
steps.append(base)
|
||||
elif op == "maintainer":
|
||||
maintainer = param
|
||||
elif op == "run":
|
||||
print "RUN " + param
|
||||
result = run_and_commit(base, param)
|
||||
result = run_and_commit(base, param, author=maintainer)
|
||||
steps.append(result)
|
||||
base = result
|
||||
print "===> " + base
|
||||
elif op == "copy":
|
||||
src, dst = param.split(" ", 1)
|
||||
result = insert(base, src, dst)
|
||||
result = insert(base, src, dst, author=maintainer)
|
||||
steps.append(result)
|
||||
base = result
|
||||
print "===> " + base
|
||||
elif op == "push":
|
||||
result = push(base, param.strip(), author=maintainer)
|
||||
steps.append(result)
|
||||
base=result
|
||||
print "===> " + base
|
||||
elif op == "expose":
|
||||
config = image_config(base)
|
||||
if config.get("PortSpecs") is None:
|
||||
config["PortSpecs"] = []
|
||||
portspec = param.strip()
|
||||
config["PortSpecs"].append(portspec)
|
||||
result = run_and_commit(base, "# (nop) expose port {}".format(portspec), author=maintainer, run=config)
|
||||
steps.append(result)
|
||||
base=result
|
||||
print "===> " + base
|
||||
elif op == "cmd":
|
||||
config = image_config(base)
|
||||
cmd = list(json.loads(param))
|
||||
config["Cmd"] = cmd
|
||||
result = run_and_commit(base, "# (nop) set default command to '{}'".format(" ".join(cmd)), author=maintainer, run=config)
|
||||
steps.append(result)
|
||||
base=result
|
||||
print "===> " + base
|
||||
else:
|
||||
print "Skipping uknown op " + op
|
||||
except:
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Start build from a know base image
|
||||
maintainer Solomon Hykes <solomon@dotcloud.com>
|
||||
from base:ubuntu-12.10
|
||||
# Update ubuntu sources
|
||||
run echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
|
||||
run apt-get update
|
||||
# Install system packages
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
|
||||
# Insert files from the host (./myscript must be present in the current directory)
|
||||
copy myscript /usr/local/bin/myscript
|
||||
copy myscript /usr/local/bin/myscript
|
||||
push /src
|
||||
|
||||
3
contrib/docker-build/myscript
Normal file
3
contrib/docker-build/myscript
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo hello, world!
|
||||
61
contrib/mkimage-debian.sh
Executable file
61
contrib/mkimage-debian.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# these should match the names found at http://www.debian.org/releases/
|
||||
stableSuite='squeeze'
|
||||
testingSuite='wheezy'
|
||||
unstableSuite='sid'
|
||||
|
||||
# if suite is equal to this, it gets the "latest" tag
|
||||
latestSuite="$testingSuite"
|
||||
|
||||
variant='minbase'
|
||||
include='iproute,iputils-ping'
|
||||
|
||||
repo="$1"
|
||||
suite="${2:-$latestSuite}"
|
||||
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
|
||||
|
||||
if [ ! "$repo" ]; then
|
||||
echo >&2 "usage: $0 repo [suite [mirror]]"
|
||||
echo >&2 " ie: $0 tianon/debian squeeze"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
target="/tmp/docker-rootfs-debian-$suite-$$-$RANDOM"
|
||||
|
||||
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||
returnTo="$(pwd -P)"
|
||||
|
||||
set -x
|
||||
|
||||
# bootstrap
|
||||
mkdir -p "$target"
|
||||
sudo debootstrap --verbose --variant="$variant" --include="$include" "$suite" "$target" "$mirror"
|
||||
|
||||
cd "$target"
|
||||
|
||||
# create the image
|
||||
img=$(sudo tar -c . | docker import -)
|
||||
|
||||
# tag suite
|
||||
docker tag $img $repo $suite
|
||||
|
||||
if [ "$suite" = "$latestSuite" ]; then
|
||||
# tag latest
|
||||
docker tag $img $repo latest
|
||||
fi
|
||||
|
||||
# test the image
|
||||
docker run -i -t $repo:$suite echo success
|
||||
|
||||
# unstable's version numbers match testing (since it's mostly just a sandbox for testing), so it doesn't get a version number tag
|
||||
if [ "$suite" != "$unstableSuite" -a "$suite" != 'unstable' ]; then
|
||||
# tag the specific version
|
||||
ver=$(docker run $repo:$suite cat /etc/debian_version)
|
||||
docker tag $img $repo $ver
|
||||
fi
|
||||
|
||||
# cleanup
|
||||
cd "$returnTo"
|
||||
sudo rm -rf "$target"
|
||||
@@ -28,6 +28,7 @@ func main() {
|
||||
// FIXME: Switch d and D ? (to be more sshd like)
|
||||
flDaemon := flag.Bool("d", false, "Daemon mode")
|
||||
flDebug := flag.Bool("D", false, "Debug mode")
|
||||
flAutoRestart := flag.Bool("r", false, "Restart previously running containers")
|
||||
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
|
||||
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
||||
flag.Parse()
|
||||
@@ -45,7 +46,7 @@ func main() {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
if err := daemon(*pidfile); err != nil {
|
||||
if err := daemon(*pidfile, *flAutoRestart); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
@@ -82,7 +83,7 @@ func removePidFile(pidfile string) {
|
||||
}
|
||||
}
|
||||
|
||||
func daemon(pidfile string) error {
|
||||
func daemon(pidfile string, autoRestart bool) error {
|
||||
if err := createPidFile(pidfile); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -97,7 +98,7 @@ func daemon(pidfile string) error {
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
service, err := docker.NewServer()
|
||||
service, err := docker.NewServer(autoRestart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
8
graph.go
8
graph.go
@@ -84,18 +84,24 @@ func (graph *Graph) Get(name string) (*Image, error) {
|
||||
}
|
||||
|
||||
// Create creates a new image and registers it in the graph.
|
||||
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string) (*Image, error) {
|
||||
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
|
||||
img := &Image{
|
||||
Id: GenerateId(),
|
||||
Comment: comment,
|
||||
Created: time.Now(),
|
||||
DockerVersion: VERSION,
|
||||
Author: author,
|
||||
Config: config,
|
||||
}
|
||||
if container != nil {
|
||||
img.Parent = container.Image
|
||||
img.Container = container.Id
|
||||
img.ContainerConfig = *container.Config
|
||||
if config == nil {
|
||||
if parentImage, err := graph.Get(container.Image); err == nil && parentImage != nil {
|
||||
img.Config = parentImage.Config
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := graph.Register(layerData, img); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestGraphCreate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
image, err := graph.Create(archive, nil, "Testing", "")
|
||||
image, err := graph.Create(archive, nil, "Testing", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func TestMount(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
image, err := graph.Create(archive, nil, "Testing", "")
|
||||
image, err := graph.Create(archive, nil, "Testing", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -166,7 +166,7 @@ func createTestImage(graph *Graph, t *testing.T) *Image {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img, err := graph.Create(archive, nil, "Test image", "")
|
||||
img, err := graph.Create(archive, nil, "Test image", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -181,7 +181,7 @@ func TestDelete(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertNImages(graph, t, 0)
|
||||
img, err := graph.Create(archive, nil, "Bla bla", "")
|
||||
img, err := graph.Create(archive, nil, "Bla bla", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -192,11 +192,11 @@ func TestDelete(t *testing.T) {
|
||||
assertNImages(graph, t, 0)
|
||||
|
||||
// Test 2 create (same name) / 1 delete
|
||||
img1, err := graph.Create(archive, nil, "Testing", "")
|
||||
img1, err := graph.Create(archive, nil, "Testing", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = graph.Create(archive, nil, "Testing", ""); err != nil {
|
||||
if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertNImages(graph, t, 2)
|
||||
|
||||
1
image.go
1
image.go
@@ -24,6 +24,7 @@ type Image struct {
|
||||
ContainerConfig Config `json:"container_config,omitempty"`
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Config *Config `json:"config,omitempty"`
|
||||
graph *Graph
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
|
||||
lxc-docker (0.2.1-1) precise; urgency=low
|
||||
|
||||
- 'docker commit -run' bundles a layer with default runtime options: command, ports etc.
|
||||
- Improve install process on Vagrant
|
||||
- New Dockerfile operation: "maintainer"
|
||||
- New Dockerfile operation: "expose"
|
||||
- New Dockerfile operation: "cmd"
|
||||
- Contrib script to build a Debian base layer
|
||||
- 'docker -d -r': restart crashed containers at daemon startup
|
||||
- Runtime: improve test coverage
|
||||
|
||||
-- dotCloud <ops@dotcloud.com> Wed, 1 May 2013 00:00:00 -0700
|
||||
|
||||
|
||||
lxc-docker (0.2.0-1) precise; urgency=low
|
||||
|
||||
- Runtime: ghost containers can be killed and waited for
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
node default {
|
||||
exec {
|
||||
"apt_update" :
|
||||
command => "/usr/bin/apt-get update"
|
||||
}
|
||||
|
||||
Package {
|
||||
require => Exec['apt_update']
|
||||
}
|
||||
|
||||
group { "puppet":
|
||||
ensure => "present"
|
||||
}
|
||||
|
||||
include "docker"
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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
|
||||
@@ -1,30 +0,0 @@
|
||||
# ~/.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
|
||||
114
runtime.go
114
runtime.go
@@ -31,6 +31,7 @@ type Runtime struct {
|
||||
idIndex *TruncIndex
|
||||
capabilities *Capabilities
|
||||
kernelVersion *KernelVersionInfo
|
||||
autoRestart bool
|
||||
}
|
||||
|
||||
var sysInitPath string
|
||||
@@ -77,12 +78,58 @@ func (runtime *Runtime) containerRoot(id string) string {
|
||||
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) {
|
||||
|
||||
// Lookup image
|
||||
img, err := runtime.repositories.LookupImage(config.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if img.Config != nil {
|
||||
runtime.mergeConfig(config, img.Config)
|
||||
}
|
||||
|
||||
if config.Cmd == nil {
|
||||
return nil, fmt.Errorf("No command specified")
|
||||
}
|
||||
|
||||
// Generate id
|
||||
id := GenerateId()
|
||||
// Generate default hostname
|
||||
@@ -103,6 +150,7 @@ func (runtime *Runtime) Create(config *Config) (*Container, error) {
|
||||
// FIXME: do we need to store this in the container?
|
||||
SysInitPath: sysInitPath,
|
||||
}
|
||||
|
||||
container.root = runtime.containerRoot(container.Id)
|
||||
// Step 1: create the container directory.
|
||||
// This doubles as a barrier to avoid race conditions.
|
||||
@@ -167,23 +215,6 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||
// init the wait lock
|
||||
container.waitLock = make(chan struct{})
|
||||
|
||||
// FIXME: if the container is supposed to be running but is not, auto restart it?
|
||||
// if so, then we need to restart monitor and init a new lock
|
||||
// If the container is supposed to be running, make sure of it
|
||||
if container.State.Running {
|
||||
if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if !strings.Contains(string(output), "RUNNING") {
|
||||
Debugf("Container %s was supposed to be running be is not.", container.Id)
|
||||
container.State.setStopped(-127)
|
||||
if err := container.ToDisk(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Even if not running, we init the lock (prevents races in start/stop/kill)
|
||||
container.State.initLock()
|
||||
|
||||
@@ -202,11 +233,43 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||
runtime.containers.PushBack(container)
|
||||
runtime.idIndex.Add(container.Id)
|
||||
|
||||
// When we actually restart, Start() do the monitoring.
|
||||
// However, when we simply 'reattach', we have to restart a monitor
|
||||
nomonitor := false
|
||||
|
||||
// FIXME: if the container is supposed to be running but is not, auto restart it?
|
||||
// if so, then we need to restart monitor and init a new lock
|
||||
// If the container is supposed to be running, make sure of it
|
||||
if container.State.Running {
|
||||
if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if !strings.Contains(string(output), "RUNNING") {
|
||||
Debugf("Container %s was supposed to be running be is not.", container.Id)
|
||||
if runtime.autoRestart {
|
||||
Debugf("Restarting")
|
||||
container.State.Ghost = false
|
||||
container.State.setStopped(0)
|
||||
if err := container.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
nomonitor = true
|
||||
} else {
|
||||
Debugf("Marking as stopped")
|
||||
container.State.setStopped(-127)
|
||||
if err := container.ToDisk(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the container is not running or just has been flagged not running
|
||||
// then close the wait lock chan (will be reset upon start)
|
||||
if !container.State.Running {
|
||||
close(container.waitLock)
|
||||
} else {
|
||||
} else if !nomonitor {
|
||||
container.allocateNetwork()
|
||||
go container.monitor()
|
||||
}
|
||||
@@ -249,7 +312,7 @@ func (runtime *Runtime) Destroy(container *Container) error {
|
||||
|
||||
// Commit creates a new filesystem image from the current state of a container.
|
||||
// The image can optionally be tagged into a repository
|
||||
func (runtime *Runtime) Commit(id, repository, tag, comment, author string) (*Image, error) {
|
||||
func (runtime *Runtime) Commit(id, repository, tag, comment, author string, config *Config) (*Image, error) {
|
||||
container := runtime.Get(id)
|
||||
if container == nil {
|
||||
return nil, fmt.Errorf("No such container: %s", id)
|
||||
@@ -261,7 +324,7 @@ func (runtime *Runtime) Commit(id, repository, tag, comment, author string) (*Im
|
||||
return nil, err
|
||||
}
|
||||
// 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)
|
||||
img, err := runtime.graph.Create(rwTar, container, comment, author, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -292,8 +355,8 @@ func (runtime *Runtime) restore() error {
|
||||
}
|
||||
|
||||
// FIXME: harmonize with NewGraph()
|
||||
func NewRuntime() (*Runtime, error) {
|
||||
runtime, err := NewRuntimeFromDirectory("/var/lib/docker")
|
||||
func NewRuntime(autoRestart bool) (*Runtime, error) {
|
||||
runtime, err := NewRuntimeFromDirectory("/var/lib/docker", autoRestart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -314,19 +377,19 @@ func NewRuntime() (*Runtime, error) {
|
||||
_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
|
||||
runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
|
||||
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"))
|
||||
runtime.capabilities.SwapLimit = err == nil
|
||||
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
|
||||
}
|
||||
|
||||
func NewRuntimeFromDirectory(root string) (*Runtime, error) {
|
||||
func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
|
||||
runtimeRepo := path.Join(root, "containers")
|
||||
|
||||
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
||||
@@ -363,6 +426,7 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
|
||||
authConfig: authConfig,
|
||||
idIndex: NewTruncIndex(),
|
||||
capabilities: &Capabilities{},
|
||||
autoRestart: autoRestart,
|
||||
}
|
||||
|
||||
if err := runtime.restore(); err != nil {
|
||||
|
||||
@@ -60,8 +60,10 @@ func init() {
|
||||
panic("docker tests needs to be run as root")
|
||||
}
|
||||
|
||||
NetworkBridgeIface = "testdockbr0"
|
||||
|
||||
// Make it our Store root
|
||||
runtime, err := NewRuntimeFromDirectory(unitTestStoreBase)
|
||||
runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -87,7 +89,7 @@ func newTestRuntime() (*Runtime, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runtime, err := NewRuntimeFromDirectory(root)
|
||||
runtime, err := NewRuntimeFromDirectory(root, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -273,7 +275,16 @@ func TestAllocatePortLocalhost(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Kill()
|
||||
time.Sleep(600 * time.Millisecond) // Wait for the container to run
|
||||
|
||||
setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
|
||||
for {
|
||||
if container.State.Running {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
})
|
||||
|
||||
conn, err := net.Dial("tcp",
|
||||
fmt.Sprintf(
|
||||
"localhost:%s", container.NetworkSettings.PortMapping["5555"],
|
||||
@@ -293,6 +304,7 @@ func TestAllocatePortLocalhost(t *testing.T) {
|
||||
string(output),
|
||||
)
|
||||
}
|
||||
container.Wait()
|
||||
}
|
||||
|
||||
func TestRestore(t *testing.T) {
|
||||
@@ -308,7 +320,7 @@ func TestRestore(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runtime1, err := NewRuntimeFromDirectory(root)
|
||||
runtime1, err := NewRuntimeFromDirectory(root, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -367,7 +379,7 @@ func TestRestore(t *testing.T) {
|
||||
|
||||
// Here are are simulating a docker restart - that is, reloading all containers
|
||||
// from scratch
|
||||
runtime2, err := NewRuntimeFromDirectory(root)
|
||||
runtime2, err := NewRuntimeFromDirectory(root, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
17
utils.go
17
utils.go
@@ -12,7 +12,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -437,17 +436,23 @@ func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
||||
}
|
||||
|
||||
func FindCgroupMountpoint(cgroupType string) (string, error) {
|
||||
output, err := exec.Command("mount").CombinedOutput()
|
||||
output, err := ioutil.ReadFile("/proc/mounts")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
reg := regexp.MustCompile(`^.* on (.*) type cgroup \(.*` + cgroupType + `[,\)]`)
|
||||
// /proc/mounts has 6 fields per line, one mount per line, e.g.
|
||||
// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
|
||||
for _, line := range strings.Split(string(output), "\n") {
|
||||
r := reg.FindStringSubmatch(line)
|
||||
if len(r) == 2 {
|
||||
return r[1], nil
|
||||
parts := strings.Split(line, " ")
|
||||
if parts[2] == "cgroup" {
|
||||
for _, opt := range strings.Split(parts[3], ",") {
|
||||
if opt == cgroupType {
|
||||
return parts[1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user