@@ -85,8 +85,14 @@ func NewCmd(p *print.Printer) *cobra.Command {
8585 Long : "Creates images." ,
8686 Args : args .NoArgs ,
8787 Example : examples .Build (
88- examples .NewExample (`Create a named imaged` , `$ stackit beta image create --name my-new-image --disk-format=raw --local-file-path=/my/raw/image` ),
89- examples .NewExample (`Create a named image with labels` , `$ stackit beta image create --name my-new-image --disk-format=raw --local-file-path=/my/raw/image--labels dev,amd64` ),
88+ examples .NewExample (
89+ `Create a named image 'my-new-image' from a raw disk image located in '/my/raw/image'` ,
90+ `$ stackit beta image create --name my-new-image --disk-format=raw --local-file-path=/my/raw/image` ,
91+ ),
92+ examples .NewExample (
93+ `Create a named image 'my-new-image' from a qcow2 image read from '/my/qcow2/image' with labels describing its contents` ,
94+ `$ stackit beta image create --name my-new-image --disk-format=qcow2 --local-file-path=/my/qcow2/image--labels os=linux,distro=alpine,version=3.12` ,
95+ ),
9096 ),
9197 RunE : func (cmd * cobra.Command , _ []string ) (err error ) {
9298 ctx := context .Background ()
@@ -132,7 +138,7 @@ func NewCmd(p *print.Printer) *cobra.Command {
132138 if ! ok {
133139 return fmt .Errorf ("create image: no upload URL has been provided" )
134140 }
135- if err := uploadFile (ctx , p , file , * url ); err != nil {
141+ if err := uploadAsync (ctx , p , file , * url ); err != nil {
136142 return err
137143 }
138144
@@ -148,41 +154,69 @@ func NewCmd(p *print.Printer) *cobra.Command {
148154 return cmd
149155}
150156
151- func uploadFile (ctx context.Context , p * print.Printer , file * os.File , url string ) (err error ) {
152- var filesize int64
153- if stat , err := file .Stat (); err != nil {
154- p .Debug (print .DebugLevel , "create image: cannot open file %q: %w" , file .Name (), err )
155- } else {
156- filesize = stat .Size ()
157- }
158- p .Debug (print .DebugLevel , "uploading image to %s" , url )
157+ func uploadAsync (ctx context.Context , p * print.Printer , file * os.File , url string ) error {
158+ ticker := time .NewTicker (5 * time .Second )
159+ ch := uploadFile (ctx , p , file , url )
160+
159161 start := time .Now ()
160- // pass the file contents as stream, as they can get arbitrarily large. We do
161- // _not_ want to load them into an internal buffer. The downside is, that we
162- // have to set the content-length header manually
163- uploadRequest , err := http .NewRequestWithContext (ctx , http .MethodPut , url , bufio .NewReader (file ))
164- if err != nil {
165- return fmt .Errorf ("create image: cannot create request: %w" , err )
162+ for {
163+ select {
164+ case <- ticker .C :
165+ p .Info ("uploading for %s\n " , time .Since (start ))
166+ case err := <- ch :
167+ return err
168+ }
166169 }
167- uploadRequest .Header .Add ("Content-Type" , "application/octet-stream" )
168- uploadRequest .ContentLength = filesize
170+ }
169171
170- uploadResponse , err := http .DefaultClient .Do (uploadRequest )
171- if err != nil {
172- return fmt .Errorf ("create image: error contacting server for upload: %w" , err )
173- }
174- defer func () {
175- if inner := uploadResponse .Body .Close (); inner != nil {
176- err = fmt .Errorf ("error closing file: %wqq (%w)" , inner , err )
172+ func uploadFile (ctx context.Context , p * print.Printer , file * os.File , url string ) chan error {
173+ ch := make (chan error )
174+ go func () {
175+ defer close (ch )
176+ var filesize int64
177+ if stat , err := file .Stat (); err != nil {
178+ ch <- fmt .Errorf ("create image: cannot read file size %q: %w" , file .Name (), err )
179+ return
180+ } else {
181+ filesize = stat .Size ()
182+ }
183+ p .Debug (print .DebugLevel , "uploading image to %s" , url )
184+
185+ start := time .Now ()
186+ // pass the file contents as stream, as they can get arbitrarily large. We do
187+ // _not_ want to load them into an internal buffer. The downside is, that we
188+ // have to set the content-length header manually
189+ uploadRequest , err := http .NewRequestWithContext (ctx , http .MethodPut , url , bufio .NewReader (file ))
190+ if err != nil {
191+ ch <- fmt .Errorf ("create image: cannot create request: %w" , err )
192+ return
177193 }
194+ uploadRequest .Header .Add ("Content-Type" , "application/octet-stream" )
195+ uploadRequest .ContentLength = filesize
196+
197+ uploadResponse , err := http .DefaultClient .Do (uploadRequest )
198+ if err != nil {
199+ ch <- fmt .Errorf ("create image: error contacting server for upload: %w" , err )
200+ return
201+ }
202+ defer func () {
203+ if inner := uploadResponse .Body .Close (); inner != nil {
204+ err = fmt .Errorf ("error closing file: %w (%w)" , inner , err )
205+ }
206+ }()
207+ if uploadResponse .StatusCode != http .StatusOK {
208+ ch <- fmt .Errorf ("create image: server rejected image upload with %s" , uploadResponse .Status )
209+ return
210+ }
211+ delay := time .Since (start )
212+ p .Debug (print .DebugLevel , "uploaded %d bytes in %v" , filesize , delay )
213+
214+ ch <- nil
215+ return
216+
178217 }()
179- if uploadResponse .StatusCode != http .StatusOK {
180- return fmt .Errorf ("create image: server rejected image upload with %s" , uploadResponse .Status )
181- }
182- delay := time .Since (start )
183- p .Debug (print .DebugLevel , "uploaded %d bytes in %v" , filesize , delay )
184218
185- return nil
219+ return ch
186220}
187221
188222func configureFlags (cmd * cobra.Command ) {
0 commit comments