service.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package graph
  2. import (
  3. "fmt"
  4. "github.com/dotcloud/docker/engine"
  5. "github.com/dotcloud/docker/image"
  6. "github.com/dotcloud/docker/utils"
  7. )
  8. func (s *TagStore) Install(eng *engine.Engine) error {
  9. eng.Register("image_set", s.CmdSet)
  10. eng.Register("image_tag", s.CmdTag)
  11. eng.Register("image_get", s.CmdGet)
  12. return nil
  13. }
  14. // CmdSet stores a new image in the graph.
  15. // Images are stored in the graph using 4 elements:
  16. // - A user-defined ID
  17. // - A collection of metadata describing the image
  18. // - A directory tree stored as a tar archive (also called the "layer")
  19. // - A reference to a "parent" ID on top of which the layer should be applied
  20. //
  21. // NOTE: even though the parent ID is only useful in relation to the layer and how
  22. // to apply it (ie you could represent the full directory tree as 'parent_layer + layer',
  23. // it is treated as a top-level property of the image. This is an artifact of early
  24. // design and should probably be cleaned up in the future to simplify the design.
  25. //
  26. // Syntax: image_set ID
  27. // Input:
  28. // - Layer content must be streamed in tar format on stdin. An empty input is
  29. // valid and represents a nil layer.
  30. //
  31. // - Image metadata must be passed in the command environment.
  32. // 'json': a json-encoded object with all image metadata.
  33. // It will be stored as-is, without any encoding/decoding artifacts.
  34. // That is a requirement of the current registry client implementation,
  35. // because a re-encoded json might invalidate the image checksum at
  36. // the next upload, even with functionaly identical content.
  37. func (s *TagStore) CmdSet(job *engine.Job) engine.Status {
  38. if len(job.Args) != 1 {
  39. return job.Errorf("usage: %s NAME", job.Name)
  40. }
  41. var (
  42. imgJSON = []byte(job.Getenv("json"))
  43. layer = job.Stdin
  44. )
  45. if len(imgJSON) == 0 {
  46. return job.Errorf("mandatory key 'json' is not set")
  47. }
  48. // We have to pass an *image.Image object, even though it will be completely
  49. // ignored in favor of the redundant json data.
  50. // FIXME: the current prototype of Graph.Register is stupid and redundant.
  51. img, err := image.NewImgJSON(imgJSON)
  52. if err != nil {
  53. return job.Error(err)
  54. }
  55. if err := s.graph.Register(imgJSON, layer, img); err != nil {
  56. return job.Error(err)
  57. }
  58. return engine.StatusOK
  59. }
  60. // CmdTag assigns a new name and tag to an existing image. If the tag already exists,
  61. // it is changed and the image previously referenced by the tag loses that reference.
  62. // This may cause the old image to be garbage-collected if its reference count reaches zero.
  63. //
  64. // Syntax: image_tag NEWNAME OLDNAME
  65. // Example: image_tag shykes/myapp:latest shykes/myapp:1.42.0
  66. func (s *TagStore) CmdTag(job *engine.Job) engine.Status {
  67. if len(job.Args) != 2 {
  68. return job.Errorf("usage: %s NEWNAME OLDNAME", job.Name)
  69. }
  70. var (
  71. newName = job.Args[0]
  72. oldName = job.Args[1]
  73. )
  74. newRepo, newTag := utils.ParseRepositoryTag(newName)
  75. // FIXME: Set should either parse both old and new name, or neither.
  76. // the current prototype is inconsistent.
  77. if err := s.Set(newRepo, newTag, oldName, true); err != nil {
  78. return job.Error(err)
  79. }
  80. return engine.StatusOK
  81. }
  82. // CmdGet returns information about an image.
  83. // If the image doesn't exist, an empty object is returned, to allow
  84. // checking for an image's existence.
  85. func (s *TagStore) CmdGet(job *engine.Job) engine.Status {
  86. if len(job.Args) != 1 {
  87. return job.Errorf("usage: %s NAME", job.Name)
  88. }
  89. name := job.Args[0]
  90. res := &engine.Env{}
  91. img, err := s.LookupImage(name)
  92. // Note: if the image doesn't exist, LookupImage returns
  93. // nil, nil.
  94. if err != nil {
  95. return job.Error(err)
  96. }
  97. if img != nil {
  98. // We don't directly expose all fields of the Image objects,
  99. // to maintain a clean public API which we can maintain over
  100. // time even if the underlying structure changes.
  101. // We should have done this with the Image object to begin with...
  102. // but we didn't, so now we're doing it here.
  103. //
  104. // Fields that we're probably better off not including:
  105. // - ID (the caller already knows it, and we stay more flexible on
  106. // naming down the road)
  107. // - Parent. That field is really an implementation detail of
  108. // layer storage ("layer is a diff against this other layer).
  109. // It doesn't belong at the same level as author/description/etc.
  110. // - Config/ContainerConfig. Those structs have the same sprawl problem,
  111. // so we shouldn't include them wholesale either.
  112. // - Comment: initially created to fulfill the "every image is a git commit"
  113. // metaphor, in practice people either ignore it or use it as a
  114. // generic description field which it isn't. On deprecation shortlist.
  115. res.Set("created", fmt.Sprintf("%v", img.Created))
  116. res.Set("author", img.Author)
  117. res.Set("os", img.OS)
  118. res.Set("architecture", img.Architecture)
  119. res.Set("docker_version", img.DockerVersion)
  120. }
  121. res.WriteTo(job.Stdout)
  122. return engine.StatusOK
  123. }