referenceStore用来管理镜像名称和id之前的关系,主要和/var/lib/docker/image/aufs/repositories.json打交道。本次分析将介绍关于referenceStore的代码。
referenceStore
来看下referenceStore(即store)的定义,在/reference/store.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| type store struct { mu sync.RWMutex jsonPath string Repositories map[string]repository referencesByIDCache map[image.ID]map[string]Named } type repository map[string]image.ID
|
可以看出,store维护两个map:从镜像名称到id的map及从id到镜像名称的map。
NewReferenceStore()
来看下如何生成ReferenceStore:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| func NewReferenceStore(jsonPath string) (Store, error) { abspath, err := filepath.Abs(jsonPath) if err != nil { return nil, err } store := &store{ jsonPath: abspath, Repositories: make(map[string]repository), referencesByIDCache: make(map[image.ID]map[string]Named), } if err := store.reload(); os.IsNotExist(err) { if err := store.save(); err != nil { return nil, err } } else if err != nil { return nil, err } return store, nil }
|
NewReferenceStore()先构造空store,然后调用reload()方法把内容填充到store的两个map中。
来看下reload():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| func (store *store) reload() error { f, err := os.Open(store.jsonPath) if err != nil { return err } defer f.Close() if err := json.NewDecoder(f).Decode(&store); err != nil { return err } for _, repository := range store.Repositories { for refStr, refID := range repository { ref, err := ParseNamed(refStr) if err != nil { continue } if store.referencesByIDCache[refID] == nil { store.referencesByIDCache[refID] = make(map[string]Named) } store.referencesByIDCache[refID][refStr] = ref } } return nil }
|
reload()读取/var/lib/docker/image/aufs/repositories.json,然后把内容直接解码到store中,并重新构造referencesByIDCache。
所以来看下repositories.json的内容:
1 2 3 4 5 6 7 8 9 10
| { "Repositories": { "nginx": { "nginx:v1": "sha256:2765df43aea203fa44f069a44b984a27cb942551493e58b0d02b04ae22e5d644" }, "ubuntu": { "ubuntu:16.04": "sha256:ac526a356ca46f915e822e2f8051b9cf3404c756b725661c51191564ae4e6ea7" } } }
|
可以看出,repositories.json的格式和referenceStore中的Repositories一样。
添加
我们可以在referenceStore中加入内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| func (store *store) AddTag(ref Named, id image.ID, force bool) error { if _, isCanonical := ref.(Canonical); isCanonical { return errors.New("refusing to create a tag with a digest reference") } return store.addReference(WithDefaultTag(ref), id, force) } func (store *store) AddDigest(ref Canonical, id image.ID, force bool) error { return store.addReference(ref, id, force) } func (store *store) addReference(ref Named, id image.ID, force bool) error { if ref.Name() == string(digest.Canonical) { return errors.New("refusing to create an ambiguous tag using digest algorithm as name") } store.mu.Lock() defer store.mu.Unlock() repository, exists := store.Repositories[ref.Name()] if !exists || repository == nil { repository = make(map[string]image.ID) store.Repositories[ref.Name()] = repository } refStr := ref.String() oldID, exists := repository[refStr] if exists { if digested, isDigest := ref.(Canonical); isDigest { return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String()) } if !force { 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()) } if store.referencesByIDCache[oldID] != nil { delete(store.referencesByIDCache[oldID], refStr) if len(store.referencesByIDCache[oldID]) == 0 { delete(store.referencesByIDCache, oldID) } } } repository[refStr] = id if store.referencesByIDCache[id] == nil { store.referencesByIDCache[id] = make(map[string]Named) } store.referencesByIDCache[id][refStr] = ref return store.save() }
|
添加的过程就是在referenceStore中加入内容,然后把整个referenceStore的内容存储到repositories.json中。
删除
当然,也可以从referenceStore上删除资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| func (store *store) Delete(ref Named) (bool, error) { ref = WithDefaultTag(ref) store.mu.Lock() defer store.mu.Unlock() repoName := ref.Name() repository, exists := store.Repositories[repoName] if !exists { return false, ErrDoesNotExist } refStr := ref.String() if id, exists := repository[refStr]; exists { delete(repository, refStr) if len(repository) == 0 { delete(store.Repositories, repoName) } if store.referencesByIDCache[id] != nil { delete(store.referencesByIDCache[id], refStr) if len(store.referencesByIDCache[id]) == 0 { delete(store.referencesByIDCache, id) } } return true, store.save() } return false, ErrDoesNotExist }
|
删除资源就是从referenceStore的两个map中把资源删除,然后把内容存储到repositories.json中。
获取
Get()
Get可以通过imageName和tag获取镜像id。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| func (store *store) Get(ref Named) (image.ID, error) { ref = WithDefaultTag(ref) store.mu.RLock() defer store.mu.RUnlock() repository, exists := store.Repositories[ref.Name()] if !exists || repository == nil { return "", ErrDoesNotExist } id, exists := repository[ref.String()] if !exists { return "", ErrDoesNotExist } return id, nil }
|
References()
References()可以返回某镜像id对应的镜像列表,如[nginx:20170820, nginx:v1]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func (store *store) References(id image.ID) []Named { store.mu.RLock() defer store.mu.RUnlock() var references []Named for _, ref := range store.referencesByIDCache[id] { references = append(references, ref) } sort.Sort(lexicalRefs(references)) return references }
|
ReferencesByName()
通过镜像名字返回Association。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| func (store *store) ReferencesByName(ref Named) []Association { store.mu.RLock() defer store.mu.RUnlock() repository, exists := store.Repositories[ref.Name()] if !exists { return nil } var associations []Association for refStr, refID := range repository { ref, err := ParseNamed(refStr) if err != nil { return nil } associations = append(associations, Association{ Ref: ref, ImageID: refID, }) } sort.Sort(lexicalAssociations(associations)) return associations } type Association struct { Ref Named ImageID image.ID }
|
持久化
save()可以把referenceStore持久化到repositories.json中。
1 2 3 4 5 6 7 8 9
| func (store *store) save() error { jsonData, err := json.Marshal(store) if err != nil { return err } return ioutils.AtomicWriteFile(store.jsonPath, jsonData, 0600) }
|
总结
referenceStore就是repositories.json在内存中的对象,用来管理镜像名称和镜像id的关系。