Files
kubesphere/vendor/github.com/deislabs/oras/pkg/content/oci.go
Roland.Ma ea8f47c73a update vendor
Signed-off-by: Roland.Ma <rolandma@yunify.com>
2021-08-16 06:58:12 +00:00

170 lines
3.9 KiB
Go

package content
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/content/local"
specs "github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// OCIStore provides content from the file system with the OCI-Image layout.
// Reference: https://github.com/opencontainers/image-spec/blob/master/image-layout.md
type OCIStore struct {
content.Store
root string
index *ocispec.Index
nameMap map[string]ocispec.Descriptor
}
// NewOCIStore creates a new OCI store
func NewOCIStore(rootPath string) (*OCIStore, error) {
fileStore, err := local.NewStore(rootPath)
if err != nil {
return nil, err
}
store := &OCIStore{
Store: fileStore,
root: rootPath,
}
if err := store.validateOCILayoutFile(); err != nil {
return nil, err
}
if err := store.LoadIndex(); err != nil {
return nil, err
}
return store, nil
}
// LoadIndex reads the index.json from the file system
func (s *OCIStore) LoadIndex() error {
path := filepath.Join(s.root, OCIImageIndexFile)
indexFile, err := os.Open(path)
if err != nil {
if !os.IsNotExist(err) {
return err
}
s.index = &ocispec.Index{
Versioned: specs.Versioned{
SchemaVersion: 2, // historical value
},
}
s.nameMap = make(map[string]ocispec.Descriptor)
return nil
}
defer indexFile.Close()
if err := json.NewDecoder(indexFile).Decode(&s.index); err != nil {
return err
}
s.nameMap = make(map[string]ocispec.Descriptor)
for _, desc := range s.index.Manifests {
if name := desc.Annotations[ocispec.AnnotationRefName]; name != "" {
s.nameMap[name] = desc
}
}
return nil
}
// SaveIndex writes the index.json to the file system
func (s *OCIStore) SaveIndex() error {
indexJSON, err := json.Marshal(s.index)
if err != nil {
return err
}
path := filepath.Join(s.root, OCIImageIndexFile)
return ioutil.WriteFile(path, indexJSON, 0644)
}
// AddReference adds or updates an reference to index.
func (s *OCIStore) AddReference(name string, desc ocispec.Descriptor) {
if desc.Annotations == nil {
desc.Annotations = map[string]string{
ocispec.AnnotationRefName: name,
}
} else {
desc.Annotations[ocispec.AnnotationRefName] = name
}
if _, ok := s.nameMap[name]; ok {
for i, ref := range s.index.Manifests {
if name == ref.Annotations[ocispec.AnnotationRefName] {
s.index.Manifests[i] = desc
return
}
}
// Process should not reach here.
// Fallthrough to `Add` scenario and recover.
}
s.index.Manifests = append(s.index.Manifests, desc)
s.nameMap[name] = desc
return
}
// DeleteReference deletes an reference from index.
func (s *OCIStore) DeleteReference(name string) {
if _, ok := s.nameMap[name]; !ok {
return
}
delete(s.nameMap, name)
for i, desc := range s.index.Manifests {
if name == desc.Annotations[ocispec.AnnotationRefName] {
s.index.Manifests[i] = s.index.Manifests[len(s.index.Manifests)-1]
s.index.Manifests = s.index.Manifests[:len(s.index.Manifests)-1]
return
}
}
}
// ListReferences lists all references in index.
func (s *OCIStore) ListReferences() map[string]ocispec.Descriptor {
return s.nameMap
}
// validateOCILayoutFile ensures the `oci-layout` file
func (s *OCIStore) validateOCILayoutFile() error {
layoutFilePath := filepath.Join(s.root, ocispec.ImageLayoutFile)
layoutFile, err := os.Open(layoutFilePath)
if err != nil {
if !os.IsNotExist(err) {
return err
}
layout := ocispec.ImageLayout{
Version: ocispec.ImageLayoutVersion,
}
layoutJSON, err := json.Marshal(layout)
if err != nil {
return err
}
return ioutil.WriteFile(layoutFilePath, layoutJSON, 0644)
}
defer layoutFile.Close()
var layout *ocispec.ImageLayout
err = json.NewDecoder(layoutFile).Decode(&layout)
if err != nil {
return err
}
if layout.Version != ocispec.ImageLayoutVersion {
return ErrUnsupportedVersion
}
return nil
}