Skip to content

Commit 6db701a

Browse files
authored
Merge pull request #182 from jacobtruman/XENG-8909
XENG-8909 Create docker network for step containers
2 parents 113264c + 5850e76 commit 6db701a

File tree

10 files changed

+113
-19
lines changed

10 files changed

+113
-19
lines changed

README.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,8 +690,7 @@ Service Containers
690690
Service containers allow you to create and start additional containers that
691691
are linked to the primary build container. This is useful, for instance, if
692692
your unit or integration tests require an outside service, such as a database
693-
service. Service containers are instantiated in the order they are listed, and
694-
service containers can rely on previously instantiated service containers.
693+
service. Service containers are instantiated in the order they are listed.
695694
Service containers have the same injected environment variables and volume
696695
mounts as build containers do, but the /source mount is read-only.
697696

buildrunner/docker/daemon.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ class DockerDaemonProxy:
1919
Class used to encapsulate Docker daemon information within a container.
2020
"""
2121

22-
def __init__(self, docker_client, log, docker_registry, container_labels):
22+
def __init__(self, docker_client, log, docker_registry, container_labels, network):
2323
""" """
2424
self.docker_client = docker_client
2525
self.docker_registry = docker_registry
2626
self.log = log
2727
self.container_labels = container_labels
28+
self.network = network
2829
self._daemon_container = None
2930
self._env = {
3031
"DOCKER_HOST": DOCKER_DEFAULT_DOCKERD_URL,
@@ -83,6 +84,12 @@ def start(self):
8384
command="/bin/sh",
8485
volumes=_volumes,
8586
host_config=self.docker_client.create_host_config(binds=_binds),
87+
labels=self.container_labels,
88+
networking_config=self.docker_client.create_networking_config(
89+
{self.network: self.docker_client.create_endpoint_config()}
90+
)
91+
if self.network
92+
else None,
8693
)["Id"]
8794
self.docker_client.start(self._daemon_container)
8895
self.log.write(

buildrunner/docker/runner.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def start(
155155
systemd_cgroup2: bool = False,
156156
cap_add=None,
157157
privileged=False,
158+
network=None,
158159
): # pylint: disable=too-many-arguments,too-many-locals
159160
"""
160161
Kwargs:
@@ -250,6 +251,18 @@ def start(
250251
if entrypoint:
251252
kwargs["entrypoint"] = entrypoint
252253
del kwargs["command"]
254+
if network:
255+
kwargs["networking_config"] = (
256+
self.docker_client.create_networking_config(
257+
{
258+
network: self.docker_client.create_endpoint_config(
259+
aliases=[hostname] if hostname else None
260+
)
261+
}
262+
)
263+
if network
264+
else None
265+
)
253266

254267
if compare_version("1.10", self.docker_client.api_version) < 0:
255268
kwargs["dns"] = dns
@@ -654,7 +667,7 @@ def _get_status(self):
654667
pass
655668
return status
656669

657-
def get_ip(self):
670+
def get_ip(self, network=None):
658671
"""
659672
Return the ip address of the running container
660673
"""
@@ -664,7 +677,15 @@ def get_ip(self):
664677
inspection = self.docker_client.inspect_container(
665678
self.container["Id"],
666679
)
667-
ipaddr = inspection.get("NetworkSettings", {}).get("IPAddress", None)
680+
network_settings = inspection.get("NetworkSettings", {})
681+
if network:
682+
ipaddr = (
683+
network_settings.get("Networks", {})
684+
.get(network, {})
685+
.get("IPAddress", None)
686+
)
687+
else:
688+
ipaddr = network_settings.get("IPAddress", None)
668689
except docker.errors.APIError:
669690
pass
670691
return ipaddr

buildrunner/sshagent/__init__.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def __init__(
9595
docker_registry,
9696
multiplatform_image_builder,
9797
container_labels,
98+
network,
9899
):
99100
""" """
100101
self.docker_client = docker_client
@@ -106,6 +107,7 @@ def __init__(
106107
self._ssh_channel = None
107108
self._multiplatform_image_builder = multiplatform_image_builder
108109
self._container_labels = container_labels
110+
self._network = network
109111

110112
def get_info(self):
111113
"""
@@ -138,6 +140,11 @@ def start(self, keys):
138140
f"{keys[0].get_name()} {keys[0].get_base64()}",
139141
],
140142
labels=self._container_labels,
143+
networking_config=self.docker_client.create_networking_config(
144+
{self._network: self.docker_client.create_endpoint_config()}
145+
)
146+
if self._network
147+
else None,
141148
host_config=self.docker_client.create_host_config(
142149
publish_all_ports=True,
143150
),
@@ -154,9 +161,15 @@ def start(self, keys):
154161
_ssh_container = self.docker_client.inspect_container(
155162
self._ssh_agent_container
156163
)
157-
_ssh_host = _ssh_container.get("NetworkSettings", {}).get(
158-
"IPAddress", _ssh_host
159-
)
164+
network_settings = _ssh_container.get("NetworkSettings", {})
165+
if self._network:
166+
_ssh_host = (
167+
network_settings.get("Networks", {})
168+
.get(self._network, {})
169+
.get("IPAddress", _ssh_host)
170+
)
171+
else:
172+
_ssh_host = network_settings.get("IPAddress", _ssh_host)
160173
_ssh_port = 22
161174
else:
162175
# get the Docker server ip address and ssh port exposed by this

buildrunner/steprunner/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ def __init__(
8080
self.id = str(uuid.uuid4()) # pylint: disable=invalid-name
8181
self.multi_platform = multi_platform
8282
self.container_labels = container_labels
83+
# network name is used to identify the network that the build step is running in
84+
self.network_name = f"{build_runner.build_id}-{step_name}"
8385

8486
def run(self):
8587
"""

buildrunner/steprunner/tasks/run.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import threading
1515
import time
1616
import uuid
17+
1718
import python_on_whales
1819

1920
import buildrunner.docker
@@ -66,6 +67,10 @@ def __init__(self, step_runner, step: StepRun):
6667
self._docker_client = buildrunner.docker.new_client(
6768
timeout=step_runner.build_runner.docker_timeout,
6869
)
70+
if self.step_runner.network_name and not self._docker_client.networks(
71+
names=[self.step_runner.network_name]
72+
):
73+
self._docker_client.create_network(self.step_runner.network_name)
6974
self._source_container = None
7075
self._service_runners = OrderedDict()
7176
self._service_links = {}
@@ -74,6 +79,12 @@ def __init__(self, step_runner, step: StepRun):
7479
self.runner = None
7580
self.images_to_remove = []
7681

82+
def __del__(self):
83+
if self.step_runner.network_name and self._docker_client.networks(
84+
names=[self.step_runner.network_name]
85+
):
86+
self._docker_client.remove_network(self.step_runner.network_name)
87+
7788
def _get_source_container(self):
7889
"""
7990
Get (creating the container if necessary) the container id of the
@@ -84,6 +95,13 @@ def _get_source_container(self):
8495
self.step_runner.build_runner.get_source_image(),
8596
command="/bin/sh",
8697
labels=self.step_runner.container_labels,
98+
networking_config=self._docker_client.create_networking_config(
99+
{
100+
self.step_runner.network_name: self._docker_client.create_endpoint_config()
101+
}
102+
)
103+
if self.step_runner.network_name
104+
else None,
87105
)["Id"]
88106
self._docker_client.start(
89107
self._source_container,
@@ -504,7 +522,7 @@ def _start_service_container(self, name, service: Service):
504522
_user = service.user
505523

506524
# determine if a hostname is specified
507-
_hostname = service.hostname
525+
_hostname = service.hostname or name
508526

509527
# determine if a dns host is specified
510528
_dns = None
@@ -618,6 +636,7 @@ def _start_service_container(self, name, service: Service):
618636
containers=_containers,
619637
systemd=systemd,
620638
systemd_cgroup2=self.is_systemd_cgroup2(systemd, service, _image),
639+
network=self.step_runner.network_name,
621640
)
622641
self._service_links[cont_name] = name
623642

@@ -658,9 +677,17 @@ def wait(self, name, wait_for_data):
658677
"""
659678
Wait for listening port on named container
660679
"""
661-
ipaddr = self._docker_client.inspect_container(name)["NetworkSettings"][
662-
"IPAddress"
663-
]
680+
network_settings = self._docker_client.inspect_container(name).get(
681+
"NetworkSettings", {}
682+
)
683+
if self.step_runner.network_name:
684+
ipaddr = (
685+
network_settings.get("Networks", {})
686+
.get(self.step_runner.network_name, {})
687+
.get("IPAddress", None)
688+
)
689+
else:
690+
ipaddr = network_settings.get("IPAddress", None)
664691
socket_open = False
665692

666693
if isinstance(wait_for_data, dict):
@@ -710,6 +737,7 @@ def wait(self, name, wait_for_data):
710737
nc_tester.start(
711738
# The shell is the command
712739
shell=f"-n -z {ipaddr} {port}",
740+
network=self.step_runner.network_name,
713741
)
714742

715743
nc_tester.attach_until_finished()
@@ -734,12 +762,14 @@ def wait(self, name, wait_for_data):
734762

735763
def _resolve_service_ip(self, service_name):
736764
"""
737-
If service_name represents a running service, return it's IP address.
765+
If service_name represents a running service, return its IP address.
738766
Otherwise, return the service_name
739767
"""
740768
rval = service_name
741769
if isinstance(service_name, str) and service_name in self._service_runners:
742-
ipaddr = self._service_runners[service_name].get_ip()
770+
ipaddr = self._service_runners[service_name].get_ip(
771+
self.step_runner.network_name
772+
)
743773
if ipaddr is not None:
744774
rval = ipaddr
745775
return rval
@@ -830,6 +860,7 @@ def run(self, context: dict): # pylint: disable=too-many-statements,too-many-br
830860
buildrunner_config.global_config.docker_registry,
831861
self.step_runner.multi_platform,
832862
self.step_runner.container_labels,
863+
self.step_runner.network_name,
833864
)
834865
self._sshagent.start(
835866
buildrunner_config.get_ssh_keys_from_aliases(
@@ -843,6 +874,7 @@ def run(self, context: dict): # pylint: disable=too-many-statements,too-many-br
843874
self.step_runner.log,
844875
buildrunner_config.global_config.docker_registry,
845876
self.step_runner.container_labels,
877+
self.step_runner.network_name,
846878
)
847879
self._dockerdaemonproxy.start()
848880

@@ -1031,6 +1063,7 @@ def run(self, context: dict): # pylint: disable=too-many-statements,too-many-br
10311063
container_args["systemd_cgroup2"] = self.is_systemd_cgroup2(
10321064
container_args["systemd"], self.step, _run_image
10331065
)
1066+
container_args["network"] = self.step_runner.network_name
10341067

10351068
container_id = self.runner.start(
10361069
links=self._service_links, **container_args
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
ARG DOCKER_REGISTRY
22
FROM $DOCKER_REGISTRY/rockylinux:8.5
3-
RUN yum -y install curl
3+
RUN mkdir /results
4+
VOLUME /results

tests/test-files/test-general-buildx.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ steps:
104104
image: {{ DOCKER_REGISTRY }}/rockylinux:8.5
105105
cmd: 'echo "hello" > /hello.txt'
106106
post-build:
107-
# use a different container here to make sure inject overrides
108-
#path: runservicecontainer
109107
inject:
110108
'tests/postbuildpath/Dockerfile': '/'
111109
push:

tests/test-files/test-general.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ steps:
105105
image: {{ DOCKER_REGISTRY }}/rockylinux:8.5
106106
cmd: 'echo "hello" > /hello.txt'
107107
post-build:
108-
# use a different container here to make sure inject overrides
109-
#path: runservicecontainer
110108
inject:
111109
'tests/postbuildpath/Dockerfile': '/'
112110
push:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
steps:
2+
my-services-step:
3+
run:
4+
image: rockylinux:8.5
5+
volumes_from:
6+
- stats1
7+
cmds:
8+
- timeout 20 sh -c 'while [ ! -e /results/stats1 ] || [ ! -e /results/stats2 ] || [ ! -e /results/stats3 ]; do sleep 5; done'
9+
services:
10+
stats1:
11+
build: tests/runservicecontainer
12+
cmd: until ping -c1 stats2 >/dev/null 2>&1 && ping -c1 stats3 >/dev/null 2>&1; do sleep 5; done && touch /results/stats1
13+
stats2:
14+
build: tests/runservicecontainer
15+
volumes_from:
16+
- stats1
17+
cmd: until ping -c1 stats1 >/dev/null 2>&1 && ping -c1 stats3 >/dev/null 2>&1; do sleep 5; done && touch /results/stats2
18+
stats3:
19+
build: tests/runservicecontainer
20+
volumes_from:
21+
- stats1
22+
cmd: until ping -c1 stats1 >/dev/null 2>&1 && ping -c1 stats2 >/dev/null 2>&1; do sleep 5; done && touch /results/stats3

0 commit comments

Comments
 (0)