store.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package reference
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "sort"
  9. "sync"
  10. "github.com/docker/distribution/digest"
  11. "github.com/docker/docker/image"
  12. "github.com/docker/docker/pkg/ioutils"
  13. )
  14. var (
  15. // ErrDoesNotExist is returned if a reference is not found in the
  16. // store.
  17. ErrDoesNotExist = errors.New("reference does not exist")
  18. )
  19. // An Association is a tuple associating a reference with an image ID.
  20. type Association struct {
  21. Ref Named
  22. ImageID image.ID
  23. }
  24. // Store provides the set of methods which can operate on a tag store.
  25. type Store interface {
  26. References(id image.ID) []Named
  27. ReferencesByName(ref Named) []Association
  28. AddTag(ref Named, id image.ID, force bool) error
  29. AddDigest(ref Canonical, id image.ID, force bool) error
  30. Delete(ref Named) (bool, error)
  31. Get(ref Named) (image.ID, error)
  32. }
  33. type store struct {
  34. mu sync.RWMutex
  35. // jsonPath is the path to the file where the serialized tag data is
  36. // stored.
  37. jsonPath string
  38. // Repositories is a map of repositories, indexed by name.
  39. Repositories map[string]repository
  40. // referencesByIDCache is a cache of references indexed by ID, to speed
  41. // up References.
  42. referencesByIDCache map[image.ID]map[string]Named
  43. }
  44. // Repository maps tags to image IDs. The key is a stringified Reference,
  45. // including the repository name.
  46. type repository map[string]image.ID
  47. type lexicalRefs []Named
  48. func (a lexicalRefs) Len() int { return len(a) }
  49. func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  50. func (a lexicalRefs) Less(i, j int) bool { return a[i].String() < a[j].String() }
  51. type lexicalAssociations []Association
  52. func (a lexicalAssociations) Len() int { return len(a) }
  53. func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  54. func (a lexicalAssociations) Less(i, j int) bool { return a[i].Ref.String() < a[j].Ref.String() }
  55. // NewReferenceStore creates a new reference store, tied to a file path where
  56. // the set of references are serialized in JSON format.
  57. func NewReferenceStore(jsonPath string) (Store, error) {
  58. abspath, err := filepath.Abs(jsonPath)
  59. if err != nil {
  60. return nil, err
  61. }
  62. store := &store{
  63. jsonPath: abspath,
  64. Repositories: make(map[string]repository),
  65. referencesByIDCache: make(map[image.ID]map[string]Named),
  66. }
  67. // Load the json file if it exists, otherwise create it.
  68. if err := store.reload(); os.IsNotExist(err) {
  69. if err := store.save(); err != nil {
  70. return nil, err
  71. }
  72. } else if err != nil {
  73. return nil, err
  74. }
  75. return store, nil
  76. }
  77. // AddTag adds a tag reference to the store. If force is set to true, existing
  78. // references can be overwritten. This only works for tags, not digests.
  79. func (store *store) AddTag(ref Named, id image.ID, force bool) error {
  80. if _, isCanonical := ref.(Canonical); isCanonical {
  81. return errors.New("refusing to create a tag with a digest reference")
  82. }
  83. return store.addReference(WithDefaultTag(ref), id, force)
  84. }
  85. // AddDigest adds a digest reference to the store.
  86. func (store *store) AddDigest(ref Canonical, id image.ID, force bool) error {
  87. return store.addReference(ref, id, force)
  88. }
  89. func (store *store) addReference(ref Named, id image.ID, force bool) error {
  90. if ref.Name() == string(digest.Canonical) {
  91. return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
  92. }
  93. store.mu.Lock()
  94. defer store.mu.Unlock()
  95. repository, exists := store.Repositories[ref.Name()]
  96. if !exists || repository == nil {
  97. repository = make(map[string]image.ID)
  98. store.Repositories[ref.Name()] = repository
  99. }
  100. refStr := ref.String()
  101. oldID, exists := repository[refStr]
  102. if exists {
  103. // force only works for tags
  104. if digested, isDigest := ref.(Canonical); isDigest {
  105. return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
  106. }
  107. if !force {
  108. return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", ref.String(), oldID.String())
  109. }
  110. if store.referencesByIDCache[oldID] != nil {
  111. delete(store.referencesByIDCache[oldID], refStr)
  112. if len(store.referencesByIDCache[oldID]) == 0 {
  113. delete(store.referencesByIDCache, oldID)
  114. }
  115. }
  116. }
  117. repository[refStr] = id
  118. if store.referencesByIDCache[id] == nil {
  119. store.referencesByIDCache[id] = make(map[string]Named)
  120. }
  121. store.referencesByIDCache[id][refStr] = ref
  122. return store.save()
  123. }
  124. // Delete deletes a reference from the store. It returns true if a deletion
  125. // happened, or false otherwise.
  126. func (store *store) Delete(ref Named) (bool, error) {
  127. ref = WithDefaultTag(ref)
  128. store.mu.Lock()
  129. defer store.mu.Unlock()
  130. repoName := ref.Name()
  131. repository, exists := store.Repositories[repoName]
  132. if !exists {
  133. return false, ErrDoesNotExist
  134. }
  135. refStr := ref.String()
  136. if id, exists := repository[refStr]; exists {
  137. delete(repository, refStr)
  138. if len(repository) == 0 {
  139. delete(store.Repositories, repoName)
  140. }
  141. if store.referencesByIDCache[id] != nil {
  142. delete(store.referencesByIDCache[id], refStr)
  143. if len(store.referencesByIDCache[id]) == 0 {
  144. delete(store.referencesByIDCache, id)
  145. }
  146. }
  147. return true, store.save()
  148. }
  149. return false, ErrDoesNotExist
  150. }
  151. // Get retrieves an item from the store by
  152. func (store *store) Get(ref Named) (image.ID, error) {
  153. ref = WithDefaultTag(ref)
  154. store.mu.RLock()
  155. defer store.mu.RUnlock()
  156. repository, exists := store.Repositories[ref.Name()]
  157. if !exists || repository == nil {
  158. return "", ErrDoesNotExist
  159. }
  160. id, exists := repository[ref.String()]
  161. if !exists {
  162. return "", ErrDoesNotExist
  163. }
  164. return id, nil
  165. }
  166. // References returns a slice of references to the given image ID. The slice
  167. // will be nil if there are no references to this image ID.
  168. func (store *store) References(id image.ID) []Named {
  169. store.mu.RLock()
  170. defer store.mu.RUnlock()
  171. // Convert the internal map to an array for two reasons:
  172. // 1) We must not return a mutable
  173. // 2) It would be ugly to expose the extraneous map keys to callers.
  174. var references []Named
  175. for _, ref := range store.referencesByIDCache[id] {
  176. references = append(references, ref)
  177. }
  178. sort.Sort(lexicalRefs(references))
  179. return references
  180. }
  181. // ReferencesByName returns the references for a given repository name.
  182. // If there are no references known for this repository name,
  183. // ReferencesByName returns nil.
  184. func (store *store) ReferencesByName(ref Named) []Association {
  185. store.mu.RLock()
  186. defer store.mu.RUnlock()
  187. repository, exists := store.Repositories[ref.Name()]
  188. if !exists {
  189. return nil
  190. }
  191. var associations []Association
  192. for refStr, refID := range repository {
  193. ref, err := ParseNamed(refStr)
  194. if err != nil {
  195. // Should never happen
  196. return nil
  197. }
  198. associations = append(associations,
  199. Association{
  200. Ref: ref,
  201. ImageID: refID,
  202. })
  203. }
  204. sort.Sort(lexicalAssociations(associations))
  205. return associations
  206. }
  207. func (store *store) save() error {
  208. // Store the json
  209. jsonData, err := json.Marshal(store)
  210. if err != nil {
  211. return err
  212. }
  213. return ioutils.AtomicWriteFile(store.jsonPath, jsonData, 0600)
  214. }
  215. func (store *store) reload() error {
  216. f, err := os.Open(store.jsonPath)
  217. if err != nil {
  218. return err
  219. }
  220. defer f.Close()
  221. if err := json.NewDecoder(f).Decode(&store); err != nil {
  222. return err
  223. }
  224. for _, repository := range store.Repositories {
  225. for refStr, refID := range repository {
  226. ref, err := ParseNamed(refStr)
  227. if err != nil {
  228. // Should never happen
  229. continue
  230. }
  231. if store.referencesByIDCache[refID] == nil {
  232. store.referencesByIDCache[refID] = make(map[string]Named)
  233. }
  234. store.referencesByIDCache[refID][refStr] = ref
  235. }
  236. }
  237. return nil
  238. }