rebase master

This commit is contained in:
Victor Vieux 2013-09-09 22:11:53 +00:00
commit bd847f66c6
15 changed files with 457 additions and 284 deletions

View file

@ -321,7 +321,7 @@ func TestGetContainersJSON(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
})
@ -357,10 +357,8 @@ func TestGetContainersExport(t *testing.T) {
srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file
container, err := builder.Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
@ -409,10 +407,8 @@ func TestGetContainersChanges(t *testing.T) {
srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file
container, err := builder.Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"},
@ -458,9 +454,7 @@ func TestGetContainersTop(t *testing.T) {
srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
container, err := builder.Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "cat"},
@ -541,10 +535,8 @@ func TestGetContainersByName(t *testing.T) {
srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file
container, err := builder.Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
@ -574,10 +566,9 @@ func TestPostCommit(t *testing.T) {
srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file
container, err := builder.Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
@ -671,7 +662,7 @@ func TestPostContainersKill(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
@ -713,7 +704,7 @@ func TestPostContainersRestart(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
@ -767,7 +758,7 @@ func TestPostContainersStart(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
@ -817,7 +808,7 @@ func TestPostContainersStop(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
@ -864,7 +855,7 @@ func TestPostContainersWait(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sleep", "1"},
@ -906,7 +897,7 @@ func TestPostContainersAttach(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
@ -998,7 +989,7 @@ func TestDeleteContainers(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
})
@ -1185,10 +1176,8 @@ func TestPostContainersCopy(t *testing.T) {
srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file
container, err := builder.Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test.txt"},

View file

@ -1,181 +0,0 @@
package docker
import (
"fmt"
"github.com/dotcloud/docker/utils"
"io/ioutil"
"os"
"path"
"time"
)
var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
type Builder struct {
runtime *Runtime
repositories *TagStore
graph *Graph
config *Config
image *Image
}
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
}
if img.Config != nil {
MergeConfig(config, img.Config)
}
if len(config.Entrypoint) != 0 && config.Cmd == nil {
config.Cmd = []string{}
} else if config.Cmd == nil || len(config.Cmd) == 0 {
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]
}
var args []string
var entrypoint string
if len(config.Entrypoint) != 0 {
entrypoint = config.Entrypoint[0]
args = append(config.Entrypoint[1:], config.Cmd...)
} else {
entrypoint = config.Cmd[0]
args = config.Cmd[1:]
}
container := &Container{
// FIXME: we should generate the ID here instead of receiving it as an argument
ID: id,
Created: time.Now(),
Path: entrypoint,
Args: args, //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
}
resolvConf, err := utils.GetResolvConf()
if err != nil {
return nil, err
}
if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
builder.runtime.Dns = defaultDns
}
// If custom dns exists, then create a resolv.conf for the container
if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 {
var dns []string
if len(config.Dns) > 0 {
dns = config.Dns
} else {
dns = builder.runtime.Dns
}
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 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: if hostname, build hostname and hosts files
container.HostnamePath = path.Join(container.root, "hostname")
ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
hostsContent := []byte(`
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
`)
container.HostsPath = path.Join(container.root, "hosts")
if container.Config.Domainname != "" {
hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
} else {
hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s\n", container.Config.Hostname)), hostsContent...)
hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s\n", container.Config.Hostname)), hostsContent...)
}
ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
// Step 4: 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, config *Config) (*Image, error) {
// FIXME: freeze the container before copying it to avoid data corruption?
// FIXME: this shouldn't be in commands.
if err := container.EnsureMounted(); err != nil {
return nil, err
}
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, config)
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
}

View file

@ -23,7 +23,6 @@ type BuildFile interface {
type buildFile struct {
runtime *Runtime
builder *Builder
srv *Server
image string
@ -337,7 +336,7 @@ func (b *buildFile) CmdAdd(args string) error {
b.config.Image = b.image
// Create the container and start it
container, err := b.builder.Create(b.config)
container, err := b.runtime.Create(b.config)
if err != nil {
return err
}
@ -372,7 +371,7 @@ func (b *buildFile) run() (string, error) {
b.config.Image = b.image
// Create the container and start it
c, err := b.builder.Create(b.config)
c, err := b.runtime.Create(b.config)
if err != nil {
return "", err
}
@ -428,7 +427,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
}
}
container, err := b.builder.Create(b.config)
container, err := b.runtime.Create(b.config)
if err != nil {
return err
}
@ -450,7 +449,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
autoConfig := *b.config
autoConfig.Cmd = autoCmd
// Commit the container
image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig)
image, err := b.runtime.Commit(container, "", "", "", b.maintainer, &autoConfig)
if err != nil {
return err
}
@ -524,7 +523,6 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildFile {
return &buildFile{
builder: NewBuilder(srv.runtime),
runtime: srv.runtime,
srv: srv,
config: &Config{},

View file

@ -367,16 +367,11 @@ func (cli *DockerCli) CmdWait(args ...string) error {
return nil
}
for _, name := range cmd.Args() {
body, _, err := cli.call("POST", "/containers/"+name+"/wait", nil)
status, err := waitForExit(cli, name)
if err != nil {
fmt.Fprintf(cli.err, "%s", err)
} else {
var out APIWait
err = json.Unmarshal(body, &out)
if err != nil {
return err
}
fmt.Fprintf(cli.out, "%d\n", out.StatusCode)
fmt.Fprintf(cli.out, "%d\n", status)
}
}
return nil
@ -1477,8 +1472,18 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
if !config.AttachStdout && !config.AttachStderr {
// Detached mode
<-wait
} else {
status, err := waitForExit(cli, runResult.ID)
if err != nil {
return err
}
if status != 0 {
return &utils.StatusError{status}
}
}
return nil
}
@ -1759,6 +1764,19 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
return err
}
func waitForExit(cli *DockerCli, containerId string) (int, error) {
body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
if err != nil {
return -1, err
}
var out APIWait
if err := json.Unmarshal(body, &out); err != nil {
return -1, err
}
return out.StatusCode, nil
}
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
var (
isTerminal = false

View file

@ -18,7 +18,7 @@ import (
func TestIDFormat(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container1, err := NewBuilder(runtime).Create(
container1, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
@ -388,7 +388,7 @@ func TestRun(t *testing.T) {
func TestOutput(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
@ -411,7 +411,7 @@ func TestKillDifferentUser(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
OpenStdin: true,
@ -471,7 +471,7 @@ func TestCreateVolume(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c, err := NewBuilder(runtime).Create(config)
c, err := runtime.Create(config)
if err != nil {
t.Fatal(err)
}
@ -486,7 +486,7 @@ func TestCreateVolume(t *testing.T) {
func TestKill(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
@ -530,9 +530,7 @@ func TestExitCode(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
builder := NewBuilder(runtime)
trueContainer, err := builder.Create(&Config{
trueContainer, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true", ""},
})
@ -547,7 +545,7 @@ func TestExitCode(t *testing.T) {
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,
Cmd: []string{"/bin/false", ""},
})
@ -566,7 +564,7 @@ func TestExitCode(t *testing.T) {
func TestRestart(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
},
@ -596,7 +594,7 @@ func TestRestart(t *testing.T) {
func TestRestartStdin(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
@ -673,10 +671,8 @@ func TestUser(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
builder := NewBuilder(runtime)
// Default user must be root
container, err := builder.Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
},
@ -694,7 +690,7 @@ func TestUser(t *testing.T) {
}
// Set a username
container, err = builder.Create(&Config{
container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -714,7 +710,7 @@ func TestUser(t *testing.T) {
}
// Set a UID
container, err = builder.Create(&Config{
container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -734,7 +730,7 @@ func TestUser(t *testing.T) {
}
// Set a different user by uid
container, err = builder.Create(&Config{
container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -756,7 +752,7 @@ func TestUser(t *testing.T) {
}
// Set a different user by username
container, err = builder.Create(&Config{
container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -776,7 +772,7 @@ func TestUser(t *testing.T) {
}
// Test an wrong username
container, err = builder.Create(&Config{
container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -797,9 +793,7 @@ func TestMultipleContainers(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
builder := NewBuilder(runtime)
container1, err := builder.Create(&Config{
container1, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
@ -809,7 +803,7 @@ func TestMultipleContainers(t *testing.T) {
}
defer runtime.Destroy(container1)
container2, err := builder.Create(&Config{
container2, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
@ -853,7 +847,7 @@ func TestMultipleContainers(t *testing.T) {
func TestStdin(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
@ -898,7 +892,7 @@ func TestStdin(t *testing.T) {
func TestTty(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
@ -943,7 +937,7 @@ func TestTty(t *testing.T) {
func TestEnv(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"env"},
},
@ -992,7 +986,7 @@ func TestEnv(t *testing.T) {
func TestEntrypoint(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Entrypoint: []string{"/bin/echo"},
@ -1015,7 +1009,7 @@ func TestEntrypoint(t *testing.T) {
func TestEntrypointNoCmd(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Entrypoint: []string{"/bin/echo", "foobar"},
@ -1066,7 +1060,7 @@ func TestLXCConfig(t *testing.T) {
cpuMin := 100
cpuMax := 10000
cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true"},
@ -1090,7 +1084,7 @@ func TestLXCConfig(t *testing.T) {
func TestCustomLxcConfig(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true"},
@ -1121,7 +1115,7 @@ func BenchmarkRunSequencial(b *testing.B) {
runtime := mkRuntime(b)
defer nuke(runtime)
for i := 0; i < b.N; i++ {
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
@ -1153,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
complete := make(chan error)
tasks = append(tasks, complete)
go func(i int, complete chan error) {
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
@ -1229,7 +1223,7 @@ func TestBindMounts(t *testing.T) {
func TestVolumesFromReadonlyMount(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
@ -1248,7 +1242,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
t.Fail()
}
container2, err := NewBuilder(runtime).Create(
container2, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
@ -1284,7 +1278,7 @@ func TestRestartWithVolumes(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
Volumes: map[string]struct{}{"/test": {}},
@ -1327,7 +1321,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
Volumes: map[string]struct{}{"/test": {}},
@ -1354,7 +1348,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
t.Fail()
}
container2, err := NewBuilder(runtime).Create(
container2, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/test/foo"},
@ -1395,7 +1389,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c, err := NewBuilder(runtime).Create(config)
c, err := runtime.Create(config)
if err != nil {
t.Fatal(err)
}

View file

@ -75,6 +75,9 @@ func main() {
}
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
if sterr, ok := err.(*utils.StatusError); ok {
os.Exit(sterr.Status)
}
log.Fatal(err)
os.Exit(-1)
}

View file

@ -175,8 +175,6 @@ and we will add the libraries here.
+======================+================+============================================+
| Python | docker-py | https://github.com/dotcloud/docker-py |
+----------------------+----------------+--------------------------------------------+
| Ruby | docker-ruby | https://github.com/ActiveState/docker-ruby |
+----------------------+----------------+--------------------------------------------+
| Ruby | docker-client | https://github.com/geku/docker-client |
+----------------------+----------------+--------------------------------------------+
| Ruby | docker-api | https://github.com/swipely/docker-api |
@ -193,3 +191,5 @@ and we will add the libraries here.
+----------------------+----------------+--------------------------------------------+
| Erlang | erldocker | https://github.com/proger/erldocker |
+----------------------+----------------+--------------------------------------------+
| Go | go-dockerclient| https://github.com/fsouza/go-dockerclient |
+----------------------+----------------+--------------------------------------------+

View file

@ -22,3 +22,4 @@ Contents:
couchdb_data_volumes
postgresql_service
mongodb
running_riak_service

View file

@ -0,0 +1,151 @@
:title: Running a Riak service
:description: Build a Docker image with Riak pre-installed
:keywords: docker, example, package installation, networking, riak
Riak Service
==============================
.. include:: example_header.inc
The goal of this example is to show you how to build a Docker image with Riak
pre-installed.
Creating a ``Dockerfile``
+++++++++++++++++++++++++
Create an empty file called ``Dockerfile``:
.. code-block:: bash
touch Dockerfile
Next, define the parent image you want to use to build your image on top of.
Well use `Ubuntu <https://index.docker.io/_/ubuntu/>`_ (tag: ``latest``),
which is available on the `docker index <http://index.docker.io>`_:
.. code-block:: bash
# Riak
#
# VERSION 0.1.0
# Use the Ubuntu base image provided by dotCloud
FROM ubuntu:latest
MAINTAINER Hector Castro hector@basho.com
Next, we update the APT cache and apply any updates:
.. code-block:: bash
# Update the APT cache
RUN sed -i.bak 's/main$/main universe/' /etc/apt/sources.list
RUN apt-get update
RUN apt-get upgrade -y
After that, we install and setup a few dependencies:
- ``curl`` is used to download Basho's APT repository key
- ``lsb-release`` helps us derive the Ubuntu release codename
- ``openssh-server`` allows us to login to containers remotely and join Riak
nodes to form a cluster
- ``supervisor`` is used manage the OpenSSH and Riak processes
.. code-block:: bash
# Install and setup project dependencies
RUN apt-get install -y curl lsb-release supervisor openssh-server
RUN mkdir -p /var/run/sshd
RUN mkdir -p /var/log/supervisor
RUN locale-gen en_US en_US.UTF-8
ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN echo 'root:basho' | chpasswd
Next, we add Basho's APT repository:
.. code-block:: bash
RUN curl -s http://apt.basho.com/gpg/basho.apt.key | apt-key add --
RUN echo "deb http://apt.basho.com $(lsb_release -cs) main" > /etc/apt/sources.list.d/basho.list
RUN apt-get update
After that, we install Riak and alter a few defaults:
.. code-block:: bash
# Install Riak and prepare it to run
RUN apt-get install -y riak
RUN sed -i.bak 's/127.0.0.1/0.0.0.0/' /etc/riak/app.config
RUN echo "ulimit -n 4096" >> /etc/default/riak
Almost there. Next, we add a hack to get us by the lack of ``initctl``:
.. code-block:: bash
# Hack for initctl
# See: https://github.com/dotcloud/docker/issues/1024
RUN dpkg-divert --local --rename --add /sbin/initctl
RUN ln -s /bin/true /sbin/initctl
Then, we expose the Riak Protocol Buffers and HTTP interfaces, along with SSH:
.. code-block:: bash
# Expose Riak Protocol Buffers and HTTP interfaces, along with SSH
EXPOSE 8087 8098 22
Finally, run ``supervisord`` so that Riak and OpenSSH are started:
.. code-block:: bash
CMD ["/usr/bin/supervisord"]
Create a ``supervisord`` configuration file
+++++++++++++++++++++++++++++++++++++++++++
Create an empty file called ``supervisord.conf``. Make sure it's at the same
level as your ``Dockerfile``:
.. code-block:: bash
touch supervisord.conf
Populate it with the following program definitions:
.. code-block:: bash
[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=true
[program:riak]
command=bash -c ". /etc/default/riak && /usr/sbin/riak console"
pidfile=/var/log/riak/riak.pid
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
Build the Docker image for Riak
+++++++++++++++++++++++++++++++
Now you should be able to build a Docker image for Riak:
.. code-block:: bash
docker build -t "<yourname>/riak" .
Next steps
++++++++++
Riak is a distributed database. Many production deployments consist of `at
least five nodes <http://basho.com/why-your-riak-cluster-should-have-at-least-
five-nodes/>`_. See the `docker-riak <https://github.com/hectcastro /docker-
riak>`_ project details on how to deploy a Riak cluster using Docker and
Pipework.

View file

@ -11,7 +11,10 @@ Gentoo Linux
.. include:: install_unofficial.inc
Installing Docker on Gentoo Linux can be accomplished by using the overlay provided at https://github.com/tianon/docker-overlay. The most up-to-date documentation for properly installing the overlay can be found in the overlay README. The information here is provided for reference, and may be out of date.
Installing Docker on Gentoo Linux can be accomplished by using the overlay
provided at https://github.com/tianon/docker-overlay. The most up-to-date
documentation for properly installing the overlay can be found in the overlay
README. The information here is provided for reference, and may be out of date.
Installation
^^^^^^^^^^^^
@ -22,30 +25,49 @@ Ensure that layman is installed:
sudo emerge -av app-portage/layman
Using your favorite editor, add ``https://raw.github.com/tianon/docker-overlay/master/repositories.xml`` to the ``overlays`` section in ``/etc/layman/layman.cfg`` (as per instructions on the `Gentoo Wiki <http://wiki.gentoo.org/wiki/Layman#Adding_custom_overlays>`_), then invoke the following:
Using your favorite editor, add
``https://raw.github.com/tianon/docker-overlay/master/repositories.xml`` to the
``overlays`` section in ``/etc/layman/layman.cfg`` (as per instructions on the
`Gentoo Wiki <http://wiki.gentoo.org/wiki/Layman#Adding_custom_overlays>`_),
then invoke the following:
.. code-block:: bash
sudo layman -f -a docker
Once that completes, the ``app-emulation/lxc-docker`` package will be available for emerge:
Once that completes, the ``app-emulation/docker`` package will be available
for emerge:
.. code-block:: bash
sudo emerge -av app-emulation/lxc-docker
sudo emerge -av app-emulation/docker
If you prefer to use the official binaries, or just do not wish to compile docker, emerge ``app-emulation/lxc-docker-bin`` instead. It is important to remember that Gentoo is still an unsupported platform, even when using the official binaries.
If you prefer to use the official binaries, or just do not wish to compile
docker, emerge ``app-emulation/docker-bin`` instead. It is important to
remember that Gentoo is still an unsupported platform, even when using the
official binaries.
The package should already include all the necessary dependencies. For the simplest installation experience, use ``sys-kernel/aufs-sources`` directly as your kernel sources. If you prefer not to use ``sys-kernel/aufs-sources``, the portage tree also contains ``sys-fs/aufs3``, which contains the patches necessary for adding AUFS support to other kernel source packages (and a ``kernel-patch`` use flag to perform the patching automatically).
The package should already include all the necessary dependencies. For the
simplest installation experience, use ``sys-kernel/aufs-sources`` directly as
your kernel sources. If you prefer not to use ``sys-kernel/aufs-sources``, the
portage tree also contains ``sys-fs/aufs3``, which contains the patches
necessary for adding AUFS support to other kernel source packages (and a
``kernel-patch`` use flag to perform the patching automatically).
Between ``app-emulation/lxc`` and ``app-emulation/lxc-docker``, all the necessary kernel configuration flags should be checked for and warned about in the standard manner.
Between ``app-emulation/lxc`` and ``app-emulation/docker``, all the
necessary kernel configuration flags should be checked for and warned about in
the standard manner.
If any issues arise from this ebuild or the resulting binary, including and especially missing kernel configuration flags and/or dependencies, `open an issue <https://github.com/tianon/docker-overlay/issues>`_ on the docker-overlay repository or ping tianon in the #docker IRC channel.
If any issues arise from this ebuild or the resulting binary, including and
especially missing kernel configuration flags and/or dependencies, `open an
issue <https://github.com/tianon/docker-overlay/issues>`_ on the docker-overlay
repository or ping tianon in the #docker IRC channel.
Starting Docker
^^^^^^^^^^^^^^^
Ensure that you are running a kernel that includes the necessary AUFS support and includes all the necessary modules and/or configuration for LXC.
Ensure that you are running a kernel that includes the necessary AUFS support
and includes all the necessary modules and/or configuration for LXC.
OpenRC
------
@ -80,7 +102,8 @@ To start on system boot:
Network Configuration
^^^^^^^^^^^^^^^^^^^^^
IPv4 packet forwarding is disabled by default, so internet access from inside the container will not work unless ``net.ipv4.ip_forward`` is enabled:
IPv4 packet forwarding is disabled by default, so internet access from inside
the container will not work unless ``net.ipv4.ip_forward`` is enabled:
.. code-block:: bash
@ -95,4 +118,8 @@ Or, to enable it more permanently:
fork/exec /usr/sbin/lxc-start: operation not permitted
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Unfortunately, Gentoo suffers from `issue #1422 <https://github.com/dotcloud/docker/issues/1422>`_, meaning that after every fresh start of docker, the first docker run fails due to some tricky terminal issues, so be sure to run something trivial (such as ``docker run -i -t busybox echo hi``) before attempting to run anything important.
Unfortunately, Gentoo suffers from `issue #1422
<https://github.com/dotcloud/docker/issues/1422>`_, meaning that after every
fresh start of docker, the first docker run fails due to some tricky terminal
issues, so be sure to run something trivial (such as ``docker run -i -t busybox
echo hi``) before attempting to run anything important.

View file

@ -12,8 +12,11 @@ import (
"path"
"sort"
"strings"
"time"
)
var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
type Capabilities struct {
MemoryLimit bool
SwapLimit bool
@ -42,6 +45,7 @@ func init() {
sysInitPath = utils.SelfPath()
}
// List returns an array of all containers registered in the runtime.
func (runtime *Runtime) List() []*Container {
containers := new(History)
for e := runtime.containers.Front(); e != nil; e = e.Next() {
@ -60,6 +64,8 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element {
return nil
}
// Get looks for a container by the specified ID or name, and returns it.
// If the container is not found, or if an error occurs, nil is returned.
func (runtime *Runtime) Get(name string) *Container {
id, err := runtime.idIndex.Get(name)
if err != nil {
@ -72,6 +78,8 @@ func (runtime *Runtime) Get(name string) *Container {
return e.Value.(*Container)
}
// Exists returns a true if a container of the specified ID or name exists,
// false otherwise.
func (runtime *Runtime) Exists(id string) bool {
return runtime.Get(id) != nil
}
@ -80,6 +88,9 @@ func (runtime *Runtime) containerRoot(id string) string {
return path.Join(runtime.repository, id)
}
// Load reads the contents of a container from disk and registers
// it with Register.
// This is typically done at startup.
func (runtime *Runtime) Load(id string) (*Container, error) {
container := &Container{root: runtime.containerRoot(id)}
if err := container.FromDisk(); err != nil {
@ -177,6 +188,7 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin
return nil
}
// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem.
func (runtime *Runtime) Destroy(container *Container) error {
if container == nil {
return fmt.Errorf("The given container is <nil>")
@ -233,6 +245,7 @@ func (runtime *Runtime) restore() error {
return nil
}
// FIXME: comment please!
func (runtime *Runtime) UpdateCapabilities(quiet bool) {
if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil {
if !quiet {
@ -260,6 +273,159 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) {
}
}
// Create creates a new container from the given configuration.
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 {
MergeConfig(config, img.Config)
}
if len(config.Entrypoint) != 0 && config.Cmd == nil {
config.Cmd = []string{}
} else if config.Cmd == nil || len(config.Cmd) == 0 {
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]
}
var args []string
var entrypoint string
if len(config.Entrypoint) != 0 {
entrypoint = config.Entrypoint[0]
args = append(config.Entrypoint[1:], config.Cmd...)
} else {
entrypoint = config.Cmd[0]
args = config.Cmd[1:]
}
container := &Container{
// FIXME: we should generate the ID here instead of receiving it as an argument
ID: id,
Created: time.Now(),
Path: entrypoint,
Args: args, //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
}
resolvConf, err := utils.GetResolvConf()
if err != nil {
return nil, err
}
if len(config.Dns) == 0 && len(runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
runtime.Dns = defaultDns
}
// If custom dns exists, then create a resolv.conf for the container
if len(config.Dns) > 0 || len(runtime.Dns) > 0 {
var dns []string
if len(config.Dns) > 0 {
dns = config.Dns
} else {
dns = runtime.Dns
}
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 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: if hostname, build hostname and hosts files
container.HostnamePath = path.Join(container.root, "hostname")
ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
hostsContent := []byte(`
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
`)
container.HostsPath = path.Join(container.root, "hosts")
if container.Config.Domainname != "" {
hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
} else {
hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s\n", container.Config.Hostname)), hostsContent...)
hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s\n", container.Config.Hostname)), hostsContent...)
}
ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
// Step 4: register the container
if err := 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 (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
// FIXME: freeze the container before copying it to avoid data corruption?
// FIXME: this shouldn't be in commands.
if err := container.EnsureMounted(); err != nil {
return nil, err
}
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
}
// FIXME: harmonize with NewGraph()
func NewRuntime(flGraphPath string, autoRestart bool, dns []string) (*Runtime, error) {
runtime, err := NewRuntimeFromDirectory(flGraphPath, autoRestart)
@ -325,6 +491,8 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
return runtime, nil
}
// History is a convenience type for storing a list of containers,
// ordered by creation date.
type History []*Container
func (history *History) Len() int {

View file

@ -144,9 +144,7 @@ func TestRuntimeCreate(t *testing.T) {
t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
}
builder := NewBuilder(runtime)
container, err := builder.Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
@ -187,7 +185,7 @@ func TestRuntimeCreate(t *testing.T) {
}
// Make sure crete with bad parameters returns an error
_, err = builder.Create(
_, err = runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
},
@ -196,7 +194,7 @@ func TestRuntimeCreate(t *testing.T) {
t.Fatal("Builder.Create should throw an error when Cmd is missing")
}
_, err = builder.Create(
_, err = runtime.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{},
@ -210,7 +208,7 @@ func TestRuntimeCreate(t *testing.T) {
func TestDestroy(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
@ -296,7 +294,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container,
t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
}
t.Log("Trying port", strPort)
container, err = NewBuilder(runtime).Create(&Config{
container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", cmd},
PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)},

View file

@ -139,8 +139,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
return "", err
}
b := NewBuilder(srv.runtime)
c, err := b.Create(config)
c, err := srv.runtime.Create(config)
if err != nil {
return "", err
}
@ -149,7 +148,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
return "", err
}
// FIXME: Handle custom repo, tag comment, author
img, err = b.Commit(c, "", "", img.Comment, img.Author, nil)
img, err = srv.runtime.Commit(c, "", "", img.Comment, img.Author, nil)
if err != nil {
return "", err
}
@ -400,7 +399,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf
if container == nil {
return "", fmt.Errorf("No such container: %s", name)
}
img, err := NewBuilder(srv.runtime).Commit(container, repo, tag, comment, author, config)
img, err := srv.runtime.Commit(container, repo, tag, comment, author, config)
if err != nil {
return "", err
}
@ -915,8 +914,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
config.MemorySwap = -1
}
b := NewBuilder(srv.runtime)
container, err := b.Create(config)
container, err := srv.runtime.Create(config)
if err != nil {
if srv.runtime.graph.IsNotExist(err) {

View file

@ -1012,3 +1012,12 @@ func (graph *DependencyGraph) GenerateTraversalMap() ([][]string, error) {
}
return result, nil
}
// An StatusError reports an unsuccessful exit by a command.
type StatusError struct {
Status int
}
func (e *StatusError) Error() string {
return fmt.Sprintf("Status: %d", e.Status)
}

View file

@ -101,7 +101,7 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf
if config.Image == "_" {
config.Image = GetTestImage(r).ID
}
c, err := NewBuilder(r).Create(config)
c, err := r.Create(config)
if err != nil {
return nil, nil, err
}