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的关系。