mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge branch 'graph' of github.com:dotcloud/docker into graph
This commit is contained in:
commit
864a8d9aca
14 changed files with 147 additions and 138 deletions
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
146
commands.go
146
commands.go
|
@ -14,7 +14,6 @@ import (
|
|||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -358,32 +357,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()
|
||||
|
@ -393,11 +386,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
|
||||
}
|
||||
|
@ -580,68 +579,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 {
|
||||
|
@ -966,7 +972,7 @@ func NewServer() (*Server, error) {
|
|||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
runtime, err := New()
|
||||
runtime, err := NewRuntime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
22
container.go
22
container.go
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/graph"
|
||||
"github.com/kr/pty"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -63,11 +62,6 @@ type NetworkSettings struct {
|
|||
PortMapping map[string]string
|
||||
}
|
||||
|
||||
func GenerateId() string {
|
||||
return graph.GenerateId() // Re-use the same code to generate container and image IDs
|
||||
// (this might change when image Ids become content-based)
|
||||
}
|
||||
|
||||
func (container *Container) Cmd() *exec.Cmd {
|
||||
return container.cmd
|
||||
}
|
||||
|
@ -376,15 +370,15 @@ func (container *Container) Wait() int {
|
|||
return container.State.ExitCode
|
||||
}
|
||||
|
||||
func (container *Container) ExportRw() (graph.Archive, error) {
|
||||
return graph.Tar(container.rwPath(), graph.Uncompressed)
|
||||
func (container *Container) ExportRw() (Archive, error) {
|
||||
return Tar(container.rwPath(), Uncompressed)
|
||||
}
|
||||
|
||||
func (container *Container) Export() (graph.Archive, error) {
|
||||
func (container *Container) Export() (Archive, error) {
|
||||
if err := container.EnsureMounted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return graph.Tar(container.RootfsPath(), graph.Uncompressed)
|
||||
return Tar(container.RootfsPath(), Uncompressed)
|
||||
}
|
||||
|
||||
func (container *Container) WaitTimeout(timeout time.Duration) error {
|
||||
|
@ -420,7 +414,7 @@ func (container *Container) Mount() error {
|
|||
return image.Mount(container.RootfsPath(), container.rwPath())
|
||||
}
|
||||
|
||||
func (container *Container) Changes() ([]graph.Change, error) {
|
||||
func (container *Container) Changes() ([]Change, error) {
|
||||
image, err := container.GetImage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -428,7 +422,7 @@ func (container *Container) Changes() ([]graph.Change, error) {
|
|||
return image.Changes(container.rwPath())
|
||||
}
|
||||
|
||||
func (container *Container) GetImage() (*graph.Image, error) {
|
||||
func (container *Container) GetImage() (*Image, error) {
|
||||
if container.runtime == nil {
|
||||
return nil, fmt.Errorf("Can't get image of unregistered container")
|
||||
}
|
||||
|
@ -436,11 +430,11 @@ func (container *Container) GetImage() (*graph.Image, error) {
|
|||
}
|
||||
|
||||
func (container *Container) Mounted() (bool, error) {
|
||||
return graph.Mounted(container.RootfsPath())
|
||||
return Mounted(container.RootfsPath())
|
||||
}
|
||||
|
||||
func (container *Container) Unmount() error {
|
||||
return graph.Unmount(container.RootfsPath())
|
||||
return Unmount(container.RootfsPath())
|
||||
}
|
||||
|
||||
func (container *Container) logPath(name string) string {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -13,7 +13,7 @@ type Graph struct {
|
|||
Root string
|
||||
}
|
||||
|
||||
func New(root string) (*Graph, error) {
|
||||
func NewGraph(root string) (*Graph, error) {
|
||||
abspath, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -84,7 +84,7 @@ func (graph *Graph) Register(layerData Archive, img *Image) error {
|
|||
}
|
||||
|
||||
func (graph *Graph) Mktemp(id string) (string, error) {
|
||||
tmp, err := New(path.Join(graph.Root, ":tmp:"))
|
||||
tmp, err := NewGraph(path.Join(graph.Root, ":tmp:"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Couldn't create temp: %s", err)
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func (graph *Graph) Mktemp(id string) (string, error) {
|
|||
}
|
||||
|
||||
func (graph *Graph) Garbage() (*Graph, error) {
|
||||
return New(path.Join(graph.Root, ":garbage:"))
|
||||
return NewGraph(path.Join(graph.Root, ":garbage:"))
|
||||
}
|
||||
|
||||
func (graph *Graph) Delete(id string) error {
|
||||
|
@ -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 {
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
|
@ -28,7 +28,7 @@ func TestInit(t *testing.T) {
|
|||
|
||||
// FIXME: Do more extensive tests (ex: create multiple, delete, recreate;
|
||||
// create multiple, check the amount of images and paths, etc..)
|
||||
func TestCreate(t *testing.T) {
|
||||
func TestGraphCreate(t *testing.T) {
|
||||
graph := tempGraph(t)
|
||||
defer os.RemoveAll(graph.Root)
|
||||
archive, err := fakeTar()
|
||||
|
@ -177,7 +177,7 @@ func tempGraph(t *testing.T) *Graph {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
graph, err := New(tmp)
|
||||
graph, err := NewGraph(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import "errors"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import "syscall"
|
||||
|
19
runtime.go
19
runtime.go
|
@ -3,7 +3,6 @@ package docker
|
|||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/graph"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -19,7 +18,8 @@ type Runtime struct {
|
|||
repository string
|
||||
containers *list.List
|
||||
networkManager *NetworkManager
|
||||
graph *graph.Graph
|
||||
graph *Graph
|
||||
repositories *TagStore
|
||||
}
|
||||
|
||||
var sysInitPath string
|
||||
|
@ -190,21 +190,25 @@ func (runtime *Runtime) restore() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func New() (*Runtime, error) {
|
||||
return NewFromDirectory("/var/lib/docker")
|
||||
func NewRuntime() (*Runtime, error) {
|
||||
return NewRuntimeFromDirectory("/var/lib/docker")
|
||||
}
|
||||
|
||||
func NewFromDirectory(root string) (*Runtime, error) {
|
||||
func NewRuntimeFromDirectory(root string) (*Runtime, error) {
|
||||
runtime_repo := path.Join(root, "containers")
|
||||
|
||||
if err := os.MkdirAll(runtime_repo, 0700); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
graph, err := graph.New(path.Join(root, "graph"))
|
||||
g, err := NewGraph(path.Join(root, "graph"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repositories, err := 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 +219,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 {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/graph"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -11,7 +10,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
|
||||
|
@ -57,7 +56,7 @@ func init() {
|
|||
unitTestStoreBase = root
|
||||
|
||||
// Make it our Store root
|
||||
runtime, err := NewFromDirectory(root)
|
||||
runtime, err := NewRuntimeFromDirectory(root)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -84,7 +83,7 @@ func newTestRuntime() (*Runtime, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
runtime, err := NewFromDirectory(root)
|
||||
runtime, err := NewRuntimeFromDirectory(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -92,7 +91,7 @@ func newTestRuntime() (*Runtime, error) {
|
|||
return runtime, nil
|
||||
}
|
||||
|
||||
func GetTestImage(runtime *Runtime) *graph.Image {
|
||||
func GetTestImage(runtime *Runtime) *Image {
|
||||
imgs, err := runtime.graph.All()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -102,7 +101,7 @@ func GetTestImage(runtime *Runtime) *graph.Image {
|
|||
return imgs[0]
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
func TestRuntimeCreate(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -269,7 +268,7 @@ func TestRestore(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runtime1, err := NewFromDirectory(root)
|
||||
runtime1, err := NewRuntimeFromDirectory(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -294,7 +293,7 @@ func TestRestore(t *testing.T) {
|
|||
|
||||
// Here are are simulating a docker restart - that is, reloading all containers
|
||||
// from scratch
|
||||
runtime2, err := NewFromDirectory(root)
|
||||
runtime2, err := NewRuntimeFromDirectory(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package graph
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue