package cache import ( "time" "github.com/moby/buildkit/cache/metadata" "github.com/moby/buildkit/client" "github.com/pkg/errors" bolt "go.etcd.io/bbolt" ) const sizeUnknown int64 = -1 const keySize = "snapshot.size" const keyEqualMutable = "cache.equalMutable" const keyCachePolicy = "cache.cachePolicy" const keyDescription = "cache.description" const keyCreatedAt = "cache.createdAt" const keyLastUsedAt = "cache.lastUsedAt" const keyUsageCount = "cache.usageCount" const keyLayerType = "cache.layerType" const keyRecordType = "cache.recordType" const keyCommitted = "snapshot.committed" const keyParent = "cache.parent" const keyDiffID = "cache.diffID" const keyChainID = "cache.chainID" const keyBlobChainID = "cache.blobChainID" const keyBlob = "cache.blob" const keySnapshot = "cache.snapshot" const keyBlobOnly = "cache.blobonly" const keyMediaType = "cache.mediatype" const keyImageRefs = "cache.imageRefs" // BlobSize is the packed blob size as specified in the oci descriptor const keyBlobSize = "cache.blobsize" const keyDeleted = "cache.deleted" func queueDiffID(si *metadata.StorageItem, str string) error { if str == "" { return nil } v, err := metadata.NewValue(str) if err != nil { return errors.Wrap(err, "failed to create diffID value") } si.Update(func(b *bolt.Bucket) error { return si.SetValue(b, keyDiffID, v) }) return nil } func getMediaType(si *metadata.StorageItem) string { v := si.Get(keyMediaType) if v == nil { return si.ID() } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func queueMediaType(si *metadata.StorageItem, str string) error { if str == "" { return nil } v, err := metadata.NewValue(str) if err != nil { return errors.Wrap(err, "failed to create mediaType value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyMediaType, v) }) return nil } func getSnapshotID(si *metadata.StorageItem) string { v := si.Get(keySnapshot) if v == nil { return si.ID() } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func queueSnapshotID(si *metadata.StorageItem, str string) error { if str == "" { return nil } v, err := metadata.NewValue(str) if err != nil { return errors.Wrap(err, "failed to create chainID value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keySnapshot, v) }) return nil } func getDiffID(si *metadata.StorageItem) string { v := si.Get(keyDiffID) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func queueChainID(si *metadata.StorageItem, str string) error { if str == "" { return nil } v, err := metadata.NewValue(str) if err != nil { return errors.Wrap(err, "failed to create chainID value") } v.Index = "chainid:" + str si.Update(func(b *bolt.Bucket) error { return si.SetValue(b, keyChainID, v) }) return nil } func getBlobChainID(si *metadata.StorageItem) string { v := si.Get(keyBlobChainID) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func queueBlobChainID(si *metadata.StorageItem, str string) error { if str == "" { return nil } v, err := metadata.NewValue(str) if err != nil { return errors.Wrap(err, "failed to create chainID value") } v.Index = "blobchainid:" + str si.Update(func(b *bolt.Bucket) error { return si.SetValue(b, keyBlobChainID, v) }) return nil } func getChainID(si *metadata.StorageItem) string { v := si.Get(keyChainID) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func queueBlob(si *metadata.StorageItem, str string) error { if str == "" { return nil } v, err := metadata.NewValue(str) if err != nil { return errors.Wrap(err, "failed to create blob value") } si.Update(func(b *bolt.Bucket) error { return si.SetValue(b, keyBlob, v) }) return nil } func getBlob(si *metadata.StorageItem) string { v := si.Get(keyBlob) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func queueBlobOnly(si *metadata.StorageItem, b bool) error { v, err := metadata.NewValue(b) if err != nil { return errors.Wrap(err, "failed to create blobonly value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyBlobOnly, v) }) return nil } func getBlobOnly(si *metadata.StorageItem) bool { v := si.Get(keyBlobOnly) if v == nil { return false } var blobOnly bool if err := v.Unmarshal(&blobOnly); err != nil { return false } return blobOnly } func setDeleted(si *metadata.StorageItem) error { v, err := metadata.NewValue(true) if err != nil { return errors.Wrap(err, "failed to create deleted value") } si.Update(func(b *bolt.Bucket) error { return si.SetValue(b, keyDeleted, v) }) return nil } func getDeleted(si *metadata.StorageItem) bool { v := si.Get(keyDeleted) if v == nil { return false } var deleted bool if err := v.Unmarshal(&deleted); err != nil { return false } return deleted } func queueCommitted(si *metadata.StorageItem) error { v, err := metadata.NewValue(true) if err != nil { return errors.Wrap(err, "failed to create committed value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyCommitted, v) }) return nil } func getCommitted(si *metadata.StorageItem) bool { v := si.Get(keyCommitted) if v == nil { return false } var committed bool if err := v.Unmarshal(&committed); err != nil { return false } return committed } func queueParent(si *metadata.StorageItem, parent string) error { if parent == "" { return nil } v, err := metadata.NewValue(parent) if err != nil { return errors.Wrap(err, "failed to create parent value") } si.Update(func(b *bolt.Bucket) error { return si.SetValue(b, keyParent, v) }) return nil } func getParent(si *metadata.StorageItem) string { v := si.Get(keyParent) if v == nil { return "" } var parent string if err := v.Unmarshal(&parent); err != nil { return "" } return parent } func setSize(si *metadata.StorageItem, s int64) error { v, err := metadata.NewValue(s) if err != nil { return errors.Wrap(err, "failed to create size value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keySize, v) }) return nil } func getSize(si *metadata.StorageItem) int64 { v := si.Get(keySize) if v == nil { return sizeUnknown } var size int64 if err := v.Unmarshal(&size); err != nil { return sizeUnknown } return size } func appendImageRef(si *metadata.StorageItem, s string) error { return si.GetAndSetValue(keyImageRefs, func(v *metadata.Value) (*metadata.Value, error) { var imageRefs []string if v != nil { if err := v.Unmarshal(&imageRefs); err != nil { return nil, err } } for _, existing := range imageRefs { if existing == s { return nil, metadata.ErrSkipSetValue } } imageRefs = append(imageRefs, s) v, err := metadata.NewValue(imageRefs) if err != nil { return nil, errors.Wrap(err, "failed to create imageRefs value") } return v, nil }) } func getImageRefs(si *metadata.StorageItem) []string { v := si.Get(keyImageRefs) if v == nil { return nil } var refs []string if err := v.Unmarshal(&refs); err != nil { return nil } return refs } func queueBlobSize(si *metadata.StorageItem, s int64) error { v, err := metadata.NewValue(s) if err != nil { return errors.Wrap(err, "failed to create blobsize value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyBlobSize, v) }) return nil } func getBlobSize(si *metadata.StorageItem) int64 { v := si.Get(keyBlobSize) if v == nil { return sizeUnknown } var size int64 if err := v.Unmarshal(&size); err != nil { return sizeUnknown } return size } func getEqualMutable(si *metadata.StorageItem) string { v := si.Get(keyEqualMutable) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func setEqualMutable(si *metadata.StorageItem, s string) error { v, err := metadata.NewValue(s) if err != nil { return errors.Wrapf(err, "failed to create %s meta value", keyEqualMutable) } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyEqualMutable, v) }) return nil } func clearEqualMutable(si *metadata.StorageItem) error { si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyEqualMutable, nil) }) return nil } func queueCachePolicy(si *metadata.StorageItem, p cachePolicy) error { v, err := metadata.NewValue(p) if err != nil { return errors.Wrap(err, "failed to create cachePolicy value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyCachePolicy, v) }) return nil } func getCachePolicy(si *metadata.StorageItem) cachePolicy { v := si.Get(keyCachePolicy) if v == nil { return cachePolicyDefault } var p cachePolicy if err := v.Unmarshal(&p); err != nil { return cachePolicyDefault } return p } func queueDescription(si *metadata.StorageItem, descr string) error { v, err := metadata.NewValue(descr) if err != nil { return errors.Wrap(err, "failed to create description value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyDescription, v) }) return nil } func GetDescription(si *metadata.StorageItem) string { v := si.Get(keyDescription) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func queueCreatedAt(si *metadata.StorageItem, tm time.Time) error { v, err := metadata.NewValue(tm.UnixNano()) if err != nil { return errors.Wrap(err, "failed to create createdAt value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyCreatedAt, v) }) return nil } func GetCreatedAt(si *metadata.StorageItem) time.Time { v := si.Get(keyCreatedAt) if v == nil { return time.Time{} } var tm int64 if err := v.Unmarshal(&tm); err != nil { return time.Time{} } return time.Unix(tm/1e9, tm%1e9) } func getLastUsed(si *metadata.StorageItem) (int, *time.Time) { v := si.Get(keyUsageCount) if v == nil { return 0, nil } var usageCount int if err := v.Unmarshal(&usageCount); err != nil { return 0, nil } v = si.Get(keyLastUsedAt) if v == nil { return usageCount, nil } var lastUsedTs int64 if err := v.Unmarshal(&lastUsedTs); err != nil || lastUsedTs == 0 { return usageCount, nil } tm := time.Unix(lastUsedTs/1e9, lastUsedTs%1e9) return usageCount, &tm } func updateLastUsed(si *metadata.StorageItem) error { count, _ := getLastUsed(si) count++ v, err := metadata.NewValue(count) if err != nil { return errors.Wrap(err, "failed to create usageCount value") } v2, err := metadata.NewValue(time.Now().UnixNano()) if err != nil { return errors.Wrap(err, "failed to create lastUsedAt value") } return si.Update(func(b *bolt.Bucket) error { if err := si.SetValue(b, keyUsageCount, v); err != nil { return err } return si.SetValue(b, keyLastUsedAt, v2) }) } func SetLayerType(m withMetadata, value string) error { v, err := metadata.NewValue(value) if err != nil { return errors.Wrap(err, "failed to create layertype value") } m.Metadata().Queue(func(b *bolt.Bucket) error { return m.Metadata().SetValue(b, keyLayerType, v) }) return m.Metadata().Commit() } func GetLayerType(m withMetadata) string { v := m.Metadata().Get(keyLayerType) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return str } func GetRecordType(m withMetadata) client.UsageRecordType { v := m.Metadata().Get(keyRecordType) if v == nil { return "" } var str string if err := v.Unmarshal(&str); err != nil { return "" } return client.UsageRecordType(str) } func SetRecordType(m withMetadata, value client.UsageRecordType) error { if err := queueRecordType(m.Metadata(), value); err != nil { return err } return m.Metadata().Commit() } func queueRecordType(si *metadata.StorageItem, value client.UsageRecordType) error { v, err := metadata.NewValue(value) if err != nil { return errors.Wrap(err, "failed to create recordtype value") } si.Queue(func(b *bolt.Bucket) error { return si.SetValue(b, keyRecordType, v) }) return nil }