mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 20:26:54 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97badbd29e | |||
| 0b0d958b88 | |||
| 03e4704ae5 | |||
| 7a8ac76299 | |||
| c05c91ca3b | |||
| 97535e5a64 | |||
| f079fbe3fa | |||
| 90d144b612 | |||
| d3db94696d | |||
| ffe16e3224 | |||
| 038e1d174b | |||
| 16aeb77d51 | |||
| 4ac3b803b9 | |||
| 3514e47edc | |||
| acb546cd1b | |||
| 2ced94b414 | |||
| 71b5806614 | |||
| 1f65c6bf4c | |||
| 965e8a02d2 | |||
| baacae8345 | |||
| 52cedb8a05 | |||
| 15c7e72e2a | |||
| 76b40ad6c9 | |||
| 6909f3911f | |||
| e49af5b6de | |||
| f3e89fae28 | |||
| c42a4179fc | |||
| 2d32ac8cff | |||
| f68d107a13 | |||
| 640efc2ed2 | |||
| 003622c8b6 | |||
| 7eda9c64b8 | |||
| 84c13a3dcf |
@@ -13,10 +13,7 @@ endif
|
||||
GIT_COMMIT = $(shell git rev-parse --short HEAD)
|
||||
GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "+CHANGES")
|
||||
|
||||
NO_MEMORY_LIMIT ?= 0
|
||||
export NO_MEMORY_LIMIT
|
||||
|
||||
BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS) -X main.NO_MEMORY_LIMIT $(NO_MEMORY_LIMIT)"
|
||||
BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS)"
|
||||
|
||||
SRC_DIR := $(GOPATH)/src
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ Running an irc bouncer
|
||||
|
||||
```bash
|
||||
BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc $USER $PASSWORD)
|
||||
echo "Configure your irc client to connect to port $(port $BOUNCER_ID 6667) of this machine"
|
||||
echo "Configure your irc client to connect to port $(docker port $BOUNCER_ID 6667) of this machine"
|
||||
```
|
||||
|
||||
Running Redis
|
||||
@@ -133,7 +133,7 @@ Running Redis
|
||||
|
||||
```bash
|
||||
REDIS_ID=$(docker run -d -p 6379 shykes/redis redis-server)
|
||||
echo "Configure your redis client to connect to port $(port $REDIS_ID 6379) of this machine"
|
||||
echo "Configure your redis client to connect to port $(docker port $REDIS_ID 6379) of this machine"
|
||||
```
|
||||
|
||||
Share your own image!
|
||||
|
||||
+36
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
@@ -86,3 +87,38 @@ func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
|
||||
}
|
||||
return pipeR, nil
|
||||
}
|
||||
|
||||
// NewTempArchive reads the content of src into a temporary file, and returns the contents
|
||||
// of that file as an archive. The archive can only be read once - as soon as reading completes,
|
||||
// the file will be deleted.
|
||||
func NewTempArchive(src Archive, dir string) (*TempArchive, error) {
|
||||
f, err := ioutil.TempFile(dir, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.Copy(f, src); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := st.Size()
|
||||
return &TempArchive{f, size}, nil
|
||||
}
|
||||
|
||||
type TempArchive struct {
|
||||
*os.File
|
||||
Size int64 // Pre-computed from Stat().Size() as a convenience
|
||||
}
|
||||
|
||||
func (archive *TempArchive) Read(data []byte) (int, error) {
|
||||
n, err := archive.File.Read(data)
|
||||
if err != nil {
|
||||
os.Remove(archive.File.Name())
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
+18
-10
@@ -18,11 +18,10 @@ import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const VERSION = "0.1.7"
|
||||
const VERSION = "0.1.8"
|
||||
|
||||
var (
|
||||
GIT_COMMIT string
|
||||
NO_MEMORY_LIMIT bool
|
||||
GIT_COMMIT string
|
||||
)
|
||||
|
||||
func (srv *Server) Name() string {
|
||||
@@ -184,10 +183,14 @@ func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||
|
||||
// 'docker version': show version information
|
||||
func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||
fmt.Fprintf(stdout, "Version:%s\n", VERSION)
|
||||
fmt.Fprintf(stdout, "Git Commit:%s\n", GIT_COMMIT)
|
||||
if NO_MEMORY_LIMIT {
|
||||
fmt.Fprintf(stdout, "Memory limit disabled\n")
|
||||
fmt.Fprintf(stdout, "Version: %s\n", VERSION)
|
||||
fmt.Fprintf(stdout, "Git Commit: %s\n", GIT_COMMIT)
|
||||
fmt.Fprintf(stdout, "Kernel: %s\n", srv.runtime.kernelVersion)
|
||||
if !srv.runtime.capabilities.MemoryLimit {
|
||||
fmt.Fprintf(stdout, "WARNING: No memory limit support\n")
|
||||
}
|
||||
if !srv.runtime.capabilities.SwapLimit {
|
||||
fmt.Fprintf(stdout, "WARNING: No swap limit support\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -472,7 +475,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args .
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout)
|
||||
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout, "Importing %v/%v (%v)")
|
||||
}
|
||||
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "")
|
||||
if err != nil {
|
||||
@@ -910,7 +913,7 @@ func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||
}
|
||||
|
||||
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
|
||||
config, err := ParseRun(args, stdout)
|
||||
config, err := ParseRun(args, stdout, srv.runtime.capabilities)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -976,8 +979,13 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
|
||||
}
|
||||
Debugf("Waiting for attach to return\n")
|
||||
<-attachErr
|
||||
container.Wait()
|
||||
// Expecting I/O pipe error, discarding
|
||||
|
||||
// If we are in stdinonce mode, wait for the process to end
|
||||
// otherwise, simply return
|
||||
if config.StdinOnce && !config.Tty {
|
||||
container.Wait()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+15
-2
@@ -228,6 +228,21 @@ func TestRunDisconnectTty(t *testing.T) {
|
||||
close(c1)
|
||||
}()
|
||||
|
||||
setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
|
||||
for {
|
||||
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||
l := runtime.List()
|
||||
if len(l) == 1 && l[0].State.Running {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
})
|
||||
|
||||
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||
container := runtime.List()[0]
|
||||
|
||||
setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
|
||||
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -242,8 +257,6 @@ func TestRunDisconnectTty(t *testing.T) {
|
||||
// In tty mode, we expect the process to stay alive even after client's stdin closes.
|
||||
// Do not wait for run to finish
|
||||
|
||||
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||
container := runtime.List()[0]
|
||||
// Give some time to monitor to do his thing
|
||||
container.WaitTimeout(500 * time.Millisecond)
|
||||
if !container.State.Running {
|
||||
|
||||
+17
-6
@@ -68,7 +68,7 @@ type Config struct {
|
||||
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
|
||||
}
|
||||
|
||||
func ParseRun(args []string, stdout io.Writer) (*Config, error) {
|
||||
func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Config, error) {
|
||||
cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
|
||||
if len(args) > 0 && args[0] != "--help" {
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
@@ -83,8 +83,8 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
|
||||
flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
|
||||
flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
|
||||
|
||||
if *flMemory > 0 && NO_MEMORY_LIMIT {
|
||||
fmt.Fprintf(stdout, "WARNING: This version of docker has been compiled without memory limit support. Discarding -m.")
|
||||
if *flMemory > 0 && !capabilities.MemoryLimit {
|
||||
fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
|
||||
*flMemory = 0
|
||||
}
|
||||
|
||||
@@ -137,6 +137,12 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
|
||||
Dns: flDns,
|
||||
Image: image,
|
||||
}
|
||||
|
||||
if *flMemory > 0 && !capabilities.SwapLimit {
|
||||
fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
||||
config.MemorySwap = -1
|
||||
}
|
||||
|
||||
// When allocating stdin in attached mode, close stdin at client disconnect
|
||||
if config.OpenStdin && config.AttachStdin {
|
||||
config.StdinOnce = true
|
||||
@@ -379,10 +385,15 @@ func (container *Container) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if container.Config.Memory > 0 && NO_MEMORY_LIMIT {
|
||||
log.Printf("WARNING: This version of docker has been compiled without memory limit support. Discarding the limit.")
|
||||
// Make sure the config is compatible with the current kernel
|
||||
if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
|
||||
log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
|
||||
container.Config.Memory = 0
|
||||
}
|
||||
if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
|
||||
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
||||
container.Config.MemorySwap = -1
|
||||
}
|
||||
|
||||
if err := container.generateLXCConfig(); err != nil {
|
||||
return err
|
||||
@@ -619,7 +630,7 @@ func (container *Container) Stop(seconds int) error {
|
||||
return nil
|
||||
}
|
||||
if container.State.Ghost {
|
||||
return fmt.Errorf("Can't stop ghot container")
|
||||
return fmt.Errorf("Can't stop ghost container")
|
||||
}
|
||||
|
||||
// 1. Send a SIGTERM
|
||||
|
||||
@@ -150,6 +150,82 @@ func TestMultipleAttachRestart(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
// Create a container and remove a file
|
||||
container1, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"/bin/rm", "/etc/passwd"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer runtime.Destroy(container1)
|
||||
|
||||
if err := container1.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check the changelog
|
||||
c, err := container1.Changes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
success := false
|
||||
for _, elem := range c {
|
||||
if elem.Path == "/etc/passwd" && elem.Kind == 2 {
|
||||
success = true
|
||||
}
|
||||
}
|
||||
if !success {
|
||||
t.Fatalf("/etc/passwd as been removed but is not present in the diff")
|
||||
}
|
||||
|
||||
// Commit the container
|
||||
rwTar, err := container1.ExportRw()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Create a new container from the commited image
|
||||
container2, err := runtime.Create(
|
||||
&Config{
|
||||
Image: img.Id,
|
||||
Cmd: []string{"cat", "/etc/passwd"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer runtime.Destroy(container2)
|
||||
|
||||
if err := container2.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check the changelog
|
||||
c, err = container2.Changes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, elem := range c {
|
||||
if elem.Path == "/etc/passwd" {
|
||||
t.Fatalf("/etc/passwd should not be present in the diff after commit.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitRun(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
|
||||
+10
-8
@@ -7,15 +7,16 @@ import (
|
||||
"github.com/dotcloud/docker/rcli"
|
||||
"github.com/dotcloud/docker/term"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
GIT_COMMIT string
|
||||
NO_MEMORY_LIMIT string
|
||||
GIT_COMMIT string
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -39,15 +40,11 @@ func main() {
|
||||
os.Setenv("DEBUG", "1")
|
||||
}
|
||||
docker.GIT_COMMIT = GIT_COMMIT
|
||||
docker.NO_MEMORY_LIMIT = NO_MEMORY_LIMIT == "1"
|
||||
if *flDaemon {
|
||||
if flag.NArg() != 0 {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
if NO_MEMORY_LIMIT == "1" {
|
||||
log.Printf("WARNING: This version of docker has been compiled without memory limit support.")
|
||||
}
|
||||
if err := daemon(*pidfile); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -59,8 +56,13 @@ func main() {
|
||||
}
|
||||
|
||||
func createPidFile(pidfile string) error {
|
||||
if _, err := os.Stat(pidfile); err == nil {
|
||||
return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile)
|
||||
if pidString, err := ioutil.ReadFile(pidfile); err == nil {
|
||||
pid, err := strconv.Atoi(string(pidString))
|
||||
if err == nil {
|
||||
if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil {
|
||||
return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(pidfile)
|
||||
|
||||
@@ -6,16 +6,16 @@ Arch Linux
|
||||
Installing on Arch Linux is not officially supported but can be handled via
|
||||
either of the following AUR packages:
|
||||
|
||||
* `dotcloud-docker <https://aur.archlinux.org/packages/dotcloud-docker/>`_
|
||||
* `dotcloud-docker-git <https://aur.archlinux.org/packages/dotcloud-docker-git/>`_
|
||||
* `lxc-docker <https://aur.archlinux.org/packages/lxc-docker/>`_
|
||||
* `lxc-docker-git <https://aur.archlinux.org/packages/lxc-docker-git/>`_
|
||||
|
||||
The dotcloud-docker package will install the latest tagged version of docker.
|
||||
The dotcloud-docker-git package will build from the current master branch.
|
||||
The lxc-docker package will install the latest tagged version of docker.
|
||||
The lxc-docker-git package will build from the current master branch.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Docker depends on several packages which will be installed automatically with
|
||||
Docker depends on several packages which are specified as dependencies in
|
||||
either AUR package.
|
||||
|
||||
* aufs3
|
||||
@@ -23,6 +23,7 @@ either AUR package.
|
||||
* go
|
||||
* iproute2
|
||||
* linux-aufs_friendly
|
||||
* lxc
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -37,7 +38,22 @@ new kernel will be compiled and this can take quite a while.
|
||||
|
||||
::
|
||||
|
||||
yaourt -S dotcloud-docker-git
|
||||
yaourt -S lxc-docker-git
|
||||
|
||||
Starting Docker
|
||||
---------------
|
||||
|
||||
Prior to starting docker modify your bootloader to use the
|
||||
**linux-aufs_friendly** kernel and reboot your system.
|
||||
|
||||
There is a systemd service unit created for docker. To start the docker service:
|
||||
|
||||
::
|
||||
|
||||
sudo systemctl start docker
|
||||
|
||||
To start on system boot:
|
||||
|
||||
::
|
||||
|
||||
sudo systemctl enable docker
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.. _ubuntu_linux:
|
||||
|
||||
Ubuntu Linux
|
||||
============
|
||||
Installing on Ubuntu Linux
|
||||
==========================
|
||||
|
||||
**Please note this project is currently under heavy development. It should not be used in production.**
|
||||
|
||||
@@ -53,4 +53,4 @@ Run your first container!
|
||||
Check out more examples
|
||||
-----------------------
|
||||
|
||||
Continue with the :ref:`hello_world` example.
|
||||
Continue with the :ref:`hello_world` example.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func getKernelVersion() (*KernelVersionInfo, error) {
|
||||
return nil, fmt.Errorf("Kernel version detection is not available on darwin")
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func getKernelVersion() (*KernelVersionInfo, error) {
|
||||
var (
|
||||
uts syscall.Utsname
|
||||
flavor string
|
||||
kernel, major, minor int
|
||||
err error
|
||||
)
|
||||
|
||||
if err := syscall.Uname(&uts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
release := make([]byte, len(uts.Release))
|
||||
|
||||
i := 0
|
||||
for _, c := range uts.Release {
|
||||
release[i] = byte(c)
|
||||
i++
|
||||
}
|
||||
|
||||
// Remove the \x00 from the release for Atoi to parse correctly
|
||||
release = release[:bytes.IndexByte(release, 0)]
|
||||
|
||||
tmp := strings.SplitN(string(release), "-", 2)
|
||||
tmp2 := strings.SplitN(tmp[0], ".", 3)
|
||||
|
||||
if len(tmp2) > 0 {
|
||||
kernel, err = strconv.Atoi(tmp2[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(tmp2) > 1 {
|
||||
major, err = strconv.Atoi(tmp2[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(tmp2) > 2 {
|
||||
minor, err = strconv.Atoi(tmp2[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(tmp) == 2 {
|
||||
flavor = tmp[1]
|
||||
} else {
|
||||
flavor = ""
|
||||
}
|
||||
|
||||
return &KernelVersionInfo{
|
||||
Kernel: kernel,
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Flavor: flavor,
|
||||
}, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
@@ -129,12 +130,32 @@ func (graph *Graph) Register(layerData Archive, img *Image) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TempLayerArchive creates a temporary archive of the given image's filesystem layer.
|
||||
// The archive is stored on disk and will be automatically deleted as soon as has been read.
|
||||
// If output is not nil, a human-readable progress bar will be written to it.
|
||||
// FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
|
||||
func (graph *Graph) TempLayerArchive(id string, compression Compression, output io.Writer) (*TempArchive, error) {
|
||||
image, err := graph.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmp, err := graph.tmp()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
archive, err := image.TarLayer(compression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTempArchive(ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root)
|
||||
}
|
||||
|
||||
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||
func (graph *Graph) Mktemp(id string) (string, error) {
|
||||
if id == "" {
|
||||
id = GenerateId()
|
||||
}
|
||||
tmp, err := NewGraph(path.Join(graph.Root, ":tmp:"))
|
||||
tmp, err := graph.tmp()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Couldn't create temp: %s", err)
|
||||
}
|
||||
@@ -144,6 +165,10 @@ func (graph *Graph) Mktemp(id string) (string, error) {
|
||||
return tmp.imageRoot(id), nil
|
||||
}
|
||||
|
||||
func (graph *Graph) tmp() (*Graph, error) {
|
||||
return NewGraph(path.Join(graph.Root, ":tmp:"))
|
||||
}
|
||||
|
||||
// Check if given error is "not empty".
|
||||
// Note: this is the way golang does it internally with os.IsNotExists.
|
||||
func isNotEmpty(err error) bool {
|
||||
|
||||
@@ -92,7 +92,7 @@ func MountAUFS(ro []string, rw string, target string) error {
|
||||
rwBranch := fmt.Sprintf("%v=rw", rw)
|
||||
roBranches := ""
|
||||
for _, layer := range ro {
|
||||
roBranches += fmt.Sprintf("%v=ro:", layer)
|
||||
roBranches += fmt.Sprintf("%v=ro+wh:", layer)
|
||||
}
|
||||
branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
|
||||
|
||||
@@ -110,6 +110,15 @@ func MountAUFS(ro []string, rw string, target string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TarLayer returns a tar archive of the image's filesystem layer.
|
||||
func (image *Image) TarLayer(compression Compression) (Archive, error) {
|
||||
layerPath, err := image.layer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Tar(layerPath, compression)
|
||||
}
|
||||
|
||||
func (image *Image) Mount(root, rw string) error {
|
||||
if mounted, err := Mounted(root); err != nil {
|
||||
return err
|
||||
@@ -127,34 +136,9 @@ func (image *Image) Mount(root, rw string) error {
|
||||
if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
// FIXME: @creack shouldn't we do this after going over changes?
|
||||
if err := MountAUFS(layers, rw, root); err != nil {
|
||||
return err
|
||||
}
|
||||
// FIXME: Create tests for deletion
|
||||
// FIXME: move this part to change.go
|
||||
// Retrieve the changeset from the parent and apply it to the container
|
||||
// - Retrieve the changes
|
||||
changes, err := Changes(layers, layers[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Iterate on changes
|
||||
for _, c := range changes {
|
||||
// If there is a delete
|
||||
if c.Kind == ChangeDelete {
|
||||
// Make sure the directory exists
|
||||
file_path, file_name := path.Dir(c.Path), path.Base(c.Path)
|
||||
if err := os.MkdirAll(path.Join(rw, file_path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
// And create the whiteout (we just need to create empty file, discard the return)
|
||||
if _, err := os.Create(path.Join(path.Join(rw, file_path),
|
||||
".wh."+path.Base(file_name))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Docker on Arch
|
||||
==============
|
||||
|
||||
The AUR lxc-docker and lxc-docker-git packages handle building docker on Arch
|
||||
linux. The PKGBUILD specifies all dependencies, build, and packaging steps.
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
The only buildtime dependencies are git and go which are available via pacman.
|
||||
The -s flag can be used on makepkg commands below to automatically install
|
||||
these dependencies.
|
||||
|
||||
Building Package
|
||||
================
|
||||
|
||||
Download the tarball for either AUR packaged to a local directory. In that
|
||||
directory makepkg can be run to build the package.
|
||||
|
||||
# Build the binary package
|
||||
makepkg
|
||||
|
||||
# Build an updated source tarball
|
||||
makepkg --source
|
||||
|
||||
+10
-13
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
@@ -135,7 +136,7 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId string, authConfig *a
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return img, ProgressReader(res.Body, int(res.ContentLength), stdout), nil
|
||||
return img, ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil
|
||||
}
|
||||
|
||||
func (graph *Graph) PullImage(stdout io.Writer, imgId string, authConfig *auth.AuthConfig) error {
|
||||
@@ -269,24 +270,20 @@ func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, authConfig *auth
|
||||
return fmt.Errorf("Failed to retrieve layer upload location: %s", err)
|
||||
}
|
||||
|
||||
// FIXME: Don't do this :D. Check the S3 requierement and implement chunks of 5MB
|
||||
// FIXME2: I won't stress it enough, DON'T DO THIS! very high priority
|
||||
layerData2, err := Tar(path.Join(graph.Root, img.Id, "layer"), Xz)
|
||||
tmp, err := ioutil.ReadAll(layerData2)
|
||||
// FIXME: stream the archive directly to the registry instead of buffering it on disk. This requires either:
|
||||
// a) Implementing S3's proprietary streaming logic, or
|
||||
// b) Stream directly to the registry instead of S3.
|
||||
// I prefer option b. because it doesn't lock us into a proprietary cloud service.
|
||||
tmpLayer, err := graph.TempLayerArchive(img.Id, Xz, stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
layerLength := len(tmp)
|
||||
|
||||
layerData, err := Tar(path.Join(graph.Root, img.Id, "layer"), Xz)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to generate layer archive: %s", err)
|
||||
}
|
||||
req3, err := http.NewRequest("PUT", url.String(), ProgressReader(layerData.(io.ReadCloser), layerLength, stdout))
|
||||
defer os.Remove(tmpLayer.Name())
|
||||
req3, err := http.NewRequest("PUT", url.String(), ProgressReader(tmpLayer, int(tmpLayer.Size), stdout, "Uploading %v/%v (%v)"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req3.ContentLength = int64(layerLength)
|
||||
req3.ContentLength = int64(tmpLayer.Size)
|
||||
|
||||
req3.TransferEncoding = []string{"none"}
|
||||
res3, err := client.Do(req3)
|
||||
|
||||
+40
-1
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -14,6 +15,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Capabilities struct {
|
||||
MemoryLimit bool
|
||||
SwapLimit bool
|
||||
}
|
||||
|
||||
type Runtime struct {
|
||||
root string
|
||||
repository string
|
||||
@@ -23,6 +29,8 @@ type Runtime struct {
|
||||
repositories *TagStore
|
||||
authConfig *auth.AuthConfig
|
||||
idIndex *TruncIndex
|
||||
capabilities *Capabilities
|
||||
kernelVersion *KernelVersionInfo
|
||||
}
|
||||
|
||||
var sysInitPath string
|
||||
@@ -282,7 +290,37 @@ func (runtime *Runtime) restore() error {
|
||||
|
||||
// FIXME: harmonize with NewGraph()
|
||||
func NewRuntime() (*Runtime, error) {
|
||||
return NewRuntimeFromDirectory("/var/lib/docker")
|
||||
runtime, err := NewRuntimeFromDirectory("/var/lib/docker")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if k, err := GetKernelVersion(); err != nil {
|
||||
log.Printf("WARNING: %s\n", err)
|
||||
} else {
|
||||
runtime.kernelVersion = k
|
||||
if CompareKernelVersion(k, &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
|
||||
log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
|
||||
}
|
||||
}
|
||||
|
||||
if cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory"); err != nil {
|
||||
log.Printf("WARNING: %s\n", err)
|
||||
} else {
|
||||
_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
|
||||
_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
|
||||
runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
|
||||
if !runtime.capabilities.MemoryLimit {
|
||||
log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
|
||||
}
|
||||
|
||||
_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
|
||||
runtime.capabilities.SwapLimit = err == nil
|
||||
if !runtime.capabilities.SwapLimit {
|
||||
log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
|
||||
}
|
||||
}
|
||||
return runtime, nil
|
||||
}
|
||||
|
||||
func NewRuntimeFromDirectory(root string) (*Runtime, error) {
|
||||
@@ -321,6 +359,7 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
|
||||
repositories: repositories,
|
||||
authConfig: authConfig,
|
||||
idIndex: NewTruncIndex(),
|
||||
capabilities: &Capabilities{},
|
||||
}
|
||||
|
||||
if err := runtime.restore(); err != nil {
|
||||
|
||||
@@ -48,8 +48,6 @@ func layerArchive(tarfile string) (io.Reader, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
NO_MEMORY_LIMIT = os.Getenv("NO_MEMORY_LIMIT") == "1"
|
||||
|
||||
// Hack to run sys init during unit testing
|
||||
if SelfPath() == "/sbin/init" {
|
||||
SysInit()
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -69,23 +70,30 @@ type progressReader struct {
|
||||
readTotal int // Expected stream length (bytes)
|
||||
readProgress int // How much has been read so far (bytes)
|
||||
lastUpdate int // How many bytes read at least update
|
||||
template string // Template to print. Default "%v/%v (%v)"
|
||||
}
|
||||
|
||||
func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
read, err := io.ReadCloser(r.reader).Read(p)
|
||||
r.readProgress += read
|
||||
|
||||
// Only update progress for every 1% read
|
||||
updateEvery := int(0.01 * float64(r.readTotal))
|
||||
if r.readProgress-r.lastUpdate > updateEvery || r.readProgress == r.readTotal {
|
||||
fmt.Fprintf(r.output, "%d/%d (%.0f%%)\r",
|
||||
r.readProgress,
|
||||
r.readTotal,
|
||||
float64(r.readProgress)/float64(r.readTotal)*100)
|
||||
updateEvery := 4096
|
||||
if r.readTotal > 0 {
|
||||
// Only update progress for every 1% read
|
||||
if increment := int(0.01 * float64(r.readTotal)); increment > updateEvery {
|
||||
updateEvery = increment
|
||||
}
|
||||
}
|
||||
if r.readProgress-r.lastUpdate > updateEvery || err != nil {
|
||||
if r.readTotal > 0 {
|
||||
fmt.Fprintf(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
|
||||
} else {
|
||||
fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a")
|
||||
}
|
||||
r.lastUpdate = r.readProgress
|
||||
}
|
||||
// Send newline when complete
|
||||
if err == io.EOF {
|
||||
if err != nil {
|
||||
fmt.Fprintf(r.output, "\n")
|
||||
}
|
||||
|
||||
@@ -94,8 +102,11 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
func (r *progressReader) Close() error {
|
||||
return io.ReadCloser(r.reader).Close()
|
||||
}
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer) *progressReader {
|
||||
return &progressReader{r, output, size, 0, 0}
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader {
|
||||
if template == "" {
|
||||
template = "%v/%v (%v)"
|
||||
}
|
||||
return &progressReader{r, output, size, 0, 0, template}
|
||||
}
|
||||
|
||||
// HumanDuration returns a human-readable approximation of a duration
|
||||
@@ -384,3 +395,59 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
|
||||
}
|
||||
return written, err
|
||||
}
|
||||
|
||||
type KernelVersionInfo struct {
|
||||
Kernel int
|
||||
Major int
|
||||
Minor int
|
||||
Flavor string
|
||||
}
|
||||
|
||||
// FIXME: this doens't build on Darwin
|
||||
func GetKernelVersion() (*KernelVersionInfo, error) {
|
||||
return getKernelVersion()
|
||||
}
|
||||
|
||||
func (k *KernelVersionInfo) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d-%s", k.Kernel, k.Major, k.Minor, k.Flavor)
|
||||
}
|
||||
|
||||
// Compare two KernelVersionInfo struct.
|
||||
// Returns -1 if a < b, = if a == b, 1 it a > b
|
||||
func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
||||
if a.Kernel < b.Kernel {
|
||||
return -1
|
||||
} else if a.Kernel > b.Kernel {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.Major < b.Major {
|
||||
return -1
|
||||
} else if a.Major > b.Major {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.Minor < b.Minor {
|
||||
return -1
|
||||
} else if a.Minor > b.Minor {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func FindCgroupMountpoint(cgroupType string) (string, error) {
|
||||
output, err := exec.Command("mount").CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
reg := regexp.MustCompile(`^cgroup on (.*) type cgroup \(.*` + cgroupType + `[,\)]`)
|
||||
for _, line := range strings.Split(string(output), "\n") {
|
||||
r := reg.FindStringSubmatch(line)
|
||||
if len(r) == 2 {
|
||||
return r[1], nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
|
||||
}
|
||||
|
||||
@@ -228,3 +228,36 @@ func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult strin
|
||||
t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
|
||||
if r := CompareKernelVersion(a, b); r != result {
|
||||
t.Fatalf("Unepected kernel version comparaison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareKernelVersion(t *testing.T) {
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
-1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "16"},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20, Flavor: "25"},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"},
|
||||
-1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user