mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-21 05:07:22 +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 |
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# 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)
|
## 0.2.0 (2012-04-23)
|
||||||
- Runtime: ghost containers can be killed and waited for
|
- Runtime: ghost containers can be killed and waited for
|
||||||
* Documentation: update install intructions
|
* Documentation: update install intructions
|
||||||
|
|||||||
Vendored
+26
-47
@@ -1,55 +1,27 @@
|
|||||||
# -*- mode: ruby -*-
|
# -*- mode: ruby -*-
|
||||||
# vi: set ft=ruby :
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
def v10(config)
|
BOX_NAME = "ubuntu"
|
||||||
config.vm.box = 'precise64'
|
BOX_URI = "http://files.vagrantup.com/precise64.box"
|
||||||
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
|
PPA_KEY = "E61D797F63561DC6"
|
||||||
|
|
||||||
# Install ubuntu packaging dependencies and create ubuntu packages
|
Vagrant::Config.run do |config|
|
||||||
config.vm.provision :shell, :inline => "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list"
|
# Setup virtual machine box. This VM configuration code is always executed.
|
||||||
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 = BOX_NAME
|
||||||
end
|
config.vm.box_url = BOX_URI
|
||||||
|
# Add docker PPA key to the local repository and install docker
|
||||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
pkg_cmd = "apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys #{PPA_KEY}; "
|
||||||
v10(config)
|
pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list; "
|
||||||
end
|
pkg_cmd << "apt-get update -qq; apt-get install -q -y lxc-docker"
|
||||||
|
if ARGV.include?("--provider=aws".downcase)
|
||||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("1") do |config|
|
# Add AUFS dependency to amazon's VM
|
||||||
v10(config)
|
pkg_cmd << "; apt-get install linux-image-extra-3.2.0-40-virtual"
|
||||||
|
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"
|
||||||
@@ -75,8 +47,15 @@ Vagrant::VERSION >= "1.2.0" and Vagrant.configure("2") do |config|
|
|||||||
end
|
end
|
||||||
|
|
||||||
config.vm.provider :virtualbox do |vb|
|
config.vm.provider :virtualbox do |vb|
|
||||||
config.vm.box = 'precise64'
|
config.vm.box = BOX_NAME
|
||||||
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
|
config.vm.box_url = BOX_URI
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
+15
-9
@@ -18,7 +18,7 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "0.2.0"
|
const VERSION = "0.2.1"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
GIT_COMMIT string
|
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)")
|
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 {
|
if err != nil {
|
||||||
return err
|
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")
|
"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("run", "", "Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`)
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -734,7 +735,16 @@ 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 *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 {
|
if err != nil {
|
||||||
return err
|
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")
|
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()
|
||||||
@@ -993,11 +999,11 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer() (*Server, error) {
|
func NewServer(autoRestart bool) (*Server, error) {
|
||||||
if runtime.GOARCH != "amd64" {
|
if runtime.GOARCH != "amd64" {
|
||||||
log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Try to avoid the timeoout in destroy. Best effort, don't check error
|
||||||
cStdin, _ := container.StdinPipe()
|
cStdin, _ := container.StdinPipe()
|
||||||
cStdin.Close()
|
cStdin.Close()
|
||||||
|
container.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-27
@@ -22,9 +22,8 @@ func TestIdFormat(t *testing.T) {
|
|||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container1, err := runtime.Create(
|
container1, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
|
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
|
||||||
Memory: 33554432,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -50,7 +49,6 @@ func TestMultipleAttachRestart(t *testing.T) {
|
|||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/bin/sh", "-c",
|
Cmd: []string{"/bin/sh", "-c",
|
||||||
"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"},
|
"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"},
|
||||||
Memory: 33554432,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -116,8 +114,8 @@ func TestMultipleAttachRestart(t *testing.T) {
|
|||||||
if err := container.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
t.Fatal(err)
|
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')
|
l1, err = bufio.NewReader(stdout1).ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -139,15 +137,8 @@ func TestMultipleAttachRestart(t *testing.T) {
|
|||||||
if strings.Trim(l3, " \r\n") != "hello" {
|
if strings.Trim(l3, " \r\n") != "hello" {
|
||||||
t.Fatalf("Unexpected output. Expected [%s], received [%s]", "hello", l3)
|
t.Fatalf("Unexpected output. Expected [%s], received [%s]", "hello", l3)
|
||||||
}
|
}
|
||||||
timeout <- false
|
})
|
||||||
}()
|
container.Wait()
|
||||||
go func() {
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
timeout <- true
|
|
||||||
}()
|
|
||||||
if <-timeout {
|
|
||||||
t.Fatalf("Timeout reading from the process")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDiff(t *testing.T) {
|
func TestDiff(t *testing.T) {
|
||||||
@@ -193,7 +184,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", "")
|
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
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()
|
runtime, err := newTestRuntime()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -234,9 +225,8 @@ func TestCommitRun(t *testing.T) {
|
|||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container1, err := runtime.Create(
|
container1, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
|
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
|
||||||
Memory: 33554432,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -258,7 +248,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", "")
|
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "", &Config{Cmd: []string{"cat", "/world"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -267,9 +257,86 @@ func TestCommitRun(t *testing.T) {
|
|||||||
|
|
||||||
container2, err := runtime.Create(
|
container2, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: img.Id,
|
Image: img.Id,
|
||||||
Memory: 33554432,
|
},
|
||||||
Cmd: []string{"cat", "/world"},
|
)
|
||||||
|
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 {
|
if err != nil {
|
||||||
@@ -354,9 +421,8 @@ func TestRun(t *testing.T) {
|
|||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := runtime.Create(
|
container, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Memory: 33554432,
|
Cmd: []string{"ls", "-al"},
|
||||||
Cmd: []string{"ls", "-al"},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+37
-7
@@ -1,17 +1,22 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"time"
|
"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) {
|
func runDaemon() (*exec.Cmd, error) {
|
||||||
os.Remove("/var/run/docker.pid")
|
os.Remove("/var/run/docker.pid")
|
||||||
|
exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
|
||||||
cmd := exec.Command(DOCKER_PATH, "-d")
|
cmd := exec.Command(DOCKER_PATH, "-d")
|
||||||
outPipe, err := cmd.StdoutPipe()
|
outPipe, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -38,19 +43,43 @@ func crashTest() error {
|
|||||||
return err
|
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 {
|
for {
|
||||||
daemon, err := runDaemon()
|
daemon, err := runDaemon()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
restartCount++
|
||||||
// time.Sleep(5000 * time.Millisecond)
|
// time.Sleep(5000 * time.Millisecond)
|
||||||
var stop bool
|
var stop bool
|
||||||
go func() error {
|
go func() error {
|
||||||
stop = false
|
stop = false
|
||||||
for i := 0; i < 100 && !stop; i++ {
|
for i := 0; i < 100 && !stop; {
|
||||||
func() error {
|
func() error {
|
||||||
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", "hello", "world")
|
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
|
||||||
log.Printf("%d", i)
|
i++
|
||||||
|
totalTestCount++
|
||||||
outPipe, err := cmd.StdoutPipe()
|
outPipe, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -62,9 +91,10 @@ func crashTest() error {
|
|||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
if conn != nil {
|
||||||
io.Copy(os.Stdout, outPipe)
|
go io.Copy(conn, outPipe)
|
||||||
}()
|
}
|
||||||
|
|
||||||
// Expecting error, do not check
|
// Expecting error, do not check
|
||||||
inPipe.Write([]byte("hello world!!!!!\n"))
|
inPipe.Write([]byte("hello world!!!!!\n"))
|
||||||
go 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):
|
def image_exists(img):
|
||||||
return docker(["inspect", img]).read().strip() != ""
|
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()
|
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
|
print "---> Waiting for " + run_id
|
||||||
result=int(docker(["wait", run_id]).read().rstrip())
|
result=int(docker(["wait", run_id]).read().rstrip())
|
||||||
if result != 0:
|
if result != 0:
|
||||||
print "!!! '{}' return non-zero exit code '{}'. Aborting.".format(cmd, result)
|
print "!!! '{}' return non-zero exit code '{}'. Aborting.".format(cmd, result)
|
||||||
sys.exit(1)
|
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)
|
print "COPY {} to {} in {}".format(src, dst, base)
|
||||||
if dst == "":
|
if dst == "":
|
||||||
raise Exception("Missing destination path")
|
raise Exception("Missing destination path")
|
||||||
stdin = file(src)
|
stdin = file(src)
|
||||||
stdin.seek(0)
|
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():
|
def main():
|
||||||
base=""
|
base=""
|
||||||
|
maintainer=""
|
||||||
steps = []
|
steps = []
|
||||||
try:
|
try:
|
||||||
for line in sys.stdin.readlines():
|
for line in sys.stdin.readlines():
|
||||||
@@ -77,22 +87,46 @@ def main():
|
|||||||
if line == "" or line[0] == "#":
|
if line == "" or line[0] == "#":
|
||||||
continue
|
continue
|
||||||
op, param = line.split(" ", 1)
|
op, param = line.split(" ", 1)
|
||||||
|
print op.upper() + " " + param
|
||||||
if op == "from":
|
if op == "from":
|
||||||
print "FROM " + param
|
|
||||||
base = param
|
base = param
|
||||||
steps.append(base)
|
steps.append(base)
|
||||||
|
elif op == "maintainer":
|
||||||
|
maintainer = param
|
||||||
elif op == "run":
|
elif op == "run":
|
||||||
print "RUN " + param
|
result = run_and_commit(base, param, author=maintainer)
|
||||||
result = run_and_commit(base, param)
|
|
||||||
steps.append(result)
|
steps.append(result)
|
||||||
base = result
|
base = result
|
||||||
print "===> " + base
|
print "===> " + base
|
||||||
elif op == "copy":
|
elif op == "copy":
|
||||||
src, dst = param.split(" ", 1)
|
src, dst = param.split(" ", 1)
|
||||||
result = insert(base, src, dst)
|
result = insert(base, src, dst, author=maintainer)
|
||||||
steps.append(result)
|
steps.append(result)
|
||||||
base = result
|
base = result
|
||||||
print "===> " + base
|
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:
|
else:
|
||||||
print "Skipping uknown op " + op
|
print "Skipping uknown op " + op
|
||||||
except:
|
except:
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# Start build from a know base image
|
# Start build from a know base image
|
||||||
|
maintainer Solomon Hykes <solomon@dotcloud.com>
|
||||||
from base:ubuntu-12.10
|
from base:ubuntu-12.10
|
||||||
# Update ubuntu sources
|
# Update ubuntu sources
|
||||||
run echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
|
run echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
|
||||||
run apt-get update
|
run apt-get update
|
||||||
# Install system packages
|
# Install system packages
|
||||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
|
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 curl
|
||||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
|
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
|
||||||
# Insert files from the host (./myscript must be present in the current directory)
|
# 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
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo hello, world!
|
||||||
Executable
+61
@@ -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"
|
||||||
+4
-3
@@ -28,6 +28,7 @@ func main() {
|
|||||||
// FIXME: Switch d and D ? (to be more sshd like)
|
// FIXME: Switch d and D ? (to be more sshd like)
|
||||||
flDaemon := flag.Bool("d", false, "Daemon mode")
|
flDaemon := flag.Bool("d", false, "Daemon mode")
|
||||||
flDebug := flag.Bool("D", false, "Debug 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")
|
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
|
||||||
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -45,7 +46,7 @@ func main() {
|
|||||||
flag.Usage()
|
flag.Usage()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := daemon(*pidfile); err != nil {
|
if err := daemon(*pidfile, *flAutoRestart); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
if err := createPidFile(pidfile); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -97,7 +98,7 @@ func daemon(pidfile string) error {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
service, err := docker.NewServer()
|
service, err := docker.NewServer(autoRestart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,18 +84,24 @@ 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) (*Image, error) {
|
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*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
|
||||||
img.Container = container.Id
|
img.Container = container.Id
|
||||||
img.ContainerConfig = *container.Config
|
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 {
|
if err := graph.Register(layerData, img); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+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", "")
|
image, err := graph.Create(archive, nil, "Testing", "", nil)
|
||||||
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", "")
|
image, err := graph.Create(archive, nil, "Testing", "", nil)
|
||||||
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", "")
|
img, err := graph.Create(archive, nil, "Test image", "", nil)
|
||||||
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", "")
|
img, err := graph.Create(archive, nil, "Bla bla", "", nil)
|
||||||
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", "")
|
img1, err := graph.Create(archive, nil, "Testing", "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assertNImages(graph, t, 2)
|
assertNImages(graph, t, 2)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
lxc-docker (0.2.0-1) precise; urgency=low
|
||||||
|
|
||||||
- Runtime: ghost containers can be killed and waited for
|
- 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
|
|
||||||
+89
-25
@@ -31,6 +31,7 @@ type Runtime struct {
|
|||||||
idIndex *TruncIndex
|
idIndex *TruncIndex
|
||||||
capabilities *Capabilities
|
capabilities *Capabilities
|
||||||
kernelVersion *KernelVersionInfo
|
kernelVersion *KernelVersionInfo
|
||||||
|
autoRestart bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var sysInitPath string
|
var sysInitPath string
|
||||||
@@ -77,12 +78,58 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if img.Config != nil {
|
||||||
|
runtime.mergeConfig(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
|
||||||
@@ -103,6 +150,7 @@ 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.
|
||||||
@@ -167,23 +215,6 @@ func (runtime *Runtime) Register(container *Container) error {
|
|||||||
// init the wait lock
|
// init the wait lock
|
||||||
container.waitLock = make(chan struct{})
|
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)
|
// Even if not running, we init the lock (prevents races in start/stop/kill)
|
||||||
container.State.initLock()
|
container.State.initLock()
|
||||||
|
|
||||||
@@ -202,11 +233,43 @@ func (runtime *Runtime) Register(container *Container) error {
|
|||||||
runtime.containers.PushBack(container)
|
runtime.containers.PushBack(container)
|
||||||
runtime.idIndex.Add(container.Id)
|
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
|
// If the container is not running or just has been flagged not running
|
||||||
// then close the wait lock chan (will be reset upon start)
|
// then close the wait lock chan (will be reset upon start)
|
||||||
if !container.State.Running {
|
if !container.State.Running {
|
||||||
close(container.waitLock)
|
close(container.waitLock)
|
||||||
} else {
|
} else if !nomonitor {
|
||||||
container.allocateNetwork()
|
container.allocateNetwork()
|
||||||
go container.monitor()
|
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.
|
// 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) (*Image, error) {
|
func (runtime *Runtime) Commit(id, repository, tag, comment, author string, config *Config) (*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)
|
||||||
@@ -261,7 +324,7 @@ func (runtime *Runtime) Commit(id, repository, tag, comment, author string) (*Im
|
|||||||
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)
|
img, err := runtime.graph.Create(rwTar, container, comment, author, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -292,8 +355,8 @@ func (runtime *Runtime) restore() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: harmonize with NewGraph()
|
// FIXME: harmonize with NewGraph()
|
||||||
func NewRuntime() (*Runtime, error) {
|
func NewRuntime(autoRestart bool) (*Runtime, error) {
|
||||||
runtime, err := NewRuntimeFromDirectory("/var/lib/docker")
|
runtime, err := NewRuntimeFromDirectory("/var/lib/docker", autoRestart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -314,19 +377,19 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuntimeFromDirectory(root string) (*Runtime, error) {
|
func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
|
||||||
runtimeRepo := path.Join(root, "containers")
|
runtimeRepo := path.Join(root, "containers")
|
||||||
|
|
||||||
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
||||||
@@ -363,6 +426,7 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
|
|||||||
authConfig: authConfig,
|
authConfig: authConfig,
|
||||||
idIndex: NewTruncIndex(),
|
idIndex: NewTruncIndex(),
|
||||||
capabilities: &Capabilities{},
|
capabilities: &Capabilities{},
|
||||||
|
autoRestart: autoRestart,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := runtime.restore(); err != nil {
|
if err := runtime.restore(); err != nil {
|
||||||
|
|||||||
+17
-5
@@ -60,8 +60,10 @@ 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, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -87,7 +89,7 @@ func newTestRuntime() (*Runtime, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime, err := NewRuntimeFromDirectory(root)
|
runtime, err := NewRuntimeFromDirectory(root, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -273,7 +275,16 @@ func TestAllocatePortLocalhost(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer container.Kill()
|
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",
|
conn, err := net.Dial("tcp",
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"localhost:%s", container.NetworkSettings.PortMapping["5555"],
|
"localhost:%s", container.NetworkSettings.PortMapping["5555"],
|
||||||
@@ -293,6 +304,7 @@ func TestAllocatePortLocalhost(t *testing.T) {
|
|||||||
string(output),
|
string(output),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
container.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRestore(t *testing.T) {
|
func TestRestore(t *testing.T) {
|
||||||
@@ -308,7 +320,7 @@ func TestRestore(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime1, err := NewRuntimeFromDirectory(root)
|
runtime1, err := NewRuntimeFromDirectory(root, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -367,7 +379,7 @@ func TestRestore(t *testing.T) {
|
|||||||
|
|
||||||
// Here are are simulating a docker restart - that is, reloading all containers
|
// Here are are simulating a docker restart - that is, reloading all containers
|
||||||
// from scratch
|
// from scratch
|
||||||
runtime2, err := NewRuntimeFromDirectory(root)
|
runtime2, err := NewRuntimeFromDirectory(root, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -437,17 +436,23 @@ func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FindCgroupMountpoint(cgroupType string) (string, error) {
|
func FindCgroupMountpoint(cgroupType string) (string, error) {
|
||||||
output, err := exec.Command("mount").CombinedOutput()
|
output, err := ioutil.ReadFile("/proc/mounts")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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") {
|
for _, line := range strings.Split(string(output), "\n") {
|
||||||
r := reg.FindStringSubmatch(line)
|
parts := strings.Split(line, " ")
|
||||||
if len(r) == 2 {
|
if parts[2] == "cgroup" {
|
||||||
return r[1], nil
|
for _, opt := range strings.Split(parts[3], ",") {
|
||||||
|
if opt == cgroupType {
|
||||||
|
return parts[1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
|
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user