Compare commits

...

9 Commits

Author SHA1 Message Date
Antonio Murdaca
2986c6d0d6 return nicer error
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-07 18:14:24 +01:00
Antonio Murdaca
01b47e3681 default to inspect docker images
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-07 18:13:30 +01:00
Antonio Murdaca
1d452edce6 make space for appc img inspect :)
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-07 18:07:01 +01:00
Antonio Murdaca
3de433ea4a re-enable and fix failing tests
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-02 13:51:10 +01:00
Antonio Murdaca
143ff7165d fix readme
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-02 13:38:21 +01:00
Antonio Murdaca
6cfd14f0c0 lots of todo and fixes
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-02 11:44:49 +01:00
Antonio Murdaca
8d22756979 move infof to debugf to not clutter stderr
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-01 17:45:19 +01:00
Antonio Murdaca
55804fad16 add todo
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-01-29 10:30:22 +01:00
Antonio Murdaca
71e0fec389 bump v0.1.5-dev
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-01-29 10:15:09 +01:00
8 changed files with 360 additions and 325 deletions

View File

@@ -66,19 +66,10 @@ $ skopeo myregistrydomain.com:5000/busybox
# let's try now to fake a non existent Docker's config file
$ skopeo --docker-cfg="" myregistrydomain.com:5000/busybox
INFO[0000] Docker cli config file not found: stat : no such file or directory, falling back to --username and --password
FATA[0000] unable to ping registry endpoint http://myregistrydomain.com:5000/v0/
v2 ping attempt failed with error: Get http://myregistrydomain.com:5000/v2/: malformed HTTP response "\x15\x03\x01\x00\x02\x02"
v1 ping attempt failed with error: Get http://myregistrydomain.com:5000/v1/_ping: malformed HTTP response "\x15\x03\x01\x00\x02\x02"
FATA[0000] Get https://myregistrydomain.com:5000/v2/busybox/manifests/latest: no basic auth credentials
# we can see the cli config isn't found so it looks for --username and --password
# but because I didn't provide them I can't auth to the registry and I receive the above error
INFO[0000] Docker cli config file not found: stat : no such file or directory, falling back to --username and --password
# passing --username and --password - we can see that everything goes fine despite an info showing
# cli config can't be found
# passing --username and --password - we can see that everything goes fine
$ skopeo --docker-cfg="" --username=testuser --password=testpassword myregistrydomain.com:5000/busybox
INFO[0000] Docker cli config file not found: stat : no such file or directory, falling back to --username and --password
{"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
@@ -113,6 +104,7 @@ TODO
-
- show repo tags via flag or when reference isn't tagged or digested
- add tests (integration with deployed registries in container - Docker-like)
- support rkt/appc image spec
NOT TODO
-

293
docker/inspect.go Normal file
View File

@@ -0,0 +1,293 @@
package docker
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
"github.com/docker/distribution/digest"
distreference "github.com/docker/distribution/reference"
"github.com/docker/docker/api"
"github.com/docker/docker/cliconfig"
"github.com/docker/docker/image"
versionPkg "github.com/docker/docker/pkg/version"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
engineTypes "github.com/docker/engine-api/types"
registryTypes "github.com/docker/engine-api/types/registry"
"github.com/runcom/skopeo/types"
"golang.org/x/net/context"
)
// fallbackError wraps an error that can possibly allow fallback to a different
// endpoint.
type fallbackError struct {
// err is the error being wrapped.
err error
// confirmedV2 is set to true if it was confirmed that the registry
// supports the v2 protocol. This is used to limit fallbacks to the v1
// protocol.
confirmedV2 bool
}
// Error renders the FallbackError as a string.
func (f fallbackError) Error() string {
return f.err.Error()
}
type manifestFetcher interface {
Fetch(ctx context.Context, ref reference.Named) (*types.ImageInspect, error)
}
func validateName(name string) error {
distref, err := distreference.ParseNamed(name)
if err != nil {
return err
}
hostname, _ := distreference.SplitHostname(distref)
if hostname == "" {
return fmt.Errorf("Please use a fully qualified repository name")
}
return nil
}
func GetData(c *cli.Context, name string) (*types.ImageInspect, error) {
if err := validateName(name); err != nil {
return nil, err
}
ref, err := reference.ParseNamed(name)
if err != nil {
return nil, err
}
repoInfo, err := registry.ParseRepositoryInfo(ref)
if err != nil {
return nil, err
}
authConfig, err := getAuthConfig(c, repoInfo.Index)
if err != nil {
return nil, err
}
if err := validateRepoName(repoInfo.Name()); err != nil {
return nil, err
}
//options := &registry.Options{}
//options.Mirrors = opts.NewListOpts(nil)
//options.InsecureRegistries = opts.NewListOpts(nil)
//options.InsecureRegistries.Set("0.0.0.0/0")
//registryService := registry.NewService(options)
registryService := registry.NewService(nil)
//// TODO(runcom): hacky, provide a way of passing tls cert (flag?) to be used to lookup
//for _, ic := range registryService.Config.IndexConfigs {
//ic.Secure = false
//}
endpoints, err := registryService.LookupPullEndpoints(repoInfo)
if err != nil {
return nil, err
}
var (
ctx = context.Background()
lastErr error
discardNoSupportErrors bool
imgInspect *types.ImageInspect
confirmedV2 bool
)
for _, endpoint := range endpoints {
// make sure I can reach the registry, same as docker pull does
v1endpoint, err := endpoint.ToV1Endpoint(nil)
if err != nil {
return nil, err
}
if _, err := v1endpoint.Ping(); err != nil {
if strings.Contains(err.Error(), "timeout") {
return nil, err
}
continue
}
if confirmedV2 && endpoint.Version == registry.APIVersion1 {
logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
continue
}
logrus.Debugf("Trying to fetch image manifest of %s repository from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
//fetcher, err := newManifestFetcher(endpoint, repoInfo, config)
fetcher, err := newManifestFetcher(endpoint, repoInfo, authConfig, registryService)
if err != nil {
lastErr = err
continue
}
if imgInspect, err = fetcher.Fetch(ctx, ref); err != nil {
// Was this fetch cancelled? If so, don't try to fall back.
fallback := false
select {
case <-ctx.Done():
default:
if fallbackErr, ok := err.(fallbackError); ok {
fallback = true
confirmedV2 = confirmedV2 || fallbackErr.confirmedV2
err = fallbackErr.err
}
}
if fallback {
if _, ok := err.(registry.ErrNoSupport); !ok {
// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
discardNoSupportErrors = true
// save the current error
lastErr = err
} else if !discardNoSupportErrors {
// Save the ErrNoSupport error, because it's either the first error or all encountered errors
// were also ErrNoSupport errors.
lastErr = err
}
continue
}
logrus.Debugf("Not continuing with error: %v", err)
return nil, err
}
return imgInspect, nil
}
if lastErr == nil {
lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
}
return nil, lastErr
}
func newManifestFetcher(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, authConfig engineTypes.AuthConfig, registryService *registry.Service) (manifestFetcher, error) {
switch endpoint.Version {
case registry.APIVersion2:
return &v2ManifestFetcher{
endpoint: endpoint,
authConfig: authConfig,
service: registryService,
repoInfo: repoInfo,
}, nil
case registry.APIVersion1:
return &v1ManifestFetcher{
endpoint: endpoint,
authConfig: authConfig,
service: registryService,
repoInfo: repoInfo,
}, nil
}
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
}
func getAuthConfig(c *cli.Context, index *registryTypes.IndexInfo) (engineTypes.AuthConfig, error) {
var (
username = c.GlobalString("username")
password = c.GlobalString("password")
cfg = c.GlobalString("docker-cfg")
defAuthConfig = engineTypes.AuthConfig{
Username: c.GlobalString("username"),
Password: c.GlobalString("password"),
Email: "stub@example.com",
}
)
// TODO(runcom): implement this to opt-in for docker-cfg, no need to make this
// work by default with docker's conf
//useDockerConf := c.GlobalString("use-docker-cfg")
if username != "" && password != "" {
return defAuthConfig, nil
}
if _, err := os.Stat(cfg); err != nil {
logrus.Debugf("Docker cli config file %q not found: %v, falling back to --username and --password if needed", cfg, err)
if os.IsNotExist(err) {
return defAuthConfig, nil
}
return engineTypes.AuthConfig{}, nil
}
confFile, err := cliconfig.Load(cfg)
if err != nil {
return engineTypes.AuthConfig{}, err
}
authConfig := registry.ResolveAuthConfig(confFile.AuthConfigs, index)
logrus.Debugf("authConfig for %s: %v", index.Name, authConfig)
return authConfig, nil
}
func validateRepoName(name string) error {
if name == "" {
return fmt.Errorf("Repository name can't be empty")
}
if name == api.NoBaseImageSpecifier {
return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
}
return nil
}
func makeImageInspect(img *image.Image, tag string, dgst digest.Digest, tagList []string) *types.ImageInspect {
var digest string
if err := dgst.Validate(); err == nil {
digest = dgst.String()
}
return &types.ImageInspect{
Tag: tag,
Digest: digest,
RepoTags: tagList,
Comment: img.Comment,
Created: img.Created.Format(time.RFC3339Nano),
ContainerConfig: &img.ContainerConfig,
DockerVersion: img.DockerVersion,
Author: img.Author,
Config: img.Config,
Architecture: img.Architecture,
Os: img.OS,
}
}
func makeRawConfigFromV1Config(imageJSON []byte, rootfs *image.RootFS, history []image.History) (map[string]*json.RawMessage, error) {
var dver struct {
DockerVersion string `json:"docker_version"`
}
if err := json.Unmarshal(imageJSON, &dver); err != nil {
return nil, err
}
useFallback := versionPkg.Version(dver.DockerVersion).LessThan("1.8.3")
if useFallback {
var v1Image image.V1Image
err := json.Unmarshal(imageJSON, &v1Image)
if err != nil {
return nil, err
}
imageJSON, err = json.Marshal(v1Image)
if err != nil {
return nil, err
}
}
var c map[string]*json.RawMessage
if err := json.Unmarshal(imageJSON, &c); err != nil {
return nil, err
}
c["rootfs"] = rawJSON(rootfs)
c["history"] = rawJSON(history)
return c, nil
}
func rawJSON(value interface{}) *json.RawMessage {
jsonval, err := json.Marshal(value)
if err != nil {
return nil
}
return (*json.RawMessage)(&jsonval)
}

View File

@@ -1,4 +1,4 @@
package main
package docker
import (
"encoding/json"
@@ -13,7 +13,8 @@ import (
"github.com/docker/docker/image/v1"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
engineTypes "github.com/docker/engine-api/types"
"github.com/runcom/skopeo/types"
"golang.org/x/net/context"
)
@@ -23,14 +24,14 @@ type v1ManifestFetcher struct {
repo distribution.Repository
confirmedV2 bool
// wrap in a config?
authConfig types.AuthConfig
authConfig engineTypes.AuthConfig
service *registry.Service
session *registry.Session
}
func (mf *v1ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*imageInspect, error) {
func (mf *v1ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
var (
imgInspect *imageInspect
imgInspect *types.ImageInspect
)
if _, isCanonical := ref.(reference.Canonical); isCanonical {
// Allowing fallback, because HTTPS v1 is before HTTP v2
@@ -65,7 +66,7 @@ func (mf *v1ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*i
return imgInspect, nil
}
func (mf *v1ManifestFetcher) fetchWithSession(ctx context.Context, ref reference.Named) (*imageInspect, error) {
func (mf *v1ManifestFetcher) fetchWithSession(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
repoData, err := mf.session.GetRepositoryData(mf.repoInfo)
if err != nil {
if strings.Contains(err.Error(), "HTTP code: 404") {

View File

@@ -1,4 +1,4 @@
package main
package docker
import (
"encoding/json"
@@ -19,7 +19,8 @@ import (
"github.com/docker/docker/image/v1"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
engineTypes "github.com/docker/engine-api/types"
"github.com/runcom/skopeo/types"
"golang.org/x/net/context"
)
@@ -29,13 +30,13 @@ type v2ManifestFetcher struct {
repo distribution.Repository
confirmedV2 bool
// wrap in a config?
authConfig types.AuthConfig
authConfig engineTypes.AuthConfig
service *registry.Service
}
func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*imageInspect, error) {
func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
var (
imgInspect *imageInspect
imgInspect *types.ImageInspect
err error
)
@@ -58,7 +59,7 @@ func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*i
return imgInspect, err
}
func (mf *v2ManifestFetcher) fetchWithRepository(ctx context.Context, ref reference.Named) (*imageInspect, error) {
func (mf *v2ManifestFetcher) fetchWithRepository(ctx context.Context, ref reference.Named) (*types.ImageInspect, error) {
var (
manifest distribution.Manifest
tagOrDigest string // Used for logging/progress only

View File

@@ -1,303 +1,35 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
"github.com/docker/distribution/digest"
distreference "github.com/docker/distribution/reference"
"github.com/docker/docker/api"
"github.com/docker/docker/cliconfig"
"github.com/docker/docker/image"
versionPkg "github.com/docker/docker/pkg/version"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
types "github.com/docker/engine-api/types"
containerTypes "github.com/docker/engine-api/types/container"
registryTypes "github.com/docker/engine-api/types/registry"
"golang.org/x/net/context"
"github.com/runcom/skopeo/docker"
"github.com/runcom/skopeo/types"
)
// fallbackError wraps an error that can possibly allow fallback to a different
// endpoint.
type fallbackError struct {
// err is the error being wrapped.
err error
// confirmedV2 is set to true if it was confirmed that the registry
// supports the v2 protocol. This is used to limit fallbacks to the v1
// protocol.
confirmedV2 bool
}
const (
imgTypeDocker = "docker"
imgTypeAppc = "appc"
)
// Error renders the FallbackError as a string.
func (f fallbackError) Error() string {
return f.err.Error()
}
type manifestFetcher interface {
Fetch(ctx context.Context, ref reference.Named) (*imageInspect, error)
}
type imageInspect struct {
Tag string
Digest string
RepoTags []string
Comment string
Created string
ContainerConfig *containerTypes.Config
DockerVersion string
Author string
Config *containerTypes.Config
Architecture string
Os string
}
func validateName(name string) error {
distref, err := distreference.ParseNamed(name)
if err != nil {
return err
}
hostname, _ := distreference.SplitHostname(distref)
if hostname == "" {
return fmt.Errorf("Please use a fully qualified repository name")
}
return nil
}
func inspect(c *cli.Context) (*imageInspect, error) {
name := c.Args().First()
if err := validateName(name); err != nil {
return nil, err
}
ref, err := reference.ParseNamed(name)
if err != nil {
return nil, err
}
imgInspect, err := getData(c, ref)
if err != nil {
return nil, err
func inspect(c *cli.Context) (*types.ImageInspect, error) {
var (
imgInspect *types.ImageInspect
err error
name = c.Args().First()
imgType = c.GlobalString("img-type")
)
switch imgType {
case imgTypeDocker:
imgInspect, err = docker.GetData(c, name)
if err != nil {
return nil, err
}
case imgTypeAppc:
return nil, fmt.Errorf("sorry, not implemented yet")
default:
return nil, fmt.Errorf("%s image type is invalid, please use either 'docker' or 'appc'", imgType)
}
return imgInspect, nil
}
func getData(c *cli.Context, ref reference.Named) (*imageInspect, error) {
repoInfo, err := registry.ParseRepositoryInfo(ref)
if err != nil {
return nil, err
}
authConfig, err := getAuthConfig(c, repoInfo.Index)
if err != nil {
return nil, err
}
if err := validateRepoName(repoInfo.Name()); err != nil {
return nil, err
}
//options := &registry.Options{}
//options.Mirrors = opts.NewListOpts(nil)
//options.InsecureRegistries = opts.NewListOpts(nil)
//options.InsecureRegistries.Set("0.0.0.0/0")
//registryService := registry.NewService(options)
registryService := registry.NewService(nil)
//// TODO(runcom): hacky, provide a way of passing tls cert (flag?) to be used to lookup
//for _, ic := range registryService.Config.IndexConfigs {
//ic.Secure = false
//}
endpoints, err := registryService.LookupPullEndpoints(repoInfo)
if err != nil {
return nil, err
}
var (
ctx = context.Background()
lastErr error
discardNoSupportErrors bool
imgInspect *imageInspect
confirmedV2 bool
)
for _, endpoint := range endpoints {
// make sure I can reach the registry, same as docker pull does
v1endpoint, err := endpoint.ToV1Endpoint(nil)
if err != nil {
return nil, err
}
if _, err := v1endpoint.Ping(); err != nil {
if strings.Contains(err.Error(), "timeout") {
return nil, err
}
continue
}
if confirmedV2 && endpoint.Version == registry.APIVersion1 {
logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
continue
}
logrus.Debugf("Trying to fetch image manifest of %s repository from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
//fetcher, err := newManifestFetcher(endpoint, repoInfo, config)
fetcher, err := newManifestFetcher(endpoint, repoInfo, authConfig, registryService)
if err != nil {
lastErr = err
continue
}
if imgInspect, err = fetcher.Fetch(ctx, ref); err != nil {
// Was this fetch cancelled? If so, don't try to fall back.
fallback := false
select {
case <-ctx.Done():
default:
if fallbackErr, ok := err.(fallbackError); ok {
fallback = true
confirmedV2 = confirmedV2 || fallbackErr.confirmedV2
err = fallbackErr.err
}
}
if fallback {
if _, ok := err.(registry.ErrNoSupport); !ok {
// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
discardNoSupportErrors = true
// save the current error
lastErr = err
} else if !discardNoSupportErrors {
// Save the ErrNoSupport error, because it's either the first error or all encountered errors
// were also ErrNoSupport errors.
lastErr = err
}
continue
}
logrus.Debugf("Not continuing with error: %v", err)
return nil, err
}
return imgInspect, nil
}
if lastErr == nil {
lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
}
return nil, lastErr
}
func newManifestFetcher(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, authConfig types.AuthConfig, registryService *registry.Service) (manifestFetcher, error) {
switch endpoint.Version {
case registry.APIVersion2:
return &v2ManifestFetcher{
endpoint: endpoint,
authConfig: authConfig,
service: registryService,
repoInfo: repoInfo,
}, nil
case registry.APIVersion1:
return &v1ManifestFetcher{
endpoint: endpoint,
authConfig: authConfig,
service: registryService,
repoInfo: repoInfo,
}, nil
}
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
}
func getAuthConfig(c *cli.Context, index *registryTypes.IndexInfo) (types.AuthConfig, error) {
var (
username = c.GlobalString("username")
password = c.GlobalString("password")
cfg = c.GlobalString("docker-cfg")
)
if _, err := os.Stat(cfg); err != nil {
logrus.Infof("Docker cli config file %q not found: %v, falling back to --username and --password if needed", cfg, err)
return types.AuthConfig{
Username: username,
Password: password,
Email: "stub@example.com",
}, nil
}
confFile, err := cliconfig.Load(cfg)
if err != nil {
return types.AuthConfig{}, err
}
authConfig := registry.ResolveAuthConfig(confFile.AuthConfigs, index)
logrus.Debugf("authConfig for %s: %v", index.Name, authConfig)
return authConfig, nil
}
func validateRepoName(name string) error {
if name == "" {
return fmt.Errorf("Repository name can't be empty")
}
if name == api.NoBaseImageSpecifier {
return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
}
return nil
}
func makeImageInspect(img *image.Image, tag string, dgst digest.Digest, tagList []string) *imageInspect {
var digest string
if err := dgst.Validate(); err == nil {
digest = dgst.String()
}
return &imageInspect{
Tag: tag,
Digest: digest,
RepoTags: tagList,
Comment: img.Comment,
Created: img.Created.Format(time.RFC3339Nano),
ContainerConfig: &img.ContainerConfig,
DockerVersion: img.DockerVersion,
Author: img.Author,
Config: img.Config,
Architecture: img.Architecture,
Os: img.OS,
}
}
func makeRawConfigFromV1Config(imageJSON []byte, rootfs *image.RootFS, history []image.History) (map[string]*json.RawMessage, error) {
var dver struct {
DockerVersion string `json:"docker_version"`
}
if err := json.Unmarshal(imageJSON, &dver); err != nil {
return nil, err
}
useFallback := versionPkg.Version(dver.DockerVersion).LessThan("1.8.3")
if useFallback {
var v1Image image.V1Image
err := json.Unmarshal(imageJSON, &v1Image)
if err != nil {
return nil, err
}
imageJSON, err = json.Marshal(v1Image)
if err != nil {
return nil, err
}
}
var c map[string]*json.RawMessage
if err := json.Unmarshal(imageJSON, &c); err != nil {
return nil, err
}
c["rootfs"] = rawJSON(rootfs)
c["history"] = rawJSON(history)
return c, nil
}
func rawJSON(value interface{}) *json.RawMessage {
jsonval, err := json.Marshal(value)
if err != nil {
return nil
}
return (*json.RawMessage)(&jsonval)
}

View File

@@ -84,11 +84,7 @@ func (s *SkopeoSuite) TestVersion(c *check.C) {
func (s *SkopeoSuite) TestCanAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
out, err := exec.Command(skopeoBinary, "--docker-cfg=''", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, fmt.Sprintf("%s/busybox:latest", s.regV2WithAuth.url)).CombinedOutput()
c.Assert(err, check.NotNil, check.Commentf(string(out)))
wanted := "falling back to --username and --password if needed"
if !strings.Contains(string(out), wanted) {
c.Fatalf("wanted %s, got %s", wanted, string(out))
}
wanted = "Error: image busybox not found"
wanted := "Error: image busybox not found"
if !strings.Contains(string(out), wanted) {
c.Fatalf("wanted %s, got %s", wanted, string(out))
}
@@ -97,11 +93,7 @@ func (s *SkopeoSuite) TestCanAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C)
func (s *SkopeoSuite) TestNeedAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
out, err := exec.Command(skopeoBinary, "--docker-cfg=''", fmt.Sprintf("%s/busybox:latest", s.regV2WithAuth.url)).CombinedOutput()
c.Assert(err, check.NotNil, check.Commentf(string(out)))
wanted := "falling back to --username and --password if needed"
if !strings.Contains(string(out), wanted) {
c.Fatalf("wanted %s, got %s", wanted, string(out))
}
wanted = "no basic auth credentials"
wanted := "no basic auth credentials"
if !strings.Contains(string(out), wanted) {
c.Fatalf("wanted %s, got %s", wanted, string(out))
}

View File

@@ -11,7 +11,7 @@ import (
)
const (
version = "0.1.4"
version = "0.1.5-dev"
usage = "inspect images on a registry"
)
@@ -52,6 +52,11 @@ func main() {
Value: cliconfig.ConfigDir(),
Usage: "Docker's cli config for auth",
},
cli.StringFlag{
Name: "img-type",
Value: imgTypeDocker,
Usage: "Either docker or appc",
},
}
app.Before = func(c *cli.Context) error {
if c.GlobalBool("debug") {

19
types/types.go Normal file
View File

@@ -0,0 +1,19 @@
package types
import (
containerTypes "github.com/docker/engine-api/types/container"
)
type ImageInspect struct {
Tag string
Digest string
RepoTags []string
Comment string
Created string
ContainerConfig *containerTypes.Config
DockerVersion string
Author string
Config *containerTypes.Config
Architecture string
Os string
}