mirror of
https://github.com/clearlinux/docker.git
synced 2026-09-06 21:51:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0a49a30c7 | ||
|
|
dc06496dc8 | ||
|
|
cfbe76e559 | ||
|
|
2fafe1efce | ||
|
|
8240e37b87 | ||
|
|
23cf3c7a33 | ||
|
|
4db64111bd | ||
|
|
e97364ecd7 | ||
|
|
2bd089dadb | ||
|
|
a81393787f | ||
|
|
b45e280ee8 | ||
|
|
5867f9e761 | ||
|
|
6132f378e0 | ||
|
|
3cf4b2c7c1 | ||
|
|
d70cbbf1fc | ||
|
|
b7e7104e92 | ||
|
|
7e60a6b1ea | ||
|
|
45979a7ef7 | ||
|
|
5c9213258a | ||
|
|
68074fce68 | ||
|
|
c42056b969 | ||
|
|
341addeccd | ||
|
|
47904290a5 | ||
|
|
a18c26392f | ||
|
|
77ad95d372 | ||
|
|
fee61895f2 | ||
|
|
f6a78f89ba | ||
|
|
081543c49a | ||
|
|
4054f31471 | ||
|
|
76a07371c5 | ||
|
|
5d364fda3c | ||
|
|
28311f61be | ||
|
|
70b731735c | ||
|
|
67453d6b48 | ||
|
|
c14450ae5c | ||
|
|
593566438d | ||
|
|
85f3ec88a6 | ||
|
|
5797e7e34e | ||
|
|
089a60c2cf | ||
|
|
a99a8386fd | ||
|
|
dfd0deefbb | ||
|
|
9946981c61 | ||
|
|
c6b4076125 | ||
|
|
b79bd4e864 |
@@ -1,5 +1,18 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.6.3 (2013-09-23)
|
||||||
|
* Packaging: Update tar vendor dependency
|
||||||
|
- Client: Fix detach issue
|
||||||
|
- Runtime: Only copy and change permissions on non-bindmount volumes
|
||||||
|
- Registry: Update regular expression to match index
|
||||||
|
* Runtime: Allow multiple volumes-from
|
||||||
|
* Packaging: Download apt key over HTTPS
|
||||||
|
* Documentation: Update section on extracting the docker binary after build
|
||||||
|
* Documentation: Update development environment docs for new build process
|
||||||
|
* Documentation: Remove 'base' image from documentation
|
||||||
|
* Packaging: Add 'docker' group on install for ubuntu package
|
||||||
|
- Runtime: Fix HTTP imports from STDIN
|
||||||
|
|
||||||
## 0.6.2 (2013-09-17)
|
## 0.6.2 (2013-09-17)
|
||||||
+ Hack: Vendor all dependencies
|
+ Hack: Vendor all dependencies
|
||||||
+ Builder: Add -rm option in order to remove intermediate containers
|
+ Builder: Add -rm option in order to remove intermediate containers
|
||||||
|
|||||||
+37
-10
@@ -6,6 +6,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
@@ -36,6 +37,10 @@ var (
|
|||||||
VERSION string
|
VERSION string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrConnectionRefused = errors.New("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||||
|
)
|
||||||
|
|
||||||
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
|
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
|
||||||
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
||||||
return reflect.TypeOf(cli).MethodByName(methodName)
|
return reflect.TypeOf(cli).MethodByName(methodName)
|
||||||
@@ -795,11 +800,13 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
|||||||
v.Set("tag", tag)
|
v.Set("tag", tag)
|
||||||
v.Set("fromSrc", src)
|
v.Set("fromSrc", src)
|
||||||
|
|
||||||
err := cli.stream("POST", "/images/create?"+v.Encode(), cli.in, cli.out, nil)
|
var in io.Reader
|
||||||
if err != nil {
|
|
||||||
return err
|
if src == "-" {
|
||||||
|
in = cli.in
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) CmdPush(args ...string) error {
|
func (cli *DockerCli) CmdPush(args ...string) error {
|
||||||
@@ -1256,7 +1263,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||||||
|
|
||||||
if container.Config.Tty {
|
if container.Config.Tty {
|
||||||
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
|
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
|
||||||
return err
|
utils.Debugf("Error monitoring tty size: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1565,12 +1572,12 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||||||
// Detached mode
|
// Detached mode
|
||||||
<-wait
|
<-wait
|
||||||
} else {
|
} else {
|
||||||
status, err := waitForExit(cli, runResult.ID)
|
status, err := getExitCode(cli, runResult.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
return &utils.StatusError{status}
|
return &utils.StatusError{Status: status}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1636,7 +1643,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
|||||||
dial, err := net.Dial(cli.proto, cli.addr)
|
dial, err := net.Dial(cli.proto, cli.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
if strings.Contains(err.Error(), "connection refused") {
|
||||||
return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
return nil, -1, ErrConnectionRefused
|
||||||
}
|
}
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
@@ -1645,7 +1652,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
|||||||
defer clientconn.Close()
|
defer clientconn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
if strings.Contains(err.Error(), "connection refused") {
|
||||||
return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
return nil, -1, ErrConnectionRefused
|
||||||
}
|
}
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
@@ -1864,7 +1871,11 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
|
|||||||
func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
||||||
body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
|
body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
// If we can't connect, then the daemon probably died.
|
||||||
|
if err != ErrConnectionRefused {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var out APIWait
|
var out APIWait
|
||||||
@@ -1874,6 +1885,22 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
|||||||
return out.StatusCode, nil
|
return out.StatusCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getExitCode(cli *DockerCli, containerId string) (int, error) {
|
||||||
|
body, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil)
|
||||||
|
if err != nil {
|
||||||
|
// If we can't connect, then the daemon probably died.
|
||||||
|
if err != ErrConnectionRefused {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
c := &Container{}
|
||||||
|
if err := json.Unmarshal(body, c); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return c.State.ExitCode, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
|
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
|
||||||
var (
|
var (
|
||||||
isTerminal = false
|
isTerminal = false
|
||||||
|
|||||||
@@ -369,6 +369,110 @@ func TestRunAttachStdin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRunDetach checks attaching and detaching with the escape sequence.
|
||||||
|
func TestRunDetach(t *testing.T) {
|
||||||
|
|
||||||
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
stdout, stdoutPipe := io.Pipe()
|
||||||
|
|
||||||
|
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
defer cleanup(globalRuntime)
|
||||||
|
|
||||||
|
ch := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
cli.CmdRun("-i", "-t", unitTestImageID, "cat")
|
||||||
|
}()
|
||||||
|
|
||||||
|
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
|
||||||
|
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
container := globalRuntime.List()[0]
|
||||||
|
|
||||||
|
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
|
||||||
|
stdinPipe.Write([]byte{'', ''})
|
||||||
|
if err := stdinPipe.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// wait for CmdRun to return
|
||||||
|
setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
|
||||||
|
<-ch
|
||||||
|
})
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
if !container.State.Running {
|
||||||
|
t.Fatal("The detached container should be still running")
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
|
||||||
|
container.Kill()
|
||||||
|
container.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAttachDetach checks that attach in tty mode can be detached
|
||||||
|
func TestAttachDetach(t *testing.T) {
|
||||||
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
stdout, stdoutPipe := io.Pipe()
|
||||||
|
|
||||||
|
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
defer cleanup(globalRuntime)
|
||||||
|
|
||||||
|
go stdout.Read(make([]byte, 1024))
|
||||||
|
setTimeout(t, "Starting container timed out", 2*time.Second, func() {
|
||||||
|
if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
container := globalRuntime.List()[0]
|
||||||
|
|
||||||
|
stdin, stdinPipe = io.Pipe()
|
||||||
|
stdout, stdoutPipe = io.Pipe()
|
||||||
|
cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
|
||||||
|
ch := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
if err := cli.CmdAttach(container.ShortID()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
|
||||||
|
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
|
||||||
|
stdinPipe.Write([]byte{'', ''})
|
||||||
|
if err := stdinPipe.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// wait for CmdRun to return
|
||||||
|
setTimeout(t, "Waiting for CmdAttach timed out", 5*time.Second, func() {
|
||||||
|
<-ch
|
||||||
|
})
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
if !container.State.Running {
|
||||||
|
t.Fatal("The detached container should be still running")
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
|
||||||
|
container.Kill()
|
||||||
|
container.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Expected behaviour, the process stays alive when the client disconnects
|
// Expected behaviour, the process stays alive when the client disconnects
|
||||||
func TestAttachDisconnect(t *testing.T) {
|
func TestAttachDisconnect(t *testing.T) {
|
||||||
stdin, stdinPipe := io.Pipe()
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
|||||||
+51
-35
@@ -146,7 +146,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
|||||||
flVolumes := NewPathOpts()
|
flVolumes := NewPathOpts()
|
||||||
cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
|
cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
|
||||||
|
|
||||||
flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
|
var flVolumesFrom ListOpts
|
||||||
|
cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container")
|
||||||
|
|
||||||
flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
|
flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
|
||||||
|
|
||||||
var flLxcOpts ListOpts
|
var flLxcOpts ListOpts
|
||||||
@@ -231,7 +233,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
|||||||
Dns: flDns,
|
Dns: flDns,
|
||||||
Image: image,
|
Image: image,
|
||||||
Volumes: flVolumes,
|
Volumes: flVolumes,
|
||||||
VolumesFrom: *flVolumesFrom,
|
VolumesFrom: strings.Join(flVolumesFrom, ","),
|
||||||
Entrypoint: entrypoint,
|
Entrypoint: entrypoint,
|
||||||
Privileged: *flPrivileged,
|
Privileged: *flPrivileged,
|
||||||
WorkingDir: *flWorkingDir,
|
WorkingDir: *flWorkingDir,
|
||||||
@@ -639,21 +641,25 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
|||||||
|
|
||||||
// Apply volumes from another container if requested
|
// Apply volumes from another container if requested
|
||||||
if container.Config.VolumesFrom != "" {
|
if container.Config.VolumesFrom != "" {
|
||||||
c := container.runtime.Get(container.Config.VolumesFrom)
|
volumes := strings.Split(container.Config.VolumesFrom, ",")
|
||||||
if c == nil {
|
for _, v := range volumes {
|
||||||
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
|
c := container.runtime.Get(v)
|
||||||
}
|
if c == nil {
|
||||||
for volPath, id := range c.Volumes {
|
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
|
||||||
if _, exists := container.Volumes[volPath]; exists {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
for volPath, id := range c.Volumes {
|
||||||
return err
|
if _, exists := container.Volumes[volPath]; exists {
|
||||||
}
|
continue
|
||||||
container.Volumes[volPath] = id
|
}
|
||||||
if isRW, exists := c.VolumesRW[volPath]; exists {
|
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
||||||
container.VolumesRW[volPath] = isRW
|
return err
|
||||||
|
}
|
||||||
|
container.Volumes[volPath] = id
|
||||||
|
if isRW, exists := c.VolumesRW[volPath]; exists {
|
||||||
|
container.VolumesRW[volPath] = isRW
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,9 +671,11 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var srcPath string
|
var srcPath string
|
||||||
|
var isBindMount bool
|
||||||
srcRW := false
|
srcRW := false
|
||||||
// If an external bind is defined for this volume, use that as a source
|
// If an external bind is defined for this volume, use that as a source
|
||||||
if bindMap, exists := binds[volPath]; exists {
|
if bindMap, exists := binds[volPath]; exists {
|
||||||
|
isBindMount = true
|
||||||
srcPath = bindMap.SrcPath
|
srcPath = bindMap.SrcPath
|
||||||
if strings.ToLower(bindMap.Mode) == "rw" {
|
if strings.ToLower(bindMap.Mode) == "rw" {
|
||||||
srcRW = true
|
srcRW = true
|
||||||
@@ -691,7 +699,9 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
|||||||
if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if srcRW {
|
|
||||||
|
// Do not copy or change permissions if we are mounting from the host
|
||||||
|
if srcRW && !isBindMount {
|
||||||
volList, err := ioutil.ReadDir(rootVolPath)
|
volList, err := ioutil.ReadDir(rootVolPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -702,22 +712,26 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(srcList) == 0 {
|
if len(srcList) == 0 {
|
||||||
|
// If the source volume is empty copy files from the root into the volume
|
||||||
if err := CopyWithTar(rootVolPath, srcPath); err != nil {
|
if err := CopyWithTar(rootVolPath, srcPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
var stat syscall.Stat_t
|
||||||
var stat syscall.Stat_t
|
if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
||||||
if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
var srcStat syscall.Stat_t
|
||||||
var srcStat syscall.Stat_t
|
if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
||||||
if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
// Change the source volume's ownership if it differs from the root
|
||||||
if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
|
// files that where just copied
|
||||||
if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
|
||||||
return err
|
if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -950,14 +964,19 @@ func (container *Container) monitor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.Debugf("Process finished")
|
utils.Debugf("Process finished")
|
||||||
if container.runtime != nil && container.runtime.srv != nil {
|
|
||||||
container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image))
|
|
||||||
}
|
|
||||||
exitCode := -1
|
exitCode := -1
|
||||||
if container.cmd != nil {
|
if container.cmd != nil {
|
||||||
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Report status back
|
||||||
|
container.State.setStopped(exitCode)
|
||||||
|
|
||||||
|
if container.runtime != nil && container.runtime.srv != nil {
|
||||||
|
container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image))
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
container.releaseNetwork()
|
container.releaseNetwork()
|
||||||
if container.Config.OpenStdin {
|
if container.Config.OpenStdin {
|
||||||
@@ -987,9 +1006,6 @@ func (container *Container) monitor() {
|
|||||||
container.stdin, container.stdinPipe = io.Pipe()
|
container.stdin, container.stdinPipe = io.Pipe()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report status back
|
|
||||||
container.State.setStopped(exitCode)
|
|
||||||
|
|
||||||
// Release the lock
|
// Release the lock
|
||||||
close(container.waitLock)
|
close(container.waitLock)
|
||||||
|
|
||||||
|
|||||||
+80
-28
@@ -1202,7 +1202,7 @@ func TestCopyVolumeUidGid(t *testing.T) {
|
|||||||
defer nuke(r)
|
defer nuke(r)
|
||||||
|
|
||||||
// Add directory not owned by root
|
// Add directory not owned by root
|
||||||
container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && chown daemon.daemon /hello"}, t)
|
container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t)
|
||||||
defer r.Destroy(container1)
|
defer r.Destroy(container1)
|
||||||
|
|
||||||
if container1.State.Running {
|
if container1.State.Running {
|
||||||
@@ -1227,18 +1227,10 @@ func TestCopyVolumeUidGid(t *testing.T) {
|
|||||||
// Test that the uid and gid is copied from the image to the volume
|
// Test that the uid and gid is copied from the image to the volume
|
||||||
tmpDir1 := tempDir(t)
|
tmpDir1 := tempDir(t)
|
||||||
defer os.RemoveAll(tmpDir1)
|
defer os.RemoveAll(tmpDir1)
|
||||||
stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "stat", "-c", "%U %G", "/hello"}, t)
|
stdout1, _ := runContainer(r, []string{"-v", "/hello", img.ID, "stat", "-c", "%U %G", "/hello"}, t)
|
||||||
if !strings.Contains(stdout1, "daemon daemon") {
|
if !strings.Contains(stdout1, "daemon daemon") {
|
||||||
t.Fatal("Container failed to transfer uid and gid to volume")
|
t.Fatal("Container failed to transfer uid and gid to volume")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the uid and gid is not copied from the image when the volume is read only
|
|
||||||
tmpDir2 := tempDir(t)
|
|
||||||
defer os.RemoveAll(tmpDir1)
|
|
||||||
stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "stat", "-c", "%U %G", "/hello"}, t)
|
|
||||||
if strings.Contains(stdout2, "daemon daemon") {
|
|
||||||
t.Fatal("Container transfered uid and gid to volume")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for #1582
|
// Test for #1582
|
||||||
@@ -1272,27 +1264,10 @@ func TestCopyVolumeContent(t *testing.T) {
|
|||||||
// Test that the content is copied from the image to the volume
|
// Test that the content is copied from the image to the volume
|
||||||
tmpDir1 := tempDir(t)
|
tmpDir1 := tempDir(t)
|
||||||
defer os.RemoveAll(tmpDir1)
|
defer os.RemoveAll(tmpDir1)
|
||||||
stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "find", "/hello"}, t)
|
stdout1, _ := runContainer(r, []string{"-v", "/hello", img.ID, "find", "/hello"}, t)
|
||||||
if !(strings.Contains(stdout1, "/hello/local/world") && strings.Contains(stdout1, "/hello/local")) {
|
if !(strings.Contains(stdout1, "/hello/local/world") && strings.Contains(stdout1, "/hello/local")) {
|
||||||
t.Fatal("Container failed to transfer content to volume")
|
t.Fatal("Container failed to transfer content to volume")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the content is not copied when the volume is readonly
|
|
||||||
tmpDir2 := tempDir(t)
|
|
||||||
defer os.RemoveAll(tmpDir2)
|
|
||||||
stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "find", "/hello"}, t)
|
|
||||||
if strings.Contains(stdout2, "/hello/local/world") || strings.Contains(stdout2, "/hello/local") {
|
|
||||||
t.Fatal("Container transfered content to readonly volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that the content is not copied when the volume is non-empty
|
|
||||||
tmpDir3 := tempDir(t)
|
|
||||||
defer os.RemoveAll(tmpDir3)
|
|
||||||
writeFile(path.Join(tmpDir3, "touch-me"), "", t)
|
|
||||||
stdout3, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:rw", tmpDir3), img.ID, "find", "/hello"}, t)
|
|
||||||
if strings.Contains(stdout3, "/hello/local/world") || strings.Contains(stdout3, "/hello/local") || !strings.Contains(stdout3, "/hello/touch-me") {
|
|
||||||
t.Fatal("Container transfered content to non-empty volume")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBindMounts(t *testing.T) {
|
func TestBindMounts(t *testing.T) {
|
||||||
@@ -1549,3 +1524,80 @@ func TestPrivilegedCannotMount(t *testing.T) {
|
|||||||
t.Fatal("Could mount into secure container")
|
t.Fatal("Could mount into secure container")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultipleVolumesFrom(t *testing.T) {
|
||||||
|
runtime := mkRuntime(t)
|
||||||
|
defer nuke(runtime)
|
||||||
|
|
||||||
|
container, err := runtime.Create(&Config{
|
||||||
|
Image: GetTestImage(runtime).ID,
|
||||||
|
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
|
||||||
|
Volumes: map[string]struct{}{"/test": {}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer runtime.Destroy(container)
|
||||||
|
|
||||||
|
for key := range container.Config.Volumes {
|
||||||
|
if key != "/test" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = container.Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := container.Volumes["/test"]
|
||||||
|
if expected == "" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
container2, err := runtime.Create(
|
||||||
|
&Config{
|
||||||
|
Image: GetTestImage(runtime).ID,
|
||||||
|
Cmd: []string{"sh", "-c", "echo -n bar > /other/foo"},
|
||||||
|
Volumes: map[string]struct{}{"/other": {}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer runtime.Destroy(container2)
|
||||||
|
|
||||||
|
for key := range container2.Config.Volumes {
|
||||||
|
if key != "/other" {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := container2.Output(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container3, err := runtime.Create(
|
||||||
|
&Config{
|
||||||
|
Image: GetTestImage(runtime).ID,
|
||||||
|
Cmd: []string{"/bin/echo", "-n", "foobar"},
|
||||||
|
VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer runtime.Destroy(container3)
|
||||||
|
|
||||||
|
if _, err := container3.Output(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(container3.Volumes)
|
||||||
|
if container3.Volumes["/test"] != container.Volumes["/test"] {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if container3.Volumes["/other"] != container2.Volumes["/other"] {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -1 +1,2 @@
|
|||||||
Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
|
Kawsar Saiyeed <kawsar.saiyeed@projiris.com> (@KSid)
|
||||||
|
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func crashTest() error {
|
|||||||
stop = false
|
stop = false
|
||||||
for i := 0; i < 100 && !stop; {
|
for i := 0; i < 100 && !stop; {
|
||||||
func() error {
|
func() error {
|
||||||
cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
|
cmd := exec.Command(DOCKERPATH, "run", "ubuntu", "echo", fmt.Sprintf("%d", totalTestCount))
|
||||||
i++
|
i++
|
||||||
totalTestCount++
|
totalTestCount++
|
||||||
outPipe, err := cmd.StdoutPipe()
|
outPipe, err := cmd.StdoutPipe()
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ What's new
|
|||||||
|
|
||||||
.. http:post:: /images/create
|
.. http:post:: /images/create
|
||||||
|
|
||||||
**New!** When pull a repo, all images are now downloaded in parallel.
|
**New!** When pulling a repo, all images are now downloaded in parallel.
|
||||||
|
|
||||||
.. http:get:: /containers/(id)/top
|
.. http:get:: /containers/(id)/top
|
||||||
|
|
||||||
|
|||||||
@@ -49,28 +49,28 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "centos:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "fedora:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
@@ -117,7 +117,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns":null,
|
"Dns":null,
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{},
|
"Volumes":{},
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ Inspect a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": ""
|
"VolumesFrom": ""
|
||||||
},
|
},
|
||||||
@@ -490,14 +490,14 @@ List Images
|
|||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-12.10",
|
"Tag":"precise",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658
|
"Created":1364102658
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-quantal",
|
"Tag":"12.04",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658
|
"Created":1364102658
|
||||||
}
|
}
|
||||||
@@ -529,9 +529,9 @@ List Images
|
|||||||
"d6434d954665" -> "d82cbacda43a"
|
"d6434d954665" -> "d82cbacda43a"
|
||||||
base -> "e9aa60c60128" [style=invis]
|
base -> "e9aa60c60128" [style=invis]
|
||||||
"074be284591f" -> "f71189fff3de"
|
"074be284591f" -> "f71189fff3de"
|
||||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
base [style=invisible]
|
base [style=invisible]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +552,7 @@ Create an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -572,8 +572,8 @@ Create an image
|
|||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
|
|
||||||
Insert a file in a image
|
Insert a file in an image
|
||||||
************************
|
*************************
|
||||||
|
|
||||||
.. http:post:: /images/(name)/insert
|
.. http:post:: /images/(name)/insert
|
||||||
|
|
||||||
@@ -608,7 +608,7 @@ Inspect an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/centos/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -638,7 +638,7 @@ Inspect an image
|
|||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"]
|
"Cmd": ["/bin/bash"]
|
||||||
,"Dns":null,
|
,"Dns":null,
|
||||||
"Image":"base",
|
"Image":"centos",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
}
|
}
|
||||||
@@ -660,7 +660,7 @@ Get the history of an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/fedora/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|||||||
@@ -49,28 +49,28 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "centos:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "fedora:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0"
|
"Status": "Exit 0"
|
||||||
@@ -117,7 +117,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns":null,
|
"Dns":null,
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{},
|
"Volumes":{},
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ Inspect a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": ""
|
"VolumesFrom": ""
|
||||||
},
|
},
|
||||||
@@ -490,14 +490,14 @@ List Images
|
|||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-12.10",
|
"Tag":"precise",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658
|
"Created":1364102658
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-quantal",
|
"Tag":"12.04",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658
|
"Created":1364102658
|
||||||
}
|
}
|
||||||
@@ -529,9 +529,9 @@ List Images
|
|||||||
"d6434d954665" -> "d82cbacda43a"
|
"d6434d954665" -> "d82cbacda43a"
|
||||||
base -> "e9aa60c60128" [style=invis]
|
base -> "e9aa60c60128" [style=invis]
|
||||||
"074be284591f" -> "f71189fff3de"
|
"074be284591f" -> "f71189fff3de"
|
||||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
base [style=invisible]
|
base [style=invisible]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +552,7 @@ Create an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -575,8 +575,8 @@ Create an image
|
|||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
|
|
||||||
Insert a file in a image
|
Insert a file in an image
|
||||||
************************
|
*************************
|
||||||
|
|
||||||
.. http:post:: /images/(name)/insert
|
.. http:post:: /images/(name)/insert
|
||||||
|
|
||||||
@@ -615,7 +615,7 @@ Inspect an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/centos/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ Inspect an image
|
|||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"]
|
"Cmd": ["/bin/bash"]
|
||||||
,"Dns":null,
|
,"Dns":null,
|
||||||
"Image":"base",
|
"Image":"centos",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
}
|
}
|
||||||
@@ -667,7 +667,7 @@ Get the history of an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/fedora/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -59,7 +59,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -69,7 +69,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "centos:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -79,7 +79,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "fedora:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -129,7 +129,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns":null,
|
"Dns":null,
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{},
|
"Volumes":{},
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ Inspect a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": ""
|
"VolumesFrom": ""
|
||||||
},
|
},
|
||||||
@@ -502,16 +502,16 @@ List Images
|
|||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-12.10",
|
"Tag":"precise",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
"VirtualSize":180116135
|
"VirtualSize":180116135
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-quantal",
|
"Tag":"12.04",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
@@ -545,9 +545,9 @@ List Images
|
|||||||
"d6434d954665" -> "d82cbacda43a"
|
"d6434d954665" -> "d82cbacda43a"
|
||||||
base -> "e9aa60c60128" [style=invis]
|
base -> "e9aa60c60128" [style=invis]
|
||||||
"074be284591f" -> "f71189fff3de"
|
"074be284591f" -> "f71189fff3de"
|
||||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
base [style=invisible]
|
base [style=invisible]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,7 +568,7 @@ Create an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -591,8 +591,8 @@ Create an image
|
|||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
|
|
||||||
Insert a file in a image
|
Insert a file in an image
|
||||||
************************
|
*************************
|
||||||
|
|
||||||
.. http:post:: /images/(name)/insert
|
.. http:post:: /images/(name)/insert
|
||||||
|
|
||||||
@@ -631,7 +631,7 @@ Inspect an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/centos/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -661,7 +661,7 @@ Inspect an image
|
|||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"]
|
"Cmd": ["/bin/bash"]
|
||||||
,"Dns":null,
|
,"Dns":null,
|
||||||
"Image":"base",
|
"Image":"centos",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
},
|
},
|
||||||
@@ -684,7 +684,7 @@ Get the history of an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/fedora/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -696,7 +696,7 @@ Get the history of an image
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Tag":["base:latest"],
|
"Tag":["ubuntu:latest"],
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"CreatedBy":"/bin/bash"
|
"CreatedBy":"/bin/bash"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -59,7 +59,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -69,7 +69,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "centos:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -79,7 +79,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "fedora:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -130,7 +130,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns":null,
|
"Dns":null,
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{},
|
"Volumes":{},
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ Inspect a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": ""
|
"VolumesFrom": ""
|
||||||
},
|
},
|
||||||
@@ -550,16 +550,16 @@ List Images
|
|||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-12.10",
|
"Tag":"precise",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
"VirtualSize":180116135
|
"VirtualSize":180116135
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-quantal",
|
"Tag":"12.04",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
@@ -593,9 +593,9 @@ List Images
|
|||||||
"d6434d954665" -> "d82cbacda43a"
|
"d6434d954665" -> "d82cbacda43a"
|
||||||
base -> "e9aa60c60128" [style=invis]
|
base -> "e9aa60c60128" [style=invis]
|
||||||
"074be284591f" -> "f71189fff3de"
|
"074be284591f" -> "f71189fff3de"
|
||||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
base [style=invisible]
|
base [style=invisible]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,7 +616,7 @@ Create an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -639,8 +639,8 @@ Create an image
|
|||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
|
|
||||||
Insert a file in a image
|
Insert a file in an image
|
||||||
************************
|
*************************
|
||||||
|
|
||||||
.. http:post:: /images/(name)/insert
|
.. http:post:: /images/(name)/insert
|
||||||
|
|
||||||
@@ -679,7 +679,7 @@ Inspect an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/centos/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -709,7 +709,7 @@ Inspect an image
|
|||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"]
|
"Cmd": ["/bin/bash"]
|
||||||
,"Dns":null,
|
,"Dns":null,
|
||||||
"Image":"base",
|
"Image":"centos",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"VolumesFrom":""
|
"VolumesFrom":""
|
||||||
},
|
},
|
||||||
@@ -732,7 +732,7 @@ Get the history of an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/fedora/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "centos:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -76,7 +76,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "fedora:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -128,7 +128,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns":null,
|
"Dns":null,
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{},
|
"Volumes":{},
|
||||||
"VolumesFrom":"",
|
"VolumesFrom":"",
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
@@ -196,7 +196,7 @@ Inspect a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
@@ -592,16 +592,16 @@ List Images
|
|||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-12.10",
|
"Tag":"precise",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
"VirtualSize":180116135
|
"VirtualSize":180116135
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-quantal",
|
"Tag":"12.04",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
@@ -635,9 +635,9 @@ List Images
|
|||||||
"d6434d954665" -> "d82cbacda43a"
|
"d6434d954665" -> "d82cbacda43a"
|
||||||
base -> "e9aa60c60128" [style=invis]
|
base -> "e9aa60c60128" [style=invis]
|
||||||
"074be284591f" -> "f71189fff3de"
|
"074be284591f" -> "f71189fff3de"
|
||||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
base [style=invisible]
|
base [style=invisible]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,7 +658,7 @@ Create an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -721,7 +721,7 @@ Inspect an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/centos/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -751,7 +751,7 @@ Inspect an image
|
|||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"]
|
"Cmd": ["/bin/bash"]
|
||||||
,"Dns":null,
|
,"Dns":null,
|
||||||
"Image":"base",
|
"Image":"centos",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"VolumesFrom":"",
|
"VolumesFrom":"",
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
@@ -776,7 +776,7 @@ Get the history of an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/fedora/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1126,10 +1126,10 @@ Monitor Docker's events
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
|
{"status":"create","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924}
|
||||||
{"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
|
{"status":"start","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924}
|
||||||
{"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966}
|
{"status":"stop","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067966}
|
||||||
{"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970}
|
{"status":"destroy","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067970}
|
||||||
|
|
||||||
:query since: timestamp used for polling
|
:query since: timestamp used for polling
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "centos:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -76,7 +76,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "fedora:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -128,7 +128,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns":null,
|
"Dns":null,
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{},
|
"Volumes":{},
|
||||||
"VolumesFrom":"",
|
"VolumesFrom":"",
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
@@ -196,7 +196,7 @@ Inspect a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
@@ -591,16 +591,16 @@ List Images
|
|||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-12.10",
|
"Tag":"precise",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
"VirtualSize":180116135
|
"VirtualSize":180116135
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Repository":"base",
|
"Repository":"ubuntu",
|
||||||
"Tag":"ubuntu-quantal",
|
"Tag":"12.04",
|
||||||
"Id":"b750fe79269d",
|
"Id":"b750fe79269d",
|
||||||
"Created":1364102658,
|
"Created":1364102658,
|
||||||
"Size":24653,
|
"Size":24653,
|
||||||
@@ -634,9 +634,9 @@ List Images
|
|||||||
"d6434d954665" -> "d82cbacda43a"
|
"d6434d954665" -> "d82cbacda43a"
|
||||||
base -> "e9aa60c60128" [style=invis]
|
base -> "e9aa60c60128" [style=invis]
|
||||||
"074be284591f" -> "f71189fff3de"
|
"074be284591f" -> "f71189fff3de"
|
||||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||||
base [style=invisible]
|
base [style=invisible]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,7 +657,7 @@ Create an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -724,7 +724,7 @@ Inspect an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/centos/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -754,7 +754,7 @@ Inspect an image
|
|||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"]
|
"Cmd": ["/bin/bash"]
|
||||||
,"Dns":null,
|
,"Dns":null,
|
||||||
"Image":"base",
|
"Image":"centos",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"VolumesFrom":"",
|
"VolumesFrom":"",
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
@@ -778,7 +778,7 @@ Get the history of an image
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/fedora/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1131,10 +1131,10 @@ Monitor Docker's events
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
|
{"status":"create","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924}
|
||||||
{"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
|
{"status":"start","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924}
|
||||||
{"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966}
|
{"status":"stop","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067966}
|
||||||
{"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970}
|
{"status":"destroy","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067970}
|
||||||
|
|
||||||
:query since: timestamp used for polling
|
:query since: timestamp used for polling
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
|
|||||||
@@ -541,10 +541,11 @@ Search
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"query":"search_term",
|
{"query":"search_term",
|
||||||
"num_results": 2,
|
"num_results": 3,
|
||||||
"results" : [
|
"results" : [
|
||||||
{"name": "dotcloud/base", "description": "A base ubuntu64 image..."},
|
{"name": "ubuntu", "description": "An ubuntu image..."},
|
||||||
{"name": "base2", "description": "A base ubuntu64 image..."},
|
{"name": "centos", "description": "A centos image..."},
|
||||||
|
{"name": "fedora", "description": "A fedora image..."}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
:keywords: Docker, documentation, developers, contributing, dev environment
|
:keywords: Docker, documentation, developers, contributing, dev environment
|
||||||
|
|
||||||
Setting Up a Dev Environment
|
Setting Up a Dev Environment
|
||||||
============================
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
To make it easier to contribute to Docker, we provide a standard
|
To make it easier to contribute to Docker, we provide a standard
|
||||||
development environment. It is important that the same environment be
|
development environment. It is important that the same environment be
|
||||||
@@ -12,11 +12,11 @@ environment defines all build dependencies: system libraries and
|
|||||||
binaries, go environment, go dependencies, etc.
|
binaries, go environment, go dependencies, etc.
|
||||||
|
|
||||||
|
|
||||||
Step 1: install docker
|
Step 1: Install Docker
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Docker's build environment itself is a Docker container, so the first
|
Docker's build environment itself is a Docker container, so the first
|
||||||
step is to install docker on your system.
|
step is to install Docker on your system.
|
||||||
|
|
||||||
You can follow the `install instructions most relevant to your system
|
You can follow the `install instructions most relevant to your system
|
||||||
<https://docs.docker.io/en/latest/installation/>`_. Make sure you have
|
<https://docs.docker.io/en/latest/installation/>`_. Make sure you have
|
||||||
@@ -24,40 +24,109 @@ a working, up-to-date docker installation, then continue to the next
|
|||||||
step.
|
step.
|
||||||
|
|
||||||
|
|
||||||
Step 2: check out the source
|
Step 2: Check out the Source
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
::
|
.. code-block:: bash
|
||||||
|
|
||||||
git clone http://git@github.com/dotcloud/docker
|
git clone http://git@github.com/dotcloud/docker
|
||||||
cd docker
|
cd docker
|
||||||
|
|
||||||
|
To checkout a different revision just use ``git checkout`` with the name of branch or revision number.
|
||||||
|
|
||||||
Step 3: build
|
|
||||||
-------------
|
|
||||||
|
|
||||||
When you are ready to build docker, run this command:
|
Step 3: Build the Environment
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
::
|
This following command will build a development environment using the Dockerfile in the current directory. Essentially, it will install all the build and runtime dependencies necessary to build and test Docker. This command will take some time to complete when you first execute it.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
sudo docker build -t docker .
|
sudo docker build -t docker .
|
||||||
|
|
||||||
This will build the revision currently checked out in the
|
|
||||||
repository. Feel free to check out the version of your choice.
|
|
||||||
|
|
||||||
If the build is successful, congratulations! You have produced a clean
|
|
||||||
build of docker, neatly encapsulated in a standard build environment.
|
|
||||||
|
|
||||||
You can run an interactive session in the newly built container:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
sudo docker run -i -t docker bash
|
|
||||||
|
|
||||||
|
|
||||||
To extract the binaries from the container:
|
If the build is successful, congratulations! You have produced a clean build of docker, neatly encapsulated in a standard build environment.
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
sudo docker run docker sh -c 'cat $(which docker)' > docker-build && chmod +x docker-build
|
Step 4: Build the Docker Binary
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
To create the Docker binary, run this command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary
|
||||||
|
|
||||||
|
This will create the Docker binary in ``./bundles/<version>-dev/binary/``
|
||||||
|
|
||||||
|
|
||||||
|
Step 5: Run the Tests
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
To run the Docker test cases you first need to disable `AppArmor <https://wiki.ubuntu.com/AppArmor>`_ using the following commands
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
sudo /etc/init.d/apparmor stop
|
||||||
|
sudo /etc/init.d/apparmor teardown
|
||||||
|
|
||||||
|
To execute the test cases, run this command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test
|
||||||
|
|
||||||
|
|
||||||
|
If the test are successful then the tail of the output should look something like this
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
--- PASS: TestWriteBroadcaster (0.00 seconds)
|
||||||
|
=== RUN TestRaceWriteBroadcaster
|
||||||
|
--- PASS: TestRaceWriteBroadcaster (0.00 seconds)
|
||||||
|
=== RUN TestTruncIndex
|
||||||
|
--- PASS: TestTruncIndex (0.00 seconds)
|
||||||
|
=== RUN TestCompareKernelVersion
|
||||||
|
--- PASS: TestCompareKernelVersion (0.00 seconds)
|
||||||
|
=== RUN TestHumanSize
|
||||||
|
--- PASS: TestHumanSize (0.00 seconds)
|
||||||
|
=== RUN TestParseHost
|
||||||
|
--- PASS: TestParseHost (0.00 seconds)
|
||||||
|
=== RUN TestParseRepositoryTag
|
||||||
|
--- PASS: TestParseRepositoryTag (0.00 seconds)
|
||||||
|
=== RUN TestGetResolvConf
|
||||||
|
--- PASS: TestGetResolvConf (0.00 seconds)
|
||||||
|
=== RUN TestCheckLocalDns
|
||||||
|
--- PASS: TestCheckLocalDns (0.00 seconds)
|
||||||
|
=== RUN TestParseRelease
|
||||||
|
--- PASS: TestParseRelease (0.00 seconds)
|
||||||
|
=== RUN TestDependencyGraphCircular
|
||||||
|
--- PASS: TestDependencyGraphCircular (0.00 seconds)
|
||||||
|
=== RUN TestDependencyGraph
|
||||||
|
--- PASS: TestDependencyGraph (0.00 seconds)
|
||||||
|
PASS
|
||||||
|
ok github.com/dotcloud/docker/utils 0.017s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Step 6: Use Docker
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
You can run an interactive session in the newly built container:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
sudo docker run -privileged -i -t docker bash
|
||||||
|
|
||||||
|
# type 'exit' to exit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. note:: The binary is availalbe outside the container in the directory ``./bundles/<version>-dev/binary/``.
|
||||||
|
|
||||||
|
|
||||||
|
**Need More Help?**
|
||||||
|
|
||||||
|
If you need more help then hop on to the `#docker-dev IRC channel <irc://chat.freenode.net#docker-dev>`_ or post a message on the `Docker developer mailinglist <https://groups.google.com/d/forum/docker-dev>`_.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql
|
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql
|
||||||
|
|
||||||
|
|
||||||
|
.. _example_list:
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ The password is 'screencast'
|
|||||||
# I had it so it was quick
|
# I had it so it was quick
|
||||||
# now let's connect using -i for interactive and with -t for terminal
|
# now let's connect using -i for interactive and with -t for terminal
|
||||||
# we execute /bin/bash to get a prompt.
|
# we execute /bin/bash to get a prompt.
|
||||||
$ docker run -i -t base /bin/bash
|
$ docker run -i -t ubuntu /bin/bash
|
||||||
# yes! we are in!
|
# yes! we are in!
|
||||||
# now lets install openssh
|
# now lets install openssh
|
||||||
$ apt-get update
|
$ apt-get update
|
||||||
|
|||||||
@@ -25,15 +25,11 @@ Ensure that layman is installed:
|
|||||||
|
|
||||||
sudo emerge -av app-portage/layman
|
sudo emerge -av app-portage/layman
|
||||||
|
|
||||||
Using your favorite editor, add
|
Add the "docker" overlay using layman:
|
||||||
``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
|
.. code-block:: bash
|
||||||
|
|
||||||
sudo layman -f -a docker
|
sudo layman -a docker
|
||||||
|
|
||||||
Once that completes, the ``app-emulation/docker`` package will be available
|
Once that completes, the ``app-emulation/docker`` package will be available
|
||||||
for emerge:
|
for emerge:
|
||||||
@@ -44,7 +40,7 @@ for emerge:
|
|||||||
|
|
||||||
If you prefer to use the official binaries, or just do not wish to compile
|
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
|
docker, emerge ``app-emulation/docker-bin`` instead. It is important to
|
||||||
remember that Gentoo is still an unsupported platform, even when using the
|
remember that Gentoo is still an unofficial platform, even when using the
|
||||||
official binaries.
|
official binaries.
|
||||||
|
|
||||||
The package should already include all the necessary dependencies. For the
|
The package should already include all the necessary dependencies. For the
|
||||||
@@ -123,3 +119,7 @@ Unfortunately, Gentoo suffers from `issue #1422
|
|||||||
fresh start of docker, the first docker run fails due to some tricky terminal
|
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
|
issues, so be sure to run something trivial (such as ``docker run -i -t busybox
|
||||||
echo hi``) before attempting to run anything important.
|
echo hi``) before attempting to run anything important.
|
||||||
|
|
||||||
|
There is a tentative (and very hacky) workaround for this in the OpenRC init
|
||||||
|
script, and it can be enabled by modifying the appropriate value in
|
||||||
|
``/etc/conf.d/docker`` after successful installation.
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ to follow them again.*
|
|||||||
|
|
||||||
# Add the Docker repository key to your local keychain
|
# Add the Docker repository key to your local keychain
|
||||||
# using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9
|
# using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9
|
||||||
sudo sh -c "curl http://get.docker.io/gpg | apt-key add -"
|
sudo sh -c "curl https://get.docker.io/gpg | apt-key add -"
|
||||||
|
|
||||||
# Add the Docker repository to your apt sources list.
|
# Add the Docker repository to your apt sources list.
|
||||||
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
|
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
|
||||||
|
|||||||
@@ -37,7 +37,5 @@ There are more example scripts for creating base images in the
|
|||||||
Docker Github Repo:
|
Docker Github Repo:
|
||||||
|
|
||||||
* `BusyBox <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-busybox.sh>`_
|
* `BusyBox <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-busybox.sh>`_
|
||||||
* `CentOS
|
|
||||||
<https://github.com/dotcloud/docker/blob/master/contrib/mkimage-centos.sh>`_
|
|
||||||
* `Debian
|
* `Debian
|
||||||
<https://github.com/dotcloud/docker/blob/master/contrib/mkimage-debian.sh>`_
|
<https://github.com/dotcloud/docker/blob/master/contrib/mkimage-debian.sh>`_
|
||||||
|
|||||||
@@ -52,31 +52,58 @@ repositories in these examples.
|
|||||||
* User images are not checked, it is therefore up to you whether or
|
* User images are not checked, it is therefore up to you whether or
|
||||||
not you trust the creator of this image.
|
not you trust the creator of this image.
|
||||||
|
|
||||||
Find public images available on the Central Index
|
.. _searching_central_index:
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
Search by name, namespace or description
|
Find Public Images on the Central Index
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
You can search the Central Index `online <https://index.docker.io>`_
|
||||||
|
or by the CLI. Searching can find images by name, user name or
|
||||||
|
description:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
sudo docker search <value>
|
$ sudo docker help search
|
||||||
|
Usage: docker search NAME
|
||||||
|
|
||||||
|
Search the docker index for images
|
||||||
|
|
||||||
Download them simply by their name
|
-notrunc=false: Don't truncate output
|
||||||
|
$ sudo docker search centos
|
||||||
|
Found 25 results matching your query ("centos")
|
||||||
|
NAME DESCRIPTION
|
||||||
|
centos
|
||||||
|
slantview/centos-chef-solo CentOS 6.4 with chef-solo.
|
||||||
|
...
|
||||||
|
|
||||||
|
There you can see two example results: ``centos`` and
|
||||||
|
``slantview/centos-chef-solo``. The second result shows that it comes
|
||||||
|
from the public repository of a user, ``slantview/``, while the first
|
||||||
|
result (``centos``) doesn't explicitly list a repository so it comes
|
||||||
|
from the trusted Central Repository. The ``/`` character separates a
|
||||||
|
user's repository and the image name.
|
||||||
|
|
||||||
|
Once you have found the image name, you can download it:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
sudo docker pull <value>
|
# sudo docker pull <value>
|
||||||
|
$ sudo docker pull centos
|
||||||
|
Pulling repository centos
|
||||||
|
539c0211cd76: Download complete
|
||||||
|
|
||||||
|
What can you do with that image? Check out the :ref:`example_list`
|
||||||
|
and, when you're ready with your own image, come back here to learn
|
||||||
|
how to share it.
|
||||||
|
|
||||||
Very similarly you can search for and browse the index online on
|
Contributing to the Central Registry
|
||||||
https://index.docker.io
|
------------------------------------
|
||||||
|
|
||||||
|
Anyone can pull public images from the Central Registry, but if you
|
||||||
Connecting to the Central Registry
|
would like to share one of your own images, then you must register a
|
||||||
----------------------------------
|
unique user name first. You can create your username and login on the
|
||||||
|
`central Docker Index online
|
||||||
You can create a user on the central Docker Index online, or by running
|
<https://index.docker.io/account/signup/>`_, or by running
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -85,22 +112,27 @@ You can create a user on the central Docker Index online, or by running
|
|||||||
This will prompt you for a username, which will become a public
|
This will prompt you for a username, which will become a public
|
||||||
namespace for your public repositories.
|
namespace for your public repositories.
|
||||||
|
|
||||||
If your username does not exist it will prompt you to also enter a
|
If your username is available then ``docker`` will also prompt you to
|
||||||
password and your e-mail address. It will then automatically log you
|
enter a password and your e-mail address. It will then automatically
|
||||||
in.
|
log you in. Now you're ready to commit and push your own images!
|
||||||
|
|
||||||
.. _container_commit:
|
.. _container_commit:
|
||||||
|
|
||||||
Committing a container to a named image
|
Committing a Container to a Named Image
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
In order to commit to the repository it is required to have committed
|
When you make changes to an existing image, those changes get saved to
|
||||||
your container to an image within your username namespace.
|
a container's file system. You can then promote that container to
|
||||||
|
become an image by making a ``commit``. In addition to converting the
|
||||||
|
container to an image, this is also your opportunity to name the
|
||||||
|
image, specifically a name that includes your user name from the
|
||||||
|
Central Docker Index (as you did a ``login`` above) and a meaningful
|
||||||
|
name for the image.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# for example docker commit $CONTAINER_ID dhrp/kickassapp
|
# format is "sudo docker commit <container_id> <username>/<imagename>"
|
||||||
sudo docker commit <container_id> <username>/<repo_name>
|
$ sudo docker commit $CONTAINER_ID myname/kickassapp
|
||||||
|
|
||||||
.. _image_push:
|
.. _image_push:
|
||||||
|
|
||||||
@@ -115,15 +147,15 @@ or tag.
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# for example docker push dhrp/kickassapp
|
# format is "docker push <username>/<repo_name>"
|
||||||
sudo docker push <username>/<repo_name>
|
$ sudo docker push myname/kickassapp
|
||||||
|
|
||||||
.. _using_private_repositories:
|
.. _using_private_repositories:
|
||||||
|
|
||||||
Private Repositories
|
Private Repositories
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Right now (version 0.5), private repositories are only possible by
|
Right now (version 0.6), private repositories are only possible by
|
||||||
hosting `your own registry
|
hosting `your own registry
|
||||||
<https://github.com/dotcloud/docker-registry>`_. To push or pull to a
|
<https://github.com/dotcloud/docker-registry>`_. To push or pull to a
|
||||||
repository on your own registry, you must prefix the tag with the
|
repository on your own registry, you must prefix the tag with the
|
||||||
|
|||||||
@@ -51,11 +51,13 @@ bundle_ubuntu() {
|
|||||||
cat >/tmp/postinstall <<EOF
|
cat >/tmp/postinstall <<EOF
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
/sbin/stop docker || true
|
/sbin/stop docker || true
|
||||||
|
/bin/grep -q "^docker:" /etc/group || /usr/sbin/addgroup --system docker || true
|
||||||
/sbin/start docker
|
/sbin/start docker
|
||||||
EOF
|
EOF
|
||||||
cat >/tmp/prerm <<EOF
|
cat >/tmp/prerm <<EOF
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
/sbin/stop docker || true
|
/sbin/stop docker || true
|
||||||
|
/usr/sbin/delgroup docker || true
|
||||||
EOF
|
EOF
|
||||||
chmod +x /tmp/postinstall /tmp/prerm
|
chmod +x /tmp/postinstall /tmp/prerm
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ git_clone github.com/gorilla/context/ 708054d61e5
|
|||||||
|
|
||||||
git_clone github.com/gorilla/mux/ 9b36453141c
|
git_clone github.com/gorilla/mux/ 9b36453141c
|
||||||
|
|
||||||
git_clone github.com/dotcloud/tar/ d06045a6d9
|
git_clone github.com/dotcloud/tar/ e5ea6bb21a
|
||||||
|
|
||||||
# Docker requires code.google.com/p/go.net/websocket
|
# Docker requires code.google.com/p/go.net/websocket
|
||||||
PKG=code.google.com/p/go.net REV=84a4013f96e0
|
PKG=code.google.com/p/go.net REV=84a4013f96e0
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func validateRepositoryName(repositoryName string) error {
|
|||||||
if !validNamespace.MatchString(namespace) {
|
if !validNamespace.MatchString(namespace) {
|
||||||
return fmt.Errorf("Invalid namespace name (%s), only [a-z0-9_] are allowed, size between 4 and 30", namespace)
|
return fmt.Errorf("Invalid namespace name (%s), only [a-z0-9_] are allowed, size between 4 and 30", namespace)
|
||||||
}
|
}
|
||||||
validRepo := regexp.MustCompile(`^([a-zA-Z0-9-_.]+)$`)
|
validRepo := regexp.MustCompile(`^([a-z0-9-_.]+)$`)
|
||||||
if !validRepo.MatchString(name) {
|
if !validRepo.MatchString(name) {
|
||||||
return fmt.Errorf("Invalid repository name (%s), only [a-zA-Z0-9-_.] are allowed", name)
|
return fmt.Errorf("Invalid repository name (%s), only [a-zA-Z0-9-_.] are allowed", name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,11 +159,11 @@ func TestPushRegistryTag(t *testing.T) {
|
|||||||
func TestPushImageJSONIndex(t *testing.T) {
|
func TestPushImageJSONIndex(t *testing.T) {
|
||||||
r := spawnTestRegistry(t)
|
r := spawnTestRegistry(t)
|
||||||
imgData := []*ImgData{
|
imgData := []*ImgData{
|
||||||
&ImgData{
|
{
|
||||||
ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
|
ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
|
||||||
Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
|
Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
|
||||||
},
|
},
|
||||||
&ImgData{
|
{
|
||||||
ID: "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
|
ID: "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
|
||||||
Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
|
Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
|
||||||
},
|
},
|
||||||
@@ -196,3 +196,13 @@ func TestSearchRepositories(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assertEqual(t, results.NumResults, 0, "Expected 0 search results")
|
assertEqual(t, results.NumResults, 0, "Expected 0 search results")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidRepositoryName(t *testing.T) {
|
||||||
|
if err := validateRepositoryName("docker/docker"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := validateRepositoryName("docker/Docker"); err == nil {
|
||||||
|
t.Log("Repository name should be invalid")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+25
-36
@@ -177,18 +177,19 @@ const (
|
|||||||
|
|
||||||
// Keywords for the PAX Extended Header
|
// Keywords for the PAX Extended Header
|
||||||
const (
|
const (
|
||||||
PAX_ATIME = "atime"
|
paxAtime = "atime"
|
||||||
PAX_CHARSET = "charset"
|
paxCharset = "charset"
|
||||||
PAX_COMMENT = "comment"
|
paxComment = "comment"
|
||||||
PAX_CTIME = "ctime" // please note that ctime is not a valid pax header.
|
paxCtime = "ctime" // please note that ctime is not a valid pax header.
|
||||||
PAX_GID = "gid"
|
paxGid = "gid"
|
||||||
PAX_GNAME = "gname"
|
paxGname = "gname"
|
||||||
PAX_LINKPATH = "linkpath"
|
paxLinkpath = "linkpath"
|
||||||
PAX_MTIME = "mtime"
|
paxMtime = "mtime"
|
||||||
PAX_PATH = "path"
|
paxPath = "path"
|
||||||
PAX_SIZE = "size"
|
paxSize = "size"
|
||||||
PAX_UID = "uid"
|
paxUid = "uid"
|
||||||
PAX_UNAME = "uname"
|
paxUname = "uname"
|
||||||
|
paxNone = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileInfoHeader creates a partially-populated Header from fi.
|
// FileInfoHeader creates a partially-populated Header from fi.
|
||||||
@@ -275,36 +276,24 @@ func (sp *slicer) next(n int) (b []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isASCII7Bit(s string) bool {
|
func isASCII(s string) bool {
|
||||||
for _, character := range s {
|
for _, c := range s {
|
||||||
if (character & 0x7f) != character {
|
if c >= 0x80 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func stripTo7Bits(s string) string {
|
func toASCII(s string) string {
|
||||||
var buffer bytes.Buffer
|
if isASCII(s) {
|
||||||
for _, character := range s {
|
return s
|
||||||
if (character & 0x7f) == character {
|
}
|
||||||
buffer.WriteRune(character)
|
var buf bytes.Buffer
|
||||||
|
for _, c := range s {
|
||||||
|
if c < 0x80 {
|
||||||
|
buf.WriteByte(byte(c))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buffer.String()
|
return buf.String()
|
||||||
}
|
|
||||||
|
|
||||||
func stripTo7BitsAndShorten(s string, maxLen int) string {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
count := 0
|
|
||||||
for _, character := range s {
|
|
||||||
if count == maxLen {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (character & 0x7f) == character {
|
|
||||||
buffer.WriteRune(character)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buffer.String()
|
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-17
@@ -95,45 +95,45 @@ func (tr *Reader) Next() (*Header, error) {
|
|||||||
func mergePAX(hdr *Header, headers map[string]string) error {
|
func mergePAX(hdr *Header, headers map[string]string) error {
|
||||||
for k, v := range headers {
|
for k, v := range headers {
|
||||||
switch k {
|
switch k {
|
||||||
case PAX_PATH:
|
case paxPath:
|
||||||
hdr.Name = v
|
hdr.Name = v
|
||||||
case PAX_LINKPATH:
|
case paxLinkpath:
|
||||||
hdr.Linkname = v
|
hdr.Linkname = v
|
||||||
case PAX_GNAME:
|
case paxGname:
|
||||||
hdr.Gname = v
|
hdr.Gname = v
|
||||||
case PAX_UNAME:
|
case paxUname:
|
||||||
hdr.Uname = v
|
hdr.Uname = v
|
||||||
case PAX_UID:
|
case paxUid:
|
||||||
uid, err := strconv.ParseInt(v, 10, 0)
|
uid, err := strconv.ParseInt(v, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr.Uid = int(uid)
|
hdr.Uid = int(uid)
|
||||||
case PAX_GID:
|
case paxGid:
|
||||||
gid, err := strconv.ParseInt(v, 10, 0)
|
gid, err := strconv.ParseInt(v, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr.Gid = int(gid)
|
hdr.Gid = int(gid)
|
||||||
case PAX_ATIME:
|
case paxAtime:
|
||||||
t, err := parsePAXTime(v)
|
t, err := parsePAXTime(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr.AccessTime = t
|
hdr.AccessTime = t
|
||||||
case PAX_MTIME:
|
case paxMtime:
|
||||||
t, err := parsePAXTime(v)
|
t, err := parsePAXTime(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr.ModTime = t
|
hdr.ModTime = t
|
||||||
case PAX_CTIME:
|
case paxCtime:
|
||||||
t, err := parsePAXTime(v)
|
t, err := parsePAXTime(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr.ChangeTime = t
|
hdr.ChangeTime = t
|
||||||
case PAX_SIZE:
|
case paxSize:
|
||||||
size, err := strconv.ParseInt(v, 10, 0)
|
size, err := strconv.ParseInt(v, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -243,13 +243,15 @@ func (tr *Reader) octal(b []byte) int64 {
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removing leading spaces.
|
// Because unused fields are filled with NULs, we need
|
||||||
for len(b) > 0 && b[0] == ' ' {
|
// to skip leading NULs. Fields may also be padded with
|
||||||
b = b[1:]
|
// spaces or NULs.
|
||||||
}
|
// So we remove leading and trailing NULs and spaces to
|
||||||
// Removing trailing NULs and spaces.
|
// be sure.
|
||||||
for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') {
|
b = bytes.Trim(b, " \x00")
|
||||||
b = b[0 : len(b)-1]
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
x, err := strconv.ParseUint(cString(b), 8, 64)
|
x, err := strconv.ParseUint(cString(b), 8, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+20
-1
@@ -142,6 +142,25 @@ var untarTests = []*untarTest{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/nil-uid.tar", // golang.org/issue/5290
|
||||||
|
headers: []*Header{
|
||||||
|
{
|
||||||
|
Name: "P1050238.JPG.log",
|
||||||
|
Mode: 0664,
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 0,
|
||||||
|
Size: 14,
|
||||||
|
ModTime: time.Unix(1365454838, 0),
|
||||||
|
Typeflag: TypeReg,
|
||||||
|
Linkname: "",
|
||||||
|
Uname: "eyefi",
|
||||||
|
Gname: "eyefi",
|
||||||
|
Devmajor: 0,
|
||||||
|
Devminor: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
@@ -152,6 +171,7 @@ testLoop:
|
|||||||
t.Errorf("test %d: Unexpected error: %v", i, err)
|
t.Errorf("test %d: Unexpected error: %v", i, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
tr := NewReader(f)
|
tr := NewReader(f)
|
||||||
for j, header := range test.headers {
|
for j, header := range test.headers {
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
@@ -172,7 +192,6 @@ testLoop:
|
|||||||
if hdr != nil || err != nil {
|
if hdr != nil || err != nil {
|
||||||
t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
|
t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
|
||||||
}
|
}
|
||||||
f.Close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
+76
-89
@@ -20,11 +20,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrWriteTooLong = errors.New("archive/tar: write too long")
|
ErrWriteTooLong = errors.New("archive/tar: write too long")
|
||||||
ErrFieldTooLong = errors.New("archive/tar: header field too long")
|
ErrFieldTooLong = errors.New("archive/tar: header field too long")
|
||||||
ErrWriteAfterClose = errors.New("archive/tar: write after close")
|
ErrWriteAfterClose = errors.New("archive/tar: write after close")
|
||||||
errNameTooLong = errors.New("archive/tar: name too long")
|
errNameTooLong = errors.New("archive/tar: name too long")
|
||||||
errFieldTooLongNoAscii = errors.New("archive/tar: header field too long or contains invalid values")
|
errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values")
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Writer provides sequential writing of a tar archive in POSIX.1 format.
|
// A Writer provides sequential writing of a tar archive in POSIX.1 format.
|
||||||
@@ -67,29 +67,23 @@ func (tw *Writer) Flush() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write s into b, terminating it with a NUL if there is room.
|
// Write s into b, terminating it with a NUL if there is room.
|
||||||
func (tw *Writer) cString(b []byte, s string) {
|
// If the value is too long for the field and allowPax is true add a paxheader record instead
|
||||||
|
func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
|
||||||
|
needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
|
||||||
|
if needsPaxHeader {
|
||||||
|
paxHeaders[paxKeyword] = s
|
||||||
|
return
|
||||||
|
}
|
||||||
if len(s) > len(b) {
|
if len(s) > len(b) {
|
||||||
if tw.err == nil {
|
if tw.err == nil {
|
||||||
tw.err = ErrFieldTooLong
|
tw.err = ErrFieldTooLong
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
copy(b, s)
|
ascii := toASCII(s)
|
||||||
if len(s) < len(b) {
|
copy(b, ascii)
|
||||||
b[len(s)] = 0
|
if len(ascii) < len(b) {
|
||||||
}
|
b[len(ascii)] = 0
|
||||||
}
|
|
||||||
|
|
||||||
// Write s into b, terminating it with a NUL if there is room. If the value is too long for the field add a paxheader record instead
|
|
||||||
func (tw *Writer) fillHeaderField(b []byte, paxHeader map[string]string, paxKeyword string, s string) {
|
|
||||||
needsPaxHeader := len(s) > len(b) || !isASCII7Bit(s)
|
|
||||||
if needsPaxHeader {
|
|
||||||
paxHeader[paxKeyword] = s
|
|
||||||
return
|
|
||||||
}
|
|
||||||
copy(b, stripTo7BitsAndShorten(s, len(b)))
|
|
||||||
if len(s) < len(b) {
|
|
||||||
b[len(s)] = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,17 +94,27 @@ func (tw *Writer) octal(b []byte, x int64) {
|
|||||||
for len(s)+1 < len(b) {
|
for len(s)+1 < len(b) {
|
||||||
s = "0" + s
|
s = "0" + s
|
||||||
}
|
}
|
||||||
tw.cString(b, s)
|
tw.cString(b, s, false, paxNone, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write x into b, either as octal or as binary (GNUtar/star extension).
|
// Write x into b, either as octal or as binary (GNUtar/star extension).
|
||||||
func (tw *Writer) numeric(b []byte, x int64) {
|
// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
|
||||||
|
func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
|
||||||
// Try octal first.
|
// Try octal first.
|
||||||
s := strconv.FormatInt(x, 8)
|
s := strconv.FormatInt(x, 8)
|
||||||
if len(s) < len(b) {
|
if len(s) < len(b) {
|
||||||
tw.octal(b, x)
|
tw.octal(b, x)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it is too long for octal, and pax is preferred, use a pax header
|
||||||
|
if allowPax && tw.preferPax {
|
||||||
|
tw.octal(b, 0)
|
||||||
|
s := strconv.FormatInt(x, 10)
|
||||||
|
paxHeaders[paxKeyword] = s
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Too big: use binary (big-endian).
|
// Too big: use binary (big-endian).
|
||||||
tw.usedBinary = true
|
tw.usedBinary = true
|
||||||
for i := len(b) - 1; x > 0 && i >= 0; i-- {
|
for i := len(b) - 1; x > 0 && i >= 0; i-- {
|
||||||
@@ -120,28 +124,6 @@ func (tw *Writer) numeric(b []byte, x int64) {
|
|||||||
b[0] |= 0x80 // highest bit indicates binary format
|
b[0] |= 0x80 // highest bit indicates binary format
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write x into b, if it is smaller than 2097151. If the value is too long for the field add a paxheader record instead
|
|
||||||
func (tw *Writer) fillNumericHeaderField(b []byte, paxHeader map[string]string, paxKeyword string, x int64) {
|
|
||||||
if tw.preferPax && x > 2097151 {
|
|
||||||
s := strconv.FormatInt(x, 10)
|
|
||||||
paxHeader[paxKeyword] = s
|
|
||||||
tw.numeric(b, 0)
|
|
||||||
} else {
|
|
||||||
tw.numeric(b, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write x into b, if it is smaller than 2097151. If the value is too long for the field add a paxheader record instead
|
|
||||||
func (tw *Writer) fillNumericLongHeaderField(b []byte, paxHeader map[string]string, paxKeyword string, x int64) {
|
|
||||||
if tw.preferPax && x > 8589934591 {
|
|
||||||
s := strconv.FormatInt(x, 10)
|
|
||||||
paxHeader[paxKeyword] = s
|
|
||||||
tw.numeric(b, 0)
|
|
||||||
} else {
|
|
||||||
tw.numeric(b, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
minTime = time.Unix(0, 0)
|
minTime = time.Unix(0, 0)
|
||||||
// There is room for 11 octal digits (33 bits) of mtime.
|
// There is room for 11 octal digits (33 bits) of mtime.
|
||||||
@@ -158,7 +140,7 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
|
|||||||
// WriteHeader writes hdr and prepares to accept the file's contents.
|
// WriteHeader writes hdr and prepares to accept the file's contents.
|
||||||
// WriteHeader calls Flush if it is not the first header.
|
// WriteHeader calls Flush if it is not the first header.
|
||||||
// Calling after a Close will return ErrWriteAfterClose.
|
// Calling after a Close will return ErrWriteAfterClose.
|
||||||
// As this method is called internally by writePax header it allows to
|
// As this method is called internally by writePax header to allow it to
|
||||||
// suppress writing the pax header.
|
// suppress writing the pax header.
|
||||||
func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
|
func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
|
||||||
if tw.closed {
|
if tw.closed {
|
||||||
@@ -172,7 +154,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// a map to hold pax header records, if any are needed
|
// a map to hold pax header records, if any are needed
|
||||||
paxHeaderRecords := make(map[string]string)
|
paxHeaders := make(map[string]string)
|
||||||
|
|
||||||
// TODO(shanemhansen): we might want to use PAX headers for
|
// TODO(shanemhansen): we might want to use PAX headers for
|
||||||
// subsecond time resolution, but for now let's just capture
|
// subsecond time resolution, but for now let's just capture
|
||||||
@@ -184,7 +166,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
|
|||||||
// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
|
// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
|
||||||
pathHeaderBytes := s.next(fileNameSize)
|
pathHeaderBytes := s.next(fileNameSize)
|
||||||
|
|
||||||
tw.fillHeaderField(pathHeaderBytes, paxHeaderRecords, PAX_PATH, hdr.Name)
|
tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
|
||||||
|
|
||||||
// Handle out of range ModTime carefully.
|
// Handle out of range ModTime carefully.
|
||||||
var modTime int64
|
var modTime int64
|
||||||
@@ -192,48 +174,48 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
|
|||||||
modTime = hdr.ModTime.Unix()
|
modTime = hdr.ModTime.Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
tw.octal(s.next(8), hdr.Mode) // 100:108
|
tw.octal(s.next(8), hdr.Mode) // 100:108
|
||||||
tw.fillNumericHeaderField(s.next(8), paxHeaderRecords, PAX_UID, int64(hdr.Uid)) // 108:116
|
tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
|
||||||
tw.fillNumericHeaderField(s.next(8), paxHeaderRecords, PAX_GID, int64(hdr.Gid)) // 116:124
|
tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
|
||||||
tw.fillNumericLongHeaderField(s.next(12), paxHeaderRecords, PAX_SIZE, hdr.Size) // 124:136
|
tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders) // 124:136
|
||||||
tw.numeric(s.next(12), modTime) // 136:148 --- consider using pax for finer granularity
|
tw.numeric(s.next(12), modTime, false, paxNone, nil) // 136:148 --- consider using pax for finer granularity
|
||||||
s.next(8) // chksum (148:156)
|
s.next(8) // chksum (148:156)
|
||||||
s.next(1)[0] = hdr.Typeflag // 156:157
|
s.next(1)[0] = hdr.Typeflag // 156:157
|
||||||
|
|
||||||
tw.fillHeaderField(s.next(100), paxHeaderRecords, PAX_LINKPATH, hdr.Linkname)
|
tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
|
||||||
|
|
||||||
copy(s.next(8), []byte("ustar\x0000")) // 257:265
|
copy(s.next(8), []byte("ustar\x0000")) // 257:265
|
||||||
tw.fillHeaderField(s.next(32), paxHeaderRecords, PAX_UNAME, hdr.Uname) // 265:297
|
tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
|
||||||
tw.fillHeaderField(s.next(32), paxHeaderRecords, PAX_GNAME, hdr.Gname) // 297:329
|
tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
|
||||||
tw.numeric(s.next(8), hdr.Devmajor) // 329:337
|
tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil) // 329:337
|
||||||
tw.numeric(s.next(8), hdr.Devminor) // 337:345
|
tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil) // 337:345
|
||||||
|
|
||||||
// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
|
// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
|
||||||
prefixHeaderBytes := s.next(155)
|
prefixHeaderBytes := s.next(155)
|
||||||
tw.cString(prefixHeaderBytes, "") // 345:500 prefix
|
tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500 prefix
|
||||||
|
|
||||||
// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
|
// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
|
||||||
if tw.usedBinary {
|
if tw.usedBinary {
|
||||||
copy(header[257:265], []byte("ustar \x00"))
|
copy(header[257:265], []byte("ustar \x00"))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, paxPathUsed := paxHeaderRecords[PAX_PATH]
|
_, paxPathUsed := paxHeaders[paxPath]
|
||||||
// try to use a ustar header when only the name is too long
|
// try to use a ustar header when only the name is too long
|
||||||
if !tw.preferPax && len(paxHeaderRecords) == 1 && paxPathUsed {
|
if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
|
||||||
suffix := hdr.Name
|
suffix := hdr.Name
|
||||||
prefix := ""
|
prefix := ""
|
||||||
if len(hdr.Name) > fileNameSize && isASCII7Bit(hdr.Name) {
|
if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
|
||||||
var err error
|
var err error
|
||||||
prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
|
prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// ok we can use a ustar long name instead of pax, now correct the fields
|
// ok we can use a ustar long name instead of pax, now correct the fields
|
||||||
|
|
||||||
// remove the path field from the pax header. this will suppress the pax header
|
// remove the path field from the pax header. this will suppress the pax header
|
||||||
delete(paxHeaderRecords, PAX_PATH)
|
delete(paxHeaders, paxPath)
|
||||||
|
|
||||||
// update the path fields
|
// update the path fields
|
||||||
tw.cString(pathHeaderBytes, suffix)
|
tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
|
||||||
tw.cString(prefixHeaderBytes, prefix)
|
tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
|
||||||
|
|
||||||
// Use the ustar magic if we used ustar long names.
|
// Use the ustar magic if we used ustar long names.
|
||||||
if len(prefix) > 0 {
|
if len(prefix) > 0 {
|
||||||
@@ -254,17 +236,16 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
|
|||||||
return tw.err
|
return tw.err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(paxHeaderRecords) > 0 {
|
if len(paxHeaders) > 0 {
|
||||||
if allowPax {
|
if !allowPax {
|
||||||
if err := tw.writePAXHeader(hdr, paxHeaderRecords); err != nil {
|
return errInvalidHeader
|
||||||
return err
|
}
|
||||||
}
|
if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
|
||||||
} else {
|
return err
|
||||||
return errFieldTooLongNoAscii
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tw.nb = int64(hdr.Size)
|
tw.nb = int64(hdr.Size)
|
||||||
tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
|
tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
|
||||||
|
|
||||||
_, tw.err = tw.w.Write(header)
|
_, tw.err = tw.w.Write(header)
|
||||||
return tw.err
|
return tw.err
|
||||||
@@ -282,8 +263,11 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er
|
|||||||
length--
|
length--
|
||||||
}
|
}
|
||||||
i := strings.LastIndex(name[:length], "/")
|
i := strings.LastIndex(name[:length], "/")
|
||||||
nlen := length - i - 1
|
// nlen contains the resulting length in the name field.
|
||||||
if i <= 0 || nlen > fileNameSize || nlen == 0 {
|
// plen contains the resulting length in the prefix field.
|
||||||
|
nlen := len(name) - i - 1
|
||||||
|
plen := i
|
||||||
|
if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
|
||||||
err = errNameTooLong
|
err = errNameTooLong
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -293,7 +277,7 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er
|
|||||||
|
|
||||||
// writePaxHeader writes an extended pax header to the
|
// writePaxHeader writes an extended pax header to the
|
||||||
// archive.
|
// archive.
|
||||||
func (tw *Writer) writePAXHeader(hdr *Header, paxHeaderRecords map[string]string) error {
|
func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
|
||||||
// Prepare extended header
|
// Prepare extended header
|
||||||
ext := new(Header)
|
ext := new(Header)
|
||||||
ext.Typeflag = TypeXHeader
|
ext.Typeflag = TypeXHeader
|
||||||
@@ -307,12 +291,16 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaderRecords map[string]string
|
|||||||
fullName := path.Join(dir,
|
fullName := path.Join(dir,
|
||||||
fmt.Sprintf("PaxHeaders.%d", pid), file)
|
fmt.Sprintf("PaxHeaders.%d", pid), file)
|
||||||
|
|
||||||
ext.Name = stripTo7BitsAndShorten(fullName, 100)
|
ascii := toASCII(fullName)
|
||||||
|
if len(ascii) > 100 {
|
||||||
|
ascii = ascii[:100]
|
||||||
|
}
|
||||||
|
ext.Name = ascii
|
||||||
// Construct the body
|
// Construct the body
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
for k, v := range paxHeaderRecords {
|
for k, v := range paxHeaders {
|
||||||
fmt.Fprint(&buf, paxHeader(k, v))
|
fmt.Fprint(&buf, paxHeader(k+"="+v))
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.Size = int64(len(buf.Bytes()))
|
ext.Size = int64(len(buf.Bytes()))
|
||||||
@@ -329,17 +317,16 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaderRecords map[string]string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// paxHeader formats a single pax record, prefixing it with the appropriate length
|
// paxHeader formats a single pax record, prefixing it with the appropriate length
|
||||||
func paxHeader(keyword string, value string) string {
|
func paxHeader(msg string) string {
|
||||||
|
const padding = 2 // Extra padding for space and newline
|
||||||
const padding = 3 // Extra padding for space and newline
|
size := len(msg) + padding
|
||||||
size := len(keyword) + len(value) + padding
|
|
||||||
size += len(strconv.Itoa(size))
|
size += len(strconv.Itoa(size))
|
||||||
record := fmt.Sprintf("%d %s=%s\n", size, keyword, value)
|
record := fmt.Sprintf("%d %s\n", size, msg)
|
||||||
if len(record) != size {
|
if len(record) != size {
|
||||||
// Final adjustment if adding size increased
|
// Final adjustment if adding size increased
|
||||||
// the number of digits in size
|
// the number of digits in size
|
||||||
size = len(record)
|
size = len(record)
|
||||||
record = fmt.Sprintf("%d %s=%s\n", size, keyword, value)
|
record = fmt.Sprintf("%d %s\n", size, msg)
|
||||||
}
|
}
|
||||||
return record
|
return record
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-8
@@ -341,17 +341,53 @@ func TestPaxNonAscii(t *testing.T) {
|
|||||||
func TestPAXHeader(t *testing.T) {
|
func TestPAXHeader(t *testing.T) {
|
||||||
medName := strings.Repeat("CD", 50)
|
medName := strings.Repeat("CD", 50)
|
||||||
longName := strings.Repeat("AB", 100)
|
longName := strings.Repeat("AB", 100)
|
||||||
paxTests := [][3]string{
|
paxTests := [][2]string{
|
||||||
{PAX_PATH, "/etc/hosts", "19 path=/etc/hosts\n"},
|
{paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"},
|
||||||
{"a", "b", "6 a=b\n"}, // Single digit length
|
{"a=b", "6 a=b\n"}, // Single digit length
|
||||||
{"a", "names", "11 a=names\n"}, // Test case involving carries
|
{"a=names", "11 a=names\n"}, // Test case involving carries
|
||||||
{PAX_PATH, longName, fmt.Sprintf("210 path=%s\n", longName)},
|
{paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)},
|
||||||
{PAX_PATH, medName, fmt.Sprintf("110 path=%s\n", medName)}}
|
{paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}}
|
||||||
|
|
||||||
for _, test := range paxTests {
|
for _, test := range paxTests {
|
||||||
field, key, expected := test[0], test[1], test[2]
|
key, expected := test[0], test[1]
|
||||||
if result := paxHeader(field, key); result != expected {
|
if result := paxHeader(key); result != expected {
|
||||||
t.Fatalf("paxHeader: got %s, expected %s", result, expected)
|
t.Fatalf("paxHeader: got %s, expected %s", result, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUSTARLongName(t *testing.T) {
|
||||||
|
// Create an archive with a path that failed to split with USTAR extension in previous versions.
|
||||||
|
fileinfo, err := os.Stat("testdata/small.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
hdr, err := FileInfoHeader(fileinfo, "")
|
||||||
|
hdr.Typeflag = TypeDir
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("os.Stat:1 %v", err)
|
||||||
|
}
|
||||||
|
// Force a PAX long name to be written. The name was taken from a practical example
|
||||||
|
// that fails and replaced ever char through numbers to anonymize the sample.
|
||||||
|
longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/"
|
||||||
|
hdr.Name = longName
|
||||||
|
|
||||||
|
hdr.Size = 0
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writer := NewWriter(&buf)
|
||||||
|
if err := writer.WriteHeader(hdr); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Test that we can get a long name back out of the archive.
|
||||||
|
reader := NewReader(&buf)
|
||||||
|
hdr, err = reader.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if hdr.Name != longName {
|
||||||
|
t.Fatal("Couldn't recover long name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user