def prompt(cls,
               console: io.IO,
               step_prompt: str,
               arguments: Dict[str, Any],
               credentials: Optional[credentials.Credentials] = None) -> str:
        """Prompt the user to a Google Cloud Platform project id.

        Args:
            console: Object to use for user I/O.
            step_prompt: A prefix showing the current step number e.g. "[1/3]".
            arguments: The arguments that have already been collected from the
                user e.g. {"project_id", "project-123"}
            credentials: The OAuth2 Credentials object to use for api calls
                during prompt.

        Returns:
            The value entered by the user.
        """
        default_project_id = cls._generate_default_project_id(
            arguments.get('project_name', None))
        while True:
            console.tell(('{} Enter a Google Cloud Platform Project ID, '
                          'or leave blank to use').format(step_prompt))
            project_id = console.ask('[{}]: '.format(default_project_id))
            if not project_id.strip():
                return default_project_id
            try:
                cls.validate(project_id)
            except ValueError as e:
                console.error(e)
                continue
            return project_id
    def _is_valid_passed_arg(self, console: io.IO, step: str,
                             value: Optional[str],
                             validate: Callable[[str], None]) -> bool:
        """Checks if the passed in argument via the command line is valid.

        All prompts that collect a parameter should call this function first.
        It uses the validate function of the prompt. The code also
        will process a passed in paramater as a step. This is used to have a
        static amount of steps that is easier to manage.

        Returns:
            A boolean indicating if the passed in argument is valid.
        """
        if value is None:
            return False

        try:
            validate(value)
        except ValueError as e:
            console.error(e)
            quit()

        msg = '{} {}: {}'.format(step, self.PARAMETER, value)
        console.tell(msg)
        return True
def _ask_prompt(question: str,
                console: io.IO,
                validate: Optional[Callable[[str], None]] = None,
                default: Optional[str] = None) -> str:
    """Used to ask for a single string value.

    Args:
        question: Question shown to the user on the console.
        console: Object to use for user I/O.
        validate: Function used to check if value provided is valid. It should
            raise a ValueError if the the value fails to validate.
        default: Default value if user provides no value. (Presses enter) If
            default is None, the user must provide an answer that is valid.

    Returns:
        The value entered by the user.
    """
    validate = validate or (lambda x: None)
    while True:
        answer = console.ask(question)
        if default and not answer:
            answer = default
        try:
            validate(answer)
            break
        except ValueError as e:
            console.error(e)

    return answer
def _binary_prompt(question: str,
                   console: io.IO,
                   default: Optional[bool] = None) -> bool:
    """Used to prompt user to choose from a yes or no question.

    Args:
        question: Question shown to the user on the console.
        console: Object to use for user I/O.
        default: Default value if user provides no value. (Presses enter) If
            default is None the user is forced to choose a value (y/n).

    Returns:
        The bool representation of the choice of the user. Yes is True.
    """

    while True:
        answer = console.ask(question).lower()

        if default is not None and not answer:
            return default

        try:
            _binary_validate(answer)
            break
        except ValueError as e:
            console.error(e)

    return answer == 'y'
    def prompt(cls,
               console: io.IO,
               step_prompt: str,
               arguments: Dict[str, Any],
               credentials: Optional[credentials.Credentials] = None) -> str:
        """Prompt the user to enter a password.

        Args:
            console: Object to use for user I/O.
            step_prompt: A prefix showing the current step number e.g. "[1/3]".
            arguments: The arguments that have already been collected from the
                user e.g. {"project_id", "project-123"}
            credentials: The OAuth2 Credentials object to use for api calls
                during prompt.

        Returns:
            The value entered by the user.
        """
        console_prompt = cls._get_prompt(arguments)
        console.tell(('{} {}').format(step_prompt, console_prompt))
        while True:
            password = console.getpass('Postgres password: ')
            try:
                cls.validate(password)
            except ValueError as e:
                console.error(e)
                continue
            return password
Exemple #6
0
    def prompt(cls,
               console: io.IO,
               step_prompt: str,
               arguments: Dict[str, Any],
               credentials: Optional[credentials.Credentials] = None) -> str:
        """Prompt the user to a Google Cloud Platform project id.

        Args:
            console: Object to use for user I/O.
            step_prompt: A prefix showing the current step number e.g. "[1/3]".
            arguments: The arguments that have already been collected from the
                user e.g. {"project_id", "project-123"}
            credentials: The OAuth2 Credentials object to use for api calls
                during prompt.

        Returns:
            The value entered by the user.
        """
        while True:
            console.tell(
                ('{} Enter the existing Google Cloud Platform Project ID '
                 'to use.').format(step_prompt))
            project_id = console.ask('Project ID: ')
            try:
                cls.validate(project_id)
            except ValueError as e:
                console.error(e)
                continue
            return project_id
    def prompt(cls,
               console: io.IO,
               step_prompt: str,
               arguments: Dict[str, Any],
               credentials: Optional[credentials.Credentials] = None) -> str:
        """Prompt the user to enter some sort of name.

        Args:
            console: Object to use for user I/O.
            step_prompt: A prefix showing the current step number e.g. "[1/3]".
            arguments: The arguments that have already been collected from the
                user e.g. {"project_id", "project-123"}
            credentials: The OAuth2 Credentials object to use for api calls
                during prompt.

        Returns:
            The value entered by the user.
        """
        default_name = cls._default_name(arguments)
        while True:
            console.tell(('{} {}').format(step_prompt, cls._PROMPT))
            project_name = console.ask('[{}]: '.format(default_name))
            if not project_name.strip():
                project_name = default_name
            try:
                cls.validate(project_name)
            except ValueError as e:
                console.error(e)
                continue
            return project_name
    def prompt(self, console: io.IO, step: str,
               args: Dict[str, Any]) -> Dict[str, Any]:
        """Extracts user arguments through the command-line.

        Args:
            console: Object to use for user I/O.
            step: Message to present to user regarding what step they are on.
            args: Dictionary holding prompts answered by user and set up
                command-line arguments.

        Returns: A Copy of args + the new parameter collected.
        """
        new_args = copy.deepcopy(args)
        if self._is_valid_passed_arg(console, step, args.get(self.PARAMETER),
                                     self._validate):
            return new_args

        while True:
            directory = self._ask_for_directory(console, step, args)
            try:
                self._validate(directory)
            except ValueError as e:
                console.error(e)
                continue
            break

        new_args[self.PARAMETER] = directory
        return new_args
Exemple #9
0
def main(args: argparse.Namespace, console: io.IO = io.ConsoleIO()):
    if not tool_requirements.check_and_handle_requirements(
            console, args.backend):
        return

    actual_parameters = {
        'project_creation_mode': workflow.ProjectCreationMode.CREATE,
        'bucket_name': getattr(args, 'bucket_name', None),
        'service_accounts': getattr(args, 'service_accounts', None),
        'services': getattr(args, 'services', None),
        'appengine_service_name': getattr(args, 'appengine_service_name',
                                          None),
        'cluster_name': getattr(args, 'cluster_name', None),
        'database_instance_name': getattr(args, 'database_instance_name',
                                          None),
    }

    prompt_args = {**vars(args), **actual_parameters}
    root_prompt = prompt.RootPrompt()
    actual_parameters = root_prompt.prompt(prompt.Command.CLOUDIFY, console,
                                           prompt_args)
    workflow_manager = workflow.WorkflowManager(
        actual_parameters['credentials'])

    django_directory_path = actual_parameters['django_directory_path_cloudify']
    django_project_name = utils.get_django_project_name(django_directory_path)
    try:
        admin_url = workflow_manager.create_and_deploy_new_project(
            project_name=actual_parameters['project_name'],
            project_id=actual_parameters['project_id'],
            project_creation_mode=actual_parameters['project_creation_mode'],
            billing_account_name=actual_parameters['billing_account_name'],
            django_project_name=django_project_name,
            django_superuser_name=actual_parameters['django_superuser_login'],
            django_superuser_email=actual_parameters['django_superuser_email'],
            django_superuser_password=actual_parameters[
                'django_superuser_password'],
            django_directory_path=django_directory_path,
            django_requirements_path=actual_parameters.get(
                'django_requirements_path'),
            django_settings_path=actual_parameters['django_settings_path'],
            database_password=actual_parameters['database_password'],
            cluster_name=actual_parameters['cluster_name'],
            database_instance_name=actual_parameters['database_instance_name'],
            required_services=actual_parameters['services'],
            required_service_accounts=actual_parameters['service_accounts'],
            appengine_service_name=actual_parameters['appengine_service_name'],
            cloud_storage_bucket_name=actual_parameters['bucket_name'],
            backend=args.backend,
            deploy_existing_django_project=True)
    except workflow.ProjectExistsError:
        console.error('A project with id "{}" already exists'.format(
            actual_parameters['project_id']))

    survey.prompt_for_survey(console)
    return admin_url
Exemple #10
0
def main(args: argparse.Namespace, console: io.IO = io.ConsoleIO()):

    vargs = vars(args)
    if not vargs.get('command_rest'):
        console.error('Please enter the management command.')
        return

    command_rest = vargs.get('command_rest')
    management_command = command_rest[0]
    if management_command not in _SUPPORTED_COMMANDS:
        console.error(('Command "{}" is not supported by Django Cloud '
                       'Deploy.').format(management_command))

    _execute(command_rest, console)
def _multiple_choice_prompt(question: str,
                            options: List[str],
                            console: io.IO,
                            default: Optional[int] = None) -> Optional[int]:
    """Used to prompt user to choose from a list of values.

    Args:
        question: Question shown to the user on the console. Should have
            a {} to insert a list of enumerated options.
        options: Possible values user should choose from.
        console: Object to use for user I/O.
        default: Default value if user provides no value. (Presses enter) If
            default is None the user is forced to choose a value in the
            option list.

    Typical usage:
        # User can press enter if user doesn't want anything.
        choice = _multiple_choice_prompt('Choose an option:\n{}\n',
                                         ['Chicken', 'Salad', 'Burger'],
                                         console,
                                         default=None)

    Returns:
        The choice made by the user. If default is none, it is guaranteed to be
        an index in the options, else it can possible be the default value.
    """
    assert '{}' in question
    assert len(options) > 0

    options_formatted = [
        '{}. {}'.format(str(i), opt) for i, opt in enumerate(options, 1)
    ]
    options = '\n'.join(options_formatted)

    while True:
        answer = console.ask(question.format(options))

        if not answer and default:
            return default

        try:
            _multiple_choice_validate(answer, len(options))
            break
        except ValueError as e:
            console.error(e)

    return int(answer) - 1
def check_and_handle_requirements(console: io.IO, backend: str) -> bool:
    """Checks that requirements are installed. Attempts to install missing ones.

    Args:
        console: Handles the input/output with the user.
        backend: Defines which platform on determines what requirements are
            needed. Options are 'gke' and 'gae'.

    Returns:
        True if all requirements have been satisfied, False otherwise.
    """
    for req in _REQUIREMENTS[backend]:
        try:
            req.check_and_handle(console)
        except MissingRequirementError as e:
            console.error(e.how_to_install_message)
            return False
    return True
Exemple #13
0
def _execute(args: List[str], console: io.IO):
    """Wraps the execution of a management command with Cloud SQL Proxy.

    Args:
        args: The full arguments of a Django management command. For example,
            ['migrate'], ['showmigrations']
        console: Handles the input/output with the user.
    """
    current_dir = os.path.abspath(os.path.expanduser('.'))

    if not config.Configuration.exist(current_dir):
        console.error(
            ('The Django project in "{}" is not deployed yet. Please '
             'deploy it to be able to use the management '
             'commands').format(current_dir))
        return

    # Assume the project was previously deployed with Django Cloud Deply,
    # then we should be able to get the following parameters
    config_obj = config.Configuration(current_dir)
    django_settings_path = config_obj.get('django_settings_path')
    root, _ = os.path.splitext(django_settings_path)
    settings_module = '.'.join(root.split('/')[:-1] + ['cloud_settings'])
    instance_name = config_obj.get('database_instance_name')
    project_id = config_obj.get('project_id')

    creds = auth.AuthClient.get_default_credentials()
    if not creds:
        creds = auth.AuthClient.create_default_credentials()
    database_client = database.DatabaseClient.from_credentials(creds)
    cloud_sql_proxy_port = portpicker.pick_unused_port()
    os.environ['CLOUD_SQL_PROXY_PORT'] = str(cloud_sql_proxy_port)
    with database_client.with_cloud_sql_proxy(project_id=project_id,
                                              instance_name=instance_name,
                                              port=cloud_sql_proxy_port):
        arguments = [
            'django-admin', *args, '='.join(['--pythonpath', current_dir]),
            '='.join(['--settings', settings_module])
        ]
        try:
            subprocess.check_call(arguments)
        except subprocess.CalledProcessError:
            # Only show error messages from Django, ignore traceback from DCD
            pass
    def prompt(cls,
               console: io.IO,
               step_prompt: str,
               arguments: Dict[str, Any],
               credentials: Optional[credentials.Credentials] = None) -> str:
        """Prompt the user to enter a file system path for their project.

        Args:
            console: Object to use for user I/O.
            step_prompt: A prefix showing the current step number e.g. "[1/3]".
            arguments: The arguments that have already been collected from the
                user e.g. {"project_id", "project-123"}
            credentials: The OAuth2 Credentials object to use for api calls
                during prompt.

        Returns:
            The value entered by the user.
        """
        home_dir = os.path.expanduser('~')
        # TODO: Remove filesystem-unsafe characters. Implement a validation
        # method that checks for these.
        default_directory = os.path.join(
            home_dir,
            arguments.get('project_name',
                          'django-project').lower().replace(' ', '-'))

        while True:
            console.tell(
                ('{} Enter a new directory path to store project source, '
                 'or leave blank to use').format(step_prompt))
            directory = console.ask('[{}]: '.format(default_directory))
            if not directory.strip():
                directory = default_directory
            try:
                cls.validate(directory)
            except ValueError as e:
                console.error(e)
                continue

            if os.path.exists(directory):
                if not cls._prompt_replace(console, directory):
                    continue
            return directory
    def prompt(cls,
               console: io.IO,
               step_prompt: str,
               arguments: Dict[str, Any],
               credentials: Optional[credentials.Credentials] = None) -> str:
        """Prompt the user to a Google Cloud Platform project id.

        If the user supplies the project_id as a flag we want to validate that
        it exists. We tell the user to supply a new one if it does not.

        Args:
            console: Object to use for user I/O.
            step_prompt: A prefix showing the current step number e.g. "[1/3]".
            arguments: The arguments that have already been collected from the
                user e.g. {"project_id", "project-123"}
            credentials: The OAuth2 Credentials object to use for api calls
                during prompt.

        Returns:
            The value entered by the user.
        """
        project_id = arguments.get('project_id', None)
        valid_project_id = False
        while not valid_project_id:
            if not project_id:
                console.tell(
                    ('{} Enter the existing Google Cloud Platform Project ID '
                     'to use.').format(step_prompt))
                project_id = console.ask('Project ID: ')
            try:
                cls.validate(project_id, credentials)
                if (arguments.get(
                        'project_creation_mode',
                        False) == workflow.ProjectCreationMode.MUST_EXIST):
                    console.tell(('{} Google Cloud Platform Project ID {}'
                                  ' is valid').format(step_prompt, project_id))
                valid_project_id = True
            except ValueError as e:
                console.error(e)
                project_id = None
                continue
        return project_id
    def prompt(cls,
               console: io.IO,
               step_prompt: str,
               arguments: Dict[str, Any],
               credentials: Optional[credentials.Credentials] = None) -> str:
        """Prompt the user to enter a file system path for their project.

        Args:
            console: Object to use for user I/O.
            step_prompt: A prefix showing the current step number e.g. "[1/3]".
            arguments: The arguments that have already been collected from the
                user e.g. {"project_id", "project-123"}
            credentials: The OAuth2 Credentials object to use for api calls
                during prompt.

        Returns:
            The value entered by the user.
        """
        home_dir = os.path.expanduser('~')
        default_directory = os.path.join(
            home_dir,
            arguments.get('project_name',
                          'django-project').lower().replace(' ', '-'))

        while True:
            console.tell(
                ('{} Enter the directory of the Django project you want to '
                 'update:'.format(step_prompt)))
            directory = console.ask('[{}]: '.format(default_directory))
            if not directory.strip():
                directory = default_directory
            directory = os.path.abspath(os.path.expanduser(directory))

            try:
                cls.validate(directory)
            except ValueError as e:
                console.error(e)
                continue

            return directory
def _password_prompt(question: str, console: io.IO) -> str:
    """Used to prompt user to choose a password field.

    Args:
        console: Object to use for user I/O.
        question: Question shown to the user on the console.

    Returns:
        The password provided by the user.
    """
    console.tell(question)
    while True:
        password1 = console.getpass('Password: '******'Password (again): ')
        if password1 != password2:
            console.error('Passwords do not match, please try again')
            continue
        return password1
Exemple #18
0
def main(args: argparse.Namespace, console: io.IO = io.ConsoleIO()):

    if not tool_requirements.check_and_handle_requirements(
            console, args.backend):
        return

    prompt_order = [
        'credentials',
        'project_id',
        'project_name',
        'billing_account_name',
        'database_password',
        'django_directory_path',
        'django_project_name',
        'django_app_name',
        'django_superuser_login',
        'django_superuser_password',
        'django_superuser_email',
    ]

    required_parameters_to_prompt = {
        'credentials': prompt.CredentialsPrompt,
        'project_id': prompt.ProjectIdPrompt,
        'project_name': prompt.GoogleCloudProjectNamePrompt,
        'billing_account_name': prompt.BillingPrompt,
        'database_password': prompt.PostgresPasswordPrompt,
        'django_directory_path': prompt.DjangoFilesystemPath,
        'django_project_name': prompt.DjangoProjectNamePrompt,
        'django_app_name': prompt.DjangoAppNamePrompt,
        'django_superuser_login': prompt.DjangoSuperuserLoginPrompt,
        'django_superuser_password': prompt.DjangoSuperuserPasswordPrompt,
        'django_superuser_email': prompt.DjangoSuperuserEmailPrompt
    }

    # Parameters that were *not* provided as command flags.
    remaining_parameters_to_prompt = {}

    actual_parameters = {
        'project_creation_mode': workflow.ProjectCreationMode.CREATE,
        'bucket_name': getattr(args, 'bucket_name', None),
        'service_accounts': getattr(args, 'service_accounts', None),
        'services': getattr(args, 'services', None)
    }

    for parameter_name, prompter in required_parameters_to_prompt.items():
        value = getattr(args, parameter_name, None)
        if value is not None:
            try:
                prompter.validate(value)
            except ValueError as e:
                print(e, file=sys.stderr)
                sys.exit(1)
            actual_parameters[parameter_name] = value
        else:
            remaining_parameters_to_prompt[parameter_name] = prompter

    if args.use_existing_project:
        actual_parameters['project_creation_mode'] = (
            workflow.ProjectCreationMode.MUST_EXIST)
        remaining_parameters_to_prompt['project_name'] = (
            prompt.GoogleCloudProjectNamePrompt)
        remaining_parameters_to_prompt['project_id'] = (
            prompt.ExistingProjectIdPrompt)

    if remaining_parameters_to_prompt:
        num_steps = len(remaining_parameters_to_prompt)
        console.tell(
            '<b>{} steps to setup your new project</b>'.format(num_steps))
        console.tell()
        parameter_and_prompt = sorted(remaining_parameters_to_prompt.items(),
                                      key=lambda i: prompt_order.index(i[0]))

        for step, (parameter_name,
                   prompter) in enumerate(parameter_and_prompt):
            step = '<b>[{}/{}]</b>'.format(step + 1, num_steps)
            actual_parameters[parameter_name] = prompter.prompt(
                console, step, actual_parameters,
                actual_parameters.get('credentials', None))

    workflow_manager = workflow.WorkflowManager(
        actual_parameters['credentials'], args.backend)

    try:
        admin_url = workflow_manager.create_and_deploy_new_project(
            project_name=actual_parameters['project_name'],
            project_id=actual_parameters['project_id'],
            project_creation_mode=actual_parameters['project_creation_mode'],
            billing_account_name=actual_parameters['billing_account_name'],
            django_project_name=actual_parameters['django_project_name'],
            django_app_name=actual_parameters['django_app_name'],
            django_superuser_name=actual_parameters['django_superuser_login'],
            django_superuser_email=actual_parameters['django_superuser_email'],
            django_superuser_password=actual_parameters[
                'django_superuser_password'],
            django_directory_path=actual_parameters['django_directory_path'],
            database_password=actual_parameters['database_password'],
            required_services=actual_parameters['services'],
            required_service_accounts=actual_parameters['service_accounts'],
            cloud_storage_bucket_name=actual_parameters['bucket_name'],
            backend=args.backend)
        return admin_url
    except workflow.ProjectExistsError:
        console.error('A project with id "{}" already exists'.format(
            actual_parameters['project_id']))