// NewRegistryPullThroughCache creates a registry acting as a pull through cache func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Namespace, driver driver.StorageDriver, config configuration.Proxy) (distribution.Namespace, error) { _, err := url.Parse(config.RemoteURL) if err != nil { return nil, err } v := storage.NewVacuum(ctx, driver) s := scheduler.New(ctx, driver, "/scheduler-state.json") s.OnBlobExpire(func(digest string) error { return v.RemoveBlob(digest) }) s.OnManifestExpire(func(repoName string) error { return v.RemoveRepository(repoName) }) err = s.Start() if err != nil { return nil, err } challengeManager := auth.NewSimpleChallengeManager() cs, err := ConfigureAuth(config.RemoteURL, config.Username, config.Password, challengeManager) if err != nil { return nil, err } return &proxyingRegistry{ embedded: registry, scheduler: s, challengeManager: challengeManager, credentialStore: cs, remoteURL: config.RemoteURL, }, nil }
// NewV2Repository returns a repository (v2 only). It creates a HTTP transport // providing timeout settings and authentication support, and also verifies the // remote API version. func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *cliconfig.AuthConfig, actions ...string) (distribution.Repository, error) { ctx := context.Background() repoName := repoInfo.CanonicalName // If endpoint does not support CanonicalName, use the RemoteName instead if endpoint.TrimHostname { repoName = repoInfo.RemoteName } // TODO(dmcgowan): Call close idle connections when complete, use keep alive base := &http.Transport{ Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).Dial, TLSHandshakeTimeout: 10 * time.Second, TLSClientConfig: endpoint.TLSConfig, // TODO(dmcgowan): Call close idle connections when complete and use keep alive DisableKeepAlives: true, } modifiers := registry.DockerHeaders(metaHeaders) authTransport := transport.NewTransport(base, modifiers...) pingClient := &http.Client{ Transport: authTransport, Timeout: 15 * time.Second, } endpointStr := endpoint.URL + "/v2/" req, err := http.NewRequest("GET", endpointStr, nil) if err != nil { return nil, err } resp, err := pingClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() versions := auth.APIVersions(resp, endpoint.VersionHeader) if endpoint.VersionHeader != "" && len(endpoint.Versions) > 0 { var foundVersion bool for _, version := range endpoint.Versions { for _, pingVersion := range versions { if version == pingVersion { foundVersion = true } } } if !foundVersion { return nil, errors.New("endpoint does not support v2 API") } } challengeManager := auth.NewSimpleChallengeManager() if err := challengeManager.AddResponse(resp); err != nil { return nil, err } creds := dumbCredentialStore{auth: authConfig} tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...) basicHandler := auth.NewBasicHandler(creds) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) tr := transport.NewTransport(base, modifiers...) return client.NewRepository(ctx, repoName, endpoint.URL, tr) }