From 3fd3adc58e9caa08b68b3a0c0857f6feedc27372 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 16 Mar 2016 10:06:31 +0100 Subject: [PATCH] support multi commands Signed-off-by: Antonio Murdaca --- README.md | 14 ++++++------ docker/inspect.go | 10 ++++---- docker/inspect_v1.go | 8 +++---- docker/inspect_v2.go | 8 +++---- inspect.go | 54 ++++++++++++++++++++------------------------ layers.go | 1 + main.go | 20 ++++------------ types/types.go | 19 +++++++++++++++- utils.go | 18 +++++++++++++++ 9 files changed, 85 insertions(+), 67 deletions(-) create mode 100644 layers.go create mode 100644 utils.go diff --git a/README.md b/README.md index 3acd4cff..8c510f8c 100644 --- a/README.md +++ b/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 diff --git a/docker/inspect.go b/docker/inspect.go index a284fc13..c350feb6 100644 --- a/docker/inspect.go +++ b/docker/inspect.go @@ -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, diff --git a/docker/inspect_v1.go b/docker/inspect_v1.go index 19f0b4eb..93b18a0f 100644 --- a/docker/inspect_v1.go +++ b/docker/inspect_v1.go @@ -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) { diff --git a/docker/inspect_v2.go b/docker/inspect_v2.go index 4948f93b..719d61e3 100644 --- a/docker/inspect_v2.go +++ b/docker/inspect_v2.go @@ -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) { diff --git a/inspect.go b/inspect.go index f505593b..76b4cd64 100644 --- a/inspect.go +++ b/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 } diff --git a/layers.go b/layers.go new file mode 100644 index 00000000..06ab7d0f --- /dev/null +++ b/layers.go @@ -0,0 +1 @@ +package main diff --git a/main.go b/main.go index 0dc7bf83..963cca72 100644 --- a/main.go +++ b/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) } diff --git a/types/types.go b/types/types.go index 7f040fe5..a129a8bc 100644 --- a/types/types.go +++ b/types/types.go @@ -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 diff --git a/utils.go b/utils.go new file mode 100644 index 00000000..25ed6e08 --- /dev/null +++ b/utils.go @@ -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") +}