github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/bindings/images/pull.go (about)

     1  package images
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"strconv"
    14  
    15  	"github.com/containers/image/v5/types"
    16  	"github.com/containers/podman/v2/pkg/auth"
    17  	"github.com/containers/podman/v2/pkg/bindings"
    18  	"github.com/containers/podman/v2/pkg/domain/entities"
    19  	"github.com/hashicorp/go-multierror"
    20  )
    21  
    22  // Pull is the binding for libpod's v2 endpoints for pulling images.  Note that
    23  // `rawImage` must be a reference to a registry (i.e., of docker transport or be
    24  // normalized to one).  Other transports are rejected as they do not make sense
    25  // in a remote context. Progress reported on stderr
    26  func Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) ([]string, error) {
    27  	conn, err := bindings.GetClient(ctx)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  	params := url.Values{}
    32  	params.Set("reference", rawImage)
    33  	params.Set("overrideArch", options.OverrideArch)
    34  	params.Set("overrideOS", options.OverrideOS)
    35  	params.Set("overrideVariant", options.OverrideVariant)
    36  
    37  	if options.SkipTLSVerify != types.OptionalBoolUndefined {
    38  		// Note: we have to verify if skipped is false.
    39  		verifyTLS := bool(options.SkipTLSVerify == types.OptionalBoolFalse)
    40  		params.Set("tlsVerify", strconv.FormatBool(verifyTLS))
    41  	}
    42  	params.Set("allTags", strconv.FormatBool(options.AllTags))
    43  
    44  	// TODO: have a global system context we can pass around (1st argument)
    45  	header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	response, err := conn.DoRequest(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.Quiet {
    63  		stderr = ioutil.Discard
    64  	}
    65  
    66  	dec := json.NewDecoder(response.Body)
    67  	var images []string
    68  	var mErr 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  			return images, mErr
    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  			mErr = multierror.Append(mErr, errors.New(report.Error))
    90  		case len(report.Images) > 0:
    91  			images = report.Images
    92  		case report.ID != "":
    93  		default:
    94  			return images, errors.New("failed to parse pull results stream, unexpected input")
    95  		}
    96  
    97  	}
    98  	return images, mErr
    99  }