store.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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/reference"
  11. "github.com/docker/docker/pkg/ioutils"
  12. "github.com/opencontainers/go-digest"
  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 reference.Named
  22. ID digest.Digest
  23. }
  24. // Store provides the set of methods which can operate on a tag store.
  25. type Store interface {
  26. References(id digest.Digest) []reference.Named
  27. ReferencesByName(ref reference.Named) []Association
  28. AddTag(ref reference.Named, id digest.Digest, force bool) error
  29. AddDigest(ref reference.Canonical, id digest.Digest, force bool) error
  30. Delete(ref reference.Named) (bool, error)
  31. Get(ref reference.Named) (digest.Digest, 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[digest.Digest]map[string]reference.Named
  43. }
  44. // Repository maps tags to digests. The key is a stringified Reference,
  45. // including the repository name.
  46. type repository map[string]digest.Digest
  47. type lexicalRefs []reference.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 {
  51. return a[i].String() < a[j].String()
  52. }
  53. type lexicalAssociations []Association
  54. func (a lexicalAssociations) Len() int { return len(a) }
  55. func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  56. func (a lexicalAssociations) Less(i, j int) bool {
  57. return a[i].Ref.String() < a[j].Ref.String()
  58. }
  59. // NewReferenceStore creates a new reference store, tied to a file path where
  60. // the set of references are serialized in JSON format.
  61. func NewReferenceStore(jsonPath string) (Store, error) {
  62. abspath, err := filepath.Abs(jsonPath)
  63. if err != nil {
  64. return nil, err
  65. }
  66. store := &store{
  67. jsonPath: abspath,
  68. Repositories: make(map[string]repository),
  69. referencesByIDCache: make(map[digest.Digest]map[string]reference.Named),
  70. }
  71. // Load the json file if it exists, otherwise create it.
  72. if err := store.reload(); os.IsNotExist(err) {
  73. if err := store.save(); err != nil {
  74. return nil, err
  75. }
  76. } else if err != nil {
  77. return nil, err
  78. }
  79. return store, nil
  80. }
  81. // AddTag adds a tag reference to the store. If force is set to true, existing
  82. // references can be overwritten. This only works for tags, not digests.
  83. func (store *store) AddTag(ref reference.Named, id digest.Digest, force bool) error {
  84. if _, isCanonical := ref.(reference.Canonical); isCanonical {
  85. return errors.New("refusing to create a tag with a digest reference")
  86. }
  87. return store.addReference(reference.TagNameOnly(ref), id, force)
  88. }
  89. // AddDigest adds a digest reference to the store.
  90. func (store *store) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error {
  91. return store.addReference(ref, id, force)
  92. }
  93. func (store *store) addReference(ref reference.Named, id digest.Digest, force bool) error {
  94. // If the reference includes a digest and a tag, we must store only the
  95. // digest.
  96. canonical, isCanonical := ref.(reference.Canonical)
  97. _, isNamedTagged := ref.(reference.NamedTagged)
  98. if isCanonical && isNamedTagged {
  99. trimmed, err := reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest())
  100. if err != nil {
  101. // should never happen
  102. return err
  103. }
  104. ref = trimmed
  105. }
  106. refName := reference.FamiliarName(ref)
  107. refStr := reference.FamiliarString(ref)
  108. if refName == string(digest.Canonical) {
  109. return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
  110. }
  111. store.mu.Lock()
  112. defer store.mu.Unlock()
  113. repository, exists := store.Repositories[refName]
  114. if !exists || repository == nil {
  115. repository = make(map[string]digest.Digest)
  116. store.Repositories[refName] = repository
  117. }
  118. oldID, exists := repository[refStr]
  119. if exists {
  120. // force only works for tags
  121. if digested, isDigest := ref.(reference.Canonical); isDigest {
  122. return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
  123. }
  124. if !force {
  125. return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", refStr, oldID.String())
  126. }
  127. if store.referencesByIDCache[oldID] != nil {
  128. delete(store.referencesByIDCache[oldID], refStr)
  129. if len(store.referencesByIDCache[oldID]) == 0 {
  130. delete(store.referencesByIDCache, oldID)
  131. }
  132. }
  133. }
  134. repository[refStr] = id
  135. if store.referencesByIDCache[id] == nil {
  136. store.referencesByIDCache[id] = make(map[string]reference.Named)
  137. }
  138. store.referencesByIDCache[id][refStr] = ref
  139. return store.save()
  140. }
  141. // Delete deletes a reference from the store. It returns true if a deletion
  142. // happened, or false otherwise.
  143. func (store *store) Delete(ref reference.Named) (bool, error) {
  144. ref = reference.TagNameOnly(ref)
  145. refName := reference.FamiliarName(ref)
  146. refStr := reference.FamiliarString(ref)
  147. store.mu.Lock()
  148. defer store.mu.Unlock()
  149. repository, exists := store.Repositories[refName]
  150. if !exists {
  151. return false, ErrDoesNotExist
  152. }
  153. if id, exists := repository[refStr]; exists {
  154. delete(repository, refStr)
  155. if len(repository) == 0 {
  156. delete(store.Repositories, refName)
  157. }
  158. if store.referencesByIDCache[id] != nil {
  159. delete(store.referencesByIDCache[id], refStr)
  160. if len(store.referencesByIDCache[id]) == 0 {
  161. delete(store.referencesByIDCache, id)
  162. }
  163. }
  164. return true, store.save()
  165. }
  166. return false, ErrDoesNotExist
  167. }
  168. // Get retrieves an item from the store by reference
  169. func (store *store) Get(ref reference.Named) (digest.Digest, error) {
  170. if canonical, ok := ref.(reference.Canonical); ok {
  171. // If reference contains both tag and digest, only
  172. // lookup by digest as it takes precendent over
  173. // tag, until tag/digest combos are stored.
  174. if _, ok := ref.(reference.Tagged); ok {
  175. var err error
  176. ref, err = reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest())
  177. if err != nil {
  178. return "", err
  179. }
  180. }
  181. } else {
  182. ref = reference.TagNameOnly(ref)
  183. }
  184. refName := reference.FamiliarName(ref)
  185. refStr := reference.FamiliarString(ref)
  186. store.mu.RLock()
  187. defer store.mu.RUnlock()
  188. repository, exists := store.Repositories[refName]
  189. if !exists || repository == nil {
  190. return "", ErrDoesNotExist
  191. }
  192. id, exists := repository[refStr]
  193. if !exists {
  194. return "", ErrDoesNotExist
  195. }
  196. return id, nil
  197. }
  198. // References returns a slice of references to the given ID. The slice
  199. // will be nil if there are no references to this ID.
  200. func (store *store) References(id digest.Digest) []reference.Named {
  201. store.mu.RLock()
  202. defer store.mu.RUnlock()
  203. // Convert the internal map to an array for two reasons:
  204. // 1) We must not return a mutable
  205. // 2) It would be ugly to expose the extraneous map keys to callers.
  206. var references []reference.Named
  207. for _, ref := range store.referencesByIDCache[id] {
  208. references = append(references, ref)
  209. }
  210. sort.Sort(lexicalRefs(references))
  211. return references
  212. }
  213. // ReferencesByName returns the references for a given repository name.
  214. // If there are no references known for this repository name,
  215. // ReferencesByName returns nil.
  216. func (store *store) ReferencesByName(ref reference.Named) []Association {
  217. refName := reference.FamiliarName(ref)
  218. store.mu.RLock()
  219. defer store.mu.RUnlock()
  220. repository, exists := store.Repositories[refName]
  221. if !exists {
  222. return nil
  223. }
  224. var associations []Association
  225. for refStr, refID := range repository {
  226. ref, err := reference.ParseNormalizedNamed(refStr)
  227. if err != nil {
  228. // Should never happen
  229. return nil
  230. }
  231. associations = append(associations,
  232. Association{
  233. Ref: ref,
  234. ID: refID,
  235. })
  236. }
  237. sort.Sort(lexicalAssociations(associations))
  238. return associations
  239. }
  240. func (store *store) save() error {
  241. // Store the json
  242. jsonData, err := json.Marshal(store)
  243. if err != nil {
  244. return err
  245. }
  246. return ioutils.AtomicWriteFile(store.jsonPath, jsonData, 0600)
  247. }
  248. func (store *store) reload() error {
  249. f, err := os.Open(store.jsonPath)
  250. if err != nil {
  251. return err
  252. }
  253. defer f.Close()
  254. if err := json.NewDecoder(f).Decode(&store); err != nil {
  255. return err
  256. }
  257. for _, repository := range store.Repositories {
  258. for refStr, refID := range repository {
  259. ref, err := reference.ParseNormalizedNamed(refStr)
  260. if err != nil {
  261. // Should never happen
  262. continue
  263. }
  264. if store.referencesByIDCache[refID] == nil {
  265. store.referencesByIDCache[refID] = make(map[string]reference.Named)
  266. }
  267. store.referencesByIDCache[refID][refStr] = ref
  268. }
  269. }
  270. return nil
  271. }