forked from oras-project/oras
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add basic OCIStore * load index at startup * CRUD for index * user provided provider and ingester * simplify code * allow pulling blobs with no names
- Loading branch information
Showing
7 changed files
with
232 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package content | ||
|
||
import "github.com/containerd/containerd/content" | ||
|
||
// ProvideIngester is the interface that groups the basic Read and Write methods. | ||
type ProvideIngester interface { | ||
content.Provider | ||
content.Ingester | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters