Merge branch 'master' into 0.6.5-dm-plugin
Conflicts: server.go
This commit is contained in:
commit
2382a0f920
14 changed files with 501 additions and 30 deletions
|
@ -46,10 +46,9 @@ run apt-get install -y -q ruby1.9.3 rubygems libffi-dev
|
|||
run gem install --no-rdoc --no-ri fpm
|
||||
run apt-get install -y -q reprepro dpkg-sig
|
||||
|
||||
# Install s3cmd 1.0.1 (earlier versions don't support env variables in the config)
|
||||
run apt-get install -y -q python-pip
|
||||
run pip install s3cmd
|
||||
run pip install python-magic
|
||||
run pip install s3cmd==1.1.0-beta3
|
||||
run pip install python-magic==0.4.6
|
||||
run /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg
|
||||
|
||||
# Runtime dependencies
|
||||
|
|
22
api.go
22
api.go
|
@ -534,6 +534,26 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesGet(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
name := vars["name"]
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
}
|
||||
err := srv.ImageExport(name, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func postImagesLoad(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
err := srv.ImageLoad(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return nil
|
||||
|
@ -1036,6 +1056,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
|||
"/images/json": getImagesJSON,
|
||||
"/images/viz": getImagesViz,
|
||||
"/images/search": getImagesSearch,
|
||||
"/images/{name:.*}/get": getImagesGet,
|
||||
"/images/{name:.*}/history": getImagesHistory,
|
||||
"/images/{name:.*}/json": getImagesByName,
|
||||
"/containers/ps": getContainersJSON,
|
||||
|
@ -1052,6 +1073,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
|||
"/build": postBuild,
|
||||
"/images/create": postImagesCreate,
|
||||
"/images/{name:.*}/insert": postImagesInsert,
|
||||
"/images/load": postImagesLoad,
|
||||
"/images/{name:.*}/push": postImagesPush,
|
||||
"/images/{name:.*}/tag": postImagesTag,
|
||||
"/containers/create": postContainersCreate,
|
||||
|
|
53
commands.go
53
commands.go
|
@ -92,6 +92,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
|||
{"insert", "Insert a file in an image"},
|
||||
{"inspect", "Return low-level information on a container"},
|
||||
{"kill", "Kill a running container"},
|
||||
{"load", "Load an image from a tar archive"},
|
||||
{"login", "Register or Login to the docker registry server"},
|
||||
{"logs", "Fetch the logs of a container"},
|
||||
{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
||||
|
@ -102,6 +103,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
|||
{"rm", "Remove one or more containers"},
|
||||
{"rmi", "Remove one or more images"},
|
||||
{"run", "Run a command in a new container"},
|
||||
{"save", "Save an image to a tar archive"},
|
||||
{"search", "Search for an image in the docker index"},
|
||||
{"start", "Start a stopped container"},
|
||||
{"stop", "Stop a running container"},
|
||||
|
@ -577,6 +579,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
}
|
||||
|
||||
var cErr chan error
|
||||
var tty bool
|
||||
if *attach || *openStdin {
|
||||
if cmd.NArg() > 1 {
|
||||
return fmt.Errorf("Impossible to start and attach multiple containers at once.")
|
||||
|
@ -593,17 +596,13 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
tty = container.Config.Tty
|
||||
|
||||
if !container.Config.Tty {
|
||||
sigc := cli.forwardAllSignals(cmd.Arg(0))
|
||||
defer utils.StopCatch(sigc)
|
||||
}
|
||||
|
||||
if container.Config.Tty && cli.isTerminal {
|
||||
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var in io.ReadCloser
|
||||
|
||||
v := url.Values{}
|
||||
|
@ -641,7 +640,13 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
}
|
||||
return encounteredError
|
||||
}
|
||||
|
||||
if *openStdin || *attach {
|
||||
if tty && cli.isTerminal {
|
||||
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
|
||||
utils.Errorf("Error monitoring TTY size: %s\n", err)
|
||||
}
|
||||
}
|
||||
return <-cErr
|
||||
}
|
||||
return nil
|
||||
|
@ -1967,6 +1972,42 @@ func (cli *DockerCli) CmdCp(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdSave(args ...string) error {
|
||||
cmd := Subcmd("save", "IMAGE DESTINATION", "Save an image to a tar archive")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
if cmd.NArg() != 1 {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
image := cmd.Arg(0)
|
||||
|
||||
if err := cli.stream("GET", "/images/"+image+"/get", nil, cli.out, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdLoad(args ...string) error {
|
||||
cmd := Subcmd("load", "SOURCE", "Load an image from a tar archive")
|
||||
|
||||
if cmd.NArg() != 0 {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
err := cli.stream("POST", "/images/load", cli.in, cli.out, nil)
|
||||
if err != nil {
|
||||
fmt.Println("Send failed", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) {
|
||||
var params io.Reader
|
||||
if data != nil {
|
||||
|
|
|
@ -1171,6 +1171,53 @@ Monitor Docker's events
|
|||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
Get a tarball containing all images and tags in a repository
|
||||
************************************************************
|
||||
|
||||
.. http:get:: /images/(name)/get
|
||||
|
||||
Get a tarball containing all images and metadata for the repository specified by ``name``.
|
||||
|
||||
**Example request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/ubuntu/get
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/x-tar
|
||||
|
||||
Binary data stream
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
Load a tarball with a set of images and tags into docker
|
||||
********************************************************
|
||||
|
||||
.. http:post:: /images/load
|
||||
|
||||
Load a set of images and tags into the docker repository.
|
||||
|
||||
**Example request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /images/load
|
||||
|
||||
Tarball in body
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
3. Going further
|
||||
================
|
||||
|
|
|
@ -559,6 +559,18 @@ Known Issues (kill)
|
|||
* :issue:`197` indicates that ``docker kill`` may leave directories
|
||||
behind and make it difficult to remove the container.
|
||||
|
||||
.. _cli_load:
|
||||
|
||||
``load``
|
||||
--------
|
||||
|
||||
::
|
||||
|
||||
Usage: docker load < repository.tar
|
||||
|
||||
Loads a tarred repository from the standard input stream.
|
||||
Restores both images and tags.
|
||||
|
||||
.. _cli_login:
|
||||
|
||||
``login``
|
||||
|
@ -852,6 +864,17 @@ Known Issues (run -volumes-from)
|
|||
could indicate a permissions problem with AppArmor. Please see the
|
||||
issue for a workaround.
|
||||
|
||||
.. _cli_save:
|
||||
|
||||
``save``
|
||||
|
||||
::
|
||||
|
||||
Usage: docker save image > repository.tar
|
||||
|
||||
Streams a tarred repository to the standard output stream.
|
||||
Contains all parent layers, and all tags + versions.
|
||||
|
||||
.. _cli_search:
|
||||
|
||||
``search``
|
||||
|
|
55
engine/engine_test.go
Normal file
55
engine/engine_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
if err := Register("dummy1", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := Register("dummy1", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
|
||||
eng := newTestEngine(t)
|
||||
|
||||
//Should fail because globan handlers are copied
|
||||
//at the engine creation
|
||||
if err := eng.Register("dummy1", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
|
||||
if err := eng.Register("dummy2", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := eng.Register("dummy2", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJob(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
job1 := eng.Job("dummy1", "--level=awesome")
|
||||
|
||||
if job1.handler != nil {
|
||||
t.Fatalf("job1.handler should be empty")
|
||||
}
|
||||
|
||||
h := func(j *Job) string {
|
||||
return j.Name
|
||||
}
|
||||
|
||||
eng.Register("dummy2", h)
|
||||
job2 := eng.Job("dummy2", "--level=awesome")
|
||||
|
||||
if job2.handler == nil {
|
||||
t.Fatalf("job2.handler shouldn't be nil")
|
||||
}
|
||||
|
||||
if job2.handler(job2) != job2.Name {
|
||||
t.Fatalf("handler dummy2 was not found in job2")
|
||||
}
|
||||
}
|
|
@ -23,7 +23,101 @@ func TestSetenv(t *testing.T) {
|
|||
if val := job.Getenv("foo"); val != "bar" {
|
||||
t.Fatalf("Getenv returns incorrect value: %s", val)
|
||||
}
|
||||
|
||||
job.Setenv("bar", "")
|
||||
if val := job.Getenv("bar"); val != "" {
|
||||
t.Fatalf("Getenv returns incorrect value: %s", val)
|
||||
}
|
||||
if val := job.Getenv("nonexistent"); val != "" {
|
||||
t.Fatalf("Getenv returns incorrect value: %s", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetenvBool(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
job.SetenvBool("foo", true)
|
||||
if val := job.GetenvBool("foo"); !val {
|
||||
t.Fatalf("GetenvBool returns incorrect value: %b", val)
|
||||
}
|
||||
|
||||
job.SetenvBool("bar", false)
|
||||
if val := job.GetenvBool("bar"); val {
|
||||
t.Fatalf("GetenvBool returns incorrect value: %b", val)
|
||||
}
|
||||
|
||||
if val := job.GetenvBool("nonexistent"); val {
|
||||
t.Fatalf("GetenvBool returns incorrect value: %b", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetenvInt(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
|
||||
job.SetenvInt("foo", -42)
|
||||
if val := job.GetenvInt("foo"); val != -42 {
|
||||
t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
||||
}
|
||||
|
||||
job.SetenvInt("bar", 42)
|
||||
if val := job.GetenvInt("bar"); val != 42 {
|
||||
t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
||||
}
|
||||
if val := job.GetenvInt("nonexistent"); val != -1 {
|
||||
t.Fatalf("GetenvInt returns incorrect value: %d", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetenvList(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
|
||||
job.SetenvList("foo", []string{"bar"})
|
||||
if val := job.GetenvList("foo"); len(val) != 1 || val[0] != "bar" {
|
||||
t.Fatalf("GetenvList returns incorrect value: %v", val)
|
||||
}
|
||||
|
||||
job.SetenvList("bar", nil)
|
||||
if val := job.GetenvList("bar"); val != nil {
|
||||
t.Fatalf("GetenvList returns incorrect value: %v", val)
|
||||
}
|
||||
if val := job.GetenvList("nonexistent"); val != nil {
|
||||
t.Fatalf("GetenvList returns incorrect value: %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportEnv(t *testing.T) {
|
||||
type dummy struct {
|
||||
DummyInt int
|
||||
DummyStringArray []string
|
||||
}
|
||||
|
||||
job := mkJob(t, "dummy")
|
||||
if err := job.ImportEnv(&dummy{42, []string{"foo", "bar"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dmy := dummy{}
|
||||
if err := job.ExportEnv(&dmy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if dmy.DummyInt != 42 {
|
||||
t.Fatalf("Expected 42, got %d", dmy.DummyInt)
|
||||
}
|
||||
|
||||
if len(dmy.DummyStringArray) != 2 || dmy.DummyStringArray[0] != "foo" || dmy.DummyStringArray[1] != "bar" {
|
||||
t.Fatalf("Expected {foo, bar}, got %v", dmy.DummyStringArray)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEnviron(t *testing.T) {
|
||||
job := mkJob(t, "dummy")
|
||||
job.Setenv("foo", "bar")
|
||||
val, exists := job.Environ()["foo"]
|
||||
if !exists {
|
||||
t.Fatalf("foo not found in the environ")
|
||||
}
|
||||
if val != "bar" {
|
||||
t.Fatalf("bar not found in the environ")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,6 @@ import (
|
|||
|
||||
var globalTestID string
|
||||
|
||||
func init() {
|
||||
Register("dummy", func(job *Job) string { return "" })
|
||||
}
|
||||
|
||||
func newTestEngine(t *testing.T) *Engine {
|
||||
// Use the caller function name as a prefix.
|
||||
// This helps trace temp directories back to their test.
|
|
@ -205,8 +205,12 @@ func (job *Job) SetenvInt(key string, value int64) {
|
|||
job.Setenv(key, fmt.Sprintf("%d", value))
|
||||
}
|
||||
|
||||
// Returns nil if key not found
|
||||
func (job *Job) GetenvList(key string) []string {
|
||||
sval := job.Getenv(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
l := make([]string, 0, 1)
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
|
@ -234,7 +238,7 @@ func (job *Job) Setenv(key, value string) {
|
|||
// DecodeEnv decodes `src` as a json dictionary, and adds
|
||||
// each decoded key-value pair to the environment.
|
||||
//
|
||||
// If `text` cannot be decoded as a json dictionary, an error
|
||||
// If `src` cannot be decoded as a json dictionary, an error
|
||||
// is returned.
|
||||
func (job *Job) DecodeEnv(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
|
|
|
@ -329,7 +329,7 @@ func TestRunDisconnectTty(t *testing.T) {
|
|||
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
setTimeout(t, "Read/Write assertion timed out", 2000*time.Second, func() {
|
||||
setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
|
||||
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -330,6 +330,11 @@ func TestCommitRun(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestStart(t *testing.T) {
|
||||
_, err1 := os.Stat("/sys/fs/cgroup/cpuacct,cpu")
|
||||
_, err2 := os.Stat("/sys/fs/cgroup/cpu,cpuacct")
|
||||
if err1 == nil || err2 == nil {
|
||||
t.Skip("Fixme. Setting cpu cgroup shares doesn't work in dind on a Fedora host. The lxc utils are confused by the cpu,cpuacct mount.")
|
||||
}
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t)
|
||||
|
@ -563,7 +568,7 @@ func TestExitCode(t *testing.T) {
|
|||
|
||||
trueContainer, _, err := runtime.Create(&docker.Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/true", ""},
|
||||
Cmd: []string{"/bin/true"},
|
||||
}, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -578,7 +583,7 @@ func TestExitCode(t *testing.T) {
|
|||
|
||||
falseContainer, _, err := runtime.Create(&docker.Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/false", ""},
|
||||
Cmd: []string{"/bin/false"},
|
||||
}, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -109,7 +109,7 @@ func TestCreateRmVolumes(t *testing.T) {
|
|||
srv := mkServerFromEngine(eng, t)
|
||||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||
|
||||
config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo test"}, nil)
|
||||
config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
|
|||
srv := mkServerFromEngine(eng, t)
|
||||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||
|
||||
config, hostConfig, _, err := docker.ParseRun([]string{unitTestImageID, "/bin/cat"}, nil)
|
||||
config, hostConfig, _, err := docker.ParseRun([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ func TestRmi(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config, hostConfig, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
|
||||
config, hostConfig, _, err := docker.ParseRun([]string{unitTestImageID, "echo", "test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -256,6 +256,10 @@ func TestRmi(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := srv.ContainerWait(containerID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
imageID, err := srv.ContainerCommit(containerID, "test", "", "", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -277,6 +281,10 @@ func TestRmi(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := srv.ContainerWait(containerID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = srv.ContainerCommit(containerID, "test", "", "", "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
197
server.go
197
server.go
|
@ -197,6 +197,185 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
|
|||
return fmt.Errorf("No such container: %s", name)
|
||||
}
|
||||
|
||||
// ImageExport exports all images with the given tag. All versions
|
||||
// containing the same tag are exported. The resulting output is an
|
||||
// uncompressed tar ball.
|
||||
// name is the set of tags to export.
|
||||
// out is the writer where the images are written to.
|
||||
func (srv *Server) ImageExport(name string, out io.Writer) error {
|
||||
// get image json
|
||||
tempdir, err := ioutil.TempDir("", "docker-export-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tempdir)
|
||||
|
||||
utils.Debugf("Serializing %s", name)
|
||||
|
||||
rootRepo := srv.runtime.repositories.Repositories[name]
|
||||
for _, rootImage := range rootRepo {
|
||||
image, _ := srv.ImageInspect(rootImage)
|
||||
for i := image; i != nil; {
|
||||
// temporary directory
|
||||
tmpImageDir := path.Join(tempdir, i.ID)
|
||||
if err := os.Mkdir(tmpImageDir, os.ModeDir); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpImageDir)
|
||||
|
||||
var version = "1.0"
|
||||
var versionBuf = []byte(version)
|
||||
|
||||
if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.ModeAppend); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// serialize json
|
||||
b, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(path.Join(tmpImageDir, "json"), b, os.ModeAppend); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// serialize filesystem
|
||||
fs, err := archive.Tar(path.Join(srv.runtime.graph.Root, i.ID, "layer"), archive.Uncompressed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = io.Copy(fsTar, fs); err != nil {
|
||||
return err
|
||||
}
|
||||
fsTar.Close()
|
||||
|
||||
// find parent
|
||||
if i.Parent != "" {
|
||||
i, err = srv.ImageInspect(i.Parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
i = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write repositories
|
||||
rootRepoMap := map[string]Repository{}
|
||||
rootRepoMap[name] = rootRepo
|
||||
rootRepoJson, _ := json.Marshal(rootRepoMap)
|
||||
|
||||
if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.ModeAppend); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fs, err := archive.Tar(tempdir, archive.Uncompressed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(out, fs); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loads a set of images into the repository. This is the complementary of ImageExport.
|
||||
// The input stream is an uncompressed tar ball containing images and metadata.
|
||||
func (srv *Server) ImageLoad(in io.Reader) error {
|
||||
tmpImageDir, err := ioutil.TempDir("", "docker-import-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpImageDir)
|
||||
|
||||
var (
|
||||
repoTarFile = path.Join(tmpImageDir, "repo.tar")
|
||||
repoDir = path.Join(tmpImageDir, "repo")
|
||||
)
|
||||
|
||||
tarFile, err := os.Create(repoTarFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(tarFile, in); err != nil {
|
||||
return err
|
||||
}
|
||||
tarFile.Close()
|
||||
|
||||
repoFile, err := os.Open(repoTarFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Mkdir(repoDir, os.ModeDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := archive.Untar(repoFile, repoDir, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
repositoriesJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", "repositories"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repositories := map[string]Repository{}
|
||||
if err := json.Unmarshal(repositoriesJson, &repositories); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for imageName, tagMap := range repositories {
|
||||
for tag, address := range tagMap {
|
||||
if err := srv.recursiveLoad(address, tmpImageDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.runtime.repositories.Set(imageName, tag, address, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) recursiveLoad(address, tmpImageDir string) error {
|
||||
if _, err := srv.ImageInspect(address); err != nil {
|
||||
utils.Debugf("Loading %s", address)
|
||||
|
||||
imageJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", address, "json"))
|
||||
if err != nil {
|
||||
return err
|
||||
utils.Debugf("Error reading json", err)
|
||||
}
|
||||
|
||||
layer, err := os.Open(path.Join(tmpImageDir, "repo", address, "layer.tar"))
|
||||
if err != nil {
|
||||
utils.Debugf("Error reading embedded tar", err)
|
||||
return err
|
||||
}
|
||||
img, err := NewImgJSON(imageJson)
|
||||
if err != nil {
|
||||
utils.Debugf("Error unmarshalling json", err)
|
||||
return err
|
||||
}
|
||||
if img.Parent != "" {
|
||||
if !srv.runtime.graph.Exists(img.Parent) {
|
||||
if err := srv.recursiveLoad(img.Parent, tmpImageDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := srv.runtime.graph.Register(imageJson, layer, img); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
utils.Debugf("Completed processing %s", address)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImagesSearch(term string) ([]registry.SearchResult, error) {
|
||||
r, err := registry.NewRegistry(srv.runtime.config.Root, nil, srv.HTTPRequestFactory(nil))
|
||||
if err != nil {
|
||||
|
@ -473,6 +652,12 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
|
|||
var displayed int
|
||||
out := []APIContainers{}
|
||||
|
||||
names := map[string][]string{}
|
||||
srv.runtime.containerGraph.Walk("/", func(p string, e *graphdb.Entity) error {
|
||||
names[e.ID()] = append(names[e.ID()], p)
|
||||
return nil
|
||||
}, -1)
|
||||
|
||||
for _, container := range srv.runtime.List() {
|
||||
if !container.State.Running && !all && n == -1 && since == "" && before == "" {
|
||||
continue
|
||||
|
@ -493,25 +678,17 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
|
|||
break
|
||||
}
|
||||
displayed++
|
||||
c := createAPIContainer(container, size, srv.runtime)
|
||||
c := createAPIContainer(names[container.ID], container, size, srv.runtime)
|
||||
out = append(out, c)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func createAPIContainer(container *Container, size bool, runtime *Runtime) APIContainers {
|
||||
func createAPIContainer(names []string, container *Container, size bool, runtime *Runtime) APIContainers {
|
||||
c := APIContainers{
|
||||
ID: container.ID,
|
||||
}
|
||||
names := []string{}
|
||||
runtime.containerGraph.Walk("/", func(p string, e *graphdb.Entity) error {
|
||||
if e.ID() == container.ID {
|
||||
names = append(names, p)
|
||||
}
|
||||
return nil
|
||||
}, -1)
|
||||
c.Names = names
|
||||
|
||||
c.Image = runtime.repositories.ImageName(container.Image)
|
||||
c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
|
||||
c.Created = container.Created.Unix()
|
||||
|
|
Loading…
Add table
Reference in a new issue