Beispiel #1
0
func (b *bridge) createManifestEvent(action string, repo reference.Named, sm distribution.Manifest) (*Event, error) {
	event := b.createEvent(action)
	event.Target.Repository = repo.Name()

	mt, p, err := sm.Payload()
	if err != nil {
		return nil, err
	}

	// Ensure we have the canonical manifest descriptor here
	_, desc, err := distribution.UnmarshalManifest(mt, p)
	if err != nil {
		return nil, err
	}

	event.Target.MediaType = mt
	event.Target.Length = desc.Size
	event.Target.Size = desc.Size
	event.Target.Digest = desc.Digest

	ref, err := reference.WithDigest(repo, event.Target.Digest)
	if err != nil {
		return nil, err
	}

	event.Target.URL, err = b.ub.BuildManifestURL(ref)
	if err != nil {
		return nil, err
	}

	return event, nil
}
Beispiel #2
0
// Delete deletes a reference from the store. It returns true if a deletion
// happened, or false otherwise.
func (store *store) Delete(ref reference.Named) (bool, error) {
	ref = defaultTagIfNameOnly(ref)

	store.mu.Lock()
	defer store.mu.Unlock()

	repoName := ref.Name()

	repository, exists := store.Repositories[repoName]
	if !exists {
		return false, ErrDoesNotExist
	}

	refStr := ref.String()
	if id, exists := repository[refStr]; exists {
		delete(repository, refStr)
		if len(repository) == 0 {
			delete(store.Repositories, repoName)
		}
		if store.referencesByIDCache[id] != nil {
			delete(store.referencesByIDCache[id], refStr)
			if len(store.referencesByIDCache[id]) == 0 {
				delete(store.referencesByIDCache, id)
			}
		}
		return true, store.save()
	}

	return false, ErrDoesNotExist
}
Beispiel #3
0
// ReferencesByName returns the references for a given repository name.
// If there are no references known for this repository name,
// ReferencesByName returns nil.
func (store *store) ReferencesByName(ref reference.Named) []Association {
	store.mu.RLock()
	defer store.mu.RUnlock()

	repository, exists := store.Repositories[ref.Name()]
	if !exists {
		return nil
	}

	var associations []Association
	for refStr, refID := range repository {
		ref, err := reference.ParseNamed(refStr)
		if err != nil {
			// Should never happen
			return nil
		}
		associations = append(associations,
			Association{
				Ref:     ref,
				ImageID: refID,
			})
	}

	return associations
}
func (r *testRegistry) Repository(ctx context.Context, ref reference.Named) (distribution.Repository, error) {
	repo, err := r.Namespace.Repository(ctx, ref)
	if err != nil {
		return nil, err
	}

	kFakeClient := ktestclient.NewSimpleFake()

	parts := strings.SplitN(ref.Name(), "/", 3)
	if len(parts) != 2 {
		return nil, fmt.Errorf("failed to parse repository name %q", ref.Name())
	}

	return &repository{
		Repository: repo,

		ctx:              ctx,
		quotaClient:      kFakeClient,
		limitClient:      kFakeClient,
		registryOSClient: r.osClient,
		registryAddr:     "localhost:5000",
		namespace:        parts[0],
		name:             parts[1],
		blobrepositorycachettl: r.blobrepositorycachettl,
		cachedLayers:           cachedLayers,
		pullthrough:            r.pullthrough,
	}, nil
}
Beispiel #5
0
func (b *bridge) createManifestDeleteEventAndWrite(action string, repo reference.Named, dgst digest.Digest) error {
	event := b.createEvent(action)
	event.Target.Repository = repo.Name()
	event.Target.Digest = dgst

	return b.sink.Write(*event)
}
Beispiel #6
0
func addTestManifest(repo reference.Named, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) {
	*m = append(*m, testutil.RequestResponseMapping{
		Request: testutil.Request{
			Method: "GET",
			Route:  "/v2/" + repo.Name() + "/manifests/" + reference,
		},
		Response: testutil.Response{
			StatusCode: http.StatusOK,
			Body:       content,
			Headers: http.Header(map[string][]string{
				"Content-Length":        {fmt.Sprint(len(content))},
				"Last-Modified":         {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
				"Content-Type":          {mediatype},
				"Docker-Content-Digest": {contentDigestString(mediatype, content)},
			}),
		},
	})
	*m = append(*m, testutil.RequestResponseMapping{
		Request: testutil.Request{
			Method: "HEAD",
			Route:  "/v2/" + repo.Name() + "/manifests/" + reference,
		},
		Response: testutil.Response{
			StatusCode: http.StatusOK,
			Headers: http.Header(map[string][]string{
				"Content-Length":        {fmt.Sprint(len(content))},
				"Last-Modified":         {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
				"Content-Type":          {mediatype},
				"Docker-Content-Digest": {digest.Canonical.FromBytes(content).String()},
			}),
		},
	})

}
Beispiel #7
0
func joinHost(host string, ref reference.Named) (reference.Named, error) {
	if host == "" {
		return ref, nil
	}

	return updateName(ref, strings.Join([]string{host, ref.Name()}, "/"))
}
Beispiel #8
0
// GetRemoteTags retrieves all tags from the given repository. It queries each
// of the registries supplied in the registries argument, and returns data from
// the first one that answers the query successfully. It returns a map with
// tag names as the keys and image IDs as the values.
func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
	repository := repositoryRef.Name()

	if strings.Count(repository, "/") == 0 {
		// This will be removed once the registry supports auto-resolution on
		// the "library" namespace
		repository = "library/" + repository
	}
	for _, host := range registries {
		endpoint := fmt.Sprintf("%srepositories/%s/tags", host, repository)
		res, err := r.client.Get(endpoint)
		if err != nil {
			return nil, err
		}

		logrus.Debugf("Got status code %d from %s", res.StatusCode, endpoint)
		defer res.Body.Close()

		if res.StatusCode == 404 {
			return nil, ErrRepoNotFound
		}
		if res.StatusCode != 200 {
			continue
		}

		result := make(map[string]string)
		if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
			return nil, err
		}
		return result, nil
	}
	return nil, fmt.Errorf("Could not reach any registry endpoint")
}
Beispiel #9
0
// normalizeLibraryRepoName removes the library prefix from
// the repository name for official repos.
func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) {
	if strings.HasPrefix(name.Name(), "library/") {
		// If pull "library/foo", it's stored locally under "foo"
		return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1])
	}
	return name, nil
}
Beispiel #10
0
func addTestManifestWithEtag(repo reference.Named, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) {
	actualDigest := digest.FromBytes(content)
	getReqWithEtag := testutil.Request{
		Method: "GET",
		Route:  "/v2/" + repo.Name() + "/manifests/" + reference,
		Headers: http.Header(map[string][]string{
			"If-None-Match": {fmt.Sprintf(`"%s"`, dgst)},
		}),
	}

	var getRespWithEtag testutil.Response
	if actualDigest.String() == dgst {
		getRespWithEtag = testutil.Response{
			StatusCode: http.StatusNotModified,
			Body:       []byte{},
			Headers: http.Header(map[string][]string{
				"Content-Length": {"0"},
				"Last-Modified":  {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
				"Content-Type":   {schema1.MediaTypeSignedManifest},
			}),
		}
	} else {
		getRespWithEtag = testutil.Response{
			StatusCode: http.StatusOK,
			Body:       content,
			Headers: http.Header(map[string][]string{
				"Content-Length": {fmt.Sprint(len(content))},
				"Last-Modified":  {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
				"Content-Type":   {schema1.MediaTypeSignedManifest},
			}),
		}

	}
	*m = append(*m, testutil.RequestResponseMapping{Request: getReqWithEtag, Response: getRespWithEtag})
}
Beispiel #11
0
func (b *bridge) BlobMounted(repo reference.Named, desc distribution.Descriptor, fromRepo reference.Named) error {
	event, err := b.createBlobEvent(EventActionMount, repo, desc)
	if err != nil {
		return err
	}
	event.Target.FromRepository = fromRepo.Name()
	return b.sink.Write(*event)
}
Beispiel #12
0
func digestRef(ref reference.Named, digst string) (reference.Canonical, error) {
	rn, err := reference.ParseNamed(ref.Name())
	if err != nil {
		return nil, err
	}

	d := digest.Digest(digst)

	return reference.WithDigest(rn, d)
}
Beispiel #13
0
// BuildBlobUploadChunkURL constructs a url for the upload identified by uuid,
// including any url values. This should generally not be used by clients, as
// this url is provided by server implementations during the blob upload
// process.
func (ub *URLBuilder) BuildBlobUploadChunkURL(name reference.Named, uuid string, values ...url.Values) (string, error) {
	route := ub.cloneRoute(RouteNameBlobUploadChunk)

	uploadURL, err := route.URL("name", name.Name(), "uuid", uuid)
	if err != nil {
		return "", err
	}

	return appendValuesURL(uploadURL, values...).String(), nil
}
Beispiel #14
0
func validateRemoteName(remoteName reference.Named) error {
	remoteNameStr := remoteName.Name()
	if !strings.Contains(remoteNameStr, "/") {
		// the repository name must not be a valid image ID
		if err := v1.ValidateID(remoteNameStr); err == nil {
			return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName)
		}
	}
	return nil
}
Beispiel #15
0
// BuildTagsURL constructs a url to list the tags in the named repository.
func (ub *URLBuilder) BuildTagsURL(name reference.Named) (string, error) {
	route := ub.cloneRoute(RouteNameTags)

	tagsURL, err := route.URL("name", name.Name())
	if err != nil {
		return "", err
	}

	return tagsURL.String(), nil
}
Beispiel #16
0
// AddManifest schedules a manifest cleanup after ttl expires
func (ttles *TTLExpirationScheduler) AddManifest(repoName reference.Named, ttl time.Duration) error {
	ttles.Lock()
	defer ttles.Unlock()

	if ttles.stopped {
		return fmt.Errorf("scheduler not started")
	}

	ttles.add(repoName.Name(), ttl, entryTypeManifest)
	return nil
}
Beispiel #17
0
// NewRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
func (config *ServiceConfig) NewRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
	if err := validateNoSchema(reposName.Name()); err != nil {
		return nil, err
	}

	repoInfo := &RepositoryInfo{}
	var (
		indexName string
		err       error
	)

	indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName)
	if err != nil {
		return nil, err
	}

	repoInfo.Index, err = config.NewIndexInfo(indexName)
	if err != nil {
		return nil, err
	}

	if repoInfo.Index.Official {
		repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName)
		if err != nil {
			return nil, err
		}
		repoInfo.RemoteName = repoInfo.LocalName

		// If the normalized name does not contain a '/' (e.g. "foo")
		// then it is an official repo.
		if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 {
			repoInfo.Official = true
			// Fix up remote name for official repos.
			repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name())
			if err != nil {
				return nil, err
			}
		}

		repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name())
		if err != nil {
			return nil, err
		}
	} else {
		repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
		if err != nil {
			return nil, err
		}
		repoInfo.CanonicalName = repoInfo.LocalName
	}

	return repoInfo, nil
}
Beispiel #18
0
func getRepoIndexFromUnnormalizedRef(ref distreference.Named) (*registrytypes.IndexInfo, error) {
	named, err := reference.ParseNamed(ref.Name())
	if err != nil {
		return nil, err
	}

	repoInfo, err := registry.ParseRepositoryInfo(named)
	if err != nil {
		return nil, err
	}

	return repoInfo.Index, nil
}
Beispiel #19
0
// loadRepositoryName returns the repo name splitted into index name
// and remote repo name. It returns an error if the name is not valid.
func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) {
	if err := validateNoSchema(reposName.Name()); err != nil {
		return "", nil, err
	}
	indexName, remoteName, err := splitReposName(reposName)

	if indexName, err = ValidateIndexName(indexName); err != nil {
		return "", nil, err
	}
	if err = validateRemoteName(remoteName); err != nil {
		return "", nil, err
	}
	return indexName, remoteName, nil
}
Beispiel #20
0
// TagImage creates a tag in the repository reponame, pointing to the image named
// imageName. If force is true, an existing tag with the same name may be
// overwritten.
func (daemon *Daemon) TagImage(newTag reference.Named, imageName string, force bool) error {
	if _, isDigested := newTag.(reference.Digested); isDigested {
		return errors.New("refusing to create a tag with a digest reference")
	}
	if newTag.Name() == string(digest.Canonical) {
		return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
	}

	newTag = registry.NormalizeLocalReference(newTag)
	imageID, err := daemon.GetImageID(imageName)
	if err != nil {
		return err
	}
	daemon.EventsService.Log("tag", newTag.String(), "")
	return daemon.tagStore.Add(newTag, imageID, force)
}
Beispiel #21
0
func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
	var cfg = tlsconfig.ServerDefault
	tlsConfig := &cfg
	nameString := repoName.Name()
	if strings.HasPrefix(nameString, DefaultNamespace+"/") {
		endpoints = append(endpoints, APIEndpoint{
			URL:          DefaultV1Registry,
			Version:      APIVersion1,
			Official:     true,
			TrimHostname: true,
			TLSConfig:    tlsConfig,
		})
		return endpoints, nil
	}

	slashIndex := strings.IndexRune(nameString, '/')
	if slashIndex <= 0 {
		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", nameString)
	}
	hostname := nameString[:slashIndex]

	tlsConfig, err = s.TLSConfig(hostname)
	if err != nil {
		return nil, err
	}

	endpoints = []APIEndpoint{
		{
			URL:          "https://" + hostname,
			Version:      APIVersion1,
			TrimHostname: true,
			TLSConfig:    tlsConfig,
		},
	}

	if tlsConfig.InsecureSkipVerify {
		endpoints = append(endpoints, APIEndpoint{ // or this
			URL:          "http://" + hostname,
			Version:      APIVersion1,
			TrimHostname: true,
			// used to check if supposed to be secure via InsecureSkipVerify
			TLSConfig: tlsConfig,
		})
	}
	return endpoints, nil
}
Beispiel #22
0
// Repository returns an instance of the repository tied to the registry.
// Instances should not be shared between goroutines but are cheap to
// allocate. In general, they should be request scoped.
func (reg *registry) Repository(ctx context.Context, canonicalName reference.Named) (distribution.Repository, error) {
	var descriptorCache distribution.BlobDescriptorService
	if reg.blobDescriptorCacheProvider != nil {
		var err error
		descriptorCache, err = reg.blobDescriptorCacheProvider.RepositoryScoped(canonicalName.Name())
		if err != nil {
			return nil, err
		}
	}

	return &repository{
		ctx:             ctx,
		registry:        reg,
		name:            canonicalName,
		descriptorCache: descriptorCache,
	}, nil
}
Beispiel #23
0
// Get retrieves an item from the store by reference.
func (store *store) Get(ref reference.Named) (image.ID, error) {
	ref = defaultTagIfNameOnly(ref)

	store.mu.RLock()
	defer store.mu.RUnlock()

	repository, exists := store.Repositories[ref.Name()]
	if !exists || repository == nil {
		return "", ErrDoesNotExist
	}

	id, exists := repository[ref.String()]
	if !exists {
		return "", ErrDoesNotExist
	}

	return id, nil
}
Beispiel #24
0
// NewReferenceManifestBuilder is used to build new manifests for the current
// schema version using schema1 dependencies.
func NewReferenceManifestBuilder(pk libtrust.PrivateKey, ref reference.Named, architecture string) distribution.ManifestBuilder {
	tag := ""
	if tagged, isTagged := ref.(reference.Tagged); isTagged {
		tag = tagged.Tag()
	}

	return &referenceManifestBuilder{
		Manifest: Manifest{
			Versioned: manifest.Versioned{
				SchemaVersion: 1,
			},
			Name:         ref.Name(),
			Tag:          tag,
			Architecture: architecture,
		},
		pk: pk,
	}
}
Beispiel #25
0
func (b *bridge) createBlobEvent(action string, repo reference.Named, desc distribution.Descriptor) (*Event, error) {
	event := b.createEvent(action)
	event.Target.Descriptor = desc
	event.Target.Length = desc.Size
	event.Target.Repository = repo.Name()

	ref, err := reference.WithDigest(repo, desc.Digest)
	if err != nil {
		return nil, err
	}

	event.Target.URL, err = b.ub.BuildBlobURL(ref)
	if err != nil {
		return nil, err
	}

	return event, nil
}
Beispiel #26
0
// BuildManifestURL constructs a url for the manifest identified by name and
// reference. The argument reference may be either a tag or digest.
func (ub *URLBuilder) BuildManifestURL(ref reference.Named) (string, error) {
	route := ub.cloneRoute(RouteNameManifest)

	tagOrDigest := ""
	switch v := ref.(type) {
	case reference.Tagged:
		tagOrDigest = v.Tag()
	case reference.Digested:
		tagOrDigest = v.Digest().String()
	}

	manifestURL, err := route.URL("name", ref.Name(), "reference", tagOrDigest)
	if err != nil {
		return "", err
	}

	return manifestURL.String(), nil
}
Beispiel #27
0
// Add adds a tag or digest to the store. If force is set to true, existing
// references can be overwritten. This only works for tags, not digests.
func (store *store) Add(ref reference.Named, id image.ID, force bool) error {
	ref = defaultTagIfNameOnly(ref)

	store.mu.Lock()
	defer store.mu.Unlock()

	repository, exists := store.Repositories[ref.Name()]
	if !exists || repository == nil {
		repository = make(map[string]image.ID)
		store.Repositories[ref.Name()] = repository
	}

	refStr := ref.String()
	oldID, exists := repository[refStr]

	if exists {
		// force only works for tags
		if digested, isDigest := ref.(reference.Digested); isDigest {
			return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
		}

		if !force {
			return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", ref.String(), oldID.String())
		}

		if store.referencesByIDCache[oldID] != nil {
			delete(store.referencesByIDCache[oldID], refStr)
			if len(store.referencesByIDCache[oldID]) == 0 {
				delete(store.referencesByIDCache, oldID)
			}
		}
	}

	repository[refStr] = id
	if store.referencesByIDCache[id] == nil {
		store.referencesByIDCache[id] = make(map[string]reference.Named)
	}
	store.referencesByIDCache[id][refStr] = ref

	return store.save()
}
Beispiel #28
0
// PushRegistryTag pushes a tag on the registry.
// Remote has the format '<user>/<repo>
func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
	// "jsonify" the string
	revision = "\"" + revision + "\""
	path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag)

	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
	if err != nil {
		return err
	}
	req.Header.Add("Content-type", "application/json")
	req.ContentLength = int64(len(revision))
	res, err := r.client.Do(req)
	if err != nil {
		return err
	}
	res.Body.Close()
	if res.StatusCode != 200 && res.StatusCode != 201 {
		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res)
	}
	return nil
}
func statSourceRepository(
	ctx context.Context,
	destRepo *repository,
	sourceRepoName reference.Named,
	dgst digest.Digest,
) (desc distribution.Descriptor, err error) {
	upstreamRepo, err := dockerRegistry.Repository(ctx, sourceRepoName)
	if err != nil {
		return distribution.Descriptor{}, err
	}
	namespace, name, err := getNamespaceName(sourceRepoName.Name())
	if err != nil {
		return distribution.Descriptor{}, err
	}

	repo := *destRepo
	repo.namespace = namespace
	repo.name = name
	repo.Repository = upstreamRepo

	return repo.Blobs(ctx).Stat(ctx, dgst)
}
Beispiel #30
0
func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
	c := pr.authChallenger

	tr := transport.NewTransport(http.DefaultTransport,
		auth.NewAuthorizer(c.challengeManager(), auth.NewTokenHandler(http.DefaultTransport, c.credentialStore(), name.Name(), "pull")))

	localRepo, err := pr.embedded.Repository(ctx, name)
	if err != nil {
		return nil, err
	}
	localManifests, err := localRepo.Manifests(ctx, storage.SkipLayerVerification())
	if err != nil {
		return nil, err
	}

	remoteRepo, err := client.NewRepository(ctx, name, pr.remoteURL.String(), tr)
	if err != nil {
		return nil, err
	}

	remoteManifests, err := remoteRepo.Manifests(ctx)
	if err != nil {
		return nil, err
	}

	return &proxiedRepository{
		blobStore: &proxyBlobStore{
			localStore:     localRepo.Blobs(ctx),
			remoteStore:    remoteRepo.Blobs(ctx),
			scheduler:      pr.scheduler,
			repositoryName: name,
			authChallenger: pr.authChallenger,
		},
		manifests: &proxyManifestStore{
			repositoryName:  name,
			localManifests:  localManifests, // Options?
			remoteManifests: remoteManifests,
			ctx:             ctx,
			scheduler:       pr.scheduler,
			authChallenger:  pr.authChallenger,
		},
		name: name,
		tags: &proxyTagService{
			localTags:      localRepo.Tags(ctx),
			remoteTags:     remoteRepo.Tags(ctx),
			authChallenger: pr.authChallenger,
		},
	}, nil
}