diff --git a/inspect.go b/inspect.go index b4657194..8433f5b4 100644 --- a/inspect.go +++ b/inspect.go @@ -3,14 +3,36 @@ package main import ( "fmt" + "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/docker/api" "github.com/docker/docker/reference" "github.com/docker/docker/registry" engineTypes "github.com/docker/engine-api/types" containerTypes "github.com/docker/engine-api/types/container" + "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) (*imageInspect, error) +} + type imageInspect struct { // I shouldn't need json tag here... ID string `json:"Id"` @@ -54,6 +76,8 @@ func inspect(c *cli.Context) (*imageInspect, error) { } _ = authConfig + // TODO(runcom): ... + // both authConfig and unqualified images return nil, nil } @@ -73,6 +97,8 @@ func getData(ref reference.Named) (*imageInspect, error) { // need to be run as root, really? :( // just pass tlsconfig via cli?!?!?! // + // this happens only with private registry, docker.io works out of the box + // // TODO(runcom): do not assume docker is installed on the system! // just fallback as for getAuthConfig endpoints, err := registryService.LookupPullEndpoints(repoInfo) @@ -80,7 +106,83 @@ func getData(ref reference.Named) (*imageInspect, error) { return nil, err } - return nil, nil + var ( + ctx = context.Background() + lastErr error + discardNoSupportErrors bool + imgInspect *imageInspect + confirmedV2 bool + ) + + for _, endpoint := range endpoints { + 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) + 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) (manifestFetcher, error) { + switch endpoint.Version { + case registry.APIVersion2: + return &v2ManifestFetcher{ + endpoint: endpoint, + //config: config, + repoInfo: repoInfo, + }, nil + //case registry.APIVersion1: + //return &v1ManifestFetcher{ + //endpoint: endpoint, + ////config: config, + //repoInfo: repoInfo, + //}, nil + } + return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL) } func getAuthConfig(c *cli.Context, ref reference.Named) (engineTypes.AuthConfig, error) { diff --git a/inspect_v2.go b/inspect_v2.go index 06ab7d0f..b55c9247 100644 --- a/inspect_v2.go +++ b/inspect_v2.go @@ -1 +1,22 @@ package main + +import ( + "fmt" + + "github.com/docker/distribution" + "github.com/docker/docker/reference" + "github.com/docker/docker/registry" + "golang.org/x/net/context" +) + +type v2ManifestFetcher struct { + endpoint registry.APIEndpoint + repoInfo *registry.RepositoryInfo + repo distribution.Repository + confirmedV2 bool +} + +func (mf *v2ManifestFetcher) Fetch(ctx context.Context, ref reference.Named) (*imageInspect, error) { + fmt.Println("ciaone") + return nil, nil +}