|
5 | 5 | "fmt" |
6 | 6 | "os" |
7 | 7 | "strings" |
| 8 | + "time" |
8 | 9 |
|
9 | 10 | "github.com/onkernel/hypeman-go" |
10 | 11 | "github.com/onkernel/hypeman-go/option" |
@@ -65,25 +66,29 @@ func handleRun(ctx context.Context, cmd *cli.Command) error { |
65 | 66 |
|
66 | 67 | client := hypeman.NewClient(getDefaultRequestOptions(cmd)...) |
67 | 68 |
|
68 | | - // Check if image exists, pull if not |
69 | | - _, err := client.Images.Get(ctx, image) |
| 69 | + // Check if image exists and is ready |
| 70 | + imgInfo, err := client.Images.Get(ctx, image) |
70 | 71 | if err != nil { |
71 | 72 | // Image not found, try to pull it |
72 | 73 | var apiErr *hypeman.Error |
73 | 74 | if ok := isNotFoundError(err, &apiErr); ok { |
74 | 75 | fmt.Fprintf(os.Stderr, "Image not found locally. Pulling %s...\n", image) |
75 | | - _, err = client.Images.New(ctx, hypeman.ImageNewParams{ |
| 76 | + imgInfo, err = client.Images.New(ctx, hypeman.ImageNewParams{ |
76 | 77 | Name: image, |
77 | 78 | }) |
78 | 79 | if err != nil { |
79 | 80 | return fmt.Errorf("failed to pull image: %w", err) |
80 | 81 | } |
81 | | - fmt.Fprintf(os.Stderr, "Pull complete.\n") |
82 | 82 | } else { |
83 | 83 | return fmt.Errorf("failed to check image: %w", err) |
84 | 84 | } |
85 | 85 | } |
86 | 86 |
|
| 87 | + // Wait for image to be ready (build is asynchronous) |
| 88 | + if err := waitForImageReady(ctx, &client, image, imgInfo); err != nil { |
| 89 | + return err |
| 90 | + } |
| 91 | + |
87 | 92 | // Generate name if not provided |
88 | 93 | name := cmd.String("name") |
89 | 94 | if name == "" { |
@@ -139,3 +144,68 @@ func isNotFoundError(err error, target **hypeman.Error) bool { |
139 | 144 | return false |
140 | 145 | } |
141 | 146 |
|
| 147 | +// waitForImageReady polls image status until it becomes ready or failed |
| 148 | +func waitForImageReady(ctx context.Context, client *hypeman.Client, imageName string, img *hypeman.Image) error { |
| 149 | + if img.Status == hypeman.ImageStatusReady { |
| 150 | + return nil |
| 151 | + } |
| 152 | + if img.Status == hypeman.ImageStatusFailed { |
| 153 | + if img.Error != "" { |
| 154 | + return fmt.Errorf("image build failed: %s", img.Error) |
| 155 | + } |
| 156 | + return fmt.Errorf("image build failed") |
| 157 | + } |
| 158 | + |
| 159 | + // Poll until ready |
| 160 | + ticker := time.NewTicker(1 * time.Second) |
| 161 | + defer ticker.Stop() |
| 162 | + |
| 163 | + // Show initial status |
| 164 | + showImageStatus(img) |
| 165 | + |
| 166 | + for { |
| 167 | + select { |
| 168 | + case <-ctx.Done(): |
| 169 | + return ctx.Err() |
| 170 | + case <-ticker.C: |
| 171 | + updated, err := client.Images.Get(ctx, imageName) |
| 172 | + if err != nil { |
| 173 | + return fmt.Errorf("failed to check image status: %w", err) |
| 174 | + } |
| 175 | + |
| 176 | + // Show status update if changed |
| 177 | + if updated.Status != img.Status { |
| 178 | + showImageStatus(updated) |
| 179 | + img = updated |
| 180 | + } |
| 181 | + |
| 182 | + switch updated.Status { |
| 183 | + case hypeman.ImageStatusReady: |
| 184 | + return nil |
| 185 | + case hypeman.ImageStatusFailed: |
| 186 | + if updated.Error != "" { |
| 187 | + return fmt.Errorf("image build failed: %s", updated.Error) |
| 188 | + } |
| 189 | + return fmt.Errorf("image build failed") |
| 190 | + } |
| 191 | + } |
| 192 | + } |
| 193 | +} |
| 194 | + |
| 195 | +// showImageStatus prints image build status to stderr |
| 196 | +func showImageStatus(img *hypeman.Image) { |
| 197 | + switch img.Status { |
| 198 | + case hypeman.ImageStatusPending: |
| 199 | + if img.QueuePosition > 0 { |
| 200 | + fmt.Fprintf(os.Stderr, "Queued (position %d)...\n", img.QueuePosition) |
| 201 | + } else { |
| 202 | + fmt.Fprintf(os.Stderr, "Queued...\n") |
| 203 | + } |
| 204 | + case hypeman.ImageStatusPulling: |
| 205 | + fmt.Fprintf(os.Stderr, "Pulling image...\n") |
| 206 | + case hypeman.ImageStatusConverting: |
| 207 | + fmt.Fprintf(os.Stderr, "Converting to disk image...\n") |
| 208 | + case hypeman.ImageStatusReady: |
| 209 | + fmt.Fprintf(os.Stderr, "Image ready.\n") |
| 210 | + } |
| 211 | +} |
0 commit comments