mirror of https://github.com/k3d-io/k3d
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
5.4 KiB
222 lines
5.4 KiB
// Copyright 2023 Google LLC All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package remote
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/google/go-containerregistry/pkg/name"
|
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
|
"github.com/google/go-containerregistry/pkg/v1/partial"
|
|
"github.com/google/go-containerregistry/pkg/v1/types"
|
|
)
|
|
|
|
type Puller struct {
|
|
o *options
|
|
|
|
// map[resource]*reader
|
|
readers sync.Map
|
|
}
|
|
|
|
func NewPuller(options ...Option) (*Puller, error) {
|
|
o, err := makeOptions(options...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return newPuller(o), nil
|
|
}
|
|
|
|
func newPuller(o *options) *Puller {
|
|
if o.puller != nil {
|
|
return o.puller
|
|
}
|
|
return &Puller{
|
|
o: o,
|
|
}
|
|
}
|
|
|
|
type reader struct {
|
|
// in
|
|
target resource
|
|
o *options
|
|
|
|
// f()
|
|
once sync.Once
|
|
|
|
// out
|
|
f *fetcher
|
|
err error
|
|
}
|
|
|
|
// this will run once per reader instance
|
|
func (r *reader) init(ctx context.Context) error {
|
|
r.once.Do(func() {
|
|
r.f, r.err = makeFetcher(ctx, r.target, r.o)
|
|
})
|
|
return r.err
|
|
}
|
|
|
|
func (p *Puller) fetcher(ctx context.Context, target resource) (*fetcher, error) {
|
|
v, _ := p.readers.LoadOrStore(target, &reader{
|
|
target: target,
|
|
o: p.o,
|
|
})
|
|
rr := v.(*reader)
|
|
return rr.f, rr.init(ctx)
|
|
}
|
|
|
|
// Head is like remote.Head, but avoids re-authenticating when possible.
|
|
func (p *Puller) Head(ctx context.Context, ref name.Reference) (*v1.Descriptor, error) {
|
|
f, err := p.fetcher(ctx, ref.Context())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return f.headManifest(ctx, ref, allManifestMediaTypes)
|
|
}
|
|
|
|
// Get is like remote.Get, but avoids re-authenticating when possible.
|
|
func (p *Puller) Get(ctx context.Context, ref name.Reference) (*Descriptor, error) {
|
|
return p.get(ctx, ref, allManifestMediaTypes, p.o.platform)
|
|
}
|
|
|
|
func (p *Puller) get(ctx context.Context, ref name.Reference, acceptable []types.MediaType, platform v1.Platform) (*Descriptor, error) {
|
|
f, err := p.fetcher(ctx, ref.Context())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return f.get(ctx, ref, acceptable, platform)
|
|
}
|
|
|
|
// Layer is like remote.Layer, but avoids re-authenticating when possible.
|
|
func (p *Puller) Layer(ctx context.Context, ref name.Digest) (v1.Layer, error) {
|
|
f, err := p.fetcher(ctx, ref.Context())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h, err := v1.NewHash(ref.Identifier())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l, err := partial.CompressedToLayer(&remoteLayer{
|
|
fetcher: *f,
|
|
ctx: ctx,
|
|
digest: h,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &MountableLayer{
|
|
Layer: l,
|
|
Reference: ref,
|
|
}, nil
|
|
}
|
|
|
|
// List lists tags in a repo and handles pagination, returning the full list of tags.
|
|
func (p *Puller) List(ctx context.Context, repo name.Repository) ([]string, error) {
|
|
lister, err := p.Lister(ctx, repo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tagList := []string{}
|
|
for lister.HasNext() {
|
|
tags, err := lister.Next(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tagList = append(tagList, tags.Tags...)
|
|
}
|
|
|
|
return tagList, nil
|
|
}
|
|
|
|
// Lister lists tags in a repo and returns a Lister for paginating through the results.
|
|
func (p *Puller) Lister(ctx context.Context, repo name.Repository) (*Lister, error) {
|
|
return p.lister(ctx, repo, p.o.pageSize)
|
|
}
|
|
|
|
func (p *Puller) lister(ctx context.Context, repo name.Repository, pageSize int) (*Lister, error) {
|
|
f, err := p.fetcher(ctx, repo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
page, err := f.listPage(ctx, repo, "", pageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Lister{
|
|
f: f,
|
|
repo: repo,
|
|
pageSize: pageSize,
|
|
page: page,
|
|
err: err,
|
|
}, nil
|
|
}
|
|
|
|
// Catalog lists repos in a registry and handles pagination, returning the full list of repos.
|
|
func (p *Puller) Catalog(ctx context.Context, reg name.Registry) ([]string, error) {
|
|
return p.catalog(ctx, reg, p.o.pageSize)
|
|
}
|
|
|
|
func (p *Puller) catalog(ctx context.Context, reg name.Registry, pageSize int) ([]string, error) {
|
|
catalogger, err := p.catalogger(ctx, reg, pageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
repoList := []string{}
|
|
for catalogger.HasNext() {
|
|
repos, err := catalogger.Next(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
repoList = append(repoList, repos.Repos...)
|
|
}
|
|
return repoList, nil
|
|
}
|
|
|
|
// Catalogger lists repos in a registry and returns a Catalogger for paginating through the results.
|
|
func (p *Puller) Catalogger(ctx context.Context, reg name.Registry) (*Catalogger, error) {
|
|
return p.catalogger(ctx, reg, p.o.pageSize)
|
|
}
|
|
|
|
func (p *Puller) catalogger(ctx context.Context, reg name.Registry, pageSize int) (*Catalogger, error) {
|
|
f, err := p.fetcher(ctx, reg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
page, err := f.catalogPage(ctx, reg, "", pageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Catalogger{
|
|
f: f,
|
|
reg: reg,
|
|
pageSize: pageSize,
|
|
page: page,
|
|
err: err,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Puller) referrers(ctx context.Context, d name.Digest, filter map[string]string) (v1.ImageIndex, error) {
|
|
f, err := p.fetcher(ctx, d.Context())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return f.fetchReferrers(ctx, filter, d)
|
|
}
|
|
|