mirror of
https://github.com/containers/skopeo.git
synced 2025-06-27 15:18:00 +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")
|
signBy := context.String("sign-by")
|
||||||
|
|
||||||
manifest, err := src.Manifest()
|
manifest, _, err := src.Manifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error reading manifest: %v", err)
|
return fmt.Errorf("Error reading manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
layers, err := src.LayerDigests()
|
blobDigests, err := src.BlobDigests()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error parsing manifest: %v", err)
|
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
|
// TODO(mitr): do not ignore the size param returned here
|
||||||
stream, _, err := rawSource.GetBlob(layer)
|
stream, _, err := rawSource.GetBlob(digest)
|
||||||
if err != nil {
|
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()
|
defer stream.Close()
|
||||||
if err := dest.PutBlob(layer, stream); err != nil {
|
if err := dest.PutBlob(digest, stream); err != nil {
|
||||||
return fmt.Errorf("Error writing layer: %v", err)
|
return fmt.Errorf("Error writing blob: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ var inspectCmd = cli.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rawManifest, err := img.Manifest()
|
rawManifest, _, err := img.Manifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,35 +19,35 @@ var layersCmd = cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
src := image.FromSource(rawSource)
|
src := image.FromSource(rawSource)
|
||||||
layers := c.Args().Tail()
|
blobDigests := c.Args().Tail()
|
||||||
if len(layers) == 0 {
|
if len(blobDigests) == 0 {
|
||||||
ls, err := src.LayerDigests()
|
b, err := src.BlobDigests()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
layers = ls
|
blobDigests = b
|
||||||
}
|
}
|
||||||
tmpDir, err := ioutil.TempDir(".", "layers-")
|
tmpDir, err := ioutil.TempDir(".", "layers-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dest := directory.NewDirImageDestination(tmpDir)
|
dest := directory.NewDirImageDestination(tmpDir)
|
||||||
manifest, err := src.Manifest()
|
manifest, _, err := src.Manifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := dest.PutManifest(manifest); err != nil {
|
if err := dest.PutManifest(manifest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, l := range layers {
|
for _, digest := range blobDigests {
|
||||||
if !strings.HasPrefix(l, "sha256:") {
|
if !strings.HasPrefix(digest, "sha256:") {
|
||||||
l = "sha256:" + l
|
digest = "sha256:" + digest
|
||||||
}
|
}
|
||||||
r, _, err := rawSource.GetBlob(l)
|
r, _, err := rawSource.GetBlob(digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := dest.PutBlob(l, r); err != nil {
|
if err := dest.PutBlob(digest, r); err != nil {
|
||||||
r.Close()
|
r.Close()
|
||||||
return err
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// manifestPath returns a path for the manifest within a directory using our conventions.
|
// 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")
|
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 {
|
func layerPath(dir string, digest string) string {
|
||||||
// FIXME: Should we keep the digest identification?
|
// FIXME: Should we keep the digest identification?
|
||||||
return filepath.Join(dir, strings.TrimPrefix(digest, "sha256:")+".tar")
|
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 {
|
func signaturePath(dir string, index int) string {
|
||||||
return filepath.Join(dir, fmt.Sprintf("signature-%d", index+1))
|
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"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/containers/image/manifest"
|
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,6 +58,19 @@ func (s *dockerImageSource) IntendedDockerReference() string {
|
|||||||
return fmt.Sprintf("%s:%s", s.ref.Name(), s.tag)
|
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) {
|
func (s *dockerImageSource) GetManifest(mimetypes []string) ([]byte, string, error) {
|
||||||
url := fmt.Sprintf(manifestURL, s.ref.RemoteName(), s.tag)
|
url := fmt.Sprintf(manifestURL, s.ref.RemoteName(), s.tag)
|
||||||
// TODO(runcom) set manifest version header! schema1 for now - then schema2 etc etc and v1
|
// 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}
|
return nil, "", errFetchManifest{res.StatusCode, manblob}
|
||||||
}
|
}
|
||||||
// We might validate manblob against the Docker-Content-Digest header here to protect against transport errors.
|
// 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) {
|
func (s *dockerImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
|
||||||
|
185
vendor/github.com/containers/image/image/image.go
generated
vendored
185
vendor/github.com/containers/image/image/image.go
generated
vendored
@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -25,8 +26,14 @@ var (
|
|||||||
// do not care, and those who care about `skopeo/docker.Image` know they do.
|
// do not care, and those who care about `skopeo/docker.Image` know they do.
|
||||||
type genericImage struct {
|
type genericImage struct {
|
||||||
src types.ImageSource
|
src types.ImageSource
|
||||||
cachedManifest []byte // Private cache for Manifest(); nil if not yet known.
|
// private cache for Manifest(); nil if not yet known.
|
||||||
cachedSignatures [][]byte // Private cache for Signatures(); 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.
|
// 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.
|
// 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.
|
// NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed.
|
||||||
func (i *genericImage) Manifest() ([]byte, error) {
|
func (i *genericImage) Manifest() ([]byte, string, error) {
|
||||||
if i.cachedManifest == nil {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
i.cachedManifest = m
|
i.cachedManifest = m
|
||||||
|
if mt == "" {
|
||||||
|
mt = manifest.GuessMIMEType(i.cachedManifest)
|
||||||
}
|
}
|
||||||
return i.cachedManifest, nil
|
i.cachedManifestMIMEType = mt
|
||||||
|
}
|
||||||
|
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.
|
// 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) {
|
func (i *genericImage) Inspect() (*types.ImageInspectInfo, error) {
|
||||||
// TODO(runcom): unused version param for now, default to docker v2-1
|
// TODO(runcom): unused version param for now, default to docker v2-1
|
||||||
m, err := i.getSchema1Manifest()
|
m, err := i.getParsedManifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ms1, ok := m.(*manifestSchema1)
|
return m.ImageInspectInfo()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
@ -110,13 +105,19 @@ type v1Image struct {
|
|||||||
|
|
||||||
// will support v1 one day...
|
// will support v1 one day...
|
||||||
type genericManifest interface {
|
type genericManifest interface {
|
||||||
GetLayers() []string
|
Config() ([]byte, error)
|
||||||
|
LayerDigests() []string
|
||||||
|
BlobDigests() []string
|
||||||
|
ImageInspectInfo() (*types.ImageInspectInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type fsLayersSchema1 struct {
|
type fsLayersSchema1 struct {
|
||||||
BlobSum string `json:"blobSum"`
|
BlobSum string `json:"blobSum"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compile-time check that manifestSchema1 implements genericManifest
|
||||||
|
var _ genericManifest = (*manifestSchema1)(nil)
|
||||||
|
|
||||||
type manifestSchema1 struct {
|
type manifestSchema1 struct {
|
||||||
Name string
|
Name string
|
||||||
Tag string
|
Tag string
|
||||||
@ -128,7 +129,7 @@ type manifestSchema1 struct {
|
|||||||
//Signature []byte `json:"signature"`
|
//Signature []byte `json:"signature"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestSchema1) GetLayers() []string {
|
func (m *manifestSchema1) LayerDigests() []string {
|
||||||
layers := make([]string, len(m.FSLayers))
|
layers := make([]string, len(m.FSLayers))
|
||||||
for i, layer := range m.FSLayers {
|
for i, layer := range m.FSLayers {
|
||||||
layers[i] = layer.BlobSum
|
layers[i] = layer.BlobSum
|
||||||
@ -136,15 +137,106 @@ func (m *manifestSchema1) GetLayers() []string {
|
|||||||
return layers
|
return layers
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSchema1Manifest parses the manifest into a data structure, cleans it up, and returns it.
|
func (m *manifestSchema1) BlobDigests() []string {
|
||||||
// NOTE: The manifest may have been modified in the process; DO NOT reserialize and store the return value
|
return m.LayerDigests()
|
||||||
// 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) {
|
func (m *manifestSchema1) Config() ([]byte, error) {
|
||||||
manblob, err := i.Manifest()
|
return []byte(m.History[0].V1Compatibility), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
|
v1 := &v1Image{}
|
||||||
|
config, err := m.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := json.Unmarshal(config, v1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{}
|
mschema1 := &manifestSchema1{}
|
||||||
if err := json.Unmarshal(manblob, mschema1); err != nil {
|
if err := json.Unmarshal(manblob, mschema1); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -160,14 +252,25 @@ func (i *genericImage) getSchema1Manifest() (genericManifest, error) {
|
|||||||
//return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
|
//return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
|
||||||
//}
|
//}
|
||||||
return mschema1, nil
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// uniqueLayerDigests returns a list of layer digests referenced from a manifest.
|
// 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.
|
// 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
|
var res []string
|
||||||
seen := make(map[string]struct{})
|
seen := make(map[string]struct{})
|
||||||
for _, digest := range m.GetLayers() {
|
for _, digest := range m.BlobDigests() {
|
||||||
if _, ok := seen[digest]; ok {
|
if _, ok := seen[digest]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -177,15 +280,15 @@ func uniqueLayerDigests(m genericManifest) []string {
|
|||||||
return res
|
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.
|
// 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().
|
// NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest().
|
||||||
func (i *genericImage) LayerDigests() ([]string, error) {
|
func (i *genericImage) BlobDigests() ([]string, error) {
|
||||||
m, err := i.getSchema1Manifest()
|
m, err := i.getParsedManifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return uniqueLayerDigests(m), nil
|
return uniqueBlobDigests(m), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *genericImage) getLayer(dest types.ImageDestination, digest string) error {
|
func (i *genericImage) getLayer(dest types.ImageDestination, digest string) error {
|
||||||
|
9
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
9
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
@ -14,11 +14,12 @@ import (
|
|||||||
const (
|
const (
|
||||||
// DockerV2Schema1MIMEType MIME type represents Docker manifest schema 1
|
// DockerV2Schema1MIMEType MIME type represents Docker manifest schema 1
|
||||||
DockerV2Schema1MIMEType = "application/vnd.docker.distribution.manifest.v1+json"
|
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 MIME type represents Docker manifest schema 2
|
||||||
DockerV2Schema2MIMEType = "application/vnd.docker.distribution.manifest.v2+json"
|
DockerV2Schema2MIMEType = "application/vnd.docker.distribution.manifest.v2+json"
|
||||||
// DockerV2ListMIMEType MIME type represents Docker manifest schema 2 list
|
// DockerV2ListMIMEType MIME type represents Docker manifest schema 2 list
|
||||||
DockerV2ListMIMEType = "application/vnd.docker.distribution.manifest.list.v2+json"
|
DockerV2ListMIMEType = "application/vnd.docker.distribution.manifest.list.v2+json"
|
||||||
|
|
||||||
// OCIV1DescriptorMIMEType TODO
|
// OCIV1DescriptorMIMEType TODO
|
||||||
OCIV1DescriptorMIMEType = "application/vnd.oci.descriptor.v1+json"
|
OCIV1DescriptorMIMEType = "application/vnd.oci.descriptor.v1+json"
|
||||||
// OCIV1ImageManifestMIMEType TODO
|
// OCIV1ImageManifestMIMEType TODO
|
||||||
@ -42,6 +43,7 @@ func GuessMIMEType(manifest []byte) string {
|
|||||||
meta := struct {
|
meta := struct {
|
||||||
MediaType string `json:"mediaType"`
|
MediaType string `json:"mediaType"`
|
||||||
SchemaVersion int `json:"schemaVersion"`
|
SchemaVersion int `json:"schemaVersion"`
|
||||||
|
Signatures interface{} `json:"signatures"`
|
||||||
}{}
|
}{}
|
||||||
if err := json.Unmarshal(manifest, &meta); err != nil {
|
if err := json.Unmarshal(manifest, &meta); err != nil {
|
||||||
return ""
|
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.
|
// 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 {
|
switch meta.SchemaVersion {
|
||||||
case 1:
|
case 1:
|
||||||
|
if meta.Signatures != nil {
|
||||||
|
return DockerV2Schema1SignedMIMEType
|
||||||
|
}
|
||||||
return DockerV2Schema1MIMEType
|
return DockerV2Schema1MIMEType
|
||||||
case 2: // Really should not happen, meta.MediaType should have been set. But given the data, this is our best guess.
|
case 2: // Really should not happen, meta.MediaType should have been set. But given the data, this is our best guess.
|
||||||
return DockerV2Schema2MIMEType
|
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.
|
// Digest returns the a digest of a docker manifest, with any necessary implied transformations like stripping v1s1 signatures.
|
||||||
func Digest(manifest []byte) (string, error) {
|
func Digest(manifest []byte) (string, error) {
|
||||||
if GuessMIMEType(manifest) == DockerV2Schema1MIMEType {
|
if GuessMIMEType(manifest) == DockerV2Schema1SignedMIMEType {
|
||||||
sig, err := libtrust.ParsePrettySignature(manifest, "signatures")
|
sig, err := libtrust.ParsePrettySignature(manifest, "signatures")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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
|
return nil
|
||||||
},
|
},
|
||||||
validateSignedDockerManifestDigest: func(digest string) error {
|
validateSignedDockerManifestDigest: func(digest string) error {
|
||||||
m, err := image.Manifest()
|
m, _, err := image.Manifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// May be "" if unknown.
|
||||||
IntendedDockerReference() string
|
IntendedDockerReference() string
|
||||||
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
// 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.
|
// NOTE: It is essential for signature verification that Manifest returns the manifest from which BlobDigests is computed.
|
||||||
Manifest() ([]byte, error)
|
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 is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
||||||
Signatures() ([][]byte, error)
|
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.
|
// 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().
|
// NOTE: It is essential for signature verification that BlobDigests is computed from the same manifest which is returned by Manifest().
|
||||||
LayerDigests() ([]string, error)
|
BlobDigests() ([]string, error)
|
||||||
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
||||||
Inspect() (*ImageInspectInfo, error)
|
Inspect() (*ImageInspectInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageInspectInfo is a set of metadata describing Docker images, primarily their manifest and configuration.
|
// 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 {
|
type ImageInspectInfo struct {
|
||||||
Tag string
|
Tag string
|
||||||
Created time.Time
|
Created time.Time
|
||||||
|
Loading…
Reference in New Issue
Block a user