store.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. package reference // import "github.com/docker/docker/reference"
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "sort"
  8. "sync"
  9. "github.com/docker/distribution/reference"
  10. "github.com/docker/docker/pkg/ioutils"
  11. digest "github.com/opencontainers/go-digest"
  12. "github.com/pkg/errors"
  13. )
  14. var (
  15. // ErrDoesNotExist is returned if a reference is not found in the
  16. // store.
  17. ErrDoesNotExist notFoundError = "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 reference 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.WithStack(invalidTagError("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 favorDigest(originalRef reference.Named) (reference.Named, error) {
  94. ref := originalRef
  95. // If the reference includes a digest and a tag, we must store only the
  96. // digest.
  97. canonical, isCanonical := originalRef.(reference.Canonical)
  98. _, isNamedTagged := originalRef.(reference.NamedTagged)
  99. if isCanonical && isNamedTagged {
  100. trimmed, err := reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest())
  101. if err != nil {
  102. // should never happen
  103. return originalRef, err
  104. }
  105. ref = trimmed
  106. }
  107. return ref, nil
  108. }
  109. func (store *store) addReference(ref reference.Named, id digest.Digest, force bool) error {
  110. ref, err := favorDigest(ref)
  111. if err != nil {
  112. return err
  113. }
  114. refName := reference.FamiliarName(ref)
  115. refStr := reference.FamiliarString(ref)
  116. if refName == string(digest.Canonical) {
  117. return errors.WithStack(invalidTagError("refusing to create an ambiguous tag using digest algorithm as name"))
  118. }
  119. store.mu.Lock()
  120. defer store.mu.Unlock()
  121. repository, exists := store.Repositories[refName]
  122. if !exists || repository == nil {
  123. repository = make(map[string]digest.Digest)
  124. store.Repositories[refName] = repository
  125. }
  126. oldID, exists := repository[refStr]
  127. if exists {
  128. if oldID == id {
  129. // Nothing to do. The caller may have checked for this using store.Get in advance, but store.mu was unlocked in the meantime, so this can legitimately happen nevertheless.
  130. return nil
  131. }
  132. // force only works for tags
  133. if digested, isDigest := ref.(reference.Canonical); isDigest {
  134. return errors.WithStack(conflictingTagError("Cannot overwrite digest " + digested.Digest().String()))
  135. }
  136. if !force {
  137. return errors.WithStack(
  138. conflictingTagError(
  139. fmt.Sprintf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use the force option", refStr, oldID.String()),
  140. ),
  141. )
  142. }
  143. if store.referencesByIDCache[oldID] != nil {
  144. delete(store.referencesByIDCache[oldID], refStr)
  145. if len(store.referencesByIDCache[oldID]) == 0 {
  146. delete(store.referencesByIDCache, oldID)
  147. }
  148. }
  149. }
  150. repository[refStr] = id
  151. if store.referencesByIDCache[id] == nil {
  152. store.referencesByIDCache[id] = make(map[string]reference.Named)
  153. }
  154. store.referencesByIDCache[id][refStr] = ref
  155. return store.save()
  156. }
  157. // Delete deletes a reference from the store. It returns true if a deletion
  158. // happened, or false otherwise.
  159. func (store *store) Delete(ref reference.Named) (bool, error) {
  160. ref, err := favorDigest(ref)
  161. if err != nil {
  162. return false, err
  163. }
  164. ref = reference.TagNameOnly(ref)
  165. refName := reference.FamiliarName(ref)
  166. refStr := reference.FamiliarString(ref)
  167. store.mu.Lock()
  168. defer store.mu.Unlock()
  169. repository, exists := store.Repositories[refName]
  170. if !exists {
  171. return false, ErrDoesNotExist
  172. }
  173. if id, exists := repository[refStr]; exists {
  174. delete(repository, refStr)
  175. if len(repository) == 0 {
  176. delete(store.Repositories, refName)
  177. }
  178. if store.referencesByIDCache[id] != nil {
  179. delete(store.referencesByIDCache[id], refStr)
  180. if len(store.referencesByIDCache[id]) == 0 {
  181. delete(store.referencesByIDCache, id)
  182. }
  183. }
  184. return true, store.save()
  185. }
  186. return false, ErrDoesNotExist
  187. }
  188. // Get retrieves an item from the store by reference
  189. func (store *store) Get(ref reference.Named) (digest.Digest, error) {
  190. if canonical, ok := ref.(reference.Canonical); ok {
  191. // If reference contains both tag and digest, only
  192. // lookup by digest as it takes precedence over
  193. // tag, until tag/digest combos are stored.
  194. if _, ok := ref.(reference.Tagged); ok {
  195. var err error
  196. ref, err = reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest())
  197. if err != nil {
  198. return "", err
  199. }
  200. }
  201. } else {
  202. ref = reference.TagNameOnly(ref)
  203. }
  204. refName := reference.FamiliarName(ref)
  205. refStr := reference.FamiliarString(ref)
  206. store.mu.RLock()
  207. defer store.mu.RUnlock()
  208. repository, exists := store.Repositories[refName]
  209. if !exists || repository == nil {
  210. return "", ErrDoesNotExist
  211. }
  212. id, exists := repository[refStr]
  213. if !exists {
  214. return "", ErrDoesNotExist
  215. }
  216. return id, nil
  217. }
  218. // References returns a slice of references to the given ID. The slice
  219. // will be nil if there are no references to this ID.
  220. func (store *store) References(id digest.Digest) []reference.Named {
  221. store.mu.RLock()
  222. defer store.mu.RUnlock()
  223. // Convert the internal map to an array for two reasons:
  224. // 1) We must not return a mutable
  225. // 2) It would be ugly to expose the extraneous map keys to callers.
  226. var references []reference.Named
  227. for _, ref := range store.referencesByIDCache[id] {
  228. references = append(references, ref)
  229. }
  230. sort.Sort(lexicalRefs(references))
  231. return references
  232. }
  233. // ReferencesByName returns the references for a given repository name.
  234. // If there are no references known for this repository name,
  235. // ReferencesByName returns nil.
  236. func (store *store) ReferencesByName(ref reference.Named) []Association {
  237. refName := reference.FamiliarName(ref)
  238. store.mu.RLock()
  239. defer store.mu.RUnlock()
  240. repository, exists := store.Repositories[refName]
  241. if !exists {
  242. return nil
  243. }
  244. var associations []Association
  245. for refStr, refID := range repository {
  246. ref, err := reference.ParseNormalizedNamed(refStr)
  247. if err != nil {
  248. // Should never happen
  249. return nil
  250. }
  251. associations = append(associations,
  252. Association{
  253. Ref: ref,
  254. ID: refID,
  255. })
  256. }
  257. sort.Sort(lexicalAssociations(associations))
  258. return associations
  259. }
  260. func (store *store) save() error {
  261. // Store the json
  262. jsonData, err := json.Marshal(store)
  263. if err != nil {
  264. return err
  265. }
  266. return ioutils.AtomicWriteFile(store.jsonPath, jsonData, 0600)
  267. }
  268. func (store *store) reload() error {
  269. f, err := os.Open(store.jsonPath)
  270. if err != nil {
  271. return err
  272. }
  273. defer f.Close()
  274. if err := json.NewDecoder(f).Decode(&store); err != nil {
  275. return err
  276. }
  277. for _, repository := range store.Repositories {
  278. for refStr, refID := range repository {
  279. ref, err := reference.ParseNormalizedNamed(refStr)
  280. if err != nil {
  281. // Should never happen
  282. continue
  283. }
  284. if store.referencesByIDCache[refID] == nil {
  285. store.referencesByIDCache[refID] = make(map[string]reference.Named)
  286. }
  287. store.referencesByIDCache[refID][refStr] = ref
  288. }
  289. }
  290. return nil
  291. }