@@ -10,7 +10,6 @@ import (
1010 "time"
1111
1212 "github.com/google/go-containerregistry/pkg/name"
13- "github.com/google/go-containerregistry/pkg/v1/daemon"
1413 "github.com/kernel/hypeman-go"
1514 "github.com/kernel/hypeman-go/option"
1615 "github.com/tidwall/gjson"
@@ -95,24 +94,20 @@ func handleRemotePushTarget(ctx context.Context, cmd *cli.Command, target string
9594 // The one-argument form follows Docker's local-tag flow: TARGET must be
9695 // present in the local Docker daemon before it can be staged and pushed.
9796 // Cached Hypeman images use the explicit IMAGE TARGET form instead.
98- srcRef , err := name .ParseReference (target )
99- if err != nil {
100- return err
101- }
102- img , err := daemon .Image (srcRef )
97+ img , err := loadDockerImage (target )
10398 if err != nil {
10499 return fmt .Errorf ("load local Docker image %q: %w; tag it first or use hypeman push <image> <target> for a cached Hypeman image" , target , err )
105100 }
106101
107102 fmt .Fprintf (os .Stderr , "Staging local image %s in Hypeman...\n " , target )
108- if err := pushLocalImage (ctx , cmd , target , target , img ); err != nil {
103+ if err := uploadLocalImage (ctx , cmd , target , img ); err != nil {
109104 return err
110105 }
111106
112107 client := hypeman .NewClient (getDefaultRequestOptions (cmd )... )
113- imported , err := client . Images . Get (ctx , url . PathEscape ( target ) )
108+ imported , err := waitForImageRecord (ctx , & client , target )
114109 if err != nil {
115- return fmt . Errorf ( "get staged image %s: %w" , target , err )
110+ return err
116111 }
117112 if err := waitForImageReady (ctx , & client , imported ); err != nil {
118113 return err
@@ -134,6 +129,27 @@ func validateRemotePushTarget(target string) error {
134129 return nil
135130}
136131
132+ func waitForImageRecord (ctx context.Context , client * hypeman.Client , imageName string ) (* hypeman.Image , error ) {
133+ ticker := time .NewTicker (300 * time .Millisecond )
134+ defer ticker .Stop ()
135+
136+ for {
137+ img , err := client .Images .Get (ctx , url .PathEscape (imageName ))
138+ if err == nil {
139+ return img , nil
140+ }
141+ if ! isNotFoundError (err ) {
142+ return nil , fmt .Errorf ("get staged image %s: %w" , imageName , err )
143+ }
144+
145+ select {
146+ case <- ctx .Done ():
147+ return nil , ctx .Err ()
148+ case <- ticker .C :
149+ }
150+ }
151+ }
152+
137153func handlePushCreate (ctx context.Context , cmd * cli.Command ) error {
138154 args := cmd .Args ().Slice ()
139155 if len (args ) != 2 {
@@ -226,41 +242,29 @@ func waitForPush(ctx context.Context, client *hypeman.Client, push *hypeman.Push
226242 output : os .Stderr ,
227243 interactive : term .IsTerminal (int (os .Stderr .Fd ())),
228244 }
245+ defer renderer .finish ()
229246 }
230247
231- current := push
232248 var lastBytes int64
233- for {
234- if renderer != nil {
235- switch current .Status {
236- case hypeman .PushStatusQueued :
237- renderer .update (fmt .Sprintf ("queued · %s" , current .Target ))
238- case hypeman .PushStatusPushing :
239- if current .Bytes > lastBytes {
240- lastBytes = current .Bytes
241- }
242- renderer .update (fmt .Sprintf ("pushing %s · %d layers · %s" , formatBytes (lastBytes ), current .Layers , current .Target ))
243- case hypeman .PushStatusPushed :
244- renderer .update (fmt .Sprintf ("pushed · digest: %s" , current .Digest ))
245- case hypeman .PushStatusFailed :
246- message := current .Error
247- if message == "" {
248- message = "unknown error"
249- }
250- renderer .update ("failed · " + message )
251- }
249+ return pollPush (ctx , client , push , opts , func (current * hypeman.Push ) {
250+ if renderer == nil {
251+ return
252252 }
253+ if message := pushStatusText (current , & lastBytes ); message != "" {
254+ renderer .update (message )
255+ }
256+ })
257+ }
258+
259+ func pollPush (ctx context.Context , client * hypeman.Client , push * hypeman.Push , opts []option.RequestOption , update func (* hypeman.Push )) (* hypeman.Push , error ) {
260+ current := push
261+ for {
262+ update (current )
253263
254264 switch current .Status {
255265 case hypeman .PushStatusPushed :
256- if renderer != nil {
257- renderer .finish ()
258- }
259266 return current , nil
260267 case hypeman .PushStatusFailed :
261- if renderer != nil {
262- renderer .finish ()
263- }
264268 if current .Error != "" {
265269 return nil , fmt .Errorf ("push %s failed: %s" , push .ID , current .Error )
266270 }
@@ -283,6 +287,28 @@ func waitForPush(ctx context.Context, client *hypeman.Client, push *hypeman.Push
283287 }
284288}
285289
290+ func pushStatusText (push * hypeman.Push , lastBytes * int64 ) string {
291+ switch push .Status {
292+ case hypeman .PushStatusQueued :
293+ return fmt .Sprintf ("queued · %s" , push .Target )
294+ case hypeman .PushStatusPushing :
295+ if push .Bytes > * lastBytes {
296+ * lastBytes = push .Bytes
297+ }
298+ return fmt .Sprintf ("pushing %s · %d layers · %s" , formatBytes (* lastBytes ), push .Layers , push .Target )
299+ case hypeman .PushStatusPushed :
300+ return fmt .Sprintf ("pushed · digest: %s" , push .Digest )
301+ case hypeman .PushStatusFailed :
302+ message := push .Error
303+ if message == "" {
304+ message = "unknown error"
305+ }
306+ return "failed · " + message
307+ default :
308+ return ""
309+ }
310+ }
311+
286312type pushStatusRenderer struct {
287313 output io.Writer
288314 interactive bool
0 commit comments