support multi commands

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
Antonio Murdaca 2016-03-16 10:06:31 +01:00
parent d0fd876d7e
commit 3fd3adc58e
9 changed files with 85 additions and 67 deletions

View File

@ -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

View File

@ -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,

View File

@ -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) {

View File

@ -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) {

View File

@ -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
} }

1
layers.go Normal file
View File

@ -0,0 +1 @@
package main

20
main.go
View File

@ -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)
} }

View File

@ -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
View 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")
}