daemon: added initial support to docker nodes for setting up volumes and bind mounts

This commit is contained in:
Blake Harnden 2022-05-11 13:51:02 -07:00
parent 25c3c42b40
commit fd3be57f57
3 changed files with 39 additions and 3 deletions

View file

@ -2,7 +2,7 @@
CORE data objects. CORE data objects.
""" """
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, List, Optional, Tuple from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
import netaddr import netaddr
@ -92,6 +92,8 @@ class NodeOptions:
image: str = None image: str = None
emane: str = None emane: str = None
legacy: bool = False legacy: bool = False
binds: Dict[str, str] = field(default_factory=dict)
volumes: Dict[str, str] = field(default_factory=dict)
def set_position(self, x: float, y: float) -> None: def set_position(self, x: float, y: float) -> None:
""" """

View file

@ -521,6 +521,8 @@ class Session:
kwargs = dict(_id=_id, name=name, server=server) kwargs = dict(_id=_id, name=name, server=server)
if _class in CONTAINER_NODES: if _class in CONTAINER_NODES:
kwargs["image"] = options.image kwargs["image"] = options.image
kwargs["binds"] = options.binds
kwargs["volumes"] = options.volumes
node = self.create_node(_class, start, **kwargs) node = self.create_node(_class, start, **kwargs)
# set node attributes # set node attributes

View file

@ -1,9 +1,10 @@
import json import json
import logging import logging
import shlex import shlex
from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Dict
from core.emulator.distributed import DistributedServer from core.emulator.distributed import DistributedServer
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
@ -18,6 +19,13 @@ if TYPE_CHECKING:
DOCKER: str = "docker" DOCKER: str = "docker"
@dataclass
class DockerVolume:
src: str
dst: str
path: str = None
class DockerNode(CoreNode): class DockerNode(CoreNode):
""" """
Provides logic for creating a Docker based node. Provides logic for creating a Docker based node.
@ -31,6 +39,8 @@ class DockerNode(CoreNode):
directory: str = None, directory: str = None,
server: DistributedServer = None, server: DistributedServer = None,
image: str = None, image: str = None,
binds: Dict[str, str] = None,
volumes: Dict[str, str] = None,
) -> None: ) -> None:
""" """
Create a DockerNode instance. Create a DockerNode instance.
@ -42,9 +52,16 @@ class DockerNode(CoreNode):
:param server: remote server node :param server: remote server node
will run on, default is None for localhost will run on, default is None for localhost
:param image: image to start container with :param image: image to start container with
:param binds: bind mounts to set for the created container
:param volumes: volume mounts to set for the created container
""" """
super().__init__(session, _id, name, directory, server) super().__init__(session, _id, name, directory, server)
self.image = image if image is not None else "ubuntu" self.image: str = image if image is not None else "ubuntu"
self.binds: Dict[str, str] = binds or {}
volumes = volumes or {}
self.volumes: Dict[str, DockerVolume] = {
k: DockerVolume(k, v) for k, v in volumes.items()
}
def _create_cmd(self, args: str, shell: bool = False) -> str: def _create_cmd(self, args: str, shell: bool = False) -> str:
""" """
@ -82,14 +99,27 @@ class DockerNode(CoreNode):
if self.up: if self.up:
raise CoreError(f"starting node({self.name}) that is already up") raise CoreError(f"starting node({self.name}) that is already up")
self.makenodedir() self.makenodedir()
binds = ""
for src, dst in self.binds.items():
binds += f"--mount type=bind,source={src},target={dst} "
volumes = ""
for volume in self.volumes.values():
volumes += (
f"--mount type=volume," f"source={volume.src},target={volume.dst} "
)
self.host_cmd( self.host_cmd(
f"{DOCKER} run -td --init --net=none --hostname {self.name} " f"{DOCKER} run -td --init --net=none --hostname {self.name} "
f"--name {self.name} --sysctl net.ipv6.conf.all.disable_ipv6=0 " f"--name {self.name} --sysctl net.ipv6.conf.all.disable_ipv6=0 "
f"{binds} {volumes} "
f"--privileged {self.image} /bin/bash" f"--privileged {self.image} /bin/bash"
) )
self.pid = self.host_cmd( self.pid = self.host_cmd(
f"{DOCKER} inspect -f '{{{{.State.Pid}}}}' {self.name}" f"{DOCKER} inspect -f '{{{{.State.Pid}}}}' {self.name}"
) )
for volume in self.volumes.values():
volume.path = self.host_cmd(
f"{DOCKER} volume inspect -f '{{{{.Mountpoint}}}}' {volume.src}"
)
logger.debug("node(%s) pid: %s", self.name, self.pid) logger.debug("node(%s) pid: %s", self.name, self.pid)
self.up = True self.up = True
@ -105,6 +135,8 @@ class DockerNode(CoreNode):
with self.lock: with self.lock:
self.ifaces.clear() self.ifaces.clear()
self.host_cmd(f"{DOCKER} rm -f {self.name}") self.host_cmd(f"{DOCKER} rm -f {self.name}")
for volume in self.volumes.values():
self.host_cmd(f"{DOCKER} volume rm {volume.src}")
self.up = False self.up = False
def termcmdstring(self, sh: str = "/bin/sh") -> str: def termcmdstring(self, sh: str = "/bin/sh") -> str: