mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 20:26:54 +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
|
||||||
|
|||||||
-263
@@ -1,263 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Builder struct {
|
|
||||||
runtime *Runtime
|
|
||||||
repositories *TagStore
|
|
||||||
graph *Graph
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBuilder(runtime *Runtime) *Builder {
|
|
||||||
return &Builder{
|
|
||||||
runtime: runtime,
|
|
||||||
graph: runtime.graph,
|
|
||||||
repositories: runtime.repositories,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (builder *Builder) Create(config *Config) (*Container, error) {
|
|
||||||
// Lookup image
|
|
||||||
img, err := builder.repositories.LookupImage(config.Image)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Generate id
|
|
||||||
id := GenerateId()
|
|
||||||
// Generate default hostname
|
|
||||||
// FIXME: the lxc template no longer needs to set a default hostname
|
|
||||||
if config.Hostname == "" {
|
|
||||||
config.Hostname = id[:12]
|
|
||||||
}
|
|
||||||
|
|
||||||
container := &Container{
|
|
||||||
// FIXME: we should generate the ID here instead of receiving it as an argument
|
|
||||||
Id: id,
|
|
||||||
Created: time.Now(),
|
|
||||||
Path: config.Cmd[0],
|
|
||||||
Args: config.Cmd[1:], //FIXME: de-duplicate from config
|
|
||||||
Config: config,
|
|
||||||
Image: img.Id, // Always use the resolved image id
|
|
||||||
NetworkSettings: &NetworkSettings{},
|
|
||||||
// FIXME: do we need to store this in the container?
|
|
||||||
SysInitPath: sysInitPath,
|
|
||||||
}
|
|
||||||
container.root = builder.runtime.containerRoot(container.Id)
|
|
||||||
// Step 1: create the container directory.
|
|
||||||
// This doubles as a barrier to avoid race conditions.
|
|
||||||
if err := os.Mkdir(container.root, 0700); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If custom dns exists, then create a resolv.conf for the container
|
|
||||||
if len(config.Dns) > 0 {
|
|
||||||
container.ResolvConfPath = path.Join(container.root, "resolv.conf")
|
|
||||||
f, err := os.Create(container.ResolvConfPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
for _, dns := range config.Dns {
|
|
||||||
if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
container.ResolvConfPath = "/etc/resolv.conf"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: save the container json
|
|
||||||
if err := container.ToDisk(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Step 3: register the container
|
|
||||||
if err := builder.runtime.Register(container); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return container, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit creates a new filesystem image from the current state of a container.
|
|
||||||
// The image can optionally be tagged into a repository
|
|
||||||
func (builder *Builder) Commit(container *Container, repository, tag, comment, author string) (*Image, error) {
|
|
||||||
// FIXME: freeze the container before copying it to avoid data corruption?
|
|
||||||
// FIXME: this shouldn't be in commands.
|
|
||||||
rwTar, err := container.ExportRw()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Create a new image from the container's base layers + a new layer from container changes
|
|
||||||
img, err := builder.graph.Create(rwTar, container, comment, author)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Register the image if needed
|
|
||||||
if repository != "" {
|
|
||||||
if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil {
|
|
||||||
return img, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return img, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
|
|
||||||
for c := range containers {
|
|
||||||
tmp := builder.runtime.Get(c)
|
|
||||||
builder.runtime.Destroy(tmp)
|
|
||||||
Debugf("Removing container %s", c)
|
|
||||||
}
|
|
||||||
for i := range images {
|
|
||||||
builder.runtime.graph.Delete(i)
|
|
||||||
Debugf("Removing image %s", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, error) {
|
|
||||||
var (
|
|
||||||
image, base *Image
|
|
||||||
tmpContainers map[string]struct{} = make(map[string]struct{})
|
|
||||||
tmpImages map[string]struct{} = make(map[string]struct{})
|
|
||||||
)
|
|
||||||
defer builder.clearTmp(tmpContainers, tmpImages)
|
|
||||||
|
|
||||||
file := bufio.NewReader(dockerfile)
|
|
||||||
for {
|
|
||||||
line, err := file.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
// Skip comments and empty line
|
|
||||||
if len(line) == 0 || line[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tmp := strings.SplitN(line, " ", 2)
|
|
||||||
if len(tmp) != 2 {
|
|
||||||
return nil, fmt.Errorf("Invalid Dockerfile format")
|
|
||||||
}
|
|
||||||
instruction := tmp[0]
|
|
||||||
arguments := tmp[1]
|
|
||||||
switch strings.ToLower(instruction) {
|
|
||||||
case "from":
|
|
||||||
fmt.Fprintf(stdout, "FROM %s\n", arguments)
|
|
||||||
image, err = builder.runtime.repositories.LookupImage(arguments)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case "run":
|
|
||||||
fmt.Fprintf(stdout, "RUN %s\n", arguments)
|
|
||||||
if image == nil {
|
|
||||||
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
|
|
||||||
}
|
|
||||||
config, err := ParseRun([]string{image.Id, "/bin/sh", "-c", arguments}, nil, builder.runtime.capabilities)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the container and start it
|
|
||||||
c, err := builder.Create(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := c.Start(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tmpContainers[c.Id] = struct{}{}
|
|
||||||
|
|
||||||
// Wait for it to finish
|
|
||||||
if result := c.Wait(); result != 0 {
|
|
||||||
return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit the container
|
|
||||||
base, err = builder.Commit(c, "", "", "", "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tmpImages[base.Id] = struct{}{}
|
|
||||||
|
|
||||||
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
|
|
||||||
|
|
||||||
// use the base as the new image
|
|
||||||
image = base
|
|
||||||
|
|
||||||
break
|
|
||||||
case "insert":
|
|
||||||
if image == nil {
|
|
||||||
return nil, fmt.Errorf("Please provide a source image with `from` prior to copy")
|
|
||||||
}
|
|
||||||
tmp = strings.SplitN(arguments, " ", 2)
|
|
||||||
if len(tmp) != 2 {
|
|
||||||
return nil, fmt.Errorf("Invalid INSERT format")
|
|
||||||
}
|
|
||||||
sourceUrl := tmp[0]
|
|
||||||
destPath := tmp[1]
|
|
||||||
fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId())
|
|
||||||
|
|
||||||
file, err := Download(sourceUrl, stdout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer file.Body.Close()
|
|
||||||
|
|
||||||
config, err := ParseRun([]string{base.Id, "echo", "insert", sourceUrl, destPath}, nil, builder.runtime.capabilities)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c, err := builder.Create(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Start(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for echo to finish
|
|
||||||
if result := c.Wait(); result != 0 {
|
|
||||||
return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Inject(file.Body, destPath); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
base, err = builder.Commit(c, "", "", "", "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
|
|
||||||
|
|
||||||
image = base
|
|
||||||
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(stdout, "Skipping unknown instruction %s\n", instruction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if base != nil {
|
|
||||||
// The build is successful, keep the temporary containers and images
|
|
||||||
for i := range tmpImages {
|
|
||||||
delete(tmpImages, i)
|
|
||||||
}
|
|
||||||
for i := range tmpContainers {
|
|
||||||
delete(tmpContainers, i)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(stdout, "Build finished. image id: %s\n", base.ShortId())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(stdout, "An error occured during the build\n")
|
|
||||||
}
|
|
||||||
return base, nil
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
const Dockerfile = `
|
|
||||||
# VERSION 0.1
|
|
||||||
# DOCKER-VERSION 0.1.6
|
|
||||||
|
|
||||||
from docker-ut
|
|
||||||
run sh -c 'echo root:testpass > /tmp/passwd'
|
|
||||||
run mkdir -p /var/run/sshd
|
|
||||||
copy https://raw.github.com/dotcloud/docker/master/CHANGELOG.md /tmp/CHANGELOG.md
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestBuild(t *testing.T) {
|
|
||||||
runtime, err := newTestRuntime()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer nuke(runtime)
|
|
||||||
|
|
||||||
builder := NewBuilder(runtime)
|
|
||||||
|
|
||||||
img, err := builder.Build(strings.NewReader(Dockerfile), &nopWriter{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := builder.Create(
|
|
||||||
&Config{
|
|
||||||
Image: img.Id,
|
|
||||||
Cmd: []string{"cat", "/tmp/passwd"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer runtime.Destroy(container)
|
|
||||||
|
|
||||||
output, err := container.Output()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "root:testpass\n" {
|
|
||||||
t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, "root:testpass\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
container2, err := builder.Create(
|
|
||||||
&Config{
|
|
||||||
Image: img.Id,
|
|
||||||
Cmd: []string{"ls", "-d", "/var/run/sshd"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer runtime.Destroy(container2)
|
|
||||||
|
|
||||||
output, err = container2.Output()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "/var/run/sshd\n" {
|
|
||||||
t.Fatal("/var/run/sshd has not been created")
|
|
||||||
}
|
|
||||||
|
|
||||||
container3, err := builder.Create(
|
|
||||||
&Config{
|
|
||||||
Image: img.Id,
|
|
||||||
Cmd: []string{"cat", "/tmp/CHANGELOG.md"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer runtime.Destroy(container3)
|
|
||||||
|
|
||||||
output, err = container3.Output()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(output) == 0 {
|
|
||||||
t.Fatal("/tmp/CHANGELOG.md has not been copied")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+14
-79
@@ -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
|
||||||
@@ -33,7 +33,6 @@ func (srv *Server) Help() string {
|
|||||||
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
|
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
|
||||||
for _, cmd := range [][]string{
|
for _, cmd := range [][]string{
|
||||||
{"attach", "Attach to a running container"},
|
{"attach", "Attach to a running container"},
|
||||||
{"build", "Build a container from Dockerfile via stdin"},
|
|
||||||
{"commit", "Create a new image from a container's changes"},
|
{"commit", "Create a new image from a container's changes"},
|
||||||
{"diff", "Inspect changes on a container's filesystem"},
|
{"diff", "Inspect changes on a container's filesystem"},
|
||||||
{"export", "Stream the contents of a container as a tar archive"},
|
{"export", "Stream the contents of a container as a tar archive"},
|
||||||
@@ -41,7 +40,6 @@ func (srv *Server) Help() string {
|
|||||||
{"images", "List images"},
|
{"images", "List images"},
|
||||||
{"import", "Create a new filesystem image from the contents of a tarball"},
|
{"import", "Create a new filesystem image from the contents of a tarball"},
|
||||||
{"info", "Display system-wide information"},
|
{"info", "Display system-wide information"},
|
||||||
{"insert", "Insert a file in an image"},
|
|
||||||
{"inspect", "Return low-level information on a container"},
|
{"inspect", "Return low-level information on a container"},
|
||||||
{"kill", "Kill a running container"},
|
{"kill", "Kill a running container"},
|
||||||
{"login", "Register or Login to the docker registry server"},
|
{"login", "Register or Login to the docker registry server"},
|
||||||
@@ -65,67 +63,6 @@ func (srv *Server) Help() string {
|
|||||||
return help
|
return help
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdInsert(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
|
||||||
stdout.Flush()
|
|
||||||
cmd := rcli.Subcmd(stdout, "insert", "IMAGE URL PATH", "Insert a file from URL in the IMAGE at PATH")
|
|
||||||
if err := cmd.Parse(args); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if cmd.NArg() != 3 {
|
|
||||||
cmd.Usage()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
imageId := cmd.Arg(0)
|
|
||||||
url := cmd.Arg(1)
|
|
||||||
path := cmd.Arg(2)
|
|
||||||
|
|
||||||
img, err := srv.runtime.repositories.LookupImage(imageId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
file, err := Download(url, stdout)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Body.Close()
|
|
||||||
|
|
||||||
config, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, nil, srv.runtime.capabilities)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
b := NewBuilder(srv.runtime)
|
|
||||||
c, err := b.Create(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Inject(ProgressReader(file.Body, int(file.ContentLength), stdout, "Downloading %v/%v (%v)"), path); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// FIXME: Handle custom repo, tag comment, author
|
|
||||||
img, err = b.Commit(c, "", "", img.Comment, img.Author)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(stdout, "%s\n", img.Id)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) CmdBuild(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
|
||||||
stdout.Flush()
|
|
||||||
cmd := rcli.Subcmd(stdout, "build", "-", "Build a container from Dockerfile via stdin")
|
|
||||||
if err := cmd.Parse(args); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
img, err := NewBuilder(srv.runtime).Build(stdin, stdout)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(stdout, "%s\n", img.ShortId())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'docker login': login / register a user to registry service.
|
// 'docker login': login / register a user to registry service.
|
||||||
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||||
// Read a line on raw terminal with support for simple backspace
|
// Read a line on raw terminal with support for simple backspace
|
||||||
@@ -540,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
|
||||||
}
|
}
|
||||||
@@ -789,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
|
||||||
}
|
}
|
||||||
@@ -798,12 +736,15 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
container := srv.runtime.Get(containerName)
|
var config *Config
|
||||||
if container == nil {
|
if *flConfig != "" {
|
||||||
return fmt.Errorf("No such container: %s", containerName)
|
config = &Config{}
|
||||||
|
if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err := NewBuilder(srv.runtime).Commit(container, repository, tag, *flComment, *flAuthor)
|
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment, *flAuthor, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -994,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()
|
||||||
@@ -1006,10 +943,8 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
|
|||||||
// or tell the client there is no options
|
// or tell the client there is no options
|
||||||
stdout.Flush()
|
stdout.Flush()
|
||||||
|
|
||||||
b := NewBuilder(srv.runtime)
|
|
||||||
|
|
||||||
// Create new container
|
// Create new container
|
||||||
container, err := b.Create(config)
|
container, err := srv.runtime.Create(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If container not found, try to pull it
|
// If container not found, try to pull it
|
||||||
if srv.runtime.graph.IsNotExist(err) {
|
if srv.runtime.graph.IsNotExist(err) {
|
||||||
@@ -1017,7 +952,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
|
|||||||
if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
|
if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if container, err = b.Create(config); err != nil {
|
if container, err = srv.runtime.Create(config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1064,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
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -339,7 +339,7 @@ func TestAttachDisconnect(t *testing.T) {
|
|||||||
|
|
||||||
srv := &Server{runtime: runtime}
|
srv := &Server{runtime: runtime}
|
||||||
|
|
||||||
container, err := NewBuilder(runtime).Create(
|
container, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Memory: 33554432,
|
Memory: 33554432,
|
||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,23 +168,6 @@ func (settings *NetworkSettings) PortMappingHuman() string {
|
|||||||
return strings.Join(mapping, ", ")
|
return strings.Join(mapping, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject the io.Reader at the given path. Note: do not close the reader
|
|
||||||
func (container *Container) Inject(file io.Reader, pth string) error {
|
|
||||||
// Make sure the directory exists
|
|
||||||
if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// FIXME: Handle permissions/already existing dest
|
|
||||||
dest, err := os.Create(path.Join(container.rwPath(), pth))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(dest, file); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (container *Container) Cmd() *exec.Cmd {
|
func (container *Container) Cmd() *exec.Cmd {
|
||||||
return container.cmd
|
return container.cmd
|
||||||
}
|
}
|
||||||
|
|||||||
+121
-66
@@ -20,11 +20,10 @@ func TestIdFormat(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container1, err := NewBuilder(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 {
|
||||||
@@ -45,12 +44,11 @@ func TestMultipleAttachRestart(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(
|
container, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
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) {
|
||||||
@@ -157,10 +148,8 @@ func TestDiff(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
|
|
||||||
builder := NewBuilder(runtime)
|
|
||||||
|
|
||||||
// Create a container and remove a file
|
// Create a container and remove a file
|
||||||
container1, err := builder.Create(
|
container1, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/bin/rm", "/etc/passwd"},
|
Cmd: []string{"/bin/rm", "/etc/passwd"},
|
||||||
@@ -195,13 +184,13 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new container from the commited image
|
// Create a new container from the commited image
|
||||||
container2, err := builder.Create(
|
container2, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: img.Id,
|
Image: img.Id,
|
||||||
Cmd: []string{"cat", "/etc/passwd"},
|
Cmd: []string{"cat", "/etc/passwd"},
|
||||||
@@ -228,20 +217,16 @@ 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)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
|
container1, err := runtime.Create(
|
||||||
builder := NewBuilder(runtime)
|
|
||||||
|
|
||||||
container1, err := builder.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 {
|
||||||
@@ -263,18 +248,95 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
|
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
|
||||||
|
|
||||||
container2, err := builder.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 {
|
||||||
@@ -318,7 +380,7 @@ func TestStart(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(
|
container, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Memory: 33554432,
|
Memory: 33554432,
|
||||||
@@ -357,11 +419,10 @@ func TestRun(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(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 {
|
||||||
@@ -386,7 +447,7 @@ func TestOutput(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(
|
container, err := runtime.Create(
|
||||||
&Config{
|
&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"echo", "-n", "foobar"},
|
Cmd: []string{"echo", "-n", "foobar"},
|
||||||
@@ -411,7 +472,7 @@ func TestKillDifferentUser(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"tail", "-f", "/etc/resolv.conf"},
|
Cmd: []string{"tail", "-f", "/etc/resolv.conf"},
|
||||||
User: "daemon",
|
User: "daemon",
|
||||||
@@ -459,7 +520,7 @@ func TestKill(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"cat", "/dev/zero"},
|
Cmd: []string{"cat", "/dev/zero"},
|
||||||
},
|
},
|
||||||
@@ -505,9 +566,7 @@ func TestExitCode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
|
|
||||||
builder := NewBuilder(runtime)
|
trueContainer, err := runtime.Create(&Config{
|
||||||
|
|
||||||
trueContainer, err := builder.Create(&Config{
|
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/bin/true", ""},
|
Cmd: []string{"/bin/true", ""},
|
||||||
})
|
})
|
||||||
@@ -522,7 +581,7 @@ func TestExitCode(t *testing.T) {
|
|||||||
t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
|
t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
falseContainer, err := builder.Create(&Config{
|
falseContainer, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/bin/false", ""},
|
Cmd: []string{"/bin/false", ""},
|
||||||
})
|
})
|
||||||
@@ -544,7 +603,7 @@ func TestRestart(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"echo", "-n", "foobar"},
|
Cmd: []string{"echo", "-n", "foobar"},
|
||||||
},
|
},
|
||||||
@@ -577,7 +636,7 @@ func TestRestartStdin(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"cat"},
|
Cmd: []string{"cat"},
|
||||||
|
|
||||||
@@ -656,10 +715,8 @@ func TestUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
|
|
||||||
builder := NewBuilder(runtime)
|
|
||||||
|
|
||||||
// Default user must be root
|
// Default user must be root
|
||||||
container, err := builder.Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"id"},
|
Cmd: []string{"id"},
|
||||||
},
|
},
|
||||||
@@ -677,7 +734,7 @@ func TestUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a username
|
// Set a username
|
||||||
container, err = builder.Create(&Config{
|
container, err = runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"id"},
|
Cmd: []string{"id"},
|
||||||
|
|
||||||
@@ -697,7 +754,7 @@ func TestUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a UID
|
// Set a UID
|
||||||
container, err = builder.Create(&Config{
|
container, err = runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"id"},
|
Cmd: []string{"id"},
|
||||||
|
|
||||||
@@ -717,7 +774,7 @@ func TestUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a different user by uid
|
// Set a different user by uid
|
||||||
container, err = builder.Create(&Config{
|
container, err = runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"id"},
|
Cmd: []string{"id"},
|
||||||
|
|
||||||
@@ -739,7 +796,7 @@ func TestUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a different user by username
|
// Set a different user by username
|
||||||
container, err = builder.Create(&Config{
|
container, err = runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"id"},
|
Cmd: []string{"id"},
|
||||||
|
|
||||||
@@ -766,9 +823,7 @@ func TestMultipleContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
|
|
||||||
builder := NewBuilder(runtime)
|
container1, err := runtime.Create(&Config{
|
||||||
|
|
||||||
container1, err := builder.Create(&Config{
|
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"cat", "/dev/zero"},
|
Cmd: []string{"cat", "/dev/zero"},
|
||||||
},
|
},
|
||||||
@@ -778,7 +833,7 @@ func TestMultipleContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer runtime.Destroy(container1)
|
defer runtime.Destroy(container1)
|
||||||
|
|
||||||
container2, err := builder.Create(&Config{
|
container2, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"cat", "/dev/zero"},
|
Cmd: []string{"cat", "/dev/zero"},
|
||||||
},
|
},
|
||||||
@@ -824,7 +879,7 @@ func TestStdin(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"cat"},
|
Cmd: []string{"cat"},
|
||||||
|
|
||||||
@@ -871,7 +926,7 @@ func TestTty(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"cat"},
|
Cmd: []string{"cat"},
|
||||||
|
|
||||||
@@ -918,7 +973,7 @@ func TestEnv(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/usr/bin/env"},
|
Cmd: []string{"/usr/bin/env"},
|
||||||
},
|
},
|
||||||
@@ -992,7 +1047,7 @@ func TestLXCConfig(t *testing.T) {
|
|||||||
memMin := 33554432
|
memMin := 33554432
|
||||||
memMax := 536870912
|
memMax := 536870912
|
||||||
mem := memMin + rand.Intn(memMax-memMin)
|
mem := memMin + rand.Intn(memMax-memMin)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"/bin/true"},
|
Cmd: []string{"/bin/true"},
|
||||||
|
|
||||||
@@ -1019,7 +1074,7 @@ func BenchmarkRunSequencial(b *testing.B) {
|
|||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"echo", "-n", "foo"},
|
Cmd: []string{"echo", "-n", "foo"},
|
||||||
},
|
},
|
||||||
@@ -1054,7 +1109,7 @@ func BenchmarkRunParallel(b *testing.B) {
|
|||||||
complete := make(chan error)
|
complete := make(chan error)
|
||||||
tasks = append(tasks, complete)
|
tasks = append(tasks, complete)
|
||||||
go func(i int, complete chan error) {
|
go func(i int, complete chan error) {
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"echo", "-n", "foo"},
|
Cmd: []string{"echo", "-n", "foo"},
|
||||||
},
|
},
|
||||||
|
|||||||
+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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
==============
|
|
||||||
Docker Builder
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. contents:: Table of Contents
|
|
||||||
|
|
||||||
1. Format
|
|
||||||
=========
|
|
||||||
|
|
||||||
The Docker builder format is quite simple:
|
|
||||||
|
|
||||||
``instruction arguments``
|
|
||||||
|
|
||||||
The first instruction must be `FROM`
|
|
||||||
|
|
||||||
All instruction are to be placed in a file named `Dockerfile`
|
|
||||||
|
|
||||||
In order to place comments within a Dockerfile, simply prefix the line with "`#`"
|
|
||||||
|
|
||||||
2. Instructions
|
|
||||||
===============
|
|
||||||
|
|
||||||
Docker builder comes with a set of instructions:
|
|
||||||
|
|
||||||
1. FROM: Set from what image to build
|
|
||||||
2. RUN: Execute a command
|
|
||||||
3. INSERT: Insert a remote file (http) into the image
|
|
||||||
|
|
||||||
2.1 FROM
|
|
||||||
--------
|
|
||||||
``FROM <image>``
|
|
||||||
|
|
||||||
The `FROM` instruction must be the first one in order for Builder to know from where to run commands.
|
|
||||||
|
|
||||||
`FROM` can also be used in order to build multiple images within a single Dockerfile
|
|
||||||
|
|
||||||
2.2 RUN
|
|
||||||
-------
|
|
||||||
``RUN <command>``
|
|
||||||
|
|
||||||
The `RUN` instruction is the main one, it allows you to execute any commands on the `FROM` image and to save the results.
|
|
||||||
You can use as many `RUN` as you want within a Dockerfile, the commands will be executed on the result of the previous command.
|
|
||||||
|
|
||||||
2.3 INSERT
|
|
||||||
----------
|
|
||||||
|
|
||||||
``INSERT <file url> <path>``
|
|
||||||
|
|
||||||
The `INSERT` instruction will download the file at the given url and place it within the image at the given path.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
The path must include the file name.
|
|
||||||
|
|
||||||
3. Dockerfile Examples
|
|
||||||
======================
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Nginx
|
|
||||||
#
|
|
||||||
# VERSION 0.0.1
|
|
||||||
# DOCKER-VERSION 0.2
|
|
||||||
|
|
||||||
from ubuntu
|
|
||||||
|
|
||||||
# make sure the package repository is up to date
|
|
||||||
run echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
|
|
||||||
run apt-get update
|
|
||||||
|
|
||||||
run apt-get install -y inotify-tools nginx apache openssh-server
|
|
||||||
insert https://raw.github.com/creack/docker-vps/master/nginx-wrapper.sh /usr/sbin/nginx-wrapper
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Firefox over VNC
|
|
||||||
#
|
|
||||||
# VERSION 0.3
|
|
||||||
# DOCKER-VERSION 0.2
|
|
||||||
|
|
||||||
from ubuntu
|
|
||||||
# make sure the package repository is up to date
|
|
||||||
run echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
|
|
||||||
run apt-get update
|
|
||||||
|
|
||||||
# Install vnc, xvfb in order to create a 'fake' display and firefox
|
|
||||||
run apt-get install -y x11vnc xvfb firefox
|
|
||||||
run mkdir /.vnc
|
|
||||||
# Setup a password
|
|
||||||
run x11vnc -storepasswd 1234 ~/.vnc/passwd
|
|
||||||
# Autostart firefox (might not be the best way to do it, but it does the trick)
|
|
||||||
run bash -c 'echo "firefox" >> /.bashrc'
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
:title: docker documentation
|
|
||||||
:description: Documentation for docker builder
|
|
||||||
:keywords: docker, builder, dockerfile
|
|
||||||
|
|
||||||
|
|
||||||
Builder
|
|
||||||
=======
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
basics
|
|
||||||
@@ -27,7 +27,6 @@ Available Commands
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
command/attach
|
command/attach
|
||||||
command/build
|
|
||||||
command/commit
|
command/commit
|
||||||
command/diff
|
command/diff
|
||||||
command/export
|
command/export
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
===========================================
|
|
||||||
``build`` -- Build a container from Dockerfile via stdin
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
Usage: docker build -
|
|
||||||
Example: cat Dockerfile | docker build -
|
|
||||||
Build a new image from the Dockerfile passed via stdin
|
|
||||||
@@ -15,8 +15,7 @@ This documentation has the following resources:
|
|||||||
examples/index
|
examples/index
|
||||||
contributing/index
|
contributing/index
|
||||||
commandline/index
|
commandline/index
|
||||||
builder/index
|
|
||||||
faq
|
faq
|
||||||
|
|
||||||
|
|
||||||
.. image:: http://www.docker.io/_static/lego_docker.jpg
|
.. image:: http://www.docker.io/_static/lego_docker.jpg
|
||||||
@@ -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
|
|
||||||
+174
-21
@@ -12,6 +12,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Capabilities struct {
|
type Capabilities struct {
|
||||||
@@ -30,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
|
||||||
@@ -76,6 +78,114 @@ 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) {
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// FIXME: the lxc template no longer needs to set a default hostname
|
||||||
|
if config.Hostname == "" {
|
||||||
|
config.Hostname = id[:12]
|
||||||
|
}
|
||||||
|
|
||||||
|
container := &Container{
|
||||||
|
// FIXME: we should generate the ID here instead of receiving it as an argument
|
||||||
|
Id: id,
|
||||||
|
Created: time.Now(),
|
||||||
|
Path: config.Cmd[0],
|
||||||
|
Args: config.Cmd[1:], //FIXME: de-duplicate from config
|
||||||
|
Config: config,
|
||||||
|
Image: img.Id, // Always use the resolved image id
|
||||||
|
NetworkSettings: &NetworkSettings{},
|
||||||
|
// 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.
|
||||||
|
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If custom dns exists, then create a resolv.conf for the container
|
||||||
|
if len(config.Dns) > 0 {
|
||||||
|
container.ResolvConfPath = path.Join(container.root, "resolv.conf")
|
||||||
|
f, err := os.Create(container.ResolvConfPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
for _, dns := range config.Dns {
|
||||||
|
if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
container.ResolvConfPath = "/etc/resolv.conf"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: save the container json
|
||||||
|
if err := container.ToDisk(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Step 3: register the container
|
||||||
|
if err := runtime.Register(container); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Load(id string) (*Container, error) {
|
func (runtime *Runtime) Load(id string) (*Container, error) {
|
||||||
container := &Container{root: runtime.containerRoot(id)}
|
container := &Container{root: runtime.containerRoot(id)}
|
||||||
if err := container.FromDisk(); err != nil {
|
if err := container.FromDisk(); err != nil {
|
||||||
@@ -105,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()
|
||||||
|
|
||||||
@@ -140,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()
|
||||||
}
|
}
|
||||||
@@ -185,6 +310,33 @@ func (runtime *Runtime) Destroy(container *Container) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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, config *Config) (*Image, error) {
|
||||||
|
container := runtime.Get(id)
|
||||||
|
if container == nil {
|
||||||
|
return nil, fmt.Errorf("No such container: %s", id)
|
||||||
|
}
|
||||||
|
// FIXME: freeze the container before copying it to avoid data corruption?
|
||||||
|
// FIXME: this shouldn't be in commands.
|
||||||
|
rwTar, err := container.ExportRw()
|
||||||
|
if err != nil {
|
||||||
|
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, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Register the image if needed
|
||||||
|
if repository != "" {
|
||||||
|
if err := runtime.repositories.Set(repository, tag, img.Id, true); err != nil {
|
||||||
|
return img, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) restore() error {
|
func (runtime *Runtime) restore() error {
|
||||||
dir, err := ioutil.ReadDir(runtime.repository)
|
dir, err := ioutil.ReadDir(runtime.repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -203,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
|
||||||
}
|
}
|
||||||
@@ -237,7 +389,7 @@ func NewRuntime() (*Runtime, error) {
|
|||||||
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) {
|
||||||
@@ -274,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 {
|
||||||
|
|||||||
+25
-18
@@ -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
|
||||||
}
|
}
|
||||||
@@ -116,7 +118,7 @@ func TestRuntimeCreate(t *testing.T) {
|
|||||||
if len(runtime.List()) != 0 {
|
if len(runtime.List()) != 0 {
|
||||||
t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
|
t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
|
||||||
}
|
}
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"ls", "-al"},
|
Cmd: []string{"ls", "-al"},
|
||||||
},
|
},
|
||||||
@@ -163,7 +165,7 @@ func TestDestroy(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"ls", "-al"},
|
Cmd: []string{"ls", "-al"},
|
||||||
},
|
},
|
||||||
@@ -210,10 +212,7 @@ func TestGet(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer nuke(runtime)
|
defer nuke(runtime)
|
||||||
|
container1, err := runtime.Create(&Config{
|
||||||
builder := NewBuilder(runtime)
|
|
||||||
|
|
||||||
container1, err := builder.Create(&Config{
|
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"ls", "-al"},
|
Cmd: []string{"ls", "-al"},
|
||||||
},
|
},
|
||||||
@@ -223,7 +222,7 @@ func TestGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer runtime.Destroy(container1)
|
defer runtime.Destroy(container1)
|
||||||
|
|
||||||
container2, err := builder.Create(&Config{
|
container2, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"ls", "-al"},
|
Cmd: []string{"ls", "-al"},
|
||||||
},
|
},
|
||||||
@@ -233,7 +232,7 @@ func TestGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer runtime.Destroy(container2)
|
defer runtime.Destroy(container2)
|
||||||
|
|
||||||
container3, err := builder.Create(&Config{
|
container3, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"ls", "-al"},
|
Cmd: []string{"ls", "-al"},
|
||||||
},
|
},
|
||||||
@@ -263,7 +262,7 @@ func TestAllocatePortLocalhost(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
container, err := NewBuilder(runtime).Create(&Config{
|
container, err := runtime.Create(&Config{
|
||||||
Image: GetTestImage(runtime).Id,
|
Image: GetTestImage(runtime).Id,
|
||||||
Cmd: []string{"sh", "-c", "echo well hello there | nc -l -p 5555"},
|
Cmd: []string{"sh", "-c", "echo well hello there | nc -l -p 5555"},
|
||||||
PortSpecs: []string{"5555"},
|
PortSpecs: []string{"5555"},
|
||||||
@@ -276,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"],
|
||||||
@@ -296,6 +304,7 @@ func TestAllocatePortLocalhost(t *testing.T) {
|
|||||||
string(output),
|
string(output),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
container.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRestore(t *testing.T) {
|
func TestRestore(t *testing.T) {
|
||||||
@@ -311,15 +320,13 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
builder := NewBuilder(runtime1)
|
|
||||||
|
|
||||||
// Create a container with one instance of docker
|
// Create a container with one instance of docker
|
||||||
container1, err := builder.Create(&Config{
|
container1, err := runtime1.Create(&Config{
|
||||||
Image: GetTestImage(runtime1).Id,
|
Image: GetTestImage(runtime1).Id,
|
||||||
Cmd: []string{"ls", "-al"},
|
Cmd: []string{"ls", "-al"},
|
||||||
},
|
},
|
||||||
@@ -330,7 +337,7 @@ func TestRestore(t *testing.T) {
|
|||||||
defer runtime1.Destroy(container1)
|
defer runtime1.Destroy(container1)
|
||||||
|
|
||||||
// Create a second container meant to be killed
|
// Create a second container meant to be killed
|
||||||
container2, err := builder.Create(&Config{
|
container2, err := runtime1.Create(&Config{
|
||||||
Image: GetTestImage(runtime1).Id,
|
Image: GetTestImage(runtime1).Id,
|
||||||
Cmd: []string{"/bin/cat"},
|
Cmd: []string{"/bin/cat"},
|
||||||
OpenStdin: true,
|
OpenStdin: true,
|
||||||
@@ -372,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"
|
||||||
@@ -154,13 +153,6 @@ func SelfPath() string {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
type nopWriter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *nopWriter) Write(buf []byte) (int, error) {
|
|
||||||
return len(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type nopWriteCloser struct {
|
type nopWriteCloser struct {
|
||||||
io.Writer
|
io.Writer
|
||||||
}
|
}
|
||||||
@@ -444,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