Quellcode durchsuchen

Add scan message after docker build, add e2e tests

Signed-off-by: Guillaume Tardif <[email protected]>
Guillaume Tardif vor 4 Jahren
Ursprung
Commit
435a51f9e4
4 geänderte Dateien mit 141 neuen und 7 gelöschten Zeilen
  1. 5 0
      cli/mobycli/exec.go
  2. 7 2
      local/compose/build.go
  3. 122 0
      local/e2e/compose/scan_message_test.go
  4. 7 5
      utils/scan_suggest.go

+ 5 - 0
cli/mobycli/exec.go

@@ -30,6 +30,7 @@ import (
 	"github.com/docker/compose-cli/api/context/store"
 	"github.com/docker/compose-cli/cli/metrics"
 	"github.com/docker/compose-cli/cli/mobycli/resolvepath"
+	"github.com/docker/compose-cli/utils"
 )
 
 var delegatedContextTypes = []string{store.DefaultContextType}
@@ -100,6 +101,10 @@ func Exec(root *cobra.Command) {
 		fmt.Fprintln(os.Stderr, err)
 		os.Exit(1)
 	}
+	command := metrics.GetCommand(os.Args[1:])
+	if command == "build" {
+		utils.DisplayScanSuggestMsg()
+	}
 	metrics.Track(store.DefaultContextType, os.Args[1:], metrics.SuccessStatus)
 
 	os.Exit(0)

+ 7 - 2
local/compose/build.go

@@ -25,6 +25,7 @@ import (
 	"strings"
 
 	"github.com/docker/compose-cli/api/compose"
+	"github.com/docker/compose-cli/utils"
 
 	"github.com/compose-spec/compose-go/types"
 	"github.com/containerd/containerd/platforms"
@@ -67,7 +68,9 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
 
 	err := s.build(ctx, project, opts, options.Progress)
 	if err == nil {
-		displayScanSuggestMsg(imagesToBuild)
+		if len(imagesToBuild) > 0 {
+			utils.DisplayScanSuggestMsg()
+		}
 	}
 
 	return err
@@ -125,7 +128,9 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
 
 	err := s.build(ctx, project, opts, mode)
 	if err == nil {
-		displayScanSuggestMsg(imagesToBuild)
+		if len(imagesToBuild) > 0 {
+			utils.DisplayScanSuggestMsg()
+		}
 	}
 	return err
 }

+ 122 - 0
local/e2e/compose/scan_message_test.go

@@ -0,0 +1,122 @@
+/*
+   Copyright 2020 Docker Compose CLI 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 e2e
+
+import (
+	"io"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"testing"
+
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+
+	. "github.com/docker/compose-cli/utils/e2e"
+)
+
+func TestDisplayScanMessageAfterBuild(t *testing.T) {
+	c := NewParallelE2eCLI(t, binDir)
+	setupScanPlugin(t, c)
+
+	res := c.RunDockerCmd("info")
+	res.Assert(t, icmd.Expected{Out: "scan: Docker Scan"})
+
+	t.Run("display when docker build", func(t *testing.T) {
+		res := c.RunDockerCmd("build", "-t", "test-image-scan-msg", "./fixtures/build-test/nginx-build")
+		res.Assert(t, icmd.Expected{Out: "Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them"})
+	})
+
+	t.Run("do not display if envvar  DOCKER_SCAN_SUGGEST=false", func(t *testing.T) {
+		cmd := c.NewDockerCmd("build", "-t", "test-image-scan-msg", "./fixtures/build-test/nginx-build")
+		cmd.Env = append(cmd.Env, "DOCKER_SCAN_SUGGEST=false")
+		res := icmd.StartCmd(cmd)
+		assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined())
+	})
+
+	t.Run("display on compose build", func(t *testing.T) {
+		res := c.RunDockerCmd("compose", "-f", "./fixtures/build-test/compose.yml", "build")
+		res.Assert(t, icmd.Expected{Out: "Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them"})
+	})
+
+	_ = c.RunDockerOrExitError("rmi", "build-test_nginx")
+
+	t.Run("display on compose up if image is built", func(t *testing.T) {
+		res := c.RunDockerCmd("compose", "-f", "./fixtures/build-test/compose.yml", "up", "-d")
+		defer c.RunDockerCmd("compose", "-f", "./fixtures/build-test/compose.yml", "down")
+		res.Assert(t, icmd.Expected{Out: "Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them"})
+	})
+
+	t.Run("do not display on compose up if no image built", func(t *testing.T) { // re-run the same Compose aproject
+		res := c.RunDockerCmd("compose", "-f", "./fixtures/build-test/compose.yml", "up", "-d")
+		defer c.RunDockerCmd("compose", "-f", "./fixtures/build-test/compose.yml", "down")
+		assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined())
+	})
+
+	t.Run("do not display if scan already invoked", func(t *testing.T) {
+		_ = os.MkdirAll(filepath.Join(c.ConfigDir, "scan"), 0755)
+		scanConfigFile := filepath.Join(c.ConfigDir, "scan", "config.json")
+		err := ioutil.WriteFile(scanConfigFile, []byte(`{"optin":true}`), 0644)
+		assert.NilError(t, err)
+
+		res := c.RunDockerCmd("build", "-t", "test-image-scan-msg", "./fixtures/build-test/nginx-build")
+		assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined())
+	})
+}
+
+func setupScanPlugin(t *testing.T, c *E2eCLI) {
+	_ = os.MkdirAll(filepath.Join(c.ConfigDir, "cli-plugins"), 0755)
+
+	scanPluginFile := "docker-scan"
+	scanPluginURL := "https://github.com/docker/scan-cli-plugin/releases/download/v0.7.0/docker-scan_linux_amd64"
+	if runtime.GOOS == "windows" {
+		scanPluginFile += ".exe"
+		scanPluginURL = "https://github.com/docker/scan-cli-plugin/releases/download/v0.7.0/docker-scan_windows_amd64.exe"
+	}
+	if runtime.GOOS == "darwin" {
+		scanPluginURL = "https://github.com/docker/scan-cli-plugin/releases/download/v0.7.0/docker-scan_darwin_amd64"
+	}
+
+	localScanBinary := filepath.Join("..", "..", "..", "bin", scanPluginFile)
+	if _, err := os.Stat(localScanBinary); os.IsNotExist(err) {
+		out, err := os.Create(localScanBinary)
+		assert.NilError(t, err)
+		defer out.Close() //nolint:errcheck
+		resp, err := http.Get(scanPluginURL)
+		assert.NilError(t, err)
+		defer resp.Body.Close() //nolint:errcheck
+		_, err = io.Copy(out, resp.Body)
+		assert.NilError(t, err)
+	}
+
+	finalScanBinaryFile := filepath.Join(c.ConfigDir, "cli-plugins", scanPluginFile)
+
+	out, err := os.Create(finalScanBinaryFile)
+	assert.NilError(t, err)
+	defer out.Close() //nolint:errcheck
+	in, err := os.Open(localScanBinary)
+	assert.NilError(t, err)
+	defer in.Close() //nolint:errcheck
+	_, err = io.Copy(out, in)
+	assert.NilError(t, err)
+
+	err = os.Chmod(finalScanBinaryFile, 7777)
+	assert.NilError(t, err)
+}

+ 7 - 5
local/compose/scan_suggest.go → utils/scan_suggest.go

@@ -14,7 +14,7 @@
    limitations under the License.
 */
 
-package compose
+package utils
 
 import (
 	"encoding/json"
@@ -28,16 +28,18 @@ import (
 	cliConfig "github.com/docker/cli/cli/config"
 )
 
-func displayScanSuggestMsg(builtImages []string) {
-	if len(builtImages) <= 0 {
+// DisplayScanSuggestMsg displlay a message suggesting users can scan new image
+func DisplayScanSuggestMsg() {
+	if os.Getenv("DOCKER_SCAN_SUGGEST") == "false" {
 		return
 	}
-	if os.Getenv("DOCKER_SCAN_SUGGEST") == "false" {
+	if !scanAvailable() {
 		return
 	}
-	if !scanAvailable() || scanAlreadyInvoked() {
+	if scanAlreadyInvoked() {
 		return
 	}
+	fmt.Println()
 	fmt.Println("Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them")
 }