tags.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package docker
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/dotcloud/docker/utils"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "sort"
  10. "strings"
  11. )
  12. const DEFAULTTAG = "latest"
  13. type TagStore struct {
  14. path string
  15. graph *Graph
  16. Repositories map[string]Repository
  17. }
  18. type Repository map[string]string
  19. func NewTagStore(path string, graph *Graph) (*TagStore, error) {
  20. abspath, err := filepath.Abs(path)
  21. if err != nil {
  22. return nil, err
  23. }
  24. store := &TagStore{
  25. path: abspath,
  26. graph: graph,
  27. Repositories: make(map[string]Repository),
  28. }
  29. // Load the json file if it exists, otherwise create it.
  30. if err := store.Reload(); os.IsNotExist(err) {
  31. if err := store.Save(); err != nil {
  32. return nil, err
  33. }
  34. } else if err != nil {
  35. return nil, err
  36. }
  37. return store, nil
  38. }
  39. func (store *TagStore) Save() error {
  40. // Store the json ball
  41. jsonData, err := json.Marshal(store)
  42. if err != nil {
  43. return err
  44. }
  45. if err := ioutil.WriteFile(store.path, jsonData, 0600); err != nil {
  46. return err
  47. }
  48. return nil
  49. }
  50. func (store *TagStore) Reload() error {
  51. jsonData, err := ioutil.ReadFile(store.path)
  52. if err != nil {
  53. return err
  54. }
  55. if err := json.Unmarshal(jsonData, store); err != nil {
  56. return err
  57. }
  58. return nil
  59. }
  60. func (store *TagStore) LookupImage(name string) (*Image, error) {
  61. // FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
  62. // (so we can pass all errors here)
  63. repos, tag := utils.ParseRepositoryTag(name)
  64. if tag == "" {
  65. tag = DEFAULTTAG
  66. }
  67. img, err := store.GetImage(repos, tag)
  68. if err != nil {
  69. return nil, err
  70. } else if img == nil {
  71. if img, err = store.graph.Get(name); err != nil {
  72. return nil, err
  73. }
  74. }
  75. return img, nil
  76. }
  77. // Return a reverse-lookup table of all the names which refer to each image
  78. // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
  79. func (store *TagStore) ByID() map[string][]string {
  80. byID := make(map[string][]string)
  81. for repoName, repository := range store.Repositories {
  82. for tag, id := range repository {
  83. name := repoName + ":" + tag
  84. if _, exists := byID[id]; !exists {
  85. byID[id] = []string{name}
  86. } else {
  87. byID[id] = append(byID[id], name)
  88. sort.Strings(byID[id])
  89. }
  90. }
  91. }
  92. return byID
  93. }
  94. func (store *TagStore) ImageName(id string) string {
  95. if names, exists := store.ByID()[id]; exists && len(names) > 0 {
  96. return names[0]
  97. }
  98. return utils.TruncateID(id)
  99. }
  100. func (store *TagStore) DeleteAll(id string) error {
  101. names, exists := store.ByID()[id]
  102. if !exists || len(names) == 0 {
  103. return nil
  104. }
  105. for _, name := range names {
  106. if strings.Contains(name, ":") {
  107. nameParts := strings.Split(name, ":")
  108. if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil {
  109. return err
  110. }
  111. } else {
  112. if _, err := store.Delete(name, ""); err != nil {
  113. return err
  114. }
  115. }
  116. }
  117. return nil
  118. }
  119. func (store *TagStore) Delete(repoName, tag string) (bool, error) {
  120. deleted := false
  121. if err := store.Reload(); err != nil {
  122. return false, err
  123. }
  124. if r, exists := store.Repositories[repoName]; exists {
  125. if tag != "" {
  126. if _, exists2 := r[tag]; exists2 {
  127. delete(r, tag)
  128. if len(r) == 0 {
  129. delete(store.Repositories, repoName)
  130. }
  131. deleted = true
  132. } else {
  133. return false, fmt.Errorf("No such tag: %s:%s", repoName, tag)
  134. }
  135. } else {
  136. delete(store.Repositories, repoName)
  137. deleted = true
  138. }
  139. } else {
  140. fmt.Errorf("No such repository: %s", repoName)
  141. }
  142. return deleted, store.Save()
  143. }
  144. func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
  145. img, err := store.LookupImage(imageName)
  146. if err != nil {
  147. return err
  148. }
  149. if tag == "" {
  150. tag = DEFAULTTAG
  151. }
  152. if err := validateRepoName(repoName); err != nil {
  153. return err
  154. }
  155. if err := validateTagName(tag); err != nil {
  156. return err
  157. }
  158. if err := store.Reload(); err != nil {
  159. return err
  160. }
  161. var repo Repository
  162. if r, exists := store.Repositories[repoName]; exists {
  163. repo = r
  164. } else {
  165. repo = make(map[string]string)
  166. if old, exists := store.Repositories[repoName]; exists && !force {
  167. return fmt.Errorf("Conflict: Tag %s:%s is already set to %s", repoName, tag, old)
  168. }
  169. store.Repositories[repoName] = repo
  170. }
  171. repo[tag] = img.ID
  172. return store.Save()
  173. }
  174. func (store *TagStore) Get(repoName string) (Repository, error) {
  175. if err := store.Reload(); err != nil {
  176. return nil, err
  177. }
  178. if r, exists := store.Repositories[repoName]; exists {
  179. return r, nil
  180. }
  181. return nil, nil
  182. }
  183. func (store *TagStore) GetImage(repoName, tagOrID string) (*Image, error) {
  184. repo, err := store.Get(repoName)
  185. if err != nil {
  186. return nil, err
  187. } else if repo == nil {
  188. return nil, nil
  189. }
  190. if revision, exists := repo[tagOrID]; exists {
  191. return store.graph.Get(revision)
  192. }
  193. // If no matching tag is found, search through images for a matching image id
  194. for _, revision := range repo {
  195. if strings.HasPrefix(revision, tagOrID) {
  196. return store.graph.Get(revision)
  197. }
  198. }
  199. return nil, nil
  200. }
  201. // Validate the name of a repository
  202. func validateRepoName(name string) error {
  203. if name == "" {
  204. return fmt.Errorf("Repository name can't be empty")
  205. }
  206. return nil
  207. }
  208. // Validate the name of a tag
  209. func validateTagName(name string) error {
  210. if name == "" {
  211. return fmt.Errorf("Tag name can't be empty")
  212. }
  213. if strings.Contains(name, "/") || strings.Contains(name, ":") {
  214. return fmt.Errorf("Illegal tag name: %s", name)
  215. }
  216. return nil
  217. }