mirror of
https://github.com/containers/skopeo.git
synced 2025-08-14 04:36:10 +00:00
support multi commands
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
d0fd876d7e
commit
3fd3adc58e
14
README.md
14
README.md
@ -11,7 +11,7 @@ a repository or a tag before pulling it (using disk space) - e.g. - which tags a
|
|||||||
Examples:
|
Examples:
|
||||||
```sh
|
```sh
|
||||||
# show repository's labels of rhel7:latest
|
# show repository's labels of rhel7:latest
|
||||||
$ skopeo registry.access.redhat.com/rhel7 | jq '.Config.Labels'
|
$ skopeo inspect registry.access.redhat.com/rhel7 | jq '.Config.Labels'
|
||||||
{
|
{
|
||||||
"Architecture": "x86_64",
|
"Architecture": "x86_64",
|
||||||
"Authoritative_Registry": "registry.access.redhat.com",
|
"Authoritative_Registry": "registry.access.redhat.com",
|
||||||
@ -24,7 +24,7 @@ $ skopeo registry.access.redhat.com/rhel7 | jq '.Config.Labels'
|
|||||||
}
|
}
|
||||||
|
|
||||||
# show repository's tags
|
# show repository's tags
|
||||||
$ skopeo docker.io/fedora | jq '.RepoTags'
|
$ skopeo inspect docker.io/fedora | jq '.RepoTags'
|
||||||
[
|
[
|
||||||
"20",
|
"20",
|
||||||
"21",
|
"21",
|
||||||
@ -36,11 +36,11 @@ $ skopeo docker.io/fedora | jq '.RepoTags'
|
|||||||
]
|
]
|
||||||
|
|
||||||
# show image's digest
|
# show image's digest
|
||||||
$ skopeo docker.io/fedora:rawhide | jq '.Digest'
|
$ skopeo inspect docker.io/fedora:rawhide | jq '.Digest'
|
||||||
"sha256:905b4846938c8aef94f52f3e41a11398ae5b40f5855fb0e40ed9c157e721d7f8"
|
"sha256:905b4846938c8aef94f52f3e41a11398ae5b40f5855fb0e40ed9c157e721d7f8"
|
||||||
|
|
||||||
# show image's label "Name"
|
# show image's label "Name"
|
||||||
$ skopeo registry.access.redhat.com/rhel7 | jq '.Config.Labels.Name'
|
$ skopeo inspect registry.access.redhat.com/rhel7 | jq '.Config.Labels.Name'
|
||||||
"rhel7/rhel"
|
"rhel7/rhel"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -65,15 +65,15 @@ $ cat /home/runcom/.docker/config.json
|
|||||||
}
|
}
|
||||||
|
|
||||||
# we can see I'm already authenticated via docker login so everything will be fine
|
# we can see I'm already authenticated via docker login so everything will be fine
|
||||||
$ skopeo myregistrydomain.com:5000/busybox
|
$ skopeo inspect myregistrydomain.com:5000/busybox
|
||||||
{"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"}
|
{"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"}
|
||||||
|
|
||||||
# let's try now to fake a non existent Docker's config file
|
# let's try now to fake a non existent Docker's config file
|
||||||
$ skopeo --docker-cfg="" myregistrydomain.com:5000/busybox
|
$ skopeo --docker-cfg="" inspect myregistrydomain.com:5000/busybox
|
||||||
FATA[0000] Get https://myregistrydomain.com:5000/v2/busybox/manifests/latest: no basic auth credentials
|
FATA[0000] Get https://myregistrydomain.com:5000/v2/busybox/manifests/latest: no basic auth credentials
|
||||||
|
|
||||||
# passing --username and --password - we can see that everything goes fine
|
# passing --username and --password - we can see that everything goes fine
|
||||||
$ skopeo --docker-cfg="" --username=testuser --password=testpassword myregistrydomain.com:5000/busybox
|
$ skopeo --docker-cfg="" --username=testuser --password=testpassword inspect myregistrydomain.com:5000/busybox
|
||||||
{"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"}
|
{"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"}
|
||||||
```
|
```
|
||||||
If your cli config is found but it doesn't contain the necessary credentials for the queried registry
|
If your cli config is found but it doesn't contain the necessary credentials for the queried registry
|
||||||
|
@ -47,7 +47,7 @@ func (f fallbackError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type manifestFetcher interface {
|
type manifestFetcher interface {
|
||||||
Fetch(ctx context.Context, ref reference.Named) (*types.ImageInspect, error)
|
Fetch(ctx context.Context, ref reference.Named) (*types.ImageManifest, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateName(name string) error {
|
func validateName(name string) error {
|
||||||
@ -62,7 +62,7 @@ func validateName(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetData(c *cli.Context, name string) (*types.ImageInspect, error) {
|
func GetData(c *cli.Context, name string) (*types.ImageManifest, error) {
|
||||||
if err := validateName(name); err != nil {
|
if err := validateName(name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ func GetData(c *cli.Context, name string) (*types.ImageInspect, error) {
|
|||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
lastErr error
|
lastErr error
|
||||||
discardNoSupportErrors bool
|
discardNoSupportErrors bool
|
||||||
imgInspect *types.ImageInspect
|
imgInspect *types.ImageManifest
|
||||||
confirmedV2 bool
|
confirmedV2 bool
|
||||||
confirmedTLSRegistries = make(map[string]struct{})
|
confirmedTLSRegistries = make(map[string]struct{})
|
||||||
)
|
)
|
||||||
@ -248,12 +248,12 @@ func validateRepoName(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeImageInspect(img *image.Image, tag string, dgst digest.Digest, tagList []string) *types.ImageInspect {
|
func makeImageManifest(img *image.Image, tag string, dgst digest.Digest, tagList []string) *types.ImageManifest {
|
||||||
var digest string
|
var digest string
|
||||||
if err := dgst.Validate(); err == nil {
|
if err := dgst.Validate(); err == nil {
|
||||||
digest = dgst.String()
|
digest = dgst.String()
|
||||||
}
|
}
|
||||||
return &types.ImageInspect{
|
return &types.ImageManifest{
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Digest: digest,
|
Digest: digest,
|
||||||
RepoTags: tagList,
|
RepoTags: tagList,
|
||||||
|
@ -31,9 +31,9 @@ type v1ManifestFetcher struct {
|
|||||||
session *registry.Session
|
session *registry.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mf *v1ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
|
func (mf *v1ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*types.ImageManifest, error) {
|
||||||
var (
|
var (
|
||||||
imgInspect *types.ImageInspect
|
imgInspect *types.ImageManifest
|
||||||
)
|
)
|
||||||
if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
||||||
// Allowing fallback, because HTTPS v1 is before HTTP v2
|
// Allowing fallback, because HTTPS v1 is before HTTP v2
|
||||||
@ -68,7 +68,7 @@ func (mf *v1ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*t
|
|||||||
return imgInspect, nil
|
return imgInspect, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mf *v1ManifestFetcher) fetchWithSession(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
|
func (mf *v1ManifestFetcher) fetchWithSession(ctx context.Context, ref reference.Named) (*types.ImageManifest, error) {
|
||||||
repoData, err := mf.session.GetRepositoryData(mf.repoInfo)
|
repoData, err := mf.session.GetRepositoryData(mf.repoInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "HTTP code: 404") {
|
if strings.Contains(err.Error(), "HTTP code: 404") {
|
||||||
@ -142,7 +142,7 @@ func (mf *v1ManifestFetcher) fetchWithSession(ctx context.Context, ref reference
|
|||||||
return nil, fmt.Errorf("No such image %s:%s", mf.repoInfo.FullName(), tag)
|
return nil, fmt.Errorf("No such image %s:%s", mf.repoInfo.FullName(), tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeImageInspect(pulledImg, tag, "", tagList), nil
|
return makeImageManifest(pulledImg, tag, "", tagList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mf *v1ManifestFetcher) pullImageJSON(imgID, endpoint string, token []string) (*image.Image, error) {
|
func (mf *v1ManifestFetcher) pullImageJSON(imgID, endpoint string, token []string) (*image.Image, error) {
|
||||||
|
@ -34,9 +34,9 @@ type v2ManifestFetcher struct {
|
|||||||
service *registry.Service
|
service *registry.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
|
func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*types.ImageManifest, error) {
|
||||||
var (
|
var (
|
||||||
imgInspect *types.ImageInspect
|
imgInspect *types.ImageManifest
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*t
|
|||||||
return imgInspect, err
|
return imgInspect, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mf *v2ManifestFetcher) fetchWithRepository(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
|
func (mf *v2ManifestFetcher) fetchWithRepository(ctx context.Context, ref reference.Named) (*types.ImageManifest, error) {
|
||||||
var (
|
var (
|
||||||
manifest distribution.Manifest
|
manifest distribution.Manifest
|
||||||
tagOrDigest string // Used for logging/progress only
|
tagOrDigest string // Used for logging/progress only
|
||||||
@ -146,7 +146,7 @@ func (mf *v2ManifestFetcher) fetchWithRepository(ctx context.Context, ref refere
|
|||||||
//ref = reference.WithDefaultTag(ref)
|
//ref = reference.WithDefaultTag(ref)
|
||||||
//}
|
//}
|
||||||
//_ = showTags
|
//_ = showTags
|
||||||
return makeImageInspect(image, tagOrDigest, manifestDigest, tagList), nil
|
return makeImageManifest(image, tagOrDigest, manifestDigest, tagList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mf *v2ManifestFetcher) pullSchema1(ctx context.Context, ref reference.Named, unverifiedManifest *schema1.SignedManifest) (img *image.Image, manifestDigest digest.Digest, err error) {
|
func (mf *v2ManifestFetcher) pullSchema1(ctx context.Context, ref reference.Named, unverifiedManifest *schema1.SignedManifest) (img *image.Image, manifestDigest digest.Digest, err error) {
|
||||||
|
50
inspect.go
50
inspect.go
@ -1,55 +1,49 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/projectatomic/skopeo/docker"
|
"github.com/projectatomic/skopeo/docker"
|
||||||
"github.com/projectatomic/skopeo/types"
|
"github.com/projectatomic/skopeo/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type imgKind int
|
var inspectCmd = cli.Command{
|
||||||
|
Name: "inspect",
|
||||||
const (
|
Usage: "inspect images on a registry",
|
||||||
imgTypeDocker = "docker://"
|
ArgsUsage: ``,
|
||||||
imgTypeAppc = "appc://"
|
Action: func(c *cli.Context) {
|
||||||
|
// get the Image interface before inspecting...utils.go parseImage
|
||||||
kindUnknown = iota
|
imgInspect, err := inspect(c)
|
||||||
kindDocker
|
if err != nil {
|
||||||
kindAppc
|
logrus.Fatal(err)
|
||||||
)
|
|
||||||
|
|
||||||
func getImgType(img string) imgKind {
|
|
||||||
if strings.HasPrefix(img, imgTypeDocker) {
|
|
||||||
return kindDocker
|
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(img, imgTypeAppc) {
|
out, err := json.Marshal(imgInspect)
|
||||||
return kindAppc
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
// TODO(runcom): v2 will support this
|
fmt.Println(string(out))
|
||||||
//return kindUnknown
|
},
|
||||||
return kindDocker
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func inspect(c *cli.Context) (*types.ImageInspect, error) {
|
func inspect(c *cli.Context) (*types.ImageManifest, error) {
|
||||||
var (
|
var (
|
||||||
imgInspect *types.ImageInspect
|
imgInspect *types.ImageManifest
|
||||||
err error
|
err error
|
||||||
name = c.Args().First()
|
name = c.Args().First()
|
||||||
kind = getImgType(name)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
switch kind {
|
switch {
|
||||||
case kindDocker:
|
case strings.HasPrefix(name, types.DockerPrefix):
|
||||||
imgInspect, err = docker.GetData(c, strings.Replace(name, imgTypeDocker, "", -1))
|
imgInspect, err = docker.GetData(c, strings.Replace(name, "docker://", "", -1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case kindAppc:
|
|
||||||
return nil, fmt.Errorf("not implemented yet")
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("%s image is invalid, please use either 'docker://' or 'appc://'", name)
|
return nil, fmt.Errorf("%s image is invalid, please use 'docker://'", name)
|
||||||
}
|
}
|
||||||
return imgInspect, nil
|
return imgInspect, nil
|
||||||
}
|
}
|
||||||
|
20
main.go
20
main.go
@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
@ -12,21 +10,9 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
version = "0.1.12-dev"
|
version = "0.1.12-dev"
|
||||||
usage = "inspect images on a registry"
|
usage = "interact with registries"
|
||||||
)
|
)
|
||||||
|
|
||||||
var inspectCmd = func(c *cli.Context) {
|
|
||||||
imgInspect, err := inspect(c)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
out, err := json.Marshal(imgInspect)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(string(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "skopeo"
|
app.Name = "skopeo"
|
||||||
@ -61,7 +47,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
app.Action = inspectCmd
|
app.Commands = []cli.Command{
|
||||||
|
inspectCmd,
|
||||||
|
}
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,24 @@ import (
|
|||||||
containerTypes "github.com/docker/engine-api/types/container"
|
containerTypes "github.com/docker/engine-api/types/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ImageInspect struct {
|
type Kind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
KindUnknown Kind = iota
|
||||||
|
KindDocker
|
||||||
|
KindAppc
|
||||||
|
|
||||||
|
DockerPrefix = "docker://"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Image interface {
|
||||||
|
Kind() Kind
|
||||||
|
GetLayers(layers []string) error
|
||||||
|
GetManifest(version string) ([]byte, error)
|
||||||
|
GetRawManifest(version string) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageManifest struct {
|
||||||
Tag string
|
Tag string
|
||||||
Digest string
|
Digest string
|
||||||
RepoTags []string
|
RepoTags []string
|
||||||
|
18
utils.go
Normal file
18
utils.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/projectatomic/skopeo/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseImage(img string) (types.Image, error) {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(img, types.DockerPrefix):
|
||||||
|
//return parseDockerImage(strings.TrimPrefix(img, dockerPrefix))
|
||||||
|
//case strings.HasPrefix(img, appcPrefix):
|
||||||
|
//
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no valid prefix provided")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user