/* Copyright 2018 The Perkeep Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package images // import "perkeep.org/internal/images" import ( "bufio" "bytes" "errors" "fmt" "log" "os/exec" "strings" "go4.org/syncutil" ) // TODO(mpl): refactor somewhere with pkg/test/dockertest const thumbnailImage = "gcr.io/perkeep-containers/thumbnail" // without version const thumbnailImageID = "sha256:6b810d57896125f25d5d009328e102dc444d95e7b6d5891f19932bd76244c4c3" var ( thumbnailPullGate = syncutil.NewGate(1) haveThumbnailImage bool ) func setUpThumbnailContainer() error { if !haveDocker() { return errors.New("'docker' command not found") } thumbnailPullGate.Start() defer thumbnailPullGate.Done() if haveThumbnailImage { return nil } if ok, err := haveImageID(thumbnailImage, thumbnailImageID); !ok || err != nil { if err != nil { return fmt.Errorf("error running docker to check for %s: %v", thumbnailImage, err) } log.Printf("pulling docker image %s ...", thumbnailImage) if err := pull(thumbnailImage); err != nil { return fmt.Errorf("error pulling %s: %v", thumbnailImage, err) } } haveThumbnailImage = true return nil } // haveDocker returns whether the "docker" command was found. func haveDocker() bool { _, err := exec.LookPath("docker") return err == nil } func haveImageID(name, id string) (ok bool, err error) { out, err := exec.Command( "docker", "images", "--quiet", "--no-trunc", "--format", "{{.ID}}", name, ).Output() if err != nil { return false, err } scanner := bufio.NewScanner(bytes.NewReader(out)) for scanner.Scan() { line := strings.TrimSpace((scanner.Text())) if line == id { return true, nil } } return false, nil } // Pull retrieves the docker image with 'docker pull'. func pull(image string) error { var stdout, stderr bytes.Buffer cmd := exec.Command("docker", "pull", image) cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() out := stdout.String() // TODO(mpl): if it turns out docker respects conventions and the // "Authentication is required" message does come from stderr, then quit // checking stdout. if err != nil || stderr.Len() != 0 || strings.Contains(out, "Authentication is required") { return fmt.Errorf("docker pull failed: stdout: %s, stderr: %s, err: %v", out, stderr.String(), err) } return nil }