github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/bindings/images/pull.go (about)

     1  package images
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"os"
    11  	"strconv"
    12  
    13  	"github.com/containers/image/v5/types"
    14  	"github.com/hanks177/podman/v4/pkg/auth"
    15  	"github.com/hanks177/podman/v4/pkg/bindings"
    16  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    17  	"github.com/hanks177/podman/v4/pkg/errorhandling"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  // Pull is the binding for libpod's v2 endpoints for pulling images.  Note that
    22  // `rawImage` must be a reference to a registry (i.e., of docker transport or be
    23  // normalized to one).  Other transports are rejected as they do not make sense
    24  // in a remote context. Progress reported on stderr
    25  func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, error) {
    26  	if options == nil {
    27  		options = new(PullOptions)
    28  	}
    29  	conn, err := bindings.GetClient(ctx)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	params, err := options.ToParams()
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	params.Set("reference", rawImage)
    38  
    39  	if options.SkipTLSVerify != nil {
    40  		params.Del("SkipTLSVerify")
    41  		// Note: we have to verify if skipped is false.
    42  		params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
    43  	}
    44  
    45  	header, err := auth.MakeXRegistryAuthHeader(&types.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword())
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/images/pull", params, header)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	defer response.Body.Close()
    55  
    56  	if !response.IsSuccess() {
    57  		return nil, response.Process(err)
    58  	}
    59  
    60  	// Historically pull writes status to stderr
    61  	stderr := io.Writer(os.Stderr)
    62  	if options.GetQuiet() {
    63  		stderr = ioutil.Discard
    64  	}
    65  
    66  	dec := json.NewDecoder(response.Body)
    67  	var images []string
    68  	var pullErrors []error
    69  	for {
    70  		var report entities.ImagePullReport
    71  		if err := dec.Decode(&report); err != nil {
    72  			if errors.Is(err, io.EOF) {
    73  				break
    74  			}
    75  			report.Error = err.Error() + "\n"
    76  		}
    77  
    78  		select {
    79  		case <-response.Request.Context().Done():
    80  			break
    81  		default:
    82  			// non-blocking select
    83  		}
    84  
    85  		switch {
    86  		case report.Stream != "":
    87  			fmt.Fprint(stderr, report.Stream)
    88  		case report.Error != "":
    89  			pullErrors = append(pullErrors, errors.New(report.Error))
    90  		case len(report.Images) > 0:
    91  			images = report.Images
    92  		case report.ID != "":
    93  		default:
    94  			return images, errors.Errorf("failed to parse pull results stream, unexpected input: %v", report)
    95  		}
    96  	}
    97  	return images, errorhandling.JoinErrors(pullErrors)
    98  }