github.com/containers/podman/v4@v4.9.4/pkg/bindings/images/push.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 imageTypes "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 ) 18 19 // Push is the binding for libpod's endpoints for push images. Note that 20 // `source` must be a referring to an image in the remote's container storage. 21 // The destination must be a reference to a registry (i.e., of docker transport 22 // or be normalized to one). Other transports are rejected as they do not make 23 // sense in a remote context. 24 func Push(ctx context.Context, source string, destination string, options *PushOptions) error { 25 if options == nil { 26 options = new(PushOptions) 27 } 28 conn, err := bindings.GetClient(ctx) 29 if err != nil { 30 return err 31 } 32 header, err := auth.MakeXRegistryAuthHeader(&imageTypes.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword()) 33 if err != nil { 34 return err 35 } 36 37 params, err := options.ToParams() 38 if err != nil { 39 return err 40 } 41 // SkipTLSVerify is special. It's not being serialized by ToParams() 42 // because we need to flip the boolean. 43 if options.SkipTLSVerify != nil { 44 params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) 45 } 46 params.Set("destination", destination) 47 48 path := fmt.Sprintf("/images/%s/push", source) 49 response, err := conn.DoRequest(ctx, nil, http.MethodPost, path, params, header) 50 if err != nil { 51 return err 52 } 53 defer response.Body.Close() 54 55 if !response.IsSuccess() { 56 return 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 LOOP: 71 for { 72 var report entities.ImagePushStream 73 if err := dec.Decode(&report); err != nil { 74 if errors.Is(err, io.EOF) { 75 break 76 } 77 return err 78 } 79 80 select { 81 case <-response.Request.Context().Done(): 82 break LOOP 83 default: 84 // non-blocking select 85 } 86 87 switch { 88 case report.Stream != "": 89 fmt.Fprint(writer, report.Stream) 90 case report.ManifestDigest != "": 91 options.ManifestDigest = &report.ManifestDigest 92 case report.Error != "": 93 // There can only be one error. 94 return errors.New(report.Error) 95 default: 96 return fmt.Errorf("failed to parse push results stream, unexpected input: %v", report) 97 } 98 } 99 100 return nil 101 }