github.com/dougm/docker@v1.5.0/graph/export.go (about)

     1  package graph
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path"
     9  
    10  	log "github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/engine"
    12  	"github.com/docker/docker/pkg/archive"
    13  	"github.com/docker/docker/pkg/parsers"
    14  	"github.com/docker/docker/registry"
    15  )
    16  
    17  // CmdImageExport exports all images with the given tag. All versions
    18  // containing the same tag are exported. The resulting output is an
    19  // uncompressed tar ball.
    20  // name is the set of tags to export.
    21  // out is the writer where the images are written to.
    22  func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status {
    23  	if len(job.Args) < 1 {
    24  		return job.Errorf("Usage: %s IMAGE [IMAGE...]\n", job.Name)
    25  	}
    26  	// get image json
    27  	tempdir, err := ioutil.TempDir("", "docker-export-")
    28  	if err != nil {
    29  		return job.Error(err)
    30  	}
    31  	defer os.RemoveAll(tempdir)
    32  
    33  	rootRepoMap := map[string]Repository{}
    34  	addKey := func(name string, tag string, id string) {
    35  		log.Debugf("add key [%s:%s]", name, tag)
    36  		if repo, ok := rootRepoMap[name]; !ok {
    37  			rootRepoMap[name] = Repository{tag: id}
    38  		} else {
    39  			repo[tag] = id
    40  		}
    41  	}
    42  	for _, name := range job.Args {
    43  		name = registry.NormalizeLocalName(name)
    44  		log.Debugf("Serializing %s", name)
    45  		rootRepo := s.Repositories[name]
    46  		if rootRepo != nil {
    47  			// this is a base repo name, like 'busybox'
    48  			for tag, id := range rootRepo {
    49  				addKey(name, tag, id)
    50  				if err := s.exportImage(job.Eng, id, tempdir); err != nil {
    51  					return job.Error(err)
    52  				}
    53  			}
    54  		} else {
    55  			img, err := s.LookupImage(name)
    56  			if err != nil {
    57  				return job.Error(err)
    58  			}
    59  
    60  			if img != nil {
    61  				// This is a named image like 'busybox:latest'
    62  				repoName, repoTag := parsers.ParseRepositoryTag(name)
    63  
    64  				// check this length, because a lookup of a truncated has will not have a tag
    65  				// and will not need to be added to this map
    66  				if len(repoTag) > 0 {
    67  					addKey(repoName, repoTag, img.ID)
    68  				}
    69  				if err := s.exportImage(job.Eng, img.ID, tempdir); err != nil {
    70  					return job.Error(err)
    71  				}
    72  
    73  			} else {
    74  				// this must be an ID that didn't get looked up just right?
    75  				if err := s.exportImage(job.Eng, name, tempdir); err != nil {
    76  					return job.Error(err)
    77  				}
    78  			}
    79  		}
    80  		log.Debugf("End Serializing %s", name)
    81  	}
    82  	// write repositories, if there is something to write
    83  	if len(rootRepoMap) > 0 {
    84  		rootRepoJson, _ := json.Marshal(rootRepoMap)
    85  		if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.FileMode(0644)); err != nil {
    86  			return job.Error(err)
    87  		}
    88  	} else {
    89  		log.Debugf("There were no repositories to write")
    90  	}
    91  
    92  	fs, err := archive.Tar(tempdir, archive.Uncompressed)
    93  	if err != nil {
    94  		return job.Error(err)
    95  	}
    96  	defer fs.Close()
    97  
    98  	if _, err := io.Copy(job.Stdout, fs); err != nil {
    99  		return job.Error(err)
   100  	}
   101  	log.Debugf("End export job: %s", job.Name)
   102  	return engine.StatusOK
   103  }
   104  
   105  // FIXME: this should be a top-level function, not a class method
   106  func (s *TagStore) exportImage(eng *engine.Engine, name, tempdir string) error {
   107  	for n := name; n != ""; {
   108  		// temporary directory
   109  		tmpImageDir := path.Join(tempdir, n)
   110  		if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil {
   111  			if os.IsExist(err) {
   112  				return nil
   113  			}
   114  			return err
   115  		}
   116  
   117  		var version = "1.0"
   118  		var versionBuf = []byte(version)
   119  
   120  		if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil {
   121  			return err
   122  		}
   123  
   124  		// serialize json
   125  		json, err := os.Create(path.Join(tmpImageDir, "json"))
   126  		if err != nil {
   127  			return err
   128  		}
   129  		job := eng.Job("image_inspect", n)
   130  		job.SetenvBool("raw", true)
   131  		job.Stdout.Add(json)
   132  		if err := job.Run(); err != nil {
   133  			return err
   134  		}
   135  
   136  		// serialize filesystem
   137  		fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar"))
   138  		if err != nil {
   139  			return err
   140  		}
   141  		job = eng.Job("image_tarlayer", n)
   142  		job.Stdout.Add(fsTar)
   143  		if err := job.Run(); err != nil {
   144  			return err
   145  		}
   146  
   147  		// find parent
   148  		job = eng.Job("image_get", n)
   149  		info, _ := job.Stdout.AddEnv()
   150  		if err := job.Run(); err != nil {
   151  			return err
   152  		}
   153  		n = info.Get("Parent")
   154  	}
   155  	return nil
   156  }