Ejemplo n.º 1
0
    def get_container_options(self, defaults):
        # Default options
        options = self.default_container_options()

        # User environmental variables
        if not defaults:
            write_pretty_output("Provide passwords for the three Tethys database users or press enter to accept the "
                                "default passwords shown in square brackets:")

            default_password = '******'

            # tethys_default
            prompt = 'Password for "tethys_default" database user'
            tethys_default_pass = UserInputHelper.get_verified_password(prompt, default_password)

            # tethys_db_manager
            prompt = 'Password for "tethys_db_manager" database user'
            tethys_db_manager_pass = UserInputHelper.get_verified_password(prompt, default_password)

            # tethys_super
            prompt = 'Password for "tethys_super" database user'
            tethys_super_pass = UserInputHelper.get_verified_password(prompt, default_password)

            options['environment'].update(
                TETHYS_DEFAULT_PASS=tethys_default_pass,
                TETHYS_DB_MANAGER_PASS=tethys_db_manager_pass,
                TETHYS_SUPER_PASS=tethys_super_pass
            )

        return options
Ejemplo n.º 2
0
    def get_container_options(self, defaults):
        # Default options
        options = self.default_container_options()

        # User environmental variables
        if not defaults:
            write_pretty_output("Provide passwords for the three Tethys database users or press enter to accept the "
                                "default passwords shown in square brackets:")

            default_password = '******'

            # tethys_default
            prompt = 'Password for "tethys_default" database user'
            tethys_default_pass = UserInputHelper.get_verified_password(prompt, default_password)

            # tethys_db_manager
            prompt = 'Password for "tethys_db_manager" database user'
            tethys_db_manager_pass = UserInputHelper.get_verified_password(prompt, default_password)

            # tethys_super
            prompt = 'Password for "tethys_super" database user'
            tethys_super_pass = UserInputHelper.get_verified_password(prompt, default_password)

            options['environment'].update(
                TETHYS_DEFAULT_PASS=tethys_default_pass,
                TETHYS_DB_MANAGER_PASS=tethys_db_manager_pass,
                TETHYS_SUPER_PASS=tethys_super_pass
            )

        return options
Ejemplo n.º 3
0
 def start(self):
     msg = 'Starting {} container...'
     write_pretty_output(msg.format(self.display_name))
     msg = None
     try:
         self.container.start()
     except Exception as e:
         msg = 'There was an error while attempting to start container {}: {}'.format(
             self.display_name, str(e)
         )
     return msg
Ejemplo n.º 4
0
 def start(self):
     msg = 'Starting {} container...'
     write_pretty_output(msg.format(self.display_name))
     msg = None
     try:
         self.container.start()
     except Exception as e:
         msg = 'There was an error while attempting to start container {}: {}'.format(
             self.display_name, str(e)
         )
     return msg
Ejemplo n.º 5
0
    def stop(self, silent=False):
        msg = 'Stopping {} container...'
        if not silent:
            write_pretty_output(msg.format(self.display_name))
        try:
            self.container.stop()
            msg = None
        except Exception as e:
            msg = 'There was an error while attempting to stop container {}: {}'.format(
                self.display_name, str(e)
            )

        return msg
Ejemplo n.º 6
0
    def stop(self, silent=False):
        msg = 'Stopping {} container...'
        if not silent:
            write_pretty_output(msg.format(self.display_name))
        try:
            self.container.stop()
            msg = None
        except Exception as e:
            msg = 'There was an error while attempting to stop container {}: {}'.format(
                self.display_name, str(e)
            )

        return msg
Ejemplo n.º 7
0
def install_docker_containers(containers_to_install, defaults=False):
    """
    Install all Docker containers

    Args:
        containers_to_install(list[ContainerMetadata], required):
            list of containers to install
        defaults(bool, optional, default=False):
            if True use all of the default options instead of prompting user to specify options
    """
    for container_metadata in containers_to_install:
        container_metadata.create(defaults)

    write_pretty_output("Finished installing Docker containers.")
Ejemplo n.º 8
0
def install_docker_containers(containers_to_install, defaults=False):
    """
    Install all Docker containers

    Args:
        containers_to_install(list[ContainerMetadata], required):
            list of containers to install
        defaults(bool, optional, default=False):
            if True use all of the default options instead of prompting user to specify options
    """
    for container_metadata in containers_to_install:
        container_metadata.create(defaults)

    write_pretty_output("Finished installing Docker containers.")
Ejemplo n.º 9
0
def pull_docker_images(containers_to_install):
    """
    Pull Docker container images

    Args:
        containers_to_install(list[ContainerMetadata], required):
            list of containers to install
    """
    if len(containers_to_install) < 1:
        write_pretty_output("Docker images already pulled.")
    else:
        write_pretty_output("Pulling Docker images...")

    for container in containers_to_install:
        container.pull()
Ejemplo n.º 10
0
def docker_status(containers=None):
    """
    Returns the status of the Docker containers: either Running or Stopped.
    """
    # Get container dicts
    container_statuses = get_docker_container_statuses(containers=containers)

    for container, is_running in container_statuses.items():
        if is_running is None:
            msg = '{}: Not Installed'
        elif is_running:
            msg = '{}: Running'
        else:
            msg = '{}: Not Running'
        write_pretty_output(msg.format(container.display_name))
Ejemplo n.º 11
0
def docker_status(containers=None):
    """
    Returns the status of the Docker containers: either Running or Stopped.
    """
    # Get container dicts
    container_statuses = get_docker_container_statuses(containers=containers)

    for container, is_running in container_statuses.items():
        if is_running is None:
            msg = '{}: Not Installed'
        elif is_running:
            msg = '{}: Running'
        else:
            msg = '{}: Not Running'
        write_pretty_output(msg.format(container.display_name))
Ejemplo n.º 12
0
    def get_verified_password(prompt, default):
        password_1 = getpass.getpass('{} [{}]: '.format(prompt, default))

        if password_1 == '':
            return default

        else:
            password_2 = getpass.getpass('Confirm Password: '******'Passwords do not match, please try again.')
                password_1 = getpass.getpass('{} [{}]: '.format(prompt, default))
                password_2 = getpass.getpass('Confirm Password: ')

            return password_1
Ejemplo n.º 13
0
def pull_docker_images(containers_to_install):
    """
    Pull Docker container images

    Args:
        containers_to_install(list[ContainerMetadata], required):
            list of containers to install
    """
    if len(containers_to_install) < 1:
        write_pretty_output("Docker images already pulled.")
    else:
        write_pretty_output("Pulling Docker images...")

    for container in containers_to_install:
        container.pull()
Ejemplo n.º 14
0
    def get_verified_password(prompt, default):
        password_1 = getpass.getpass('{} [{}]: '.format(prompt, default))

        if password_1 == '':
            return default

        else:
            password_2 = getpass.getpass('Confirm Password: '******'Passwords do not match, please try again.')
                password_1 = getpass.getpass('{} [{}]: '.format(prompt, default))
                password_2 = getpass.getpass('Confirm Password: ')

            return password_1
Ejemplo n.º 15
0
def docker_ip(containers=None):
    """
    Returns the hosts and ports of the Docker containers.
    """
    # Containers
    container_statuses = get_docker_container_statuses(containers=containers)

    for container_metadata, is_running in container_statuses.items():
        if is_running is None:
            msg = '{name}: Not Installed'
        elif not is_running:
            msg = '{name}: Not Running'
        else:
            msg = container_metadata.ip

        write_pretty_output(msg.format(name=container_metadata.display_name))
Ejemplo n.º 16
0
def docker_ip(containers=None):
    """
    Returns the hosts and ports of the Docker containers.
    """
    # Containers
    container_statuses = get_docker_container_statuses(containers=containers)

    for container_metadata, is_running in container_statuses.items():
        if is_running is None:
            msg = '{name}: Not Installed'
        elif not is_running:
            msg = '{name}: Not Running'
        else:
            msg = container_metadata.ip

        write_pretty_output(msg.format(name=container_metadata.display_name))
Ejemplo n.º 17
0
def docker_stop(containers=None):
    """
    Stop Docker containers
    """
    container_statuses = get_docker_container_statuses(containers=containers)

    for container_metadata, status in container_statuses.items():
        already_stopped = not status
        if status is None:
            msg = '{} container not installed.'
        elif already_stopped:
            msg = '{} container already stopped.'
        else:
            msg = container_metadata.stop()

        if msg is not None:
            write_pretty_output(msg.format(container_metadata.display_name))
Ejemplo n.º 18
0
def docker_stop(containers=None):
    """
    Stop Docker containers
    """
    container_statuses = get_docker_container_statuses(containers=containers)

    for container_metadata, status in container_statuses.items():
        already_stopped = not status
        if status is None:
            msg = '{} container not installed.'
        elif already_stopped:
            msg = '{} container already stopped.'
        else:
            msg = container_metadata.stop()

        if msg is not None:
            write_pretty_output(msg.format(container_metadata.display_name))
Ejemplo n.º 19
0
def docker_start(containers=None):
    """
    Start the docker containers
    """
    container_statuses = get_docker_container_statuses(containers=containers)

    for container_metadata, status in container_statuses.items():
        already_running = status

        if already_running is None:
            msg = '{} container not installed.'
        elif already_running:
            msg = '{} container already running.'
        else:
            msg = container_metadata.start()

        if msg is not None:
            write_pretty_output(msg.format(container_metadata.display_name))
Ejemplo n.º 20
0
def docker_start(containers=None):
    """
    Start the docker containers
    """
    container_statuses = get_docker_container_statuses(containers=containers)

    for container_metadata, status in container_statuses.items():
        already_running = status

        if already_running is None:
            msg = '{} container not installed.'
        elif already_running:
            msg = '{} container already running.'
        else:
            msg = container_metadata.start()

        if msg is not None:
            write_pretty_output(msg.format(container_metadata.display_name))
Ejemplo n.º 21
0
    def get_valid_directory_input(prompt, default=None):
        default = default or ''
        pre_prompt = ''
        prompt = '{} [{}]: '.format(prompt, default)
        while True:
            value = input('{}{}'.format(pre_prompt, prompt)) or str(default)

            if len(value) > 0 and value[0] != '/':
                value = '/' + value

            if not os.path.isdir(value):
                try:
                    os.makedirs(value)
                except OSError as e:
                    write_pretty_output('{0}: {1}'.format(repr(e), value))
                    pre_prompt = 'Please provide a valid directory\n'
                    continue

            break

        return value
Ejemplo n.º 22
0
    def get_valid_directory_input(prompt, default=None):
        default = default or ''
        pre_prompt = ''
        prompt = '{} [{}]: '.format(prompt, default)
        while True:
            value = input('{}{}'.format(pre_prompt, prompt)) or str(default)

            if len(value) > 0 and value[0] != '/':
                value = '/' + value

            if not os.path.isdir(value):
                try:
                    os.makedirs(value)
                except OSError as e:
                    write_pretty_output('{0}: {1}'.format(repr(e), value))
                    pre_prompt = 'Please provide a valid directory\n'
                    continue

            break

        return value
Ejemplo n.º 23
0
    def get_container_options(self, defaults):
        # Default environmental vars
        options = self.default_container_options()

        if not defaults:
            write_pretty_output("Provide contact information for the 52 North Web Processing Service or press enter to "
                                "accept the defaults shown in square brackets: ")

            options['environment'].update(
                NAME=UserInputHelper.get_input_with_default('Name', 'NONE'),
                POSITION=UserInputHelper.get_input_with_default('Position', 'NONE'),
                ADDRESS=UserInputHelper.get_input_with_default('Address', 'NONE'),
                CITY=UserInputHelper.get_input_with_default('City', 'NONE'),
                STATE=UserInputHelper.get_input_with_default('State', 'NONE'),
                COUNTRY=UserInputHelper.get_input_with_default('Country', 'NONE'),
                POSTAL_CODE=UserInputHelper.get_input_with_default('Postal Code', 'NONE'),
                EMAIL=UserInputHelper.get_input_with_default('Email', 'NONE'),
                PHONE=UserInputHelper.get_input_with_default('Phone', 'NONE'),
                FAX=UserInputHelper.get_input_with_default('Fax', 'NONE'),
                USERNAME=UserInputHelper.get_input_with_default('Admin Username', 'wps'),
                PASSWORD=UserInputHelper.get_verified_password('Admin Password', 'wps')
            )

        return options
Ejemplo n.º 24
0
    def get_container_options(self, defaults):
        # Default environmental vars
        options = self.default_container_options()

        if not defaults:
            write_pretty_output("Provide contact information for the 52 North Web Processing Service or press enter to "
                                "accept the defaults shown in square brackets: ")

            options['environment'].update(
                NAME=UserInputHelper.get_input_with_default('Name', 'NONE'),
                POSITION=UserInputHelper.get_input_with_default('Position', 'NONE'),
                ADDRESS=UserInputHelper.get_input_with_default('Address', 'NONE'),
                CITY=UserInputHelper.get_input_with_default('City', 'NONE'),
                STATE=UserInputHelper.get_input_with_default('State', 'NONE'),
                COUNTRY=UserInputHelper.get_input_with_default('Country', 'NONE'),
                POSTAL_CODE=UserInputHelper.get_input_with_default('Postal Code', 'NONE'),
                EMAIL=UserInputHelper.get_input_with_default('Email', 'NONE'),
                PHONE=UserInputHelper.get_input_with_default('Phone', 'NONE'),
                FAX=UserInputHelper.get_input_with_default('Fax', 'NONE'),
                USERNAME=UserInputHelper.get_input_with_default('Admin Username', 'wps'),
                PASSWORD=UserInputHelper.get_verified_password('Admin Password', 'wps')
            )

        return options
Ejemplo n.º 25
0
 def remove(self):
     write_pretty_output('Removing {} container...'.format(self.display_name))
     self.container.remove()
Ejemplo n.º 26
0
    def create(self, defaults=False):
        write_pretty_output("\nInstalling the {} Docker container...".format(self.display_name))

        options = self.get_container_options(defaults)
        options['host_config'] = self.docker_client.api.create_host_config(**options['host_config'])
        self.docker_client.api.create_container(**options)
Ejemplo n.º 27
0
 def remove(self):
     write_pretty_output('Removing {} container...'.format(self.display_name))
     self.container.remove()
Ejemplo n.º 28
0
    def get_container_options(self, defaults):
        # default configuration
        options = self.default_container_options()

        if not self.is_cluster:
            # Then all of the other options are irrelevant
            defaults = True

        if not defaults:
            # Environmental variables from user input
            environment = dict()

            write_pretty_output(
                "The GeoServer docker can be configured to run in a clustered mode (multiple instances of "
                "GeoServer running in the docker container) for better performance.\n")

            environment['ENABLED_NODES'] = UserInputHelper.get_valid_numeric_input(
                prompt='Number of GeoServer Instances Enabled',
                max_val=4,
            )

            environment['REST_NODES'] = UserInputHelper.get_valid_numeric_input(
                prompt='Number of GeoServer Instances with REST API Enabled',
                max_val=int(environment['ENABLED_NODES']),
            )

            write_pretty_output(
                "\nGeoServer can be configured with limits to certain types of requests to prevent it from "
                "becoming overwhelmed. This can be done automatically based on a number of processors or "
                "each "
                "limit can be set explicitly.\n")

            flow_control_mode = UserInputHelper.get_valid_choice_input(
                prompt='Would you like to specify number of Processors (c) OR set request limits explicitly (e)',
                choices=['c', 'e'],
                default='c',
            )

            if flow_control_mode.lower() == 'c':
                environment['NUM_CORES'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Number of Processors',
                    max_val=4,  # TODO dynamically figure out what the max is
                )

            else:
                environment['MAX_OWS_GLOBAL'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Maximum number of simultaneous OGC web service requests (e.g.: WMS, WCS, WFS)',
                    default=100
                )

                environment['MAX_WMS_GETMAP'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Maximum number of simultaneous GetMap requests',
                    default=8
                )

                environment['MAX_OWS_GWC'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Maximum number of simultaneous GeoWebCache tile renders',
                    default=16
                )

            environment['MAX_TIMEOUT'] = UserInputHelper.get_valid_numeric_input(
                prompt='Maximum request timeout in seconds',
                default=60
            )

            environment['MAX_MEMORY'] = UserInputHelper.get_valid_numeric_input(
                prompt='Maximum memory to allocate to each GeoServer instance in MB',
                max_val=4096,  # TODO dynamically figure out what the max is
                default=1024
            )

            max_memory = int(environment['MAX_MEMORY'])
            environment['MIN_MEMORY'] = UserInputHelper.get_valid_numeric_input(
                prompt='Minimum memory to allocate to each GeoServer instance in MB',
                max_val=max_memory,
                default=max_memory
            )

            options.update(
                environment=environment,
            )

            mount_data_dir = UserInputHelper.get_valid_choice_input(
                prompt='Bind the GeoServer data directory to the host?',
                choices=['y', 'n'],
                default='y',
            )

            if mount_data_dir.lower() == 'y':
                tethys_home = os.environ.get('TETHYS_HOME', os.path.expanduser('~/tethys/'))
                default_mount_location = os.path.join(tethys_home, 'geoserver', 'data')
                gs_data_volume = '/var/geoserver/data'
                mount_location = UserInputHelper.get_valid_directory_input(
                    prompt='Specify location to bind data directory',
                    default=default_mount_location
                )
                mounts = [Mount(gs_data_volume, mount_location, type='bind')]
                options['host_config'].update(mounts=mounts)

        return options
Ejemplo n.º 29
0
    def get_container_options(self, defaults):
        # default configuration
        options = self.default_container_options()

        if not self.is_cluster:
            # Then all of the other options are irrelevant
            defaults = True

        if not defaults:
            # Environmental variables from user input
            environment = dict()

            write_pretty_output(
                "The GeoServer docker can be configured to run in a clustered mode (multiple instances of "
                "GeoServer running in the docker container) for better performance.\n")

            environment['ENABLED_NODES'] = UserInputHelper.get_valid_numeric_input(
                prompt='Number of GeoServer Instances Enabled',
                max_val=4,
            )

            environment['REST_NODES'] = UserInputHelper.get_valid_numeric_input(
                prompt='Number of GeoServer Instances with REST API Enabled',
                max_val=int(environment['ENABLED_NODES']),
            )

            write_pretty_output(
                "\nGeoServer can be configured with limits to certain types of requests to prevent it from "
                "becoming overwhelmed. This can be done automatically based on a number of processors or "
                "each "
                "limit can be set explicitly.\n")

            flow_control_mode = UserInputHelper.get_valid_choice_input(
                prompt='Would you like to specify number of Processors (c) OR set request limits explicitly (e)',
                choices=['c', 'e'],
                default='c',
            )

            if flow_control_mode.lower() == 'c':
                environment['NUM_CORES'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Number of Processors',
                    max_val=4,  # TODO dynamically figure out what the max is
                )

            else:
                environment['MAX_OWS_GLOBAL'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Maximum number of simultaneous OGC web service requests (e.g.: WMS, WCS, WFS)',
                    default=100
                )

                environment['MAX_WMS_GETMAP'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Maximum number of simultaneous GetMap requests',
                    default=8
                )

                environment['MAX_OWS_GWC'] = UserInputHelper.get_valid_numeric_input(
                    prompt='Maximum number of simultaneous GeoWebCache tile renders',
                    default=16
                )

            environment['MAX_TIMEOUT'] = UserInputHelper.get_valid_numeric_input(
                prompt='Maximum request timeout in seconds',
                default=60
            )

            environment['MAX_MEMORY'] = UserInputHelper.get_valid_numeric_input(
                prompt='Maximum memory to allocate to each GeoServer instance in MB',
                max_val=4096,  # TODO dynamically figure out what the max is
                default=1024
            )

            max_memory = int(environment['MAX_MEMORY'])
            environment['MIN_MEMORY'] = UserInputHelper.get_valid_numeric_input(
                prompt='Minimum memory to allocate to each GeoServer instance in MB',
                max_val=max_memory,
                default=max_memory
            )

            options.update(
                environment=environment,
            )

            mount_data_dir = UserInputHelper.get_valid_choice_input(
                prompt='Bind the GeoServer data directory to the host?',
                choices=['y', 'n'],
                default='y',
            )

            if mount_data_dir.lower() == 'y':
                tethys_home = os.environ.get('TETHYS_HOME', os.path.expanduser('~/tethys/'))
                default_mount_location = os.path.join(tethys_home, 'geoserver', 'data')
                gs_data_volume = '/var/geoserver/data'
                mount_location = UserInputHelper.get_valid_directory_input(
                    prompt='Specify location to bind data directory',
                    default=default_mount_location
                )
                mounts = [Mount(gs_data_volume, mount_location, type='bind')]
                options['host_config'].update(mounts=mounts)

        return options
Ejemplo n.º 30
0
def log_pull_stream(stream):
    """
    Handle the printing of pull statuses
    """
    if platform.system() == 'Windows':  # i.e. can't uses curses
        for block in stream:
            lines = [l for l in block.split(b'\r\n') if l]
            for line in lines:
                json_line = json.loads(line)
                current_id = "{}:".format(json_line['id']) if 'id' in json_line else ''
                current_status = json_line['status'] if 'status' in json_line else ''
                current_progress = json_line['progress'] if 'progress' in json_line else ''

                write_pretty_output("{id}{status} {progress}".format(
                        id=current_id,
                        status=current_status,
                        progress=current_progress
                ))
    else:

        TERMINAL_STATUSES = ['Already exists', 'Download complete', 'Pull complete']
        PROGRESS_STATUSES = ['Downloading', 'Extracting']
        STATUSES = TERMINAL_STATUSES + PROGRESS_STATUSES + ['Pulling fs layer', 'Waiting', 'Verifying Checksum']

        NUMBER_OF_HEADER_ROWS = 2

        header_rows = list()
        message_log = list()
        progress_messages = dict()
        messages_to_print = list()

        # prepare console for curses window printing
        stdscr = curses.initscr()
        curses.noecho()
        curses.cbreak()

        try:
            for block in stream:
                lines = [l for l in block.split(b'\r\n') if l]
                for line in lines:
                    json_line = json.loads(line)
                    current_id = json_line['id'] if 'id' in json_line else None
                    current_status = json_line['status'] if 'status' in json_line else ''
                    current_progress = json_line['progress'] if 'progress' in json_line else ''

                    if current_id is None:
                        # save messages to print after docker images are pulled
                        messages_to_print.append(current_status.strip())
                    else:
                        # use curses window to properly display progress
                        if current_status not in STATUSES:  # Assume this is the header
                            header_rows.append(current_status)
                            header_rows.append('-' * len(current_status))

                        elif current_status in PROGRESS_STATUSES:
                                # add messages with progress to dictionary to print at the bottom of the screen
                                progress_messages[current_id] = {'id': current_id, 'status': current_status,
                                                                 'progress': current_progress}
                        else:
                            # add all other messages to list to show above progress messages
                            message_log.append("{id}: {status} {progress}".format(
                                id=current_id,
                                status=current_status,
                                progress=current_progress
                            ))

                            # remove messages from progress that have completed
                            if current_id in progress_messages:
                                del progress_messages[current_id]

                        # update window

                        # row/column calculations for proper display on screen
                        maxy, maxx = stdscr.getmaxyx()
                        number_of_rows, number_of_columns = maxy, maxx

                        current_progress_messages = sorted(progress_messages.values(),
                                                           key=lambda message: STATUSES.index(message['status']))

                        # row/column calculations for proper display on screen
                        number_of_progress_rows = len(current_progress_messages)
                        number_of_message_rows = number_of_rows - number_of_progress_rows - NUMBER_OF_HEADER_ROWS

                        # slice messages to only those that will fit on the screen
                        current_messages = [''] * number_of_message_rows + message_log
                        current_messages = current_messages[-number_of_message_rows:]

                        rows = header_rows + current_messages + ['{id}: {status} {progress}'.format(**current_message)
                                                                 for current_message in current_progress_messages]

                        for row, message in enumerate(rows):
                            message += ' ' * number_of_columns
                            message = message[:number_of_columns - 1]
                            stdscr.addstr(row, 0, message)

                        stdscr.refresh()

        finally:  # always reset console to normal regardless of success or failure
            curses.echo()
            curses.nocbreak()
            curses.endwin()

        write_pretty_output('\n'.join(messages_to_print))
Ejemplo n.º 31
0
    def test_write_pretty_output(self, mock_print):
        act_msg = 'This is a test in RED'
        expected_string = '\x1b[31mThis is a test in RED\x1b[0m'
        write_pretty_output(act_msg, FG_RED)

        mock_print.assert_called_with(expected_string)
Ejemplo n.º 32
0
def log_pull_stream(stream):
    """
    Handle the printing of pull statuses
    """
    if platform.system() == 'Windows':  # i.e. can't uses curses
        for block in stream:
            lines = [l for l in block.split(b'\r\n') if l]
            for line in lines:
                json_line = json.loads(line)
                current_id = "{}:".format(json_line['id']) if 'id' in json_line else ''
                current_status = json_line['status'] if 'status' in json_line else ''
                current_progress = json_line['progress'] if 'progress' in json_line else ''

                write_pretty_output("{id}{status} {progress}".format(
                        id=current_id,
                        status=current_status,
                        progress=current_progress
                ))
    else:

        TERMINAL_STATUSES = ['Already exists', 'Download complete', 'Pull complete']
        PROGRESS_STATUSES = ['Downloading', 'Extracting']
        STATUSES = TERMINAL_STATUSES + PROGRESS_STATUSES + ['Pulling fs layer', 'Waiting', 'Verifying Checksum']

        NUMBER_OF_HEADER_ROWS = 2

        header_rows = list()
        message_log = list()
        progress_messages = dict()
        messages_to_print = list()

        # prepare console for curses window printing
        stdscr = curses.initscr()
        curses.noecho()
        curses.cbreak()

        try:
            for block in stream:
                lines = [l for l in block.split(b'\r\n') if l]
                for line in lines:
                    json_line = json.loads(line)
                    current_id = json_line['id'] if 'id' in json_line else None
                    current_status = json_line['status'] if 'status' in json_line else ''
                    current_progress = json_line['progress'] if 'progress' in json_line else ''

                    if current_id is None:
                        # save messages to print after docker images are pulled
                        messages_to_print.append(current_status.strip())
                    else:
                        # use curses window to properly display progress
                        if current_status not in STATUSES:  # Assume this is the header
                            header_rows.append(current_status)
                            header_rows.append('-' * len(current_status))

                        elif current_status in PROGRESS_STATUSES:
                            # add messages with progress to dictionary to print at the bottom of the screen
                            progress_messages[current_id] = {
                                'id': current_id, 'status': current_status,
                                'progress': current_progress
                            }
                        else:
                            # add all other messages to list to show above progress messages
                            message_log.append("{id}: {status} {progress}".format(
                                id=current_id,
                                status=current_status,
                                progress=current_progress
                            ))

                            # remove messages from progress that have completed
                            if current_id in progress_messages:
                                del progress_messages[current_id]

                        # update window

                        # row/column calculations for proper display on screen
                        maxy, maxx = stdscr.getmaxyx()
                        number_of_rows, number_of_columns = maxy, maxx

                        current_progress_messages = sorted(progress_messages.values(),
                                                           key=lambda message: STATUSES.index(message['status']))

                        # row/column calculations for proper display on screen
                        number_of_progress_rows = len(current_progress_messages)
                        number_of_message_rows = number_of_rows - number_of_progress_rows - NUMBER_OF_HEADER_ROWS

                        # slice messages to only those that will fit on the screen
                        current_messages = [''] * number_of_message_rows + message_log
                        current_messages = current_messages[-number_of_message_rows:]

                        rows = header_rows + current_messages + ['{id}: {status} {progress}'.format(**current_message)
                                                                 for current_message in current_progress_messages]

                        for row, message in enumerate(rows):
                            message += ' ' * number_of_columns
                            message = message[:number_of_columns - 1]
                            stdscr.addstr(row, 0, message)

                        stdscr.refresh()

        finally:  # always reset console to normal regardless of success or failure
            curses.echo()
            curses.nocbreak()
            curses.endwin()

        write_pretty_output('\n'.join(messages_to_print))
Ejemplo n.º 33
0
    def create(self, defaults=False):
        write_pretty_output("\nInstalling the {} Docker container...".format(self.display_name))

        options = self.get_container_options(defaults)
        options['host_config'] = self.docker_client.api.create_host_config(**options['host_config'])
        self.docker_client.api.create_container(**options)