mirror of
https://github.com/containers/skopeo.git
synced 2025-06-26 22:57:35 +00:00
Update for changed images.Type API
This commit is contained in:
parent
4558575d9e
commit
fe6c392d45
@ -26,24 +26,24 @@ func copyHandler(context *cli.Context) error {
|
||||
}
|
||||
signBy := context.String("sign-by")
|
||||
|
||||
manifest, err := src.Manifest()
|
||||
manifest, _, err := src.Manifest()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading manifest: %v", err)
|
||||
}
|
||||
|
||||
layers, err := src.LayerDigests()
|
||||
blobDigests, err := src.BlobDigests()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing manifest: %v", err)
|
||||
}
|
||||
for _, layer := range layers {
|
||||
for _, digest := range blobDigests {
|
||||
// TODO(mitr): do not ignore the size param returned here
|
||||
stream, _, err := rawSource.GetBlob(layer)
|
||||
stream, _, err := rawSource.GetBlob(digest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading layer %s: %v", layer, err)
|
||||
return fmt.Errorf("Error reading blob %s: %v", digest, err)
|
||||
}
|
||||
defer stream.Close()
|
||||
if err := dest.PutBlob(layer, stream); err != nil {
|
||||
return fmt.Errorf("Error writing layer: %v", err)
|
||||
if err := dest.PutBlob(digest, stream); err != nil {
|
||||
return fmt.Errorf("Error writing blob: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ var inspectCmd = cli.Command{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawManifest, err := img.Manifest()
|
||||
rawManifest, _, err := img.Manifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -19,35 +19,35 @@ var layersCmd = cli.Command{
|
||||
return err
|
||||
}
|
||||
src := image.FromSource(rawSource)
|
||||
layers := c.Args().Tail()
|
||||
if len(layers) == 0 {
|
||||
ls, err := src.LayerDigests()
|
||||
blobDigests := c.Args().Tail()
|
||||
if len(blobDigests) == 0 {
|
||||
b, err := src.BlobDigests()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
layers = ls
|
||||
blobDigests = b
|
||||
}
|
||||
tmpDir, err := ioutil.TempDir(".", "layers-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest := directory.NewDirImageDestination(tmpDir)
|
||||
manifest, err := src.Manifest()
|
||||
manifest, _, err := src.Manifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dest.PutManifest(manifest); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, l := range layers {
|
||||
if !strings.HasPrefix(l, "sha256:") {
|
||||
l = "sha256:" + l
|
||||
for _, digest := range blobDigests {
|
||||
if !strings.HasPrefix(digest, "sha256:") {
|
||||
digest = "sha256:" + digest
|
||||
}
|
||||
r, _, err := rawSource.GetBlob(l)
|
||||
r, _, err := rawSource.GetBlob(digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dest.PutBlob(l, r); err != nil {
|
||||
if err := dest.PutBlob(digest, r); err != nil {
|
||||
r.Close()
|
||||
return err
|
||||
}
|
||||
|
106
vendor/github.com/containers/image/directory/directory.go
generated
vendored
106
vendor/github.com/containers/image/directory/directory.go
generated
vendored
@ -2,13 +2,8 @@ package directory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
// manifestPath returns a path for the manifest within a directory using our conventions.
|
||||
@ -16,110 +11,13 @@ func manifestPath(dir string) string {
|
||||
return filepath.Join(dir, "manifest.json")
|
||||
}
|
||||
|
||||
// manifestPath returns a path for a layer tarball within a directory using our conventions.
|
||||
// layerPath returns a path for a layer tarball within a directory using our conventions.
|
||||
func layerPath(dir string, digest string) string {
|
||||
// FIXME: Should we keep the digest identification?
|
||||
return filepath.Join(dir, strings.TrimPrefix(digest, "sha256:")+".tar")
|
||||
}
|
||||
|
||||
// manifestPath returns a path for a signature within a directory using our conventions.
|
||||
// signaturePath returns a path for a signature within a directory using our conventions.
|
||||
func signaturePath(dir string, index int) string {
|
||||
return filepath.Join(dir, fmt.Sprintf("signature-%d", index+1))
|
||||
}
|
||||
|
||||
type dirImageDestination struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
// NewDirImageDestination returns an ImageDestination for writing to an existing directory.
|
||||
func NewDirImageDestination(dir string) types.ImageDestination {
|
||||
return &dirImageDestination{dir}
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) CanonicalDockerReference() (string, error) {
|
||||
return "", fmt.Errorf("Can not determine canonical Docker reference for a local directory")
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) PutManifest(manifest []byte) error {
|
||||
return ioutil.WriteFile(manifestPath(d.dir), manifest, 0644)
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) PutBlob(digest string, stream io.Reader) error {
|
||||
layerFile, err := os.Create(layerPath(d.dir, digest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer layerFile.Close()
|
||||
if _, err := io.Copy(layerFile, stream); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := layerFile.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) PutSignatures(signatures [][]byte) error {
|
||||
for i, sig := range signatures {
|
||||
if err := ioutil.WriteFile(signaturePath(d.dir, i), sig, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type dirImageSource struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
// NewDirImageSource returns an ImageSource reading from an existing directory.
|
||||
func NewDirImageSource(dir string) types.ImageSource {
|
||||
return &dirImageSource{dir}
|
||||
}
|
||||
|
||||
// IntendedDockerReference returns the full, unambiguous, Docker reference for this image, _as specified by the user_
|
||||
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||
// May be "" if unknown.
|
||||
func (s *dirImageSource) IntendedDockerReference() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// it's up to the caller to determine the MIME type of the returned manifest's bytes
|
||||
func (s *dirImageSource) GetManifest(_ []string) ([]byte, string, error) {
|
||||
m, err := ioutil.ReadFile(manifestPath(s.dir))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return m, "", err
|
||||
}
|
||||
|
||||
func (s *dirImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
|
||||
r, err := os.Open(layerPath(s.dir, digest))
|
||||
if err != nil {
|
||||
return nil, 0, nil
|
||||
}
|
||||
fi, err := os.Stat(layerPath(s.dir, digest))
|
||||
if err != nil {
|
||||
return nil, 0, nil
|
||||
}
|
||||
return r, fi.Size(), nil
|
||||
}
|
||||
|
||||
func (s *dirImageSource) GetSignatures() ([][]byte, error) {
|
||||
signatures := [][]byte{}
|
||||
for i := 0; ; i++ {
|
||||
signature, err := ioutil.ReadFile(signaturePath(s.dir, i))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
signatures = append(signatures, signature)
|
||||
}
|
||||
return signatures, nil
|
||||
}
|
||||
|
||||
func (s *dirImageSource) Delete() error {
|
||||
return fmt.Errorf("directory#dirImageSource.Delete() not implmented")
|
||||
}
|
||||
|
51
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
Normal file
51
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package directory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
type dirImageDestination struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
// NewDirImageDestination returns an ImageDestination for writing to an existing directory.
|
||||
func NewDirImageDestination(dir string) types.ImageDestination {
|
||||
return &dirImageDestination{dir}
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) CanonicalDockerReference() (string, error) {
|
||||
return "", fmt.Errorf("Can not determine canonical Docker reference for a local directory")
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) PutManifest(manifest []byte) error {
|
||||
return ioutil.WriteFile(manifestPath(d.dir), manifest, 0644)
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) PutBlob(digest string, stream io.Reader) error {
|
||||
layerFile, err := os.Create(layerPath(d.dir, digest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer layerFile.Close()
|
||||
if _, err := io.Copy(layerFile, stream); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := layerFile.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dirImageDestination) PutSignatures(signatures [][]byte) error {
|
||||
for i, sig := range signatures {
|
||||
if err := ioutil.WriteFile(signaturePath(d.dir, i), sig, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
66
vendor/github.com/containers/image/directory/directory_src.go
generated
vendored
Normal file
66
vendor/github.com/containers/image/directory/directory_src.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package directory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
type dirImageSource struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
// NewDirImageSource returns an ImageSource reading from an existing directory.
|
||||
func NewDirImageSource(dir string) types.ImageSource {
|
||||
return &dirImageSource{dir}
|
||||
}
|
||||
|
||||
// IntendedDockerReference returns the full, unambiguous, Docker reference for this image, _as specified by the user_
|
||||
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
|
||||
// May be "" if unknown.
|
||||
func (s *dirImageSource) IntendedDockerReference() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// it's up to the caller to determine the MIME type of the returned manifest's bytes
|
||||
func (s *dirImageSource) GetManifest(_ []string) ([]byte, string, error) {
|
||||
m, err := ioutil.ReadFile(manifestPath(s.dir))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return m, "", err
|
||||
}
|
||||
|
||||
func (s *dirImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
|
||||
r, err := os.Open(layerPath(s.dir, digest))
|
||||
if err != nil {
|
||||
return nil, 0, nil
|
||||
}
|
||||
fi, err := os.Stat(layerPath(s.dir, digest))
|
||||
if err != nil {
|
||||
return nil, 0, nil
|
||||
}
|
||||
return r, fi.Size(), nil
|
||||
}
|
||||
|
||||
func (s *dirImageSource) GetSignatures() ([][]byte, error) {
|
||||
signatures := [][]byte{}
|
||||
for i := 0; ; i++ {
|
||||
signature, err := ioutil.ReadFile(signaturePath(s.dir, i))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
signatures = append(signatures, signature)
|
||||
}
|
||||
return signatures, nil
|
||||
}
|
||||
|
||||
func (s *dirImageSource) Delete() error {
|
||||
return fmt.Errorf("directory#dirImageSource.Delete() not implmented")
|
||||
}
|
18
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
18
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
@ -4,12 +4,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
@ -57,6 +58,19 @@ func (s *dockerImageSource) IntendedDockerReference() string {
|
||||
return fmt.Sprintf("%s:%s", s.ref.Name(), s.tag)
|
||||
}
|
||||
|
||||
// simplifyContentType drops parameters from a HTTP media type (see https://tools.ietf.org/html/rfc7231#section-3.1.1.1)
|
||||
// Alternatively, an empty string is returned unchanged, and invalid values are "simplified" to an empty string.
|
||||
func simplifyContentType(contentType string) string {
|
||||
if contentType == "" {
|
||||
return contentType
|
||||
}
|
||||
mimeType, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return mimeType
|
||||
}
|
||||
|
||||
func (s *dockerImageSource) GetManifest(mimetypes []string) ([]byte, string, error) {
|
||||
url := fmt.Sprintf(manifestURL, s.ref.RemoteName(), s.tag)
|
||||
// TODO(runcom) set manifest version header! schema1 for now - then schema2 etc etc and v1
|
||||
@ -76,7 +90,7 @@ func (s *dockerImageSource) GetManifest(mimetypes []string) ([]byte, string, err
|
||||
return nil, "", errFetchManifest{res.StatusCode, manblob}
|
||||
}
|
||||
// We might validate manblob against the Docker-Content-Digest header here to protect against transport errors.
|
||||
return manblob, res.Header.Get("Content-Type"), nil
|
||||
return manblob, simplifyContentType(res.Header.Get("Content-Type")), nil
|
||||
}
|
||||
|
||||
func (s *dockerImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
|
||||
|
213
vendor/github.com/containers/image/image/image.go
generated
vendored
213
vendor/github.com/containers/image/image/image.go
generated
vendored
@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
@ -24,9 +25,15 @@ var (
|
||||
// may not be a `genericImage` directly. However, most users of `types.Image`
|
||||
// do not care, and those who care about `skopeo/docker.Image` know they do.
|
||||
type genericImage struct {
|
||||
src types.ImageSource
|
||||
cachedManifest []byte // Private cache for Manifest(); nil if not yet known.
|
||||
cachedSignatures [][]byte // Private cache for Signatures(); nil if not yet known.
|
||||
src types.ImageSource
|
||||
// private cache for Manifest(); nil if not yet known.
|
||||
cachedManifest []byte
|
||||
// private cache for the manifest media type w/o having to guess it
|
||||
// this may be the empty string in case the MIME Type wasn't guessed correctly
|
||||
// this field is valid only if cachedManifest is not nil
|
||||
cachedManifestMIMEType string
|
||||
// private cache for Signatures(); nil if not yet known.
|
||||
cachedSignatures [][]byte
|
||||
}
|
||||
|
||||
// FromSource returns a types.Image implementation for source.
|
||||
@ -42,16 +49,20 @@ func (i *genericImage) IntendedDockerReference() string {
|
||||
}
|
||||
|
||||
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
||||
// NOTE: It is essential for signature verification that Manifest returns the manifest from which LayerDigests is computed.
|
||||
func (i *genericImage) Manifest() ([]byte, error) {
|
||||
// NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed.
|
||||
func (i *genericImage) Manifest() ([]byte, string, error) {
|
||||
if i.cachedManifest == nil {
|
||||
m, _, err := i.src.GetManifest([]string{manifest.DockerV2Schema1MIMEType})
|
||||
m, mt, err := i.src.GetManifest([]string{manifest.DockerV2Schema1SignedMIMEType, manifest.DockerV2Schema1MIMEType})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
i.cachedManifest = m
|
||||
if mt == "" {
|
||||
mt = manifest.GuessMIMEType(i.cachedManifest)
|
||||
}
|
||||
i.cachedManifestMIMEType = mt
|
||||
}
|
||||
return i.cachedManifest, nil
|
||||
return i.cachedManifest, i.cachedManifestMIMEType, nil
|
||||
}
|
||||
|
||||
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
||||
@ -68,27 +79,11 @@ func (i *genericImage) Signatures() ([][]byte, error) {
|
||||
|
||||
func (i *genericImage) Inspect() (*types.ImageInspectInfo, error) {
|
||||
// TODO(runcom): unused version param for now, default to docker v2-1
|
||||
m, err := i.getSchema1Manifest()
|
||||
m, err := i.getParsedManifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ms1, ok := m.(*manifestSchema1)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error retrivieng manifest schema1")
|
||||
}
|
||||
v1 := &v1Image{}
|
||||
if err := json.Unmarshal([]byte(ms1.History[0].V1Compatibility), v1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.ImageInspectInfo{
|
||||
Tag: ms1.Tag,
|
||||
DockerVersion: v1.DockerVersion,
|
||||
Created: v1.Created,
|
||||
Labels: v1.Config.Labels,
|
||||
Architecture: v1.Architecture,
|
||||
Os: v1.OS,
|
||||
Layers: ms1.GetLayers(),
|
||||
}, nil
|
||||
return m.ImageInspectInfo()
|
||||
}
|
||||
|
||||
type config struct {
|
||||
@ -110,13 +105,19 @@ type v1Image struct {
|
||||
|
||||
// will support v1 one day...
|
||||
type genericManifest interface {
|
||||
GetLayers() []string
|
||||
Config() ([]byte, error)
|
||||
LayerDigests() []string
|
||||
BlobDigests() []string
|
||||
ImageInspectInfo() (*types.ImageInspectInfo, error)
|
||||
}
|
||||
|
||||
type fsLayersSchema1 struct {
|
||||
BlobSum string `json:"blobSum"`
|
||||
}
|
||||
|
||||
// compile-time check that manifestSchema1 implements genericManifest
|
||||
var _ genericManifest = (*manifestSchema1)(nil)
|
||||
|
||||
type manifestSchema1 struct {
|
||||
Name string
|
||||
Tag string
|
||||
@ -128,7 +129,7 @@ type manifestSchema1 struct {
|
||||
//Signature []byte `json:"signature"`
|
||||
}
|
||||
|
||||
func (m *manifestSchema1) GetLayers() []string {
|
||||
func (m *manifestSchema1) LayerDigests() []string {
|
||||
layers := make([]string, len(m.FSLayers))
|
||||
for i, layer := range m.FSLayers {
|
||||
layers[i] = layer.BlobSum
|
||||
@ -136,38 +137,140 @@ func (m *manifestSchema1) GetLayers() []string {
|
||||
return layers
|
||||
}
|
||||
|
||||
// getSchema1Manifest parses the manifest into a data structure, cleans it up, and returns it.
|
||||
// NOTE: The manifest may have been modified in the process; DO NOT reserialize and store the return value
|
||||
// if you want to preserve the original manifest; use the blob returned by Manifest() directly.
|
||||
// NOTE: It is essential for signature verification that the object is computed from the same manifest which is returned by Manifest().
|
||||
func (i *genericImage) getSchema1Manifest() (genericManifest, error) {
|
||||
manblob, err := i.Manifest()
|
||||
func (m *manifestSchema1) BlobDigests() []string {
|
||||
return m.LayerDigests()
|
||||
}
|
||||
|
||||
func (m *manifestSchema1) Config() ([]byte, error) {
|
||||
return []byte(m.History[0].V1Compatibility), nil
|
||||
}
|
||||
|
||||
func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||
v1 := &v1Image{}
|
||||
config, err := m.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mschema1 := &manifestSchema1{}
|
||||
if err := json.Unmarshal(manblob, mschema1); err != nil {
|
||||
if err := json.Unmarshal(config, v1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := fixManifestLayers(mschema1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO(runcom): verify manifest schema 1, 2 etc
|
||||
//if len(m.FSLayers) != len(m.History) {
|
||||
//return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
|
||||
//}
|
||||
//if len(m.FSLayers) == 0 {
|
||||
//return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
|
||||
//}
|
||||
return mschema1, nil
|
||||
return &types.ImageInspectInfo{
|
||||
Tag: m.Tag,
|
||||
DockerVersion: v1.DockerVersion,
|
||||
Created: v1.Created,
|
||||
Labels: v1.Config.Labels,
|
||||
Architecture: v1.Architecture,
|
||||
Os: v1.OS,
|
||||
Layers: m.LayerDigests(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// uniqueLayerDigests returns a list of layer digests referenced from a manifest.
|
||||
// compile-time check that manifestSchema2 implements genericManifest
|
||||
var _ genericManifest = (*manifestSchema2)(nil)
|
||||
|
||||
type manifestSchema2 struct {
|
||||
src types.ImageSource
|
||||
ConfigDescriptor descriptor `json:"config"`
|
||||
LayersDescriptors []descriptor `json:"layers"`
|
||||
}
|
||||
|
||||
type descriptor struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
Size int64 `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
}
|
||||
|
||||
func (m *manifestSchema2) LayerDigests() []string {
|
||||
blobs := []string{}
|
||||
for _, layer := range m.LayersDescriptors {
|
||||
blobs = append(blobs, layer.Digest)
|
||||
}
|
||||
return blobs
|
||||
}
|
||||
|
||||
func (m *manifestSchema2) BlobDigests() []string {
|
||||
blobs := m.LayerDigests()
|
||||
blobs = append(blobs, m.ConfigDescriptor.Digest)
|
||||
return blobs
|
||||
}
|
||||
|
||||
func (m *manifestSchema2) Config() ([]byte, error) {
|
||||
rawConfig, _, err := m.src.GetBlob(m.ConfigDescriptor.Digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := ioutil.ReadAll(rawConfig)
|
||||
rawConfig.Close()
|
||||
return config, err
|
||||
}
|
||||
|
||||
func (m *manifestSchema2) ImageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||
config, err := m.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v1 := &v1Image{}
|
||||
if err := json.Unmarshal(config, v1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.ImageInspectInfo{
|
||||
DockerVersion: v1.DockerVersion,
|
||||
Created: v1.Created,
|
||||
Labels: v1.Config.Labels,
|
||||
Architecture: v1.Architecture,
|
||||
Os: v1.OS,
|
||||
Layers: m.LayerDigests(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getParsedManifest parses the manifest into a data structure, cleans it up, and returns it.
|
||||
// NOTE: The manifest may have been modified in the process; DO NOT reserialize and store the return value
|
||||
// if you want to preserve the original manifest; use the blob returned by Manifest() directly.
|
||||
// NOTE: It is essential for signature verification that the object is computed from the same manifest which is returned by Manifest().
|
||||
func (i *genericImage) getParsedManifest() (genericManifest, error) {
|
||||
manblob, mt, err := i.Manifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch mt {
|
||||
// "application/json" is a valid v2s1 value per https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md .
|
||||
// This works for now, when nothing else seems to return "application/json"; if that were not true, the mapping/detection might
|
||||
// need to happen within the ImageSource.
|
||||
case manifest.DockerV2Schema1MIMEType, manifest.DockerV2Schema1SignedMIMEType, "application/json":
|
||||
mschema1 := &manifestSchema1{}
|
||||
if err := json.Unmarshal(manblob, mschema1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := fixManifestLayers(mschema1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO(runcom): verify manifest schema 1, 2 etc
|
||||
//if len(m.FSLayers) != len(m.History) {
|
||||
//return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
|
||||
//}
|
||||
//if len(m.FSLayers) == 0 {
|
||||
//return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
|
||||
//}
|
||||
return mschema1, nil
|
||||
case manifest.DockerV2Schema2MIMEType:
|
||||
v2s2 := manifestSchema2{src: i.src}
|
||||
if err := json.Unmarshal(manblob, &v2s2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v2s2, nil
|
||||
case "":
|
||||
return nil, errors.New("could not guess manifest media type")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported manifest media type %s", mt)
|
||||
}
|
||||
}
|
||||
|
||||
// uniqueBlobDigests returns a list of blob digests referenced from a manifest.
|
||||
// The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image.
|
||||
func uniqueLayerDigests(m genericManifest) []string {
|
||||
func uniqueBlobDigests(m genericManifest) []string {
|
||||
var res []string
|
||||
seen := make(map[string]struct{})
|
||||
for _, digest := range m.GetLayers() {
|
||||
for _, digest := range m.BlobDigests() {
|
||||
if _, ok := seen[digest]; ok {
|
||||
continue
|
||||
}
|
||||
@ -177,15 +280,15 @@ func uniqueLayerDigests(m genericManifest) []string {
|
||||
return res
|
||||
}
|
||||
|
||||
// LayerDigests returns a list of layer digests referenced by this image.
|
||||
// BlobDigests returns a list of blob digests referenced by this image.
|
||||
// The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image.
|
||||
// NOTE: It is essential for signature verification that LayerDigests is computed from the same manifest which is returned by Manifest().
|
||||
func (i *genericImage) LayerDigests() ([]string, error) {
|
||||
m, err := i.getSchema1Manifest()
|
||||
// NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest().
|
||||
func (i *genericImage) BlobDigests() ([]string, error) {
|
||||
m, err := i.getParsedManifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uniqueLayerDigests(m), nil
|
||||
return uniqueBlobDigests(m), nil
|
||||
}
|
||||
|
||||
func (i *genericImage) getLayer(dest types.ImageDestination, digest string) error {
|
||||
|
13
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
13
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
@ -14,11 +14,12 @@ import (
|
||||
const (
|
||||
// DockerV2Schema1MIMEType MIME type represents Docker manifest schema 1
|
||||
DockerV2Schema1MIMEType = "application/vnd.docker.distribution.manifest.v1+json"
|
||||
// DockerV2Schema1MIMEType MIME type represents Docker manifest schema 1 with a JWS signature
|
||||
DockerV2Schema1SignedMIMEType = "application/vnd.docker.distribution.manifest.v1+prettyjws"
|
||||
// DockerV2Schema2MIMEType MIME type represents Docker manifest schema 2
|
||||
DockerV2Schema2MIMEType = "application/vnd.docker.distribution.manifest.v2+json"
|
||||
// DockerV2ListMIMEType MIME type represents Docker manifest schema 2 list
|
||||
DockerV2ListMIMEType = "application/vnd.docker.distribution.manifest.list.v2+json"
|
||||
|
||||
// OCIV1DescriptorMIMEType TODO
|
||||
OCIV1DescriptorMIMEType = "application/vnd.oci.descriptor.v1+json"
|
||||
// OCIV1ImageManifestMIMEType TODO
|
||||
@ -40,8 +41,9 @@ func GuessMIMEType(manifest []byte) string {
|
||||
// A subset of manifest fields; the rest is silently ignored by json.Unmarshal.
|
||||
// Also docker/distribution/manifest.Versioned.
|
||||
meta := struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
MediaType string `json:"mediaType"`
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
Signatures interface{} `json:"signatures"`
|
||||
}{}
|
||||
if err := json.Unmarshal(manifest, &meta); err != nil {
|
||||
return ""
|
||||
@ -54,6 +56,9 @@ func GuessMIMEType(manifest []byte) string {
|
||||
// this is the only way the function can return DockerV2Schema1MIMEType, and recognizing that is essential for stripping the JWS signatures = computing the correct manifest digest.
|
||||
switch meta.SchemaVersion {
|
||||
case 1:
|
||||
if meta.Signatures != nil {
|
||||
return DockerV2Schema1SignedMIMEType
|
||||
}
|
||||
return DockerV2Schema1MIMEType
|
||||
case 2: // Really should not happen, meta.MediaType should have been set. But given the data, this is our best guess.
|
||||
return DockerV2Schema2MIMEType
|
||||
@ -63,7 +68,7 @@ func GuessMIMEType(manifest []byte) string {
|
||||
|
||||
// Digest returns the a digest of a docker manifest, with any necessary implied transformations like stripping v1s1 signatures.
|
||||
func Digest(manifest []byte) (string, error) {
|
||||
if GuessMIMEType(manifest) == DockerV2Schema1MIMEType {
|
||||
if GuessMIMEType(manifest) == DockerV2Schema1SignedMIMEType {
|
||||
sig, err := libtrust.ParsePrettySignature(manifest, "signatures")
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
2
vendor/github.com/containers/image/signature/policy_eval_signedby.go
generated
vendored
2
vendor/github.com/containers/image/signature/policy_eval_signedby.go
generated
vendored
@ -76,7 +76,7 @@ func (pr *prSignedBy) isSignatureAuthorAccepted(image types.Image, sig []byte) (
|
||||
return nil
|
||||
},
|
||||
validateSignedDockerManifestDigest: func(digest string) error {
|
||||
m, err := image.Manifest()
|
||||
m, _, err := image.Manifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
12
vendor/github.com/containers/image/types/types.go
generated
vendored
12
vendor/github.com/containers/image/types/types.go
generated
vendored
@ -44,19 +44,21 @@ type Image interface {
|
||||
// May be "" if unknown.
|
||||
IntendedDockerReference() string
|
||||
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
||||
// NOTE: It is essential for signature verification that Manifest returns the manifest from which LayerDigests is computed.
|
||||
Manifest() ([]byte, error)
|
||||
// NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed.
|
||||
Manifest() ([]byte, string, error)
|
||||
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
||||
Signatures() ([][]byte, error)
|
||||
// LayerDigests returns a list of layer digests referenced by this image.
|
||||
// BlobDigests returns a list of blob digests referenced by this image.
|
||||
// The list will not contain duplicates; it is not intended to correspond to the "history" or "parent chain" of a Docker image.
|
||||
// NOTE: It is essential for signature verification that LayerDigests is computed from the same manifest which is returned by Manifest().
|
||||
LayerDigests() ([]string, error)
|
||||
// NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest().
|
||||
BlobDigests() ([]string, error)
|
||||
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
||||
Inspect() (*ImageInspectInfo, error)
|
||||
}
|
||||
|
||||
// ImageInspectInfo is a set of metadata describing Docker images, primarily their manifest and configuration.
|
||||
// The Tag field is a legacy field which is here just for the Docker v2s1 manifest. It won't be supported
|
||||
// for other manifest types.
|
||||
type ImageInspectInfo struct {
|
||||
Tag string
|
||||
Created time.Time
|
||||
|
Loading…
Reference in New Issue
Block a user