mirror of
https://github.com/containers/skopeo.git
synced 2025-06-24 13:52:27 +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:
|
||||
```sh
|
||||
# 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",
|
||||
"Authoritative_Registry": "registry.access.redhat.com",
|
||||
@ -24,7 +24,7 @@ $ skopeo registry.access.redhat.com/rhel7 | jq '.Config.Labels'
|
||||
}
|
||||
|
||||
# show repository's tags
|
||||
$ skopeo docker.io/fedora | jq '.RepoTags'
|
||||
$ skopeo inspect docker.io/fedora | jq '.RepoTags'
|
||||
[
|
||||
"20",
|
||||
"21",
|
||||
@ -36,11 +36,11 @@ $ skopeo docker.io/fedora | jq '.RepoTags'
|
||||
]
|
||||
|
||||
# show image's digest
|
||||
$ skopeo docker.io/fedora:rawhide | jq '.Digest'
|
||||
$ skopeo inspect docker.io/fedora:rawhide | jq '.Digest'
|
||||
"sha256:905b4846938c8aef94f52f3e41a11398ae5b40f5855fb0e40ed9c157e721d7f8"
|
||||
|
||||
# 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"
|
||||
```
|
||||
|
||||
@ -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
|
||||
$ 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"}
|
||||
|
||||
# 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
|
||||
|
||||
# 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"}
|
||||
```
|
||||
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 {
|
||||
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 {
|
||||
@ -62,7 +62,7 @@ func validateName(name string) error {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -101,7 +101,7 @@ func GetData(c *cli.Context, name string) (*types.ImageInspect, error) {
|
||||
ctx = context.Background()
|
||||
lastErr error
|
||||
discardNoSupportErrors bool
|
||||
imgInspect *types.ImageInspect
|
||||
imgInspect *types.ImageManifest
|
||||
confirmedV2 bool
|
||||
confirmedTLSRegistries = make(map[string]struct{})
|
||||
)
|
||||
@ -248,12 +248,12 @@ func validateRepoName(name string) error {
|
||||
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
|
||||
if err := dgst.Validate(); err == nil {
|
||||
digest = dgst.String()
|
||||
}
|
||||
return &types.ImageInspect{
|
||||
return &types.ImageManifest{
|
||||
Tag: tag,
|
||||
Digest: digest,
|
||||
RepoTags: tagList,
|
||||
|
@ -31,9 +31,9 @@ type v1ManifestFetcher struct {
|
||||
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 (
|
||||
imgInspect *types.ImageInspect
|
||||
imgInspect *types.ImageManifest
|
||||
)
|
||||
if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
||||
// 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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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 makeImageInspect(pulledImg, tag, "", tagList), nil
|
||||
return makeImageManifest(pulledImg, tag, "", tagList), nil
|
||||
}
|
||||
|
||||
func (mf *v1ManifestFetcher) pullImageJSON(imgID, endpoint string, token []string) (*image.Image, error) {
|
||||
|
@ -34,9 +34,9 @@ type v2ManifestFetcher struct {
|
||||
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 (
|
||||
imgInspect *types.ImageInspect
|
||||
imgInspect *types.ImageManifest
|
||||
err error
|
||||
)
|
||||
|
||||
@ -60,7 +60,7 @@ func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*t
|
||||
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 (
|
||||
manifest distribution.Manifest
|
||||
tagOrDigest string // Used for logging/progress only
|
||||
@ -146,7 +146,7 @@ func (mf *v2ManifestFetcher) fetchWithRepository(ctx context.Context, ref refere
|
||||
//ref = reference.WithDefaultTag(ref)
|
||||
//}
|
||||
//_ = 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) {
|
||||
|
54
inspect.go
54
inspect.go
@ -1,55 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/projectatomic/skopeo/docker"
|
||||
"github.com/projectatomic/skopeo/types"
|
||||
)
|
||||
|
||||
type imgKind int
|
||||
|
||||
const (
|
||||
imgTypeDocker = "docker://"
|
||||
imgTypeAppc = "appc://"
|
||||
|
||||
kindUnknown = iota
|
||||
kindDocker
|
||||
kindAppc
|
||||
)
|
||||
|
||||
func getImgType(img string) imgKind {
|
||||
if strings.HasPrefix(img, imgTypeDocker) {
|
||||
return kindDocker
|
||||
}
|
||||
if strings.HasPrefix(img, imgTypeAppc) {
|
||||
return kindAppc
|
||||
}
|
||||
// TODO(runcom): v2 will support this
|
||||
//return kindUnknown
|
||||
return kindDocker
|
||||
var inspectCmd = cli.Command{
|
||||
Name: "inspect",
|
||||
Usage: "inspect images on a registry",
|
||||
ArgsUsage: ``,
|
||||
Action: func(c *cli.Context) {
|
||||
// get the Image interface before inspecting...utils.go parseImage
|
||||
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 inspect(c *cli.Context) (*types.ImageInspect, error) {
|
||||
func inspect(c *cli.Context) (*types.ImageManifest, error) {
|
||||
var (
|
||||
imgInspect *types.ImageInspect
|
||||
imgInspect *types.ImageManifest
|
||||
err error
|
||||
name = c.Args().First()
|
||||
kind = getImgType(name)
|
||||
)
|
||||
|
||||
switch kind {
|
||||
case kindDocker:
|
||||
imgInspect, err = docker.GetData(c, strings.Replace(name, imgTypeDocker, "", -1))
|
||||
switch {
|
||||
case strings.HasPrefix(name, types.DockerPrefix):
|
||||
imgInspect, err = docker.GetData(c, strings.Replace(name, "docker://", "", -1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case kindAppc:
|
||||
return nil, fmt.Errorf("not implemented yet")
|
||||
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
|
||||
}
|
||||
|
20
main.go
20
main.go
@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
@ -12,21 +10,9 @@ import (
|
||||
|
||||
const (
|
||||
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() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "skopeo"
|
||||
@ -61,7 +47,9 @@ func main() {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
app.Action = inspectCmd
|
||||
app.Commands = []cli.Command{
|
||||
inspectCmd,
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
@ -4,7 +4,24 @@ import (
|
||||
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
|
||||
Digest 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