buildkit/client/ociindex/ociindex.go

114 lines
2.9 KiB
Go

package ociindex
import (
"encoding/json"
"io/ioutil"
"os"
"github.com/gofrs/flock"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
const (
// IndexJSONLockFileSuffix is the suffix of the lock file
IndexJSONLockFileSuffix = ".lock"
)
// PutDescToIndex puts desc to index with tag.
// Existing manifests with the same tag will be removed from the index.
func PutDescToIndex(index *ocispecs.Index, desc ocispecs.Descriptor, tag string) error {
if index == nil {
index = &ocispecs.Index{}
}
if index.SchemaVersion == 0 {
index.SchemaVersion = 2
}
if tag != "" {
if desc.Annotations == nil {
desc.Annotations = make(map[string]string)
}
desc.Annotations[ocispecs.AnnotationRefName] = tag
// remove existing manifests with the same tag
var manifests []ocispecs.Descriptor
for _, m := range index.Manifests {
if m.Annotations[ocispecs.AnnotationRefName] != tag {
manifests = append(manifests, m)
}
}
index.Manifests = manifests
}
index.Manifests = append(index.Manifests, desc)
return nil
}
func PutDescToIndexJSONFileLocked(indexJSONPath string, desc ocispecs.Descriptor, tag string) error {
lockPath := indexJSONPath + IndexJSONLockFileSuffix
lock := flock.New(lockPath)
locked, err := lock.TryLock()
if err != nil {
return errors.Wrapf(err, "could not lock %s", lockPath)
}
if !locked {
return errors.Errorf("could not lock %s", lockPath)
}
defer func() {
lock.Unlock()
os.RemoveAll(lockPath)
}()
f, err := os.OpenFile(indexJSONPath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return errors.Wrapf(err, "could not open %s", indexJSONPath)
}
defer f.Close()
var idx ocispecs.Index
b, err := ioutil.ReadAll(f)
if err != nil {
return errors.Wrapf(err, "could not read %s", indexJSONPath)
}
if len(b) > 0 {
if err := json.Unmarshal(b, &idx); err != nil {
return errors.Wrapf(err, "could not unmarshal %s (%q)", indexJSONPath, string(b))
}
}
if err = PutDescToIndex(&idx, desc, tag); err != nil {
return err
}
b, err = json.Marshal(idx)
if err != nil {
return err
}
if _, err = f.WriteAt(b, 0); err != nil {
return err
}
if err = f.Truncate(int64(len(b))); err != nil {
return err
}
return nil
}
func ReadIndexJSONFileLocked(indexJSONPath string) (*ocispecs.Index, error) {
lockPath := indexJSONPath + IndexJSONLockFileSuffix
lock := flock.New(lockPath)
locked, err := lock.TryRLock()
if err != nil {
return nil, errors.Wrapf(err, "could not lock %s", lockPath)
}
if !locked {
return nil, errors.Errorf("could not lock %s", lockPath)
}
defer func() {
lock.Unlock()
os.RemoveAll(lockPath)
}()
b, err := ioutil.ReadFile(indexJSONPath)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s", indexJSONPath)
}
var idx ocispecs.Index
if err := json.Unmarshal(b, &idx); err != nil {
return nil, errors.Wrapf(err, "could not unmarshal %s (%q)", indexJSONPath, string(b))
}
return &idx, nil
}