Ejemplo n.º 1
0
def main(args: argparse.Namespace, console: io.IO = io.ConsoleIO()):

    actual_parameters = {
        '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.UPDATE, console,
                                           prompt_args)

    # This got moved from the start because we wanted to save the user from
    # giving us another bit of information that we can automatically retrieve
    # It will be rare if they are updating, that they do not have the
    # requirements.
    django_dir = actual_parameters['django_directory_path_update']
    config_obj = config.Configuration(django_dir)
    backend = config_obj.get('backend')
    if not backend:
        raise InvalidConfigError(
            'Configuration file in [{}] does not contain enough '
            'information to update a Django project.'.format(django_dir))

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

    workflow_manager = workflow.WorkflowManager(
        actual_parameters['credentials'])
    workflow_manager.update_project(
        django_directory_path=actual_parameters[
            'django_directory_path_update'],
        database_password=actual_parameters['database_password'],
        cluster_name=actual_parameters['cluster_name'],
        database_instance_name=actual_parameters['database_instance_name'])
Ejemplo n.º 2
0
    def test_get_after_save(self):
        configuration = config.Configuration(
            django_directory_path=self._project_dir)
        configuration.set('var1', 'value1')
        configuration.set('var2', 2)
        configuration.set('list1', ['a', 'b'])
        configuration.set('dict1', {'a': 'b'})
        configuration.save()

        configuration = config.Configuration(
            django_directory_path=self._project_dir)

        self.assertEqual(configuration.get('var1'), 'value1')
        self.assertEqual(configuration.get('var2'), 2)
        self.assertEqual(configuration.get('list1'), ['a', 'b'])
        self.assertEqual(configuration.get('dict1'), {'a': 'b'})
        self.assertIsNone(configuration.get('var3'))
Ejemplo n.º 3
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
Ejemplo n.º 4
0
 def _save_config(django_directory_path: str, attributes: Dict[str, Any]):
     config_obj = config.Configuration(django_directory_path)
     for key, value in attributes.items():
         config_obj.set(key, value)
     config_obj.save()
Ejemplo n.º 5
0
    def update_project(self,
                       django_directory_path: str,
                       database_password: str,
                       cluster_name: Optional[str] = None,
                       database_instance_name: Optional[str] = None,
                       cloud_sql_proxy_path: str = 'cloud_sql_proxy',
                       region: str = 'us-west1',
                       open_browser: bool = True):
        """Workflow of updating a deployed Django app.

        Args:
            django_directory_path: The location where the generated Django
                project code should be stored.
            database_password: The password for the default database user.
            cluster_name: Name of the cluster to use when deploying on GKE.
            database_instance_name: Name of the Cloud SQL instance to use for
                deployment
            cloud_sql_proxy_path: The command to run your cloud sql proxy.
            region: Where the service is hosted.
            open_browser: Whether we open the browser to show the deployed app
                at the end.

        Raises:
            InvalidConfigError: When failed to read required information in the
                configuration file.
        """

        config_obj = config.Configuration(django_directory_path)
        project_id = config_obj.get('project_id')
        django_project_name = config_obj.get('django_project_name')
        backend = config_obj.get('backend')
        django_settings_path = config_obj.get('django_settings_path')
        if django_settings_path:
            django_settings_path = os.path.join(django_directory_path,
                                                django_settings_path)
        else:
            django_settings_path = os.path.join(django_directory_path,
                                                django_project_name,
                                                'settings.py')
        cloud_sql_proxy_port = portpicker.pick_unused_port()
        if not project_id or not backend or not django_project_name:
            raise InvalidConfigError(
                'Configuration file in [{}] does not contain enough '
                'information to update a Django project.'.format(
                    django_directory_path))

        # A bunch of variables necessary for deployment we hardcode for user.
        database_username = '******'
        cloud_storage_bucket_name = project_id
        sanitized_django_project_name = self._sanitize_name(
            django_project_name)
        cluster_name = cluster_name or sanitized_django_project_name
        database_instance_name = (database_instance_name or
                                  sanitized_django_project_name + '-instance')
        image_name = '/'.join(
            ['gcr.io', project_id, sanitized_django_project_name])
        self._source_generator.setup_django_environment(
            django_directory_path, database_username, database_password,
            django_settings_path, cloud_sql_proxy_port)

        static_content_dir = settings.STATIC_ROOT
        with self._console_io.progressbar(
                120,
                '[1/{}]: Database Migration'.format(self._TOTAL_UPDATE_STEPS)):
            self._database_workflow.migrate_database(
                project_dir=django_directory_path,
                project_id=project_id,
                instance_name=database_instance_name,
                cloud_sql_proxy_path=cloud_sql_proxy_path,
                region=region,
                port=cloud_sql_proxy_port)

        with self._console_io.progressbar(
                120, '[2/{}]: Static Content Update'.format(
                    self._TOTAL_UPDATE_STEPS)):
            self._static_content_workflow.update_static_content(
                cloud_storage_bucket_name, static_content_dir)

        with self._console_io.progressbar(
                180,
                '[3/{}]: Update Deployment'.format(self._TOTAL_UPDATE_STEPS)):
            if backend == 'gke':
                app_url = self.deploy_workflow.update_gke_app(
                    project_id, cluster_name, django_directory_path,
                    django_project_name, image_name)
            else:
                app_url = self.deploy_workflow.deploy_gae_app(
                    project_id, django_directory_path, is_new=False)
        self._console_io.tell('Your app is running at {}.'.format(app_url))
        if open_browser:
            webbrowser.open_url(app_url)
Ejemplo n.º 6
0
    def update_project(self,
                       django_directory_path: str,
                       database_password: str,
                       cloud_sql_proxy_path: str = 'cloud_sql_proxy',
                       region: str = 'us-west1',
                       open_browser: bool = True):
        """Workflow of updating a deployed Django app on GKE.

        Args:
            django_directory_path: The location where the generated Django
                project code should be stored.
            database_password: The password for the default database user.
            cloud_sql_proxy_path: The command to run your cloud sql proxy.
            region: Where the service is hosted.
            open_browser: Whether we open the browser to show the deployed app
                at the end.

        Raises:
            InvalidConfigError: When failed to read required information in the
                configuration file.
        """

        config_obj = config.Configuration(django_directory_path)
        project_id = config_obj.get('project_id')
        django_project_name = config_obj.get('django_project_name')
        cloud_sql_proxy_port = portpicker.pick_unused_port()
        if not project_id or not django_project_name:
            raise InvalidConfigError(
                'Configuration file in [{}] does not contain enough '
                'information to update a Django project.'.format(
                    django_directory_path))

        # A bunch of variables necessary for deployment we hardcode for user.
        database_username = '******'
        cloud_storage_bucket_name = project_id
        sanitized_django_project_name = self._sanitize_name(
            django_project_name)
        cluster_name = sanitized_django_project_name
        database_instance_name = sanitized_django_project_name + '-instance'
        image_name = '/'.join(
            ['gcr.io', project_id, sanitized_django_project_name])
        static_content_dir = os.path.join(django_directory_path, 'static')

        self._source_generator.setup_django_environment(
            django_directory_path, django_project_name, database_username,
            database_password, cloud_sql_proxy_port)
        print(
            self._generate_section_header(1, 'Database Migration',
                                          self._TOTAL_UPDATE_STEPS))
        self._database_workflow.migrate_database(
            project_id=project_id,
            instance_name=database_instance_name,
            cloud_sql_proxy_path=cloud_sql_proxy_path,
            region=region,
            port=cloud_sql_proxy_port)

        print(
            self._generate_section_header(2, 'Static Content Update',
                                          self._TOTAL_UPDATE_STEPS))
        self._static_content_workflow.update_static_content(
            cloud_storage_bucket_name, static_content_dir)

        print(
            self._generate_section_header(3, 'Update Deployment',
                                          self._TOTAL_UPDATE_STEPS))
        app_url = self._deploygke_workflow.update_app_sync(
            project_id, cluster_name, django_directory_path,
            django_project_name, image_name)
        print('Your app is running at {}.'.format(app_url))
        if open_browser:
            webbrowser.open(app_url)
Ejemplo n.º 7
0
 def test_header_in_file(self):
     configuration = config.Configuration(
         django_directory_path=self._project_dir)
     configuration.save()
     with open(configuration._config_path) as config_file:
         self.assertIn(configuration._HEADER, config_file.read())
Ejemplo n.º 8
0
 def test_create_configuration_file(self):
     configuration = config.Configuration(
         django_directory_path=self._project_dir)
     configuration.save()
     self.assertTrue(os.path.exists(configuration._config_path))