diff --git a/commands.go b/commands.go index b127106ea9..e0d4fd0390 100644 --- a/commands.go +++ b/commands.go @@ -12,7 +12,6 @@ import ( "math/rand" "net/http" "net/url" - "path" "runtime" "strconv" "strings" @@ -356,32 +355,26 @@ func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string } func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error { - cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] NAME", "Create a new filesystem image from the contents of a tarball") - fl_stdin := cmd.Bool("stdin", false, "Read tarball from stdin") + cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball") var archive io.Reader var resp *http.Response if err := cmd.Parse(args); err != nil { return nil } - name := cmd.Arg(0) - if name == "" { + src := cmd.Arg(0) + if src == "" { return errors.New("Not enough arguments") - } - if *fl_stdin { + } else if src == "-" { archive = stdin } else { - u, err := url.Parse(name) + u, err := url.Parse(src) if err != nil { return err } if u.Scheme == "" { u.Scheme = "http" } - if u.Host == "" { - u.Host = "get.docker.io" - u.Path = path.Join("/images", u.Path) - } fmt.Fprintf(stdout, "Downloading from %s\n", u.String()) // Download with curl (pretty progress bar) // If curl is not available, fallback to http.Get() @@ -391,11 +384,17 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri } archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout) } - fmt.Fprintf(stdout, "Unpacking to %s\n", name) - img, err := srv.runtime.graph.Create(archive, "", "") + img, err := srv.runtime.graph.Create(archive, "", "Imported from "+src) if err != nil { return err } + // Optionally register the image at REPO/TAG + if repository := cmd.Arg(1); repository != "" { + tag := cmd.Arg(2) // Repository will handle an empty tag properly + if err := srv.runtime.repositories.Set(repository, tag, img.Id); err != nil { + return err + } + } fmt.Fprintln(stdout, img.Id) return nil } @@ -411,68 +410,75 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri cmd.Usage() return nil } - /* - var nameFilter string - if cmd.NArg() == 1 { - nameFilter = cmd.Arg(0) - } - */ + var nameFilter string + if cmd.NArg() == 1 { + nameFilter = cmd.Arg(0) + } w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n") + fmt.Fprintf(w, "REPOSITORY\tTAG\tID\tCREATED\tPARENT\n") } - if *quiet { - images, err := srv.runtime.graph.All() - if err != nil { - return err + allImages, err := srv.runtime.graph.Map() + if err != nil { + return err + } + for name, repository := range srv.runtime.repositories.Repositories { + if nameFilter != "" && name != nameFilter { + continue } - for _, image := range images { - fmt.Fprintln(stdout, image.Id) + for tag, id := range repository { + image, err := srv.runtime.graph.Get(id) + if err != nil { + log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err) + continue + } + delete(allImages, id) + if !*quiet { + for idx, field := range []string{ + /* REPOSITORY */ name, + /* TAG */ tag, + /* ID */ id, + /* CREATED */ HumanDuration(time.Now().Sub(image.Created)) + " ago", + /* PARENT */ image.Parent, + } { + if idx == 0 { + w.Write([]byte(field)) + } else { + w.Write([]byte("\t" + field)) + } + } + w.Write([]byte{'\n'}) + } else { + stdout.Write([]byte(image.Id + "\n")) + } } - } else { - // FIXME: - // paths, err := srv.images.Paths() - // if err != nil { - // return err - // } - // for _, name := range paths { - // if nameFilter != "" && nameFilter != name { - // continue - // } - // ids, err := srv.images.List(name) - // if err != nil { - // return err - // } - // for idx, img := range ids { - // if *limit > 0 && idx >= *limit { - // break - // } - // if !*quiet { - // for idx, field := range []string{ - // /* NAME */ name, - // /* ID */ img.Id, - // /* CREATED */ HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago", - // /* PARENT */ img.Parent, - // } { - // if idx == 0 { - // w.Write([]byte(field)) - // } else { - // w.Write([]byte("\t" + field)) - // } - // } - // w.Write([]byte{'\n'}) - // } else { - // stdout.Write([]byte(img.Id + "\n")) - // } - // } - // } - // if !*quiet { - // w.Flush() - // } - // + } + // Display images which aren't part of a + if nameFilter != "" { + for id, image := range allImages { + if !*quiet { + for idx, field := range []string{ + /* REPOSITORY */ "", + /* TAG */ "", + /* ID */ id, + /* CREATED */ HumanDuration(time.Now().Sub(image.Created)) + " ago", + /* PARENT */ image.Parent, + } { + if idx == 0 { + w.Write([]byte(field)) + } else { + w.Write([]byte("\t" + field)) + } + } + } else { + stdout.Write([]byte(image.Id + "\n")) + } + } + } + if !*quiet { + w.Flush() } return nil - } func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error { diff --git a/graph/graph.go b/graph/graph.go index 7e0bc810cd..6470e17aea 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -122,6 +122,19 @@ func (graph *Graph) GarbageCollect() error { return os.RemoveAll(garbage.Root) } +func (graph *Graph) Map() (map[string]*Image, error) { + // FIXME: this should replace All() + all, err := graph.All() + if err != nil { + return nil, err + } + images := make(map[string]*Image, len(all)) + for _, image := range all { + images[image.Id] = image + } + return images, nil +} + func (graph *Graph) All() ([]*Image, error) { files, err := ioutil.ReadDir(graph.Root) if err != nil { diff --git a/graph/tags.go b/graph/tags.go index 337dc67e14..f3d702fcf7 100644 --- a/graph/tags.go +++ b/graph/tags.go @@ -6,33 +6,31 @@ import ( "path/filepath" ) -type RepoStore struct { +type TagStore struct { path string graph *Graph - Repositories map[string]*Repository + Repositories map[string]Repository } -type Repository struct { - Tags map[string]string -} +type Repository map[string]string -func NewRepoStore(path string, graph *Graph) (*RepoStore, error) { +func NewTagStore(path string, graph *Graph) (*TagStore, error) { abspath, err := filepath.Abs(path) if err != nil { return nil, err } - store := &RepoStore{ + store := &TagStore{ path: abspath, graph: graph, - Repositories: make(map[string]*Repository), + Repositories: make(map[string]Repository), } - if err := store.Reload(); err != nil { + if err := store.Save(); err != nil { return nil, err } return store, nil } -func (store *RepoStore) Save() error { +func (store *TagStore) Save() error { // Store the json ball jsonData, err := json.Marshal(store) if err != nil { @@ -44,7 +42,7 @@ func (store *RepoStore) Save() error { return nil } -func (store *RepoStore) Reload() error { +func (store *TagStore) Reload() error { jsonData, err := ioutil.ReadFile(store.path) if err != nil { return err @@ -55,22 +53,22 @@ func (store *RepoStore) Reload() error { return nil } -func (store *RepoStore) SetTag(repoName, tag, revision string) error { +func (store *TagStore) Set(repoName, tag, revision string) error { if err := store.Reload(); err != nil { return err } - var repo *Repository + var repo Repository if r, exists := store.Repositories[repoName]; exists { repo = r } else { - repo = NewRepository() + repo = make(map[string]string) store.Repositories[repoName] = repo } - repo.Tags[tag] = revision + repo[tag] = revision return store.Save() } -func (store *RepoStore) Get(repoName string) (*Repository, error) { +func (store *TagStore) Get(repoName string) (Repository, error) { if err := store.Reload(); err != nil { return nil, err } @@ -80,21 +78,15 @@ func (store *RepoStore) Get(repoName string) (*Repository, error) { return nil, nil } -func (store *RepoStore) GetImage(repoName, tag string) (*Image, error) { +func (store *TagStore) GetImage(repoName, tag string) (*Image, error) { repo, err := store.Get(repoName) if err != nil { return nil, err } else if repo == nil { return nil, nil } - if revision, exists := repo.Tags[tag]; exists { + if revision, exists := repo[tag]; exists { return store.graph.Get(revision) } return nil, nil } - -func NewRepository() *Repository { - return &Repository{ - Tags: make(map[string]string), - } -} diff --git a/runtime.go b/runtime.go index 8d017df690..ad543f0ed8 100644 --- a/runtime.go +++ b/runtime.go @@ -20,6 +20,7 @@ type Runtime struct { containers *list.List networkManager *NetworkManager graph *graph.Graph + repositories *graph.TagStore } var sysInitPath string @@ -201,10 +202,14 @@ func NewFromDirectory(root string) (*Runtime, error) { return nil, err } - graph, err := graph.New(path.Join(root, "graph")) + g, err := graph.New(path.Join(root, "graph")) if err != nil { return nil, err } + repositories, err := graph.NewTagStore(path.Join(root, "repositories"), g) + if err != nil { + return nil, fmt.Errorf("Couldn't create Tag store: %s", err) + } netManager, err := newNetworkManager(networkBridgeIface) if err != nil { return nil, err @@ -215,7 +220,8 @@ func NewFromDirectory(root string) (*Runtime, error) { repository: runtime_repo, containers: list.New(), networkManager: netManager, - graph: graph, + graph: g, + repositories: repositories, } if err := runtime.restore(); err != nil { diff --git a/runtime_test.go b/runtime_test.go index 093dae67fd..f97d049813 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -11,7 +11,7 @@ import ( ) const testLayerPath string = "/var/lib/docker/docker-ut.tar" -const unitTestImageName string = "busybox" +const unitTestImageName string = "http://get.docker.io/images/busybox" var unitTestStoreBase string var srv *Server