tobiko/tobiko/docker/_shell.py

142 lines
4.4 KiB
Python

# Copyright (c) 2019 Red Hat, Inc.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import collections
from urllib import parse
import typing
from oslo_log import log
from tobiko.docker import _exception
from tobiko.shell import sh
from tobiko.shell import ssh
LOG = log.getLogger(__name__)
def discover_docker_urls(
ssh_client: ssh.SSHClientType = None,
default_url='unix:/var/run/docker.sock',
check_urls=True,
sudo=False) \
-> typing.List[str]:
last_error: typing.Optional[Exception] = None
urls = []
try:
urls += list_docker_urls(ssh_client=ssh_client,
sudo=sudo)
except _exception.DockerUrlNotFoundError as ex:
last_error = ex
if default_url not in urls:
urls.append(default_url)
if check_urls:
urls = select_valid_urls(ssh_client=ssh_client,
urls=urls,
sudo=sudo,
last_error=last_error)
return urls
def list_docker_urls(
ssh_client: ssh.SSHClientType,
sudo=False) \
-> typing.List[str]:
urls = []
try:
processes = sh.list_processes(command='^dockerd',
ssh_client=ssh_client,
sudo=sudo)
except sh.PsError as ex:
raise _exception.DockerUrlNotFoundError(
"Docker demon is not running") from ex
for process in processes:
if process.command_line:
urls += urls_from_command_line(process.command_line)
urls = list(collections.OrderedDict.fromkeys(urls))
return urls
def select_valid_urls(
ssh_client: ssh.SSHClientType,
urls: typing.List[str],
sudo=False,
last_error: Exception = None) \
-> typing.List[str]:
valid_urls = []
for url in urls:
parsed_url = parse.urlparse(url)
if (parsed_url.scheme == 'unix' and
parsed_url.path.startswith('/')):
try:
sh.execute(f"test -r '{parsed_url.path}'",
ssh_client=ssh_client,
sudo=sudo)
except sh.ShellCommandFailed as ex:
LOG.exception(
f"Can't read from socket: {parsed_url.path}")
ex.__cause__ = last_error
last_error = ex
else:
valid_urls.append(url)
if not valid_urls:
raise _exception.DockerUrlNotFoundError(
"Docker is not running") from last_error
return valid_urls
def urls_from_command_line(command_line: sh.ShellCommand,
default_url='unix:/var/run/docker.sock') \
-> typing.List[str]:
urls = []
arg_is_url = False
for arg in command_line[1:]:
if arg == '-H':
arg_is_url = True
continue
if arg_is_url:
arg_is_url = False
try:
url = parse.urlparse(arg)
except Exception:
LOG.debug(f'Invalid URL: {arg}', exc_info=1)
continue
if url.scheme == 'fd':
urls.append(default_url)
continue
if url.scheme != 'unix':
LOG.debug(f'Unsupported URL scheme: {arg}')
continue
if not url.path.startswith('/'):
LOG.debug(f'Unsupported URL path: {arg}')
continue
urls.append(arg)
return urls
def is_docker_running(ssh_client=None, sudo=False, **execute_params):
try:
discover_docker_urls(ssh_client=ssh_client,
sudo=sudo,
**execute_params)
except _exception.DockerUrlNotFoundError:
return False
else:
return True