Skip to content

Commit

Permalink
fix: Support attaching to host network
Browse files Browse the repository at this point in the history
Address #669

Docker only supports attaching containers to the host network through
the network_mode in the create. Previously, we tried to attach the
container to the host network after creating. This caused the command
to fail. Docker does not support attaching a container to host and bridge.
Therefore, we need to handle host as a special case.
  • Loading branch information
jfuss committed Oct 18, 2018
1 parent 9986f23 commit a3eb631
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 2 deletions.
5 changes: 4 additions & 1 deletion samcli/local/docker/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,13 @@ def create(self):
# Ex: 128m => 128MB
kwargs["mem_limit"] = "{}m".format(self._memory_limit_mb)

if self.network_id == 'host':
kwargs["network_mode"] = self.network_id

real_container = self.docker_client.containers.create(self._image, **kwargs)
self.id = real_container.id

if self.network_id:
if self.network_id and self.network_id != 'host':
network = self.docker_client.networks.get(self.network_id)
network.connect(self.id)

Expand Down
5 changes: 4 additions & 1 deletion tests/integration/local/invoke/invoke_integ_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def base_command(cls):
return command

def get_command_list(self, function_to_invoke, template_path=None, event_path=None, env_var_path=None,
parameter_overrides=None, region=None):
parameter_overrides=None, region=None, docker_network=None):
command_list = [self.cmd, "local", "invoke", function_to_invoke]

if template_path:
Expand All @@ -41,6 +41,9 @@ def get_command_list(self, function_to_invoke, template_path=None, event_path=No
if env_var_path:
command_list = command_list + ["-n", env_var_path]

if docker_network:
command_list = command_list + ["--docker-network", docker_network]

if parameter_overrides:
arg_value = " ".join([
"ParameterKey={},ParameterValue={}".format(key, value) for key, value in parameter_overrides.items()
Expand Down
11 changes: 11 additions & 0 deletions tests/integration/local/invoke/test_integrations_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,14 @@ def test_invoke_with_env_using_parameters_with_custom_region(self):
environ = json.loads(process_stdout.decode('utf-8'))

self.assertEquals(environ["Region"], custom_region)

def test_invoke_with_docker_network_of_host(self):
command_list = self.get_command_list("HelloWorldServerlessFunction",
template_path=self.template_path,
event_path=self.event_path,
docker_network='host')

process = Popen(command_list, stdout=PIPE)
return_code = process.wait()

self.assertEquals(return_code, 0)
55 changes: 55 additions & 0 deletions tests/unit/local/docker/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ def test_must_connect_to_network_on_create(self):
Create a container with only required values. Optional values are not provided
:return:
"""
expected_volumes = {
self.host_dir: {
"bind": self.working_dir,
"mode": "ro"
}
}

network_id = "some id"
generated_id = "fooobar"
Expand All @@ -240,9 +246,58 @@ def test_must_connect_to_network_on_create(self):
container_id = container.create()
self.assertEquals(container_id, generated_id)

self.mock_docker_client.containers.create.assert_called_with(self.image,
command=self.cmd,
working_dir=self.working_dir,
tty=False,
volumes=expected_volumes
)

self.mock_docker_client.networks.get.assert_called_with(network_id)
network_mock.connect.assert_called_with(container_id)

def test_must_connect_to_host_network_on_create(self):
"""
Create a container with only required values. Optional values are not provided
:return:
"""
expected_volumes = {
self.host_dir: {
"bind": self.working_dir,
"mode": "ro"
}
}

network_id = "host"
generated_id = "fooobar"
self.mock_docker_client.containers.create.return_value = Mock()
self.mock_docker_client.containers.create.return_value.id = generated_id

network_mock = Mock()
self.mock_docker_client.networks.get.return_value = network_mock
network_mock.connect = Mock()

container = Container(self.image,
self.cmd,
self.working_dir,
self.host_dir,
docker_client=self.mock_docker_client)

container.network_id = network_id

container_id = container.create()
self.assertEquals(container_id, generated_id)

self.mock_docker_client.containers.create.assert_called_with(self.image,
command=self.cmd,
working_dir=self.working_dir,
tty=False,
volumes=expected_volumes,
network_mode='host'
)

self.mock_docker_client.networks.get.assert_not_called()

def test_must_fail_if_already_created(self):

container = Container(self.image,
Expand Down

0 comments on commit a3eb631

Please sign in to comment.