...

Source file src/github.com/google/go-containerregistry/pkg/v1/remote/list.go

Documentation: github.com/google/go-containerregistry/pkg/v1/remote

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package remote
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"net/http"
    22  	"net/url"
    23  	"strings"
    24  
    25  	"github.com/google/go-containerregistry/pkg/name"
    26  	"github.com/google/go-containerregistry/pkg/v1/remote/transport"
    27  )
    28  
    29  // ListWithContext calls List with the given context.
    30  //
    31  // Deprecated: Use List and WithContext. This will be removed in a future release.
    32  func ListWithContext(ctx context.Context, repo name.Repository, options ...Option) ([]string, error) {
    33  	return List(repo, append(options, WithContext(ctx))...)
    34  }
    35  
    36  // List calls /tags/list for the given repository, returning the list of tags
    37  // in the "tags" property.
    38  func List(repo name.Repository, options ...Option) ([]string, error) {
    39  	o, err := makeOptions(options...)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	return newPuller(o).List(o.context, repo)
    44  }
    45  
    46  type Tags struct {
    47  	Name string   `json:"name"`
    48  	Tags []string `json:"tags"`
    49  	Next string   `json:"next,omitempty"`
    50  }
    51  
    52  func (f *fetcher) listPage(ctx context.Context, repo name.Repository, next string, pageSize int) (*Tags, error) {
    53  	if next == "" {
    54  		uri := &url.URL{
    55  			Scheme: repo.Scheme(),
    56  			Host:   repo.RegistryStr(),
    57  			Path:   fmt.Sprintf("/v2/%s/tags/list", repo.RepositoryStr()),
    58  		}
    59  		if pageSize > 0 {
    60  			uri.RawQuery = fmt.Sprintf("n=%d", pageSize)
    61  		}
    62  		next = uri.String()
    63  	}
    64  
    65  	req, err := http.NewRequestWithContext(ctx, "GET", next, nil)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	resp, err := f.client.Do(req)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	if err := transport.CheckError(resp, http.StatusOK); err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	parsed := Tags{}
    80  	if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	if err := resp.Body.Close(); err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	uri, err := getNextPageURL(resp)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	if uri != nil {
    94  		parsed.Next = uri.String()
    95  	}
    96  
    97  	return &parsed, nil
    98  }
    99  
   100  // getNextPageURL checks if there is a Link header in a http.Response which
   101  // contains a link to the next page. If yes it returns the url.URL of the next
   102  // page otherwise it returns nil.
   103  func getNextPageURL(resp *http.Response) (*url.URL, error) {
   104  	link := resp.Header.Get("Link")
   105  	if link == "" {
   106  		return nil, nil
   107  	}
   108  
   109  	if link[0] != '<' {
   110  		return nil, fmt.Errorf("failed to parse link header: missing '<' in: %s", link)
   111  	}
   112  
   113  	end := strings.Index(link, ">")
   114  	if end == -1 {
   115  		return nil, fmt.Errorf("failed to parse link header: missing '>' in: %s", link)
   116  	}
   117  	link = link[1:end]
   118  
   119  	linkURL, err := url.Parse(link)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	if resp.Request == nil || resp.Request.URL == nil {
   124  		return nil, nil
   125  	}
   126  	linkURL = resp.Request.URL.ResolveReference(linkURL)
   127  	return linkURL, nil
   128  }
   129  
   130  type Lister struct {
   131  	f        *fetcher
   132  	repo     name.Repository
   133  	pageSize int
   134  
   135  	page *Tags
   136  	err  error
   137  
   138  	needMore bool
   139  }
   140  
   141  func (l *Lister) Next(ctx context.Context) (*Tags, error) {
   142  	if l.needMore {
   143  		l.page, l.err = l.f.listPage(ctx, l.repo, l.page.Next, l.pageSize)
   144  	} else {
   145  		l.needMore = true
   146  	}
   147  	return l.page, l.err
   148  }
   149  
   150  func (l *Lister) HasNext() bool {
   151  	return l.page != nil && (!l.needMore || l.page.Next != "")
   152  }
   153  

View as plain text