store.go 7.5 KB

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