def doctor(verbose: int) -> None: """ Diagnose common issues which stop this CLI from working correctly. """ set_logging(verbosity_level=verbose) check_functions = [check_ssh] run_doctor_commands(check_functions=check_functions)
def wait( ctx: click.core.Context, cluster_id: str, superuser_username: str, superuser_password: str, verbose: int, ) -> None: """ Wait for DC/OS to start. """ check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) set_logging(verbosity_level=verbose) cluster_vms = ClusterVMs(cluster_id=cluster_id) wait_for_dcos( dcos_variant=cluster_vms.dcos_variant, cluster=cluster_vms.cluster, superuser_username=superuser_username, superuser_password=superuser_password, http_checks=True, doctor_command=doctor, sibling_ctx=ctx, )
def inspect_cluster(cluster_id: str, verbose: int) -> None: """ Show cluster details. """ set_logging(verbosity_level=verbose) check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) cluster_vms = ClusterVMs(cluster_id=cluster_id) keys = { 'masters': cluster_vms.masters, 'agents': cluster_vms.agents, 'public_agents': cluster_vms.public_agents, } master = next(iter(cluster_vms.cluster.masters)) web_ui = 'http://' + str(master.private_ip_address) nodes = { key: [VMInspectView(vm).to_dict() for vm in vms] for key, vms in keys.items() } data = { 'Cluster ID': cluster_id, 'Web UI': web_ui, 'Nodes': nodes, } # type: Dict[Any, Any] click.echo( json.dumps(data, indent=4, separators=(',', ': '), sort_keys=True), )
def clean(verbose: int) -> None: """ Remove containers, volumes and networks created by this tool. """ set_logging(verbosity_level=verbose) client = docker_client() filters = { 'label': [ '{key}={value}'.format( key=NODE_TYPE_LABEL_KEY, value=NODE_TYPE_LOOPBACK_SIDECAR_LABEL_VALUE, ), ], } loopback_sidecars = client.containers.list(filters=filters) for loopback_sidecar in loopback_sidecars: DockerLoopbackVolume.destroy(container=loopback_sidecar) node_filters = {'name': Docker().container_name_prefix} network_filters = {'name': Docker().container_name_prefix} node_containers = client.containers.list(filters=node_filters, all=True) for container in node_containers: container.stop() container.remove(v=True) networks = client.networks.list(filters=network_filters) for network in networks: network.remove()
def wait( ctx: click.core.Context, cluster_id: str, superuser_username: str, superuser_password: str, transport: Transport, skip_http_checks: bool, verbose: int, ) -> None: """ Wait for DC/OS to start. """ check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) set_logging(verbosity_level=verbose) cluster_containers = ClusterContainers( cluster_id=cluster_id, transport=transport, ) http_checks = not skip_http_checks wait_for_dcos( dcos_variant=cluster_containers.dcos_variant, cluster=cluster_containers.cluster, superuser_username=superuser_username, superuser_password=superuser_password, http_checks=http_checks, doctor_command=doctor, sibling_ctx=ctx, )
def doctor(verbose: int) -> None: """ Diagnose common issues which stop this CLI from working correctly. """ set_logging(verbosity_level=verbose) check_functions_no_cluster = [ check_1_9_sed, _check_docker_root_free_space, _check_docker_supports_mounts, _check_memory, _check_mount_tmp, _check_networking, _check_selinux, check_ssh, _check_storage_driver, _check_tmp_free_space, _check_systemd, ] # Ideally no checks would create ``Cluster``s. # Checks which do risk showing issues unrelated to what they mean to. # We therefore run these last. check_functions_cluster_needed = [ _check_can_build, # This comes last because it depends on ``_check_can_build``. _check_can_mount_in_docker, ] check_functions = (check_functions_no_cluster + check_functions_cluster_needed) run_doctor_commands(check_functions=check_functions) _link_to_troubleshooting()
def run( cluster_id: str, node_args: Tuple[str], sync_dir: Tuple[Path], dcos_login_uname: str, dcos_login_pw: str, test_env: bool, env: Dict[str, str], aws_region: str, verbose: int, node: str, ) -> None: """ Run an arbitrary command on a node. To use special characters such as single quotes in your command, wrap the whole command in double quotes. """ # noqa: E501 set_logging(verbosity_level=verbose) check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(aws_region=aws_region), ) cluster_instances = ClusterInstances( cluster_id=cluster_id, aws_region=aws_region, ) cluster = cluster_instances.cluster host = _get_node( cluster_id=cluster_id, node_reference=node, aws_region=aws_region, ) for dcos_checkout_dir in sync_dir: sync_code_to_masters( cluster=cluster, dcos_checkout_dir=dcos_checkout_dir, dcos_variant=cluster_instances.dcos_variant, ) run_command( args=list(node_args), cluster=cluster, host=host, use_test_env=test_env, dcos_login_uname=dcos_login_uname, dcos_login_pw=dcos_login_pw, env=env, transport=Transport.SSH, )
def doctor(verbose: int) -> None: """ Diagnose common issues which stop this CLI from working correctly. """ set_logging(verbosity_level=verbose) check_functions = [ check_docker, check_1_9_sed, check_ssh, check_vagrant, check_vagrant_plugins, ] run_doctor_commands(check_functions=check_functions)
def web(cluster_id: str, verbose: int) -> None: """ Open the browser at the web UI. Note that the web UI may not be available at first. Consider using ``minidcos vagrant wait`` before running this command. """ set_logging(verbosity_level=verbose) check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) cluster_vms = ClusterVMs(cluster_id=cluster_id) cluster = cluster_vms.cluster master = next(iter(cluster.masters)) web_ui = 'http://' + str(master.public_ip_address) click.launch(web_ui)
def sync_code( cluster_id: str, dcos_checkout_dir: str, verbose: int, ) -> None: """ Sync files from a DC/OS checkout to master nodes. """ set_logging(verbosity_level=verbose) check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) cluster_vms = ClusterVMs(cluster_id=cluster_id) cluster = cluster_vms.cluster sync_code_to_masters( cluster=cluster, dcos_checkout_dir=Path(dcos_checkout_dir), dcos_variant=cluster_vms.dcos_variant, )
def web(cluster_id: str, verbose: int) -> None: """ Open the browser at the web UI. Note that the web UI may not be available at first. Consider using ``minidcos docker wait`` before running this command. """ set_logging(verbosity_level=verbose) check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) cluster_containers = ClusterContainers( cluster_id=cluster_id, # The transport is not used so does not matter. transport=Transport.DOCKER_EXEC, ) cluster = cluster_containers.cluster master = next(iter(cluster.masters)) web_ui = 'http://' + str(master.public_ip_address) click.launch(web_ui)
def inspect_cluster(cluster_id: str, env: bool, verbose: int) -> None: """ Show cluster details. To quickly get environment variables to use with Docker tooling, use the ``--env`` flag. Run ``eval $(minidcos docker inspect <CLUSTER_ID> --env)``, then run ``docker exec -it $MASTER_0`` to enter the first master, for example. """ set_logging(verbosity_level=verbose) check_cluster_id_exists( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) cluster_containers = ClusterContainers( cluster_id=cluster_id, # The transport here is not relevant as we do not make calls to the # cluster. transport=Transport.DOCKER_EXEC, ) master = next(iter(cluster_containers.masters)) web_ui = 'http://' + master.attrs['NetworkSettings']['IPAddress'] ssh_key = cluster_containers.workspace_dir / 'ssh' / 'id_rsa' keys = { 'masters': cluster_containers.masters, 'agents': cluster_containers.agents, 'public_agents': cluster_containers.public_agents, } if env: env_dict = {} for _, containers in keys.items(): for container in containers: inspect_view = ContainerInspectView(container=container) inspect_data = inspect_view.to_dict() reference = inspect_data['e2e_reference'].upper() env_dict[reference] = container.id node_ip_key = reference + '_IP' node_ip = container.attrs['NetworkSettings']['IPAddress'] env_dict[node_ip_key] = node_ip env_dict['WEB_UI'] = web_ui env_dict['SSH_KEY'] = ssh_key for key, value in env_dict.items(): click.echo('export {key}={value}'.format(key=key, value=value)) return nodes = { key: [ ContainerInspectView(container).to_dict() for container in containers ] for key, containers in keys.items() } data = { 'Cluster ID': cluster_id, 'Web UI': web_ui, 'Nodes': nodes, 'SSH key': str(ssh_key), } # type: Dict[Any, Any] click.echo( json.dumps(data, indent=4, separators=(',', ': '), sort_keys=True), )
def create( ctx: click.core.Context, agents: int, installer_url: str, extra_config: Dict[str, Any], masters: int, public_agents: int, variant: str, workspace_dir: Optional[Path], license_key: Optional[str], security_mode: Optional[str], copy_to_master: List[Tuple[Path, Path]], verbose: int, aws_region: str, linux_distribution: str, cluster_id: str, enable_selinux_enforcing: bool, genconf_dir: Optional[Path], custom_tag: Dict[str, str], wait_for_dcos: bool, ) -> None: """ Create a DC/OS cluster. DC/OS Enterprise \b DC/OS Enterprise clusters require different configuration variables to DC/OS OSS. For example, enterprise clusters require the following configuration parameters: ``superuser_username``, ``superuser_password_hash``, ``fault_domain_enabled``, ``license_key_contents`` \b These can all be set in ``--extra-config``. However, some defaults are provided for all but the license key. \b The default superuser username is ``admin``. The default superuser password is ``admin``. The default ``fault_domain_enabled`` is ``false``. \b ``license_key_contents`` must be set for DC/OS Enterprise 1.11 and above. This is set to one of the following, in order: \b * The ``license_key_contents`` set in ``--extra-config``. * The contents of the path given with ``--license-key``. * The contents of the path set in the ``DCOS_LICENSE_KEY_PATH`` environment variable. \b If none of these are set, ``license_key_contents`` is not given. """ # noqa: E501 set_logging(verbosity_level=verbose) check_cluster_id_unique( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(aws_region=aws_region), ) base_workspace_dir = workspace_dir or Path(tempfile.gettempdir()) workspace_dir = base_workspace_dir / uuid.uuid4().hex workspace_dir.mkdir(parents=True) ssh_keypair_dir = workspace_dir / 'ssh' ssh_keypair_dir.mkdir(parents=True) key_name = 'key-{random}'.format(random=uuid.uuid4().hex) public_key_path = ssh_keypair_dir / 'id_rsa.pub' private_key_path = ssh_keypair_dir / 'id_rsa' write_key_pair( public_key_path=public_key_path, private_key_path=private_key_path, ) ec2 = boto3.resource('ec2', region_name=aws_region) ec2.import_key_pair( KeyName=key_name, PublicKeyMaterial=public_key_path.read_bytes(), ) doctor_message = get_doctor_message(sibling_ctx=ctx, doctor_command=doctor) dcos_variant = get_variant( given_variant=variant, installer_path=None, workspace_dir=workspace_dir, doctor_message=doctor_message, ) variant_tag_value = { DCOSVariant.OSS: VARIANT_OSS_TAG_VALUE, DCOSVariant.ENTERPRISE: VARIANT_ENTERPRISE_TAG_VALUE, }[dcos_variant] ssh_user = { Distribution.CENTOS_7: 'centos', Distribution.COREOS: 'core', Distribution.UBUNTU_16_04: 'ubuntu', Distribution.RHEL_7: 'ec2-user', } distribution = LINUX_DISTRIBUTIONS[linux_distribution] default_user = ssh_user[distribution] cluster_tags = { SSH_USER_TAG_KEY: default_user, CLUSTER_ID_TAG_KEY: cluster_id, WORKSPACE_DIR_TAG_KEY: str(workspace_dir), KEY_NAME_TAG_KEY: key_name, VARIANT_TAG_KEY: variant_tag_value, **custom_tag, } master_tags = {NODE_TYPE_TAG_KEY: NODE_TYPE_MASTER_TAG_VALUE} agent_tags = {NODE_TYPE_TAG_KEY: NODE_TYPE_AGENT_TAG_VALUE} public_agent_tags = {NODE_TYPE_TAG_KEY: NODE_TYPE_PUBLIC_AGENT_TAG_VALUE} cluster_backend = AWS( aws_key_pair=(key_name, private_key_path), workspace_dir=workspace_dir, aws_region=aws_region, linux_distribution=distribution, ec2_instance_tags=cluster_tags, master_ec2_instance_tags=master_tags, agent_ec2_instance_tags=agent_tags, public_agent_ec2_instance_tags=public_agent_tags, ) cluster = create_cluster( cluster_backend=cluster_backend, masters=masters, agents=agents, public_agents=public_agents, sibling_ctx=ctx, doctor_command=doctor, ) nodes = {*cluster.masters, *cluster.agents, *cluster.public_agents} for node in nodes: if enable_selinux_enforcing: node.run(args=['setenforce', '1'], sudo=True) for node in cluster.masters: for path_pair in copy_to_master: local_path, remote_path = path_pair node.send_file( local_path=local_path, remote_path=remote_path, sudo=True, ) files_to_copy_to_genconf_dir = [] if genconf_dir is not None: container_genconf_path = Path('/genconf') for genconf_file in genconf_dir.glob('*'): genconf_relative = genconf_file.relative_to(genconf_dir) relative_path = container_genconf_path / genconf_relative files_to_copy_to_genconf_dir.append((genconf_file, relative_path)) dcos_config = get_config( cluster=cluster, extra_config=extra_config, dcos_variant=dcos_variant, security_mode=security_mode, license_key=license_key, ) try: with click_spinner.spinner(): cluster.install_dcos_from_url( dcos_installer=installer_url, dcos_config=dcos_config, ip_detect_path=cluster_backend.ip_detect_path, files_to_copy_to_genconf_dir=files_to_copy_to_genconf_dir, ) except CalledProcessError as exc: click.echo('Error installing DC/OS.', err=True) click.echo(doctor_message) cluster.destroy() sys.exit(exc.returncode) superuser_username = dcos_config.get( 'superuser_username', DEFAULT_SUPERUSER_USERNAME, ) superuser_password = dcos_config.get( 'superuser_password', DEFAULT_SUPERUSER_PASSWORD, ) if wait_for_dcos: dcos_e2e_cli.common.wait.wait_for_dcos( dcos_variant=dcos_variant, cluster=cluster, superuser_username=superuser_username, superuser_password=superuser_password, http_checks=True, doctor_command=doctor, sibling_ctx=ctx, ) return show_cluster_started_message( # We work on the assumption that the ``wait`` command is a sibling # command of this one. sibling_ctx=ctx, wait_command=wait, cluster_id=cluster_id, ) click.echo(cluster_id)
def create( ctx: click.core.Context, agents: int, installer: str, extra_config: Dict[str, Any], masters: int, public_agents: int, variant: str, workspace_dir: Optional[Path], license_key: Optional[str], security_mode: Optional[str], copy_to_master: List[Tuple[Path, Path]], cluster_id: str, verbose: int, enable_selinux_enforcing: bool, genconf_dir: Optional[Path], wait_for_dcos: bool, ) -> None: """ Create a DC/OS cluster. DC/OS Enterprise \b DC/OS Enterprise clusters require different configuration variables to DC/OS OSS. For example, enterprise clusters require the following configuration parameters: ``superuser_username``, ``superuser_password_hash``, ``fault_domain_enabled``, ``license_key_contents`` \b These can all be set in ``--extra-config``. However, some defaults are provided for all but the license key. \b The default superuser username is ``admin``. The default superuser password is ``admin``. The default ``fault_domain_enabled`` is ``false``. \b ``license_key_contents`` must be set for DC/OS Enterprise 1.11 and above. This is set to one of the following, in order: \b * The ``license_key_contents`` set in ``--extra-config``. * The contents of the path given with ``--license-key``. * The contents of the path set in the ``DCOS_LICENSE_KEY_PATH`` environment variable. \b If none of these are set, ``license_key_contents`` is not given. """ # noqa: E501 set_logging(verbosity_level=verbose) check_cluster_id_unique( new_cluster_id=cluster_id, existing_cluster_ids=existing_cluster_ids(), ) base_workspace_dir = workspace_dir or Path(tempfile.gettempdir()) workspace_dir = base_workspace_dir / uuid.uuid4().hex workspace_dir.mkdir(parents=True) doctor_message = get_doctor_message(sibling_ctx=ctx, doctor_command=doctor) installer_path = Path(installer).resolve() dcos_variant = get_variant( given_variant=variant, installer_path=installer_path, workspace_dir=workspace_dir, doctor_message=doctor_message, ) variant_label_value = { DCOSVariant.OSS: VARIANT_OSS_DESCRIPTION_VALUE, DCOSVariant.ENTERPRISE: VARIANT_ENTERPRISE_DESCRIPTION_VALUE, }[dcos_variant] description = { CLUSTER_ID_DESCRIPTION_KEY: cluster_id, WORKSPACE_DIR_DESCRIPTION_KEY: str(workspace_dir), VARIANT_DESCRIPTION_KEY: variant_label_value, } cluster_backend = Vagrant( workspace_dir=workspace_dir, virtualbox_description=json.dumps(obj=description), ) cluster = create_cluster( cluster_backend=cluster_backend, masters=masters, agents=agents, public_agents=public_agents, sibling_ctx=ctx, doctor_command=doctor, ) nodes = {*cluster.masters, *cluster.agents, *cluster.public_agents} for node in nodes: if enable_selinux_enforcing: node.run(args=['setenforce', '1'], sudo=True) for node in cluster.masters: for path_pair in copy_to_master: local_path, remote_path = path_pair node.send_file( local_path=local_path, remote_path=remote_path, ) files_to_copy_to_genconf_dir = [] if genconf_dir is not None: container_genconf_path = Path('/genconf') for genconf_file in genconf_dir.glob('*'): genconf_relative = genconf_file.relative_to(genconf_dir) relative_path = container_genconf_path / genconf_relative files_to_copy_to_genconf_dir.append((genconf_file, relative_path)) dcos_config = get_config( cluster=cluster, extra_config=extra_config, dcos_variant=dcos_variant, security_mode=security_mode, license_key=license_key, ) install_dcos_from_path( cluster=cluster, dcos_config=dcos_config, ip_detect_path=cluster_backend.ip_detect_path, files_to_copy_to_genconf_dir=files_to_copy_to_genconf_dir, doctor_command=doctor, sibling_ctx=ctx, installer=installer_path, ) superuser_username = dcos_config.get( 'superuser_username', DEFAULT_SUPERUSER_USERNAME, ) superuser_password = dcos_config.get( 'superuser_password', DEFAULT_SUPERUSER_PASSWORD, ) if wait_for_dcos: dcos_e2e_cli.common.wait.wait_for_dcos( dcos_variant=dcos_variant, cluster=cluster, superuser_username=superuser_username, superuser_password=superuser_password, http_checks=True, doctor_command=doctor, sibling_ctx=ctx, ) return show_cluster_started_message( # We work on the assumption that the ``wait`` command is a sibling # command of this one. sibling_ctx=ctx, wait_command=wait, cluster_id=cluster_id, ) click.echo(cluster_id)