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 }