store.go 9.3 KB

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