|  | @@ -3,6 +3,8 @@ from __future__ import absolute_import
 | 
	
		
			
				|  |  |  from __future__ import unicode_literals
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import datetime
 | 
	
		
			
				|  |  | +import os
 | 
	
		
			
				|  |  | +import tempfile
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import docker
 | 
	
		
			
				|  |  |  import pytest
 | 
	
	
		
			
				|  | @@ -11,6 +13,7 @@ from docker.errors import NotFound
 | 
	
		
			
				|  |  |  from .. import mock
 | 
	
		
			
				|  |  |  from .. import unittest
 | 
	
		
			
				|  |  |  from ..helpers import BUSYBOX_IMAGE_WITH_TAG
 | 
	
		
			
				|  |  | +from compose.config import ConfigurationError
 | 
	
		
			
				|  |  |  from compose.config.config import Config
 | 
	
		
			
				|  |  |  from compose.config.types import VolumeFromSpec
 | 
	
		
			
				|  |  |  from compose.const import COMPOSEFILE_V1 as V1
 | 
	
	
		
			
				|  | @@ -21,6 +24,7 @@ from compose.const import DEFAULT_TIMEOUT
 | 
	
		
			
				|  |  |  from compose.const import LABEL_SERVICE
 | 
	
		
			
				|  |  |  from compose.container import Container
 | 
	
		
			
				|  |  |  from compose.errors import OperationFailedError
 | 
	
		
			
				|  |  | +from compose.project import get_secrets
 | 
	
		
			
				|  |  |  from compose.project import NoSuchService
 | 
	
		
			
				|  |  |  from compose.project import Project
 | 
	
		
			
				|  |  |  from compose.project import ProjectError
 | 
	
	
		
			
				|  | @@ -841,3 +845,83 @@ class ProjectTest(unittest.TestCase):
 | 
	
		
			
				|  |  |          with mock.patch('compose.service.Service.push') as fake_push:
 | 
	
		
			
				|  |  |              project.push()
 | 
	
		
			
				|  |  |              assert fake_push.call_count == 2
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_get_secrets_no_secret_def(self):
 | 
	
		
			
				|  |  | +        service = 'foo'
 | 
	
		
			
				|  |  | +        secret_source = 'bar'
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret_defs = mock.Mock()
 | 
	
		
			
				|  |  | +        secret_defs.get.return_value = None
 | 
	
		
			
				|  |  | +        secret = mock.Mock(source=secret_source)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        with self.assertRaises(ConfigurationError):
 | 
	
		
			
				|  |  | +            get_secrets(service, [secret], secret_defs)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_get_secrets_external_warning(self):
 | 
	
		
			
				|  |  | +        service = 'foo'
 | 
	
		
			
				|  |  | +        secret_source = 'bar'
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret_def = mock.Mock()
 | 
	
		
			
				|  |  | +        secret_def.get.return_value = True
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret_defs = mock.Mock()
 | 
	
		
			
				|  |  | +        secret_defs.get.side_effect = secret_def
 | 
	
		
			
				|  |  | +        secret = mock.Mock(source=secret_source)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        with mock.patch('compose.project.log') as mock_log:
 | 
	
		
			
				|  |  | +            get_secrets(service, [secret], secret_defs)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mock_log.warning.assert_called_with("Service \"{service}\" uses secret \"{secret}\" "
 | 
	
		
			
				|  |  | +                                            "which is external. External secrets are not available"
 | 
	
		
			
				|  |  | +                                            " to containers created by docker-compose."
 | 
	
		
			
				|  |  | +                                            .format(service=service, secret=secret_source))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_get_secrets_uid_gid_mode_warning(self):
 | 
	
		
			
				|  |  | +        service = 'foo'
 | 
	
		
			
				|  |  | +        secret_source = 'bar'
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        _, filename_path = tempfile.mkstemp()
 | 
	
		
			
				|  |  | +        self.addCleanup(os.remove, filename_path)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        def mock_get(key):
 | 
	
		
			
				|  |  | +            return {'external': False, 'file': filename_path}[key]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret_def = mock.MagicMock()
 | 
	
		
			
				|  |  | +        secret_def.get = mock.MagicMock(side_effect=mock_get)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret_defs = mock.Mock()
 | 
	
		
			
				|  |  | +        secret_defs.get.return_value = secret_def
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret = mock.Mock(uid=True, gid=True, mode=True, source=secret_source)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        with mock.patch('compose.project.log') as mock_log:
 | 
	
		
			
				|  |  | +            get_secrets(service, [secret], secret_defs)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mock_log.warning.assert_called_with("Service \"{service}\" uses secret \"{secret}\" with uid, "
 | 
	
		
			
				|  |  | +                                            "gid, or mode. These fields are not supported by this "
 | 
	
		
			
				|  |  | +                                            "implementation of the Compose file"
 | 
	
		
			
				|  |  | +                                            .format(service=service, secret=secret_source))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_get_secrets_secret_file_warning(self):
 | 
	
		
			
				|  |  | +        service = 'foo'
 | 
	
		
			
				|  |  | +        secret_source = 'bar'
 | 
	
		
			
				|  |  | +        not_a_path = 'NOT_A_PATH'
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        def mock_get(key):
 | 
	
		
			
				|  |  | +            return {'external': False, 'file': not_a_path}[key]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret_def = mock.MagicMock()
 | 
	
		
			
				|  |  | +        secret_def.get = mock.MagicMock(side_effect=mock_get)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret_defs = mock.Mock()
 | 
	
		
			
				|  |  | +        secret_defs.get.return_value = secret_def
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        secret = mock.Mock(uid=False, gid=False, mode=False, source=secret_source)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        with mock.patch('compose.project.log') as mock_log:
 | 
	
		
			
				|  |  | +            get_secrets(service, [secret], secret_defs)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mock_log.warning.assert_called_with("Service \"{service}\" uses an undefined secret file "
 | 
	
		
			
				|  |  | +                                            "\"{secret_file}\", the following folder is created "
 | 
	
		
			
				|  |  | +                                            "\"{secret_file}\""
 | 
	
		
			
				|  |  | +                                            .format(service=service, secret_file=not_a_path))
 |