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'])
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'))
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 _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()
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)
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)
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())
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))