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