Changes: https://github.com/containerd/typeurl/compare/7f6e6d160d67...v2.1.0 Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
		
			
				
	
	
		
			240 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   Copyright The containerd Authors.
 | 
						|
 | 
						|
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
   you may not use this file except in compliance with the License.
 | 
						|
   You may obtain a copy of the License at
 | 
						|
 | 
						|
       http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
   Unless required by applicable law or agreed to in writing, software
 | 
						|
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
   See the License for the specific language governing permissions and
 | 
						|
   limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package boltutil
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/containerd/containerd/protobuf"
 | 
						|
	"github.com/containerd/containerd/protobuf/proto"
 | 
						|
	"github.com/containerd/containerd/protobuf/types"
 | 
						|
	"github.com/containerd/typeurl/v2"
 | 
						|
	bolt "go.etcd.io/bbolt"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	bucketKeyAnnotations = []byte("annotations")
 | 
						|
	bucketKeyLabels      = []byte("labels")
 | 
						|
	bucketKeyCreatedAt   = []byte("createdat")
 | 
						|
	bucketKeyUpdatedAt   = []byte("updatedat")
 | 
						|
	bucketKeyExtensions  = []byte("extensions")
 | 
						|
)
 | 
						|
 | 
						|
// ReadLabels reads the labels key from the bucket
 | 
						|
// Uses the key "labels"
 | 
						|
func ReadLabels(bkt *bolt.Bucket) (map[string]string, error) {
 | 
						|
	return readMap(bkt, bucketKeyLabels)
 | 
						|
}
 | 
						|
 | 
						|
// ReadAnnotations reads the OCI Descriptor Annotations key from the bucket
 | 
						|
// Uses the key "annotations"
 | 
						|
func ReadAnnotations(bkt *bolt.Bucket) (map[string]string, error) {
 | 
						|
	return readMap(bkt, bucketKeyAnnotations)
 | 
						|
}
 | 
						|
 | 
						|
func readMap(bkt *bolt.Bucket, bucketName []byte) (map[string]string, error) {
 | 
						|
	lbkt := bkt.Bucket(bucketName)
 | 
						|
	if lbkt == nil {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
	labels := map[string]string{}
 | 
						|
	if err := lbkt.ForEach(func(k, v []byte) error {
 | 
						|
		labels[string(k)] = string(v)
 | 
						|
		return nil
 | 
						|
	}); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return labels, nil
 | 
						|
}
 | 
						|
 | 
						|
// WriteLabels will write a new labels bucket to the provided bucket at key
 | 
						|
// bucketKeyLabels, replacing the contents of the bucket with the provided map.
 | 
						|
//
 | 
						|
// The provide map labels will be modified to have the final contents of the
 | 
						|
// bucket. Typically, this removes zero-value entries.
 | 
						|
// Uses the key "labels"
 | 
						|
func WriteLabels(bkt *bolt.Bucket, labels map[string]string) error {
 | 
						|
	return writeMap(bkt, bucketKeyLabels, labels)
 | 
						|
}
 | 
						|
 | 
						|
// WriteAnnotations writes the OCI Descriptor Annotations
 | 
						|
func WriteAnnotations(bkt *bolt.Bucket, labels map[string]string) error {
 | 
						|
	return writeMap(bkt, bucketKeyAnnotations, labels)
 | 
						|
}
 | 
						|
 | 
						|
func writeMap(bkt *bolt.Bucket, bucketName []byte, labels map[string]string) error {
 | 
						|
	// Remove existing labels to keep from merging
 | 
						|
	if lbkt := bkt.Bucket(bucketName); lbkt != nil {
 | 
						|
		if err := bkt.DeleteBucket(bucketName); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(labels) == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	lbkt, err := bkt.CreateBucket(bucketName)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	for k, v := range labels {
 | 
						|
		if v == "" {
 | 
						|
			delete(labels, k) // remove since we don't actually set it
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if err := lbkt.Put([]byte(k), []byte(v)); err != nil {
 | 
						|
			return fmt.Errorf("failed to set label %q=%q: %w", k, v, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// ReadTimestamps reads created and updated timestamps from a bucket.
 | 
						|
// Uses keys "createdat" and "updatedat"
 | 
						|
func ReadTimestamps(bkt *bolt.Bucket, created, updated *time.Time) error {
 | 
						|
	for _, f := range []struct {
 | 
						|
		b []byte
 | 
						|
		t *time.Time
 | 
						|
	}{
 | 
						|
		{bucketKeyCreatedAt, created},
 | 
						|
		{bucketKeyUpdatedAt, updated},
 | 
						|
	} {
 | 
						|
		v := bkt.Get(f.b)
 | 
						|
		if v != nil {
 | 
						|
			if err := f.t.UnmarshalBinary(v); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// WriteTimestamps writes created and updated timestamps to a bucket.
 | 
						|
// Uses keys "createdat" and "updatedat"
 | 
						|
func WriteTimestamps(bkt *bolt.Bucket, created, updated time.Time) error {
 | 
						|
	createdAt, err := created.MarshalBinary()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	updatedAt, err := updated.MarshalBinary()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	for _, v := range [][2][]byte{
 | 
						|
		{bucketKeyCreatedAt, createdAt},
 | 
						|
		{bucketKeyUpdatedAt, updatedAt},
 | 
						|
	} {
 | 
						|
		if err := bkt.Put(v[0], v[1]); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// WriteExtensions will write a KV map to the given bucket,
 | 
						|
// where `K` is a string key and `V` is a protobuf's Any type that represents a generic extension.
 | 
						|
func WriteExtensions(bkt *bolt.Bucket, extensions map[string]typeurl.Any) error {
 | 
						|
	if len(extensions) == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	ebkt, err := bkt.CreateBucketIfNotExists(bucketKeyExtensions)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	for name, ext := range extensions {
 | 
						|
		ext := protobuf.FromAny(ext)
 | 
						|
		p, err := proto.Marshal(ext)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		if err := ebkt.Put([]byte(name), p); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// ReadExtensions will read back a map of extensions from the given bucket, previously written by WriteExtensions
 | 
						|
func ReadExtensions(bkt *bolt.Bucket) (map[string]typeurl.Any, error) {
 | 
						|
	var (
 | 
						|
		extensions = make(map[string]typeurl.Any)
 | 
						|
		ebkt       = bkt.Bucket(bucketKeyExtensions)
 | 
						|
	)
 | 
						|
 | 
						|
	if ebkt == nil {
 | 
						|
		return extensions, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if err := ebkt.ForEach(func(k, v []byte) error {
 | 
						|
		var t types.Any
 | 
						|
		if err := proto.Unmarshal(v, &t); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		extensions[string(k)] = &t
 | 
						|
		return nil
 | 
						|
	}); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return extensions, nil
 | 
						|
}
 | 
						|
 | 
						|
// WriteAny write a protobuf's Any type to the bucket
 | 
						|
func WriteAny(bkt *bolt.Bucket, name []byte, any typeurl.Any) error {
 | 
						|
	pbany := protobuf.FromAny(any)
 | 
						|
	if pbany == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	data, err := proto.Marshal(pbany)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to marshal: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := bkt.Put(name, data); err != nil {
 | 
						|
		return fmt.Errorf("put failed: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// ReadAny reads back protobuf's Any type from the bucket
 | 
						|
func ReadAny(bkt *bolt.Bucket, name []byte) (*types.Any, error) {
 | 
						|
	bytes := bkt.Get(name)
 | 
						|
	if bytes == nil {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	out := types.Any{}
 | 
						|
	if err := proto.Unmarshal(bytes, &out); err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to unmarshal any: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return &out, nil
 | 
						|
}
 |