Esempio n. 1
0
def _check_tmp_free_space() -> CheckLevels:
    """
    Warn if there is not enough free space in the default temporary directory.
    """
    free_space = shutil.disk_usage(gettempdir()).free
    free_space_gb = free_space / 1024 / 1024 / 1024

    low_space_message = (
        'The default temporary directory ("{tmp_prefix}") has '
        '{free_space:.1f} GB of free space available. '
        'Creating a cluster typically takes approximately 2 GB of temporary '
        'storage. '
        'If you encounter problems with disk space usage, set the ``TMPDIR`` '
        'environment variable to a suitable temporary directory or use the '
        '``--workspace-dir`` option on the ``dcos-docker create`` command.'
    ).format(
        tmp_prefix=Path('/') / gettempprefix(),
        free_space=free_space_gb,
    )

    if free_space_gb < 5:
        warn(message=low_space_message)
        return CheckLevels.WARNING

    return CheckLevels.NONE
Esempio n. 2
0
def _check_storage_driver() -> CheckLevels:
    """
    Warn if the Docker storage driver is not a recommended driver.
    """
    client = docker_client()
    host_driver = client.info()['Driver']
    storage_driver_url = (
        'https://docs.docker.com/storage/storagedriver/select-storage-driver/')
    # Any image will do, we use this for another test so using it here saves
    # pulling another image.
    tiny_image = 'luca3m/sleep'
    container = client.containers.run(
        image=tiny_image,
        tty=True,
        detach=True,
        privileged=True,
        volumes={'/proc': {
            'bind': '/host/proc',
            'mode': 'rw',
        }},
    )

    cmd = ['cat', '/host/proc/filesystems']
    _, output = container.exec_run(cmd=cmd)
    container.stop()
    container.remove(v=True)
    aufs_supported = bool(b'aufs' in output.split())
    supported_host_driver = bool(host_driver in DOCKER_STORAGE_DRIVERS)
    can_work = bool(aufs_supported or supported_host_driver)

    if not can_work:
        message = (
            "The host's Docker storage driver is \"{host_driver}\". "
            'aufs is not available. '
            'Change your storage driver to one of: {supported_drivers}. '
            'Alternatively try using the `--docker-storage-driver` option '
            'with `overlay` or `overlay2`. '
            'See {help_url}.').format(
                host_driver=host_driver,
                supported_drivers=', '.join(
                    sorted(DOCKER_STORAGE_DRIVERS.keys())),
                help_url=storage_driver_url,
            )
        error(message=message)
        return CheckLevels.ERROR

    if not supported_host_driver:
        message = ("The host's Docker storage driver is \"{host_driver}\". "
                   'We recommend that you use one of: {supported_drivers}. '
                   'See {help_url}.').format(
                       host_driver=host_driver,
                       supported_drivers=', '.join(
                           sorted(DOCKER_STORAGE_DRIVERS.keys())),
                       help_url=storage_driver_url,
                   )
        warn(message=message)
        return CheckLevels.WARNING

    return CheckLevels.NONE
Esempio n. 3
0
def _check_can_mount_in_docker() -> CheckLevels:
    """
    Check for an incompatibility between some systemd versions and some
    versions of Docker.
    """
    docker_client()

    cluster_backend = Docker(docker_version=DockerVersion.v1_13_1)
    args = ['docker', 'run', '-v', '/foo', 'alpine']

    error_message_substring = 'no subsystem for mount'
    with Cluster(cluster_backend=cluster_backend) as cluster:
        (public_agent, ) = cluster.public_agents
        try:
            public_agent.run(args=args)
        except subprocess.CalledProcessError as exc:
            if error_message_substring not in exc.stderr.decode():
                raise

            message = (
                'An issue has been detected which means that, for some '
                'versions of Docker inside DC/OS nodes, it will not be '
                'possible to create containers with mounts. '
                'Some functionality may be affected by this, for example '
                'extracting the DC/OS installer on a node.'
                '\n'
                'This issue is likely because the host\'s version of systemd '
                'is greater than version 232, which causes the following '
                'known issue: '
                'https://github.com/opencontainers/runc/issues/1175.'
                '\n'
                'Newer versions of Docker, work well with new versions of '
                'systemd. '
                'To avoid issues caused by this incompatibility, do one of '
                'the following:'
                '\n* Set ``systemd.legacy_systemd_cgroup_controller=yes`` as '
                'a kernel parameter on your host.'
                '\n* Use versions of Docker newer than 1.13.1 inside the '
                'DC/OS nodes.'
                ' To do this in the ``dcos-docker`` CLI, use the '
                '``--docker-version`` option on ``dcos-docker create``.'
                ' To do this in the Python library, pass a '
                '``docker_version`` parameter to the ``Docker`` backend class.'
            )
            warn(message=message)
            return CheckLevels.WARNING

    return CheckLevels.NONE
Esempio n. 4
0
def check_docker() -> CheckLevels:
    """
    Error if Docker is not running.
    """
    try:
        docker.from_env(version='auto')
    except docker.errors.DockerException:
        message = (
            'Docker is not running. '
            'Docker is required for the "create" command to determine the '
            'DC/OS variant of the given DC/OS artifact. '
            'Use the "--variant" option when using the "create" command or '
            'install and run Docker.'
        )
        warn(message=message)
        return CheckLevels.WARNING
    return CheckLevels.NONE
Esempio n. 5
0
def _check_networking() -> CheckLevels:
    """
    Error if the Docker network is not set up correctly.
    """
    highest_level = CheckLevels.NONE
    # Image for a container which sleeps for a long time.
    tiny_image = 'luca3m/sleep'
    client = docker_client()
    docker_for_mac = bool(client.info()['OperatingSystem'] == 'Docker for Mac')

    ping_container = client.containers.run(
        image=tiny_image,
        tty=True,
        detach=True,
    )

    ping_container.reload()
    ip_address = ping_container.attrs['NetworkSettings']['IPAddress']

    try:
        subprocess.check_call(
            args=['ping', ip_address, '-c', '1', '-t', '1'],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
        )
    except subprocess.CalledProcessError:
        message = (
            'Cannot connect to a Docker container by its IP address. '
            'This is needed for features such as connecting to the web UI and '
            'using the DC/OS CLI. '
            'To use the "wait" command without resolving this issue, use the '
            '"--skip-http-checks" flag on the "wait" command.'
        )
        if docker_for_mac:
            message += (
                ' '
                'We recommend using "dcos-docker setup-mac-network" to '
                'resolve this issue.'
            )
        warn(message=message)
        highest_level = CheckLevels.WARNING

    ping_container.stop()
    ping_container.remove(v=True)
    return highest_level
Esempio n. 6
0
def _check_docker_root_free_space() -> CheckLevels:
    """
    Warn if there is not enough free space in the Docker root directory.
    """
    # Any image will do, we use this for another test so using it here saves
    # pulling another image.
    tiny_image = 'luca3m/sleep'
    client = docker_client()
    container = client.containers.run(
        image=tiny_image,
        tty=True,
        detach=True,
        privileged=True,
    )

    cmd = ['df', '/']
    _, output = container.exec_run(cmd=cmd)
    container.stop()
    container.remove(v=True)

    output_lines = output.decode().strip().split('\n')
    # We skip the first line which is headers.
    # Sometimes the information is split across multiple lines.
    information = ' '.join(line for line in output_lines[1:])
    _, _, _, avail, _, _ = information.split()
    available_bytes = int(avail)
    available_gigabytes = available_bytes / 1024 / 1024
    low_space_message = (
        'The Docker root directory is at "{docker_root_dir}". '
        'On macOS this location is on a hidden virtual machine. '
        'This directory has {free_space:.1f} GB of free space available. '
        'If you encounter problems try running ``docker volume prune``.'
    ).format(
        docker_root_dir=client.info()['DockerRootDir'],
        free_space=available_gigabytes,
    )

    # The choice of 5 GB is arbitrary. Let's see how it goes in practice and
    # potentially adjust later.
    if available_gigabytes < 5:
        warn(message=low_space_message)
        return CheckLevels.WARNING

    return CheckLevels.NONE