Skip to content

Commit 3b9ea70

Browse files
authored
Pull missing docker images automatically (#191)
1 parent 6f6674f commit 3b9ea70

File tree

2 files changed

+45
-6
lines changed

2 files changed

+45
-6
lines changed

cmd/src/actions_exec.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ Format of the action JSON files:
241241

242242
logger := newActionLogger(*verbose, *keepLogsFlag)
243243

244-
// Build Docker images etc.
245-
err = prepareAction(ctx, action)
244+
// Fetch Docker images etc.
245+
err = prepareAction(ctx, action, logger)
246246
if err != nil {
247247
return errors.Wrap(err, "Failed to prepare action")
248248
}
@@ -393,14 +393,14 @@ func validateActionDefinition(def []byte) error {
393393
return errs.ErrorOrNil()
394394
}
395395

396-
func prepareAction(ctx context.Context, action Action) error {
396+
func prepareAction(ctx context.Context, action Action, logger *actionLogger) error {
397397
// Build any Docker images.
398398
for _, step := range action.Steps {
399399
if step.Type == "docker" {
400400
// Set digests for Docker images so we don't cache action runs in 2 different images with
401401
// the same tag.
402402
var err error
403-
step.ImageContentDigest, err = getDockerImageContentDigest(ctx, step.Image)
403+
step.ImageContentDigest, err = getDockerImageContentDigest(ctx, step.Image, logger)
404404
if err != nil {
405405
return errors.Wrap(err, "Failed to get Docker image content digest")
406406
}
@@ -417,14 +417,35 @@ func prepareAction(ctx context.Context, action Action) error {
417417
// have been pulled from or pushed to a registry. See
418418
// https://windsock.io/explaining-docker-image-ids/ under "A Final Twist" for a good
419419
// explanation.
420-
func getDockerImageContentDigest(ctx context.Context, image string) (string, error) {
420+
func getDockerImageContentDigest(ctx context.Context, image string, logger *actionLogger) (string, error) {
421421
// TODO!(sqs): is image id the right thing to use here? it is NOT the
422422
// digest. but the digest is not calculated for all images (unless they are
423423
// pulled/pushed from/to a registry), see
424424
// https://github.com/moby/moby/issues/32016.
425425
out, err := exec.CommandContext(ctx, "docker", "image", "inspect", "--format", "{{.Id}}", "--", image).CombinedOutput()
426426
if err != nil {
427-
return "", fmt.Errorf("error inspecting docker image (try `docker pull %q` to fix this): %s", image, bytes.TrimSpace(out))
427+
if !strings.Contains(string(out), "No such image") {
428+
return "", fmt.Errorf("error inspecting docker image %q: %s", image, bytes.TrimSpace(out))
429+
}
430+
logger.Infof("Pulling Docker image %q...\n", image)
431+
pullCmd := exec.CommandContext(ctx, "docker", "image", "pull", image)
432+
prefix := fmt.Sprintf("docker image pull %s", image)
433+
pullCmd.Stdout = logger.InfoPipe(prefix)
434+
pullCmd.Stderr = logger.ErrorPipe(prefix)
435+
436+
err = pullCmd.Start()
437+
if err != nil {
438+
return "", fmt.Errorf("error pulling docker image %q: %s", image, err)
439+
}
440+
err = pullCmd.Wait()
441+
if err != nil {
442+
return "", fmt.Errorf("error pulling docker image %q: %s", image, err)
443+
}
444+
}
445+
out, err = exec.CommandContext(ctx, "docker", "image", "inspect", "--format", "{{.Id}}", "--", image).CombinedOutput()
446+
// This time, the image MUST be present, so the issue must be something else.
447+
if err != nil {
448+
return "", fmt.Errorf("error inspecting docker image %q: %s", image, bytes.TrimSpace(out))
428449
}
429450
id := string(bytes.TrimSpace(out))
430451
if id == "" {

cmd/src/actions_exec_logger.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,24 @@ func (a *actionLogger) RepoWriter(repoName string) (io.Writer, bool) {
150150
return w, ok
151151
}
152152

153+
func (a *actionLogger) InfoPipe(prefix string) io.Writer {
154+
if !a.verbose {
155+
return ioutil.Discard
156+
}
157+
stdoutPrefix := fmt.Sprintf("%s -> [STDOUT]: ", yellow.Sprint(prefix))
158+
stderr := textio.NewPrefixWriter(os.Stderr, stdoutPrefix)
159+
return io.Writer(stderr)
160+
}
161+
162+
func (a *actionLogger) ErrorPipe(prefix string) io.Writer {
163+
if !a.verbose {
164+
return ioutil.Discard
165+
}
166+
stderrPrefix := fmt.Sprintf("%s -> [STDERR]: ", yellow.Sprint(prefix))
167+
stderr := textio.NewPrefixWriter(os.Stderr, stderrPrefix)
168+
return io.Writer(stderr)
169+
}
170+
153171
func (a *actionLogger) RepoStdoutStderr(repoName string) (io.Writer, io.Writer, bool) {
154172
a.mu.Lock()
155173
defer a.mu.Unlock()

0 commit comments

Comments
 (0)