package metadata import ( "encoding/json" "github.com/docker/distribution/digest" "github.com/docker/docker/layer" ) // V2MetadataService maps layer IDs to a set of known metadata for // the layer. type V2MetadataService struct { store Store } // V2Metadata contains the digest and source repository information for a layer. type V2Metadata struct { Digest digest.Digest SourceRepository string } // maxMetadata is the number of metadata entries to keep per layer DiffID. const maxMetadata = 50 // NewV2MetadataService creates a new diff ID to v2 metadata mapping service. func NewV2MetadataService(store Store) *V2MetadataService { return &V2MetadataService{ store: store, } } func (serv *V2MetadataService) diffIDNamespace() string { return "v2metadata-by-diffid" } func (serv *V2MetadataService) digestNamespace() string { return "diffid-by-digest" } func (serv *V2MetadataService) diffIDKey(diffID layer.DiffID) string { return string(digest.Digest(diffID).Algorithm()) + "/" + digest.Digest(diffID).Hex() } func (serv *V2MetadataService) digestKey(dgst digest.Digest) string { return string(dgst.Algorithm()) + "/" + dgst.Hex() } // GetMetadata finds the metadata associated with a layer DiffID. func (serv *V2MetadataService) GetMetadata(diffID layer.DiffID) ([]V2Metadata, error) { jsonBytes, err := serv.store.Get(serv.diffIDNamespace(), serv.diffIDKey(diffID)) if err != nil { return nil, err } var metadata []V2Metadata if err := json.Unmarshal(jsonBytes, &metadata); err != nil { return nil, err } return metadata, nil } // GetDiffID finds a layer DiffID from a digest. func (serv *V2MetadataService) GetDiffID(dgst digest.Digest) (layer.DiffID, error) { diffIDBytes, err := serv.store.Get(serv.digestNamespace(), serv.digestKey(dgst)) if err != nil { return layer.DiffID(""), err } return layer.DiffID(diffIDBytes), nil } // Add associates metadata with a layer DiffID. If too many metadata entries are // present, the oldest one is dropped. func (serv *V2MetadataService) Add(diffID layer.DiffID, metadata V2Metadata) error { oldMetadata, err := serv.GetMetadata(diffID) if err != nil { oldMetadata = nil } newMetadata := make([]V2Metadata, 0, len(oldMetadata)+1) // Copy all other metadata to new slice for _, oldMeta := range oldMetadata { if oldMeta != metadata { newMetadata = append(newMetadata, oldMeta) } } newMetadata = append(newMetadata, metadata) if len(newMetadata) > maxMetadata { newMetadata = newMetadata[len(newMetadata)-maxMetadata:] } jsonBytes, err := json.Marshal(newMetadata) if err != nil { return err } err = serv.store.Set(serv.diffIDNamespace(), serv.diffIDKey(diffID), jsonBytes) if err != nil { return err } return serv.store.Set(serv.digestNamespace(), serv.digestKey(metadata.Digest), []byte(diffID)) } // Remove unassociates a metadata entry from a layer DiffID. func (serv *V2MetadataService) Remove(metadata V2Metadata) error { diffID, err := serv.GetDiffID(metadata.Digest) if err != nil { return err } oldMetadata, err := serv.GetMetadata(diffID) if err != nil { oldMetadata = nil } newMetadata := make([]V2Metadata, 0, len(oldMetadata)) // Copy all other metadata to new slice for _, oldMeta := range oldMetadata { if oldMeta != metadata { newMetadata = append(newMetadata, oldMeta) } } if len(newMetadata) == 0 { return serv.store.Delete(serv.diffIDNamespace(), serv.diffIDKey(diffID)) } jsonBytes, err := json.Marshal(newMetadata) if err != nil { return err } return serv.store.Set(serv.diffIDNamespace(), serv.diffIDKey(diffID), jsonBytes) }