summaryrefslogtreecommitdiff
path: root/compose
diff options
context:
space:
mode:
authorUlysses Souza <ulyssessouza@gmail.com>2021-04-01 03:16:05 -0300
committerUlysses Souza <ulyssessouza@gmail.com>2021-04-05 12:34:45 -0300
commite496c641273326c7314e01f434313a56f48ba7b0 (patch)
tree29792afdf26f14e470c5c5c7c73a6f489602e732 /compose
parent84afa518e8144c9620562c023c0b62dd7c0b4305 (diff)
Add Snyk scan suggestion when building
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
Diffstat (limited to 'compose')
-rw-r--r--compose/cli/scan_suggest.py85
-rw-r--r--compose/project.py11
-rw-r--r--compose/service.py18
3 files changed, 114 insertions, 0 deletions
diff --git a/compose/cli/scan_suggest.py b/compose/cli/scan_suggest.py
new file mode 100644
index 00000000..4ebe2cec
--- /dev/null
+++ b/compose/cli/scan_suggest.py
@@ -0,0 +1,85 @@
+import json
+import logging
+import os
+from distutils.util import strtobool
+
+from docker.constants import IS_WINDOWS_PLATFORM
+from docker.utils.config import find_config_file
+
+
+SCAN_BINARY_NAME = "docker-scan" + (".exe" if IS_WINDOWS_PLATFORM else "")
+
+log = logging.getLogger(__name__)
+
+
+class ScanConfig:
+ def __init__(self, d):
+ self.optin = False
+ vars(self).update(d)
+
+
+def display_scan_suggest_msg():
+ if environment_scan_avoid_suggest() or \
+ scan_available() is None or \
+ scan_already_invoked():
+ return
+ log.info("Use 'docker scan' to run Snyk tests against images to find vulnerabilities "
+ "and learn how to fix them")
+
+
+def environment_scan_avoid_suggest():
+ return os.getenv('DOCKER_SCAN_SUGGEST', 'true').lower() == 'false'
+
+
+def scan_already_invoked():
+ docker_folder = docker_config_folder()
+ if docker_folder is None:
+ return False
+
+ scan_config_file = os.path.join(docker_folder, 'scan', "config.json")
+ if not os.path.exists(scan_config_file):
+ return False
+
+ try:
+ data = ''
+ with open(scan_config_file) as f:
+ data = f.read()
+ scan_config = json.loads(data, object_hook=ScanConfig)
+ return scan_config.optin if isinstance(scan_config.optin, bool) else strtobool(scan_config.optin)
+ except Exception: # pylint:disable=broad-except
+ return True
+
+
+def scan_available():
+ docker_folder = docker_config_folder()
+ if docker_folder:
+ home_scan_bin = os.path.join(docker_folder, 'cli-plugins', SCAN_BINARY_NAME)
+ if os.path.isfile(home_scan_bin) or os.path.islink(home_scan_bin):
+ return home_scan_bin
+
+ if IS_WINDOWS_PLATFORM:
+ program_data_scan_bin = os.path.join('C:\\', 'ProgramData', 'Docker', 'cli-plugins',
+ SCAN_BINARY_NAME)
+ if os.path.isfile(program_data_scan_bin) or os.path.islink(program_data_scan_bin):
+ return program_data_scan_bin
+ else:
+ lib_scan_bin = os.path.join('/usr', 'local', 'lib', 'docker', 'cli-plugins', SCAN_BINARY_NAME)
+ if os.path.isfile(lib_scan_bin) or os.path.islink(lib_scan_bin):
+ return lib_scan_bin
+ lib_exec_scan_bin = os.path.join('/usr', 'local', 'libexec', 'docker', 'cli-plugins',
+ SCAN_BINARY_NAME)
+ if os.path.isfile(lib_exec_scan_bin) or os.path.islink(lib_exec_scan_bin):
+ return lib_exec_scan_bin
+ lib_scan_bin = os.path.join('/usr', 'lib', 'docker', 'cli-plugins', SCAN_BINARY_NAME)
+ if os.path.isfile(lib_scan_bin) or os.path.islink(lib_scan_bin):
+ return lib_scan_bin
+ lib_exec_scan_bin = os.path.join('/usr', 'libexec', 'docker', 'cli-plugins', SCAN_BINARY_NAME)
+ if os.path.isfile(lib_exec_scan_bin) or os.path.islink(lib_exec_scan_bin):
+ return lib_exec_scan_bin
+ return None
+
+
+def docker_config_folder():
+ docker_config_file = find_config_file()
+ return None if not docker_config_file \
+ else os.path.dirname(os.path.abspath(docker_config_file))
diff --git a/compose/project.py b/compose/project.py
index e862464d..6ae024ce 100644
--- a/compose/project.py
+++ b/compose/project.py
@@ -13,6 +13,7 @@ from docker.utils import version_lt
from . import parallel
from .cli.errors import UserError
+from .cli.scan_suggest import display_scan_suggest_msg
from .config import ConfigurationError
from .config.config import V1
from .config.sort_services import get_container_name_from_network_mode
@@ -518,6 +519,9 @@ class Project:
for service in services:
build_service(service)
+ if services:
+ display_scan_suggest_msg()
+
def create(
self,
service_names=None,
@@ -660,8 +664,15 @@ class Project:
service_names,
include_deps=start_deps)
+ must_build = False
for svc in services:
+ if svc.must_build(do_build=do_build):
+ must_build = True
svc.ensure_image_exists(do_build=do_build, silent=silent, cli=cli)
+
+ if must_build:
+ display_scan_suggest_msg()
+
plans = self._get_convergence_plans(
services,
strategy,
diff --git a/compose/service.py b/compose/service.py
index 716a7557..fda1edb2 100644
--- a/compose/service.py
+++ b/compose/service.py
@@ -366,6 +366,24 @@ class Service:
"rebuild this image you must use `docker-compose build` or "
"`docker-compose up --build`.".format(self.name))
+ def must_build(self, do_build=BuildAction.none):
+ if self.can_be_built() and do_build == BuildAction.force:
+ return True
+
+ try:
+ self.image()
+ return False
+ except NoSuchImageError:
+ pass
+
+ if not self.can_be_built():
+ return False
+
+ if do_build == BuildAction.skip:
+ return False
+
+ return True
+
def get_image_registry_data(self):
try:
return self.client.inspect_distribution(self.image_name)