Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HostManager: use "docker compose" instead of "docker-compose" #383

Merged
merged 1 commit into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions ocs/agents/host_manager/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ class HostManager:
that host (either automatically or on request).
"""

def __init__(self, agent, docker_composes=[], docker_compose_bin=None,
def __init__(self, agent, docker_composes=[],
docker_service_prefix='ocs-'):
self.agent = agent
self.running = False
self.database = {} # key is instance_id (or docker service name).
self.docker_composes = docker_composes
self.docker_compose_bin = docker_compose_bin
self.docker_service_prefix = docker_service_prefix

@inlineCallbacks
Expand Down Expand Up @@ -119,8 +118,7 @@ def _update_docker_services(self, session):
docker_services = {}
for compose in self.docker_composes:
try:
services = yield hm_utils.parse_docker_state(
compose, docker_compose_bin=self.docker_compose_bin)
services = yield hm_utils.parse_docker_state(compose)
this_ok = True
this_msg = f'Successfully parsed {compose} and its service states.'
except Exception as e:
Expand Down Expand Up @@ -690,12 +688,8 @@ def main(args=None):
docker_composes = []
if args.docker_compose:
docker_composes = args.docker_compose.split(',')
docker_compose_bin = args.docker_compose_bin
if args.docker_compose_bin is not None:
docker_compose_bin = os.path.join(os.getcwd(), docker_compose_bin)

host_manager = HostManager(agent, docker_composes=docker_composes,
docker_compose_bin=args.docker_compose_bin,
docker_service_prefix=args.docker_service_prefix)

startup_params = {}
Expand Down
58 changes: 23 additions & 35 deletions ocs/agents/host_manager/drivers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import shutil
import time
import yaml

Expand All @@ -19,8 +18,7 @@ class ManagedInstance(dict):
For instances corresponding to docker services that do not have
a corresponding SCF entry, the value here will be '[docker]'.
- 'instance_id' (str): The agent instance-id, or the docker
service name if the instance is an unmatched docker-compose
service.
service name if the instance is an unmatched docker service.
- 'full_name' (str): agent_class:instance_id

Properties that are given a default value by init function:
Expand Down Expand Up @@ -285,12 +283,9 @@ def errReceived(self, data):
self.lines['stderr'] = self.lines['stderr'][-100:]


def _run_docker_compose(args, docker_compose_bin=None):
# Help avoid some boilerplate.
if docker_compose_bin is None:
docker_compose_bin = shutil.which('docker-compose')
def _run_docker(args):
return utils.getProcessOutputAndValue(
docker_compose_bin, args, env=os.environ)
'docker', args, env=os.environ)


class DockerContainerHelper:
Expand All @@ -301,14 +296,13 @@ class DockerContainerHelper:

"""

def __init__(self, service, docker_compose_bin=None):
def __init__(self, service, docker_bin=None):
self.service = {}
self.status = -1, time.time()
self.killed = False
self.instance_id = service['service']
self.d = None
self.update(service)
self.docker_compose_bin = docker_compose_bin

def update(self, service):
"""Update self.status based on service info (in format returned by
Expand All @@ -323,34 +317,29 @@ def update(self, service):
self.killed = False

def up(self):
self.d = _run_docker_compose(
['-f', self.service['compose_file'],
'up', '-d', self.service['service']],
docker_compose_bin=self.docker_compose_bin)
self.d = _run_docker(
['compose', '-f', self.service['compose_file'],
'up', '-d', self.service['service']])
self.status = None, time.time()

def down(self):
self.d = _run_docker_compose(
['-f', self.service['compose_file'],
'rm', '--stop', '--force', self.service['service']],
docker_compose_bin=self.docker_compose_bin)
self.d = _run_docker(
['compose', '-f', self.service['compose_file'],
'rm', '--stop', '--force', self.service['service']])
self.killed = True


@inlineCallbacks
def parse_docker_state(docker_compose_file, docker_compose_bin=None):
"""Analyze a docker-compose.yaml file to get a list of services.
Using docker-compose ps and docker inspect, determine whether each
def parse_docker_state(docker_compose_file):
"""Analyze a docker compose.yaml file to get a list of services.
Using docker compose ps and docker inspect, determine whether each
service is running or not.

Use docker_compose_bin to pass in the full path to the
docker-compose executable.

Returns:
A dict where the key is the service name and each value is a
dict with the following entries:

- 'compose_file': the path to the docker-compose file
- 'compose_file': the path to the docker compose file
- 'service': service name
- 'container_found': bool, indicates whether a container for
this service was found (whether or not it was running).
Expand All @@ -373,13 +362,12 @@ def parse_docker_state(docker_compose_file, docker_compose_bin=None):
'compose_file': docker_compose_file,
}

# Query docker-compose for container ids...
out, err, code = yield _run_docker_compose(
['-f', docker_compose_file, 'ps', '-q'],
docker_compose_bin=docker_compose_bin)
# Query docker compose for container ids...
out, err, code = yield _run_docker(
['compose', '-f', docker_compose_file, 'ps', '-q'])
if code != 0:
raise RuntimeError("Could not run docker-compose or could not parse "
"docker-compose file; exit code %i, error text: %s" %
raise RuntimeError("Could not run docker compose or could not parse "
"compose.yaml file; exit code %i, error text: %s" %
(code, err))

cont_ids = [line.strip() for line in out.decode('utf8').split('\n')
Expand Down Expand Up @@ -407,19 +395,19 @@ def parse_docker_state(docker_compose_file, docker_compose_bin=None):
@inlineCallbacks
def _inspectContainer(cont_id, docker_compose_file):
"""Run docker inspect on cont_id, return dict with the results."""
out, err, code = yield utils.getProcessOutputAndValue(
'docker', ['inspect', cont_id], env=os.environ)
out, err, code = yield _run_docker(
['inspect', cont_id])
if code != 0 and 'No such object' in err.decode('utf8'):
# This is likely due to a race condition where some
# container was brought down since we ran docker-compose.
# container was brought down since we ran docker compose.
# Return None to indicate this -- caller should just ignore for now.
print(f'(_inspectContainer: warning, no such object: {cont_id}')
return None
elif code != 0:
raise RuntimeError(
f'Trouble running "docker inspect {cont_id}".\n'
f'stdout: {out}\n stderr {err}')
# Reconcile config against docker-compose ...
# Reconcile config against docker compose ...
info = yaml.safe_load(out)[0]
config = info['Config']['Labels']
_dc_file = os.path.join(config['com.docker.compose.project.working_dir'],
Expand Down