|
@@ -5,13 +5,10 @@
|
|
|
package server
|
|
|
|
|
|
import (
|
|
|
- "encoding/json"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"io/ioutil"
|
|
|
- "log"
|
|
|
"net"
|
|
|
- "net/http"
|
|
|
"net/url"
|
|
|
"os"
|
|
|
"os/exec"
|
|
@@ -22,146 +19,12 @@ import (
|
|
|
"github.com/docker/docker/archive"
|
|
|
"github.com/docker/docker/builder"
|
|
|
"github.com/docker/docker/engine"
|
|
|
- "github.com/docker/docker/graph"
|
|
|
"github.com/docker/docker/image"
|
|
|
"github.com/docker/docker/pkg/parsers"
|
|
|
- "github.com/docker/docker/pkg/parsers/filters"
|
|
|
"github.com/docker/docker/registry"
|
|
|
"github.com/docker/docker/utils"
|
|
|
)
|
|
|
|
|
|
-// 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(job *engine.Job) engine.Status {
|
|
|
- if len(job.Args) != 1 {
|
|
|
- return job.Errorf("Usage: %s IMAGE\n", job.Name)
|
|
|
- }
|
|
|
- name := job.Args[0]
|
|
|
- // get image json
|
|
|
- tempdir, err := ioutil.TempDir("", "docker-export-")
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- defer os.RemoveAll(tempdir)
|
|
|
-
|
|
|
- utils.Debugf("Serializing %s", name)
|
|
|
-
|
|
|
- rootRepoMap := map[string]graph.Repository{}
|
|
|
- rootRepo, err := srv.daemon.Repositories().Get(name)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- if rootRepo != nil {
|
|
|
- // this is a base repo name, like 'busybox'
|
|
|
-
|
|
|
- for _, id := range rootRepo {
|
|
|
- if err := srv.exportImage(job.Eng, id, tempdir); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- }
|
|
|
- rootRepoMap[name] = rootRepo
|
|
|
- } else {
|
|
|
- img, err := srv.daemon.Repositories().LookupImage(name)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- if img != nil {
|
|
|
- // This is a named image like 'busybox:latest'
|
|
|
- repoName, repoTag := parsers.ParseRepositoryTag(name)
|
|
|
- if err := srv.exportImage(job.Eng, img.ID, tempdir); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- // check this length, because a lookup of a truncated has will not have a tag
|
|
|
- // and will not need to be added to this map
|
|
|
- if len(repoTag) > 0 {
|
|
|
- rootRepoMap[repoName] = graph.Repository{repoTag: img.ID}
|
|
|
- }
|
|
|
- } else {
|
|
|
- // this must be an ID that didn't get looked up just right?
|
|
|
- if err := srv.exportImage(job.Eng, name, tempdir); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- // write repositories, if there is something to write
|
|
|
- if len(rootRepoMap) > 0 {
|
|
|
- rootRepoJson, _ := json.Marshal(rootRepoMap)
|
|
|
-
|
|
|
- if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.FileMode(0644)); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- } else {
|
|
|
- utils.Debugf("There were no repositories to write")
|
|
|
- }
|
|
|
-
|
|
|
- fs, err := archive.Tar(tempdir, archive.Uncompressed)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- defer fs.Close()
|
|
|
-
|
|
|
- if _, err := io.Copy(job.Stdout, fs); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- utils.Debugf("End Serializing %s", name)
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-
|
|
|
-func (srv *Server) exportImage(eng *engine.Engine, name, tempdir string) error {
|
|
|
- for n := name; n != ""; {
|
|
|
- // temporary directory
|
|
|
- tmpImageDir := path.Join(tempdir, n)
|
|
|
- if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil {
|
|
|
- if os.IsExist(err) {
|
|
|
- return nil
|
|
|
- }
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- var version = "1.0"
|
|
|
- var versionBuf = []byte(version)
|
|
|
-
|
|
|
- if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // serialize json
|
|
|
- json, err := os.Create(path.Join(tmpImageDir, "json"))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- job := eng.Job("image_inspect", n)
|
|
|
- job.SetenvBool("raw", true)
|
|
|
- job.Stdout.Add(json)
|
|
|
- if err := job.Run(); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // serialize filesystem
|
|
|
- fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar"))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- job = eng.Job("image_tarlayer", n)
|
|
|
- job.Stdout.Add(fsTar)
|
|
|
- if err := job.Run(); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // find parent
|
|
|
- job = eng.Job("image_get", n)
|
|
|
- info, _ := job.Stdout.AddEnv()
|
|
|
- if err := job.Run(); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- n = info.Get("Parent")
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
func (srv *Server) Build(job *engine.Job) engine.Status {
|
|
|
if len(job.Args) != 0 {
|
|
|
return job.Errorf("Usage: %s\n", job.Name)
|
|
@@ -242,282 +105,6 @@ func (srv *Server) Build(job *engine.Job) engine.Status {
|
|
|
return engine.StatusOK
|
|
|
}
|
|
|
|
|
|
-// 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(job *engine.Job) engine.Status {
|
|
|
- tmpImageDir, err := ioutil.TempDir("", "docker-import-")
|
|
|
- if err != nil {
|
|
|
- return job.Error(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 job.Error(err)
|
|
|
- }
|
|
|
- if _, err := io.Copy(tarFile, job.Stdin); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- tarFile.Close()
|
|
|
-
|
|
|
- repoFile, err := os.Open(repoTarFile)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- if err := os.Mkdir(repoDir, os.ModeDir); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- if err := archive.Untar(repoFile, repoDir, nil); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
-
|
|
|
- dirs, err := ioutil.ReadDir(repoDir)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
-
|
|
|
- for _, d := range dirs {
|
|
|
- if d.IsDir() {
|
|
|
- if err := srv.recursiveLoad(job.Eng, d.Name(), tmpImageDir); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- repositoriesJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", "repositories"))
|
|
|
- if err == nil {
|
|
|
- repositories := map[string]graph.Repository{}
|
|
|
- if err := json.Unmarshal(repositoriesJson, &repositories); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
-
|
|
|
- for imageName, tagMap := range repositories {
|
|
|
- for tag, address := range tagMap {
|
|
|
- if err := srv.daemon.Repositories().Set(imageName, tag, address, true); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else if !os.IsNotExist(err) {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
-
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-
|
|
|
-func (srv *Server) recursiveLoad(eng *engine.Engine, address, tmpImageDir string) error {
|
|
|
- if err := eng.Job("image_get", address).Run(); err != nil {
|
|
|
- utils.Debugf("Loading %s", address)
|
|
|
-
|
|
|
- imageJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", address, "json"))
|
|
|
- if err != nil {
|
|
|
- utils.Debugf("Error reading json", err)
|
|
|
- return 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 := image.NewImgJSON(imageJson)
|
|
|
- if err != nil {
|
|
|
- utils.Debugf("Error unmarshalling json", err)
|
|
|
- return err
|
|
|
- }
|
|
|
- if img.Parent != "" {
|
|
|
- if !srv.daemon.Graph().Exists(img.Parent) {
|
|
|
- if err := srv.recursiveLoad(eng, img.Parent, tmpImageDir); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if err := srv.daemon.Graph().Register(imageJson, layer, img); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- utils.Debugf("Completed processing %s", address)
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (srv *Server) ImagesViz(job *engine.Job) engine.Status {
|
|
|
- images, _ := srv.daemon.Graph().Map()
|
|
|
- if images == nil {
|
|
|
- return engine.StatusOK
|
|
|
- }
|
|
|
- job.Stdout.Write([]byte("digraph docker {\n"))
|
|
|
-
|
|
|
- var (
|
|
|
- parentImage *image.Image
|
|
|
- err error
|
|
|
- )
|
|
|
- for _, image := range images {
|
|
|
- parentImage, err = image.GetParent()
|
|
|
- if err != nil {
|
|
|
- return job.Errorf("Error while getting parent image: %v", err)
|
|
|
- }
|
|
|
- if parentImage != nil {
|
|
|
- job.Stdout.Write([]byte(" \"" + parentImage.ID + "\" -> \"" + image.ID + "\"\n"))
|
|
|
- } else {
|
|
|
- job.Stdout.Write([]byte(" base -> \"" + image.ID + "\" [style=invis]\n"))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for id, repos := range srv.daemon.Repositories().GetRepoRefs() {
|
|
|
- job.Stdout.Write([]byte(" \"" + id + "\" [label=\"" + id + "\\n" + strings.Join(repos, "\\n") + "\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n"))
|
|
|
- }
|
|
|
- job.Stdout.Write([]byte(" base [style=invisible]\n}\n"))
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-
|
|
|
-func (srv *Server) Images(job *engine.Job) engine.Status {
|
|
|
- var (
|
|
|
- allImages map[string]*image.Image
|
|
|
- err error
|
|
|
- filt_tagged = true
|
|
|
- )
|
|
|
-
|
|
|
- imageFilters, err := filters.FromParam(job.Getenv("filters"))
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- if i, ok := imageFilters["dangling"]; ok {
|
|
|
- for _, value := range i {
|
|
|
- if strings.ToLower(value) == "true" {
|
|
|
- filt_tagged = false
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if job.GetenvBool("all") && filt_tagged {
|
|
|
- allImages, err = srv.daemon.Graph().Map()
|
|
|
- } else {
|
|
|
- allImages, err = srv.daemon.Graph().Heads()
|
|
|
- }
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- lookup := make(map[string]*engine.Env)
|
|
|
- srv.daemon.Repositories().Lock()
|
|
|
- for name, repository := range srv.daemon.Repositories().Repositories {
|
|
|
- if job.Getenv("filter") != "" {
|
|
|
- if match, _ := path.Match(job.Getenv("filter"), name); !match {
|
|
|
- continue
|
|
|
- }
|
|
|
- }
|
|
|
- for tag, id := range repository {
|
|
|
- image, err := srv.daemon.Graph().Get(id)
|
|
|
- if err != nil {
|
|
|
- log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- if out, exists := lookup[id]; exists {
|
|
|
- if filt_tagged {
|
|
|
- out.SetList("RepoTags", append(out.GetList("RepoTags"), fmt.Sprintf("%s:%s", name, tag)))
|
|
|
- }
|
|
|
- } else {
|
|
|
- // get the boolean list for if only the untagged images are requested
|
|
|
- delete(allImages, id)
|
|
|
- if filt_tagged {
|
|
|
- out := &engine.Env{}
|
|
|
- out.Set("ParentId", image.Parent)
|
|
|
- out.SetList("RepoTags", []string{fmt.Sprintf("%s:%s", name, tag)})
|
|
|
- out.Set("Id", image.ID)
|
|
|
- out.SetInt64("Created", image.Created.Unix())
|
|
|
- out.SetInt64("Size", image.Size)
|
|
|
- out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
|
|
- lookup[id] = out
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- srv.daemon.Repositories().Unlock()
|
|
|
-
|
|
|
- outs := engine.NewTable("Created", len(lookup))
|
|
|
- for _, value := range lookup {
|
|
|
- outs.Add(value)
|
|
|
- }
|
|
|
-
|
|
|
- // Display images which aren't part of a repository/tag
|
|
|
- if job.Getenv("filter") == "" {
|
|
|
- for _, image := range allImages {
|
|
|
- out := &engine.Env{}
|
|
|
- out.Set("ParentId", image.Parent)
|
|
|
- out.SetList("RepoTags", []string{"<none>:<none>"})
|
|
|
- out.Set("Id", image.ID)
|
|
|
- out.SetInt64("Created", image.Created.Unix())
|
|
|
- out.SetInt64("Size", image.Size)
|
|
|
- out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
|
|
- outs.Add(out)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- outs.ReverseSort()
|
|
|
- if _, err := outs.WriteListTo(job.Stdout); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-
|
|
|
-func (srv *Server) ImageHistory(job *engine.Job) engine.Status {
|
|
|
- if n := len(job.Args); n != 1 {
|
|
|
- return job.Errorf("Usage: %s IMAGE", job.Name)
|
|
|
- }
|
|
|
- name := job.Args[0]
|
|
|
- foundImage, err := srv.daemon.Repositories().LookupImage(name)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
-
|
|
|
- lookupMap := make(map[string][]string)
|
|
|
- for name, repository := range srv.daemon.Repositories().Repositories {
|
|
|
- for tag, id := range repository {
|
|
|
- // If the ID already has a reverse lookup, do not update it unless for "latest"
|
|
|
- if _, exists := lookupMap[id]; !exists {
|
|
|
- lookupMap[id] = []string{}
|
|
|
- }
|
|
|
- lookupMap[id] = append(lookupMap[id], name+":"+tag)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- outs := engine.NewTable("Created", 0)
|
|
|
- err = foundImage.WalkHistory(func(img *image.Image) error {
|
|
|
- out := &engine.Env{}
|
|
|
- out.Set("Id", img.ID)
|
|
|
- out.SetInt64("Created", img.Created.Unix())
|
|
|
- out.Set("CreatedBy", strings.Join(img.ContainerConfig.Cmd, " "))
|
|
|
- out.SetList("Tags", lookupMap[img.ID])
|
|
|
- out.SetInt64("Size", img.Size)
|
|
|
- outs.Add(out)
|
|
|
- return nil
|
|
|
- })
|
|
|
- if _, err := outs.WriteListTo(job.Stdout); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-func (srv *Server) ImageTag(job *engine.Job) engine.Status {
|
|
|
- if len(job.Args) != 2 && len(job.Args) != 3 {
|
|
|
- return job.Errorf("Usage: %s IMAGE REPOSITORY [TAG]\n", job.Name)
|
|
|
- }
|
|
|
- var tag string
|
|
|
- if len(job.Args) == 3 {
|
|
|
- tag = job.Args[2]
|
|
|
- }
|
|
|
- if err := srv.daemon.Repositories().Set(job.Args[1], tag, job.Args[0], job.GetenvBool("force")); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-
|
|
|
func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) error {
|
|
|
history, err := r.GetRemoteHistory(imgID, endpoint, token)
|
|
|
if err != nil {
|
|
@@ -1038,198 +625,6 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
|
|
|
return engine.StatusOK
|
|
|
}
|
|
|
|
|
|
-func (srv *Server) ImageImport(job *engine.Job) engine.Status {
|
|
|
- if n := len(job.Args); n != 2 && n != 3 {
|
|
|
- return job.Errorf("Usage: %s SRC REPO [TAG]", job.Name)
|
|
|
- }
|
|
|
- var (
|
|
|
- src = job.Args[0]
|
|
|
- repo = job.Args[1]
|
|
|
- tag string
|
|
|
- sf = utils.NewStreamFormatter(job.GetenvBool("json"))
|
|
|
- archive archive.ArchiveReader
|
|
|
- resp *http.Response
|
|
|
- )
|
|
|
- if len(job.Args) > 2 {
|
|
|
- tag = job.Args[2]
|
|
|
- }
|
|
|
-
|
|
|
- if src == "-" {
|
|
|
- archive = job.Stdin
|
|
|
- } else {
|
|
|
- u, err := url.Parse(src)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- if u.Scheme == "" {
|
|
|
- u.Scheme = "http"
|
|
|
- u.Host = src
|
|
|
- u.Path = ""
|
|
|
- }
|
|
|
- job.Stdout.Write(sf.FormatStatus("", "Downloading from %s", u))
|
|
|
- resp, err = utils.Download(u.String())
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- progressReader := utils.ProgressReader(resp.Body, int(resp.ContentLength), job.Stdout, sf, true, "", "Importing")
|
|
|
- defer progressReader.Close()
|
|
|
- archive = progressReader
|
|
|
- }
|
|
|
- img, err := srv.daemon.Graph().Create(archive, "", "", "Imported from "+src, "", nil, nil)
|
|
|
- if err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- // Optionally register the image at REPO/TAG
|
|
|
- if repo != "" {
|
|
|
- if err := srv.daemon.Repositories().Set(repo, tag, img.ID, true); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- }
|
|
|
- job.Stdout.Write(sf.FormatStatus("", img.ID))
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force, noprune bool) error {
|
|
|
- var (
|
|
|
- repoName, tag string
|
|
|
- tags = []string{}
|
|
|
- tagDeleted bool
|
|
|
- )
|
|
|
-
|
|
|
- repoName, tag = parsers.ParseRepositoryTag(name)
|
|
|
- if tag == "" {
|
|
|
- tag = graph.DEFAULTTAG
|
|
|
- }
|
|
|
-
|
|
|
- img, err := srv.daemon.Repositories().LookupImage(name)
|
|
|
- if err != nil {
|
|
|
- if r, _ := srv.daemon.Repositories().Get(repoName); r != nil {
|
|
|
- return fmt.Errorf("No such image: %s:%s", repoName, tag)
|
|
|
- }
|
|
|
- return fmt.Errorf("No such image: %s", name)
|
|
|
- }
|
|
|
-
|
|
|
- if strings.Contains(img.ID, name) {
|
|
|
- repoName = ""
|
|
|
- tag = ""
|
|
|
- }
|
|
|
-
|
|
|
- byParents, err := srv.daemon.Graph().ByParent()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- //If delete by id, see if the id belong only to one repository
|
|
|
- if repoName == "" {
|
|
|
- for _, repoAndTag := range srv.daemon.Repositories().ByID()[img.ID] {
|
|
|
- parsedRepo, parsedTag := parsers.ParseRepositoryTag(repoAndTag)
|
|
|
- if repoName == "" || repoName == parsedRepo {
|
|
|
- repoName = parsedRepo
|
|
|
- if parsedTag != "" {
|
|
|
- tags = append(tags, parsedTag)
|
|
|
- }
|
|
|
- } else if repoName != parsedRepo && !force {
|
|
|
- // the id belongs to multiple repos, like base:latest and user:test,
|
|
|
- // in that case return conflict
|
|
|
- return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name)
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- tags = append(tags, tag)
|
|
|
- }
|
|
|
-
|
|
|
- if !first && len(tags) > 0 {
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- //Untag the current image
|
|
|
- for _, tag := range tags {
|
|
|
- tagDeleted, err = srv.daemon.Repositories().Delete(repoName, tag)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- if tagDeleted {
|
|
|
- out := &engine.Env{}
|
|
|
- out.Set("Untagged", repoName+":"+tag)
|
|
|
- imgs.Add(out)
|
|
|
- srv.LogEvent("untag", img.ID, "")
|
|
|
- }
|
|
|
- }
|
|
|
- tags = srv.daemon.Repositories().ByID()[img.ID]
|
|
|
- if (len(tags) <= 1 && repoName == "") || len(tags) == 0 {
|
|
|
- if len(byParents[img.ID]) == 0 {
|
|
|
- if err := srv.canDeleteImage(img.ID, force, tagDeleted); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- if err := srv.daemon.Repositories().DeleteAll(img.ID); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- if err := srv.daemon.Graph().Delete(img.ID); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- out := &engine.Env{}
|
|
|
- out.Set("Deleted", img.ID)
|
|
|
- imgs.Add(out)
|
|
|
- srv.LogEvent("delete", img.ID, "")
|
|
|
- if img.Parent != "" && !noprune {
|
|
|
- err := srv.DeleteImage(img.Parent, imgs, false, force, noprune)
|
|
|
- if first {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
|
|
|
- if n := len(job.Args); n != 1 {
|
|
|
- return job.Errorf("Usage: %s IMAGE", job.Name)
|
|
|
- }
|
|
|
- imgs := engine.NewTable("", 0)
|
|
|
- if err := srv.DeleteImage(job.Args[0], imgs, true, job.GetenvBool("force"), job.GetenvBool("noprune")); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- if len(imgs.Data) == 0 {
|
|
|
- return job.Errorf("Conflict, %s wasn't deleted", job.Args[0])
|
|
|
- }
|
|
|
- if _, err := imgs.WriteListTo(job.Stdout); err != nil {
|
|
|
- return job.Error(err)
|
|
|
- }
|
|
|
- return engine.StatusOK
|
|
|
-}
|
|
|
-
|
|
|
-func (srv *Server) canDeleteImage(imgID string, force, untagged bool) error {
|
|
|
- var message string
|
|
|
- if untagged {
|
|
|
- message = " (docker untagged the image)"
|
|
|
- }
|
|
|
- for _, container := range srv.daemon.List() {
|
|
|
- parent, err := srv.daemon.Repositories().LookupImage(container.Image)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- if err := parent.WalkHistory(func(p *image.Image) error {
|
|
|
- if imgID == p.ID {
|
|
|
- if container.State.IsRunning() {
|
|
|
- if force {
|
|
|
- return fmt.Errorf("Conflict, cannot force delete %s because the running container %s is using it%s, stop it and retry", utils.TruncateID(imgID), utils.TruncateID(container.ID), message)
|
|
|
- }
|
|
|
- return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it%s, stop it and use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID), message)
|
|
|
- } else if !force {
|
|
|
- return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it%s, use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID), message)
|
|
|
- }
|
|
|
- }
|
|
|
- return nil
|
|
|
- }); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
func (srv *Server) poolAdd(kind, key string) (chan struct{}, error) {
|
|
|
srv.Lock()
|
|
|
defer srv.Unlock()
|