def _check_docker_supports_mounts() -> CheckLevels: """ Check to is to avoid: docker.errors.InvalidVersion: mounts param is not supported in API versions < 1.30 """ client = docker_client() mount = docker.types.Mount(source=None, target='/etc') # Any image will do, we use this for another test so using it here saves # pulling another image. tiny_image = 'luca3m/sleep' try: container = client.containers.run( image=tiny_image, mounts=[mount], detach=True, ) except docker.errors.InvalidVersion as exc: if 'mounts param is not supported' in str(exc): message = ( 'The Docker API version must be >= 1.30. ' 'This is because this tool uses the ``mounts`` parameter.') error(message=message) return CheckLevels.ERROR raise container.stop() container.remove(v=True) return CheckLevels.NONE
def check_vagrant_plugin_versions() -> CheckLevels: """ Error if `vagrant-vbguest` is not of at least version 0.17.2. """ # We import Vagrant here instead of at the top of the file because, if # the Vagrant executable is not found, a warning is logged. # # We want to avoid that warning for users of other backends who do not # have the Vagrant executable. import vagrant client = vagrant.Vagrant() plugin_list = client.plugin_list() name = 'vagrant-vbguest' (plugin, ) = set(plugin for plugin in plugin_list if plugin.name == name) version_info = VersionInfo.parse(plugin.version) minimum_version = VersionInfo(0, 17, 2) # We require a minimum version to work around # https://github.com/dotless-de/vagrant-vbguest/issues/320. plugin_recent = version_info >= minimum_version if plugin_recent: return CheckLevels.NONE message = ('The "vagrant-vbguest" plugin version is {plugin_version}. ' 'The minimum supported version is 0.17.2. ' 'Run "vagrant plugin update".').format( plugin_version=plugin.version) error(message=message) return CheckLevels.ERROR
def _check_systemd() -> CheckLevels: """ Check that the host supports systemd. See https://jira.mesosphere.com/browse/DCOS_OSS-4475 for removing the need for this. """ client = docker_client() tiny_image = 'luca3m/sleep' cgroup_mount = docker.types.Mount( source='/sys/fs/cgroup/systemd', target='/sys/fs/cgroup/systemd', read_only=True, type='bind', ) try: client.containers.run( image=tiny_image, mounts=[cgroup_mount], detach=True, ) except docker.errors.APIError as exc: expected = ( 'bind mount source path does not exist: /sys/fs/cgroup/systemd"') if expected in str(exc): message = 'systemd is required.' error(message=message) return CheckLevels.ERROR raise return CheckLevels.NONE
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
def check_vagrant() -> CheckLevels: """ Error if `vagrant` is not available on the path. """ if shutil.which('vagrant') is None: error(message='`vagrant` must be available on the PATH.') return CheckLevels.ERROR return CheckLevels.NONE
def check_virtualbox() -> CheckLevels: """ Error if VirtualBox is not installed. """ if shutil.which('VBoxManage') is None: message = ('"VBoxManage" is not available on the PATH. ' 'To resolve this, install VirtualBox from ' 'https://www.virtualbox.org/wiki/Downloads.') error(message=message) return CheckLevels.ERROR return CheckLevels.NONE
def _check_mount_var() -> CheckLevels: """ Check that `/var/folders` can be mounted. """ source = Path('/var').resolve() client = docker_client() tiny_image = 'luca3m/sleep' var_mount = docker.types.Mount( source=str(source), target='/var', read_only=True, type='bind', ) try: container = client.containers.run( image=tiny_image, mounts=[var_mount], detach=True, ) except docker.errors.APIError as exc: expected = 'bind mount source path does not exist: {source}'.format( source=source, ) expected_docker_machine = ( 'bind source path does not exist: {source}').format(source=source) if expected in str(exc) or expected_docker_machine in str(exc): message = ('There was an error mounting "{source}" ' 'from the host into a Docker container. ' 'This is required for multiple operations.').format( source=source) operating_system_info = client.info()['OperatingSystem'] boot2docker = bool('Boot2Docker' in operating_system_info) if boot2docker: message += ( '\n' 'It appears that you are using Boot2Docker or ' 'docker-machine and this might be the cause of the ' 'problem. ' 'These are known to be incompatible with DC/OS E2E and ' 'minidcos.') if sys.platform == 'darwin': message += ( '\n' 'Consider upgrading to Docker for Mac. ' 'See https://docs.docker.com/docker-for-mac/install/.') error(message=message) return CheckLevels.ERROR raise container.stop() container.remove(v=True) return CheckLevels.NONE
def _check_selinux() -> CheckLevels: """ Error if SELinux is enabled. This can cause problems such as mount problems for the installer. """ if shutil.which('getenforce') is None: return CheckLevels.NONE result = subprocess.check_output(args=['getenforce']) if result == b'Enforcing': message = ('SELinux is in "Enforcing" mode. ' 'SELinux must be in "Permissive" or "Disabled" mode.') error(message=message) return CheckLevels.ERROR return CheckLevels.NONE
def check_vagrant_plugins() -> CheckLevels: """ Error if `vagrant-vbguest` is not installed. """ # We import Vagrant here instead of at the top of the file because, if # the Vagrant executable is not found, a warning is logged. # # We want to avoid that warning for users of other backends who do not # have the Vagrant executable. import vagrant client = vagrant.Vagrant() if 'vagrant-vbguest' in set(plugin.name for plugin in client.plugin_list()): return CheckLevels.NONE error(message='The `vagrant-vbguest` plugin must be installed.') return CheckLevels.ERROR
def _check_can_build() -> CheckLevels: """ Check that the default cluster images can be built. """ cluster_backend = Docker(docker_version=DockerVersion.v1_13_1) try: with Cluster(cluster_backend=cluster_backend): pass except docker.errors.BuildError as exc: message = ('There was an error building a Docker image. ' 'The Docker logs follow.\n' '\n') for item in exc.build_log: if 'stream' in item: message += '\t' + item['stream'] error(message=message) return CheckLevels.ERROR return CheckLevels.NONE
def _check_systemd() -> CheckLevels: """ Check that the host supports systemd. See https://jira.mesosphere.com/browse/DCOS_OSS-4475 for removing the need for this. """ client = docker_client() tiny_image = 'luca3m/sleep' cgroup_mount = docker.types.Mount( source='/sys/fs/cgroup/systemd', target='/sys/fs/cgroup/systemd', read_only=True, type='bind', ) try: container = client.containers.run( image=tiny_image, mounts=[cgroup_mount], detach=True, ) except docker.errors.APIError as exc: expected = ( 'bind mount source path does not exist: /sys/fs/cgroup/systemd"') if expected in str(exc): message = ( 'Launching various applications requires ``/sys/fs/cgroup`` ' 'to be mounted from the host. ' 'This is because UCR applications require cgroup isolation. ' 'Therefore, by default, ``/sys/fs/cgroup`` is mounted from ' 'the host. ' 'It appears that this is not available on the host. ' 'Therefore, to launch a cluster you must use ' '``--no-mount-sys-fs-cgroup``. ' 'Some applications will not work on the launched cluster.') error(message=message) return CheckLevels.WARNING raise container.stop() container.remove(v=True) return CheckLevels.NONE
def _check_mount_tmp() -> CheckLevels: """ Error if it is not possible to mount the temporary directory. """ highest_level = CheckLevels.NONE # 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() tmp_path = Path('/tmp').resolve() try: private_mount_container = client.containers.run( image=tiny_image, tty=True, detach=True, volumes={ str(tmp_path): { 'bind': '/test', }, }, ) except docker.errors.APIError as exc: message = ('There was an error mounting the temporary directory path ' '"{tmp_path}" in container: \n\n' '{exception_detail}').format( tmp_path=tmp_path, exception_detail=exc.explanation.decode( 'ascii', 'backslashreplace', ), ) error(message=message) highest_level = CheckLevels.ERROR private_mount_container.stop() private_mount_container.remove(v=True) return highest_level