def collect_static_content(self): """Collect static content of the provided Django project. This function should be called only after django.setup() is called. Raises: StaticContentServeError: If Django environment is not correctly setup. """ if not settings.configured: raise StaticContentServeError( 'Django environment is not setup correctly or the settings ' 'module is invalid. We cannot collect static files.') cwd = os.getcwd() # Change directory to the Django project directory. If we do not do # this, static content will be collected in your current directory. # This is not expected. os.chdir(settings.BASE_DIR) try: management.call_command( 'collectstatic', verbosity=0, interactive=False) except Exception as e: raise crash_handling.UserError( 'Not able to collect static files.') from e finally: os.chdir(cwd)
def setup_django_environment(self, project_dir: str, project_name: str, database_user: str, database_password: str, cloud_sql_proxy_port: Optional[int] = None): """Setup Django environment. This makes Django command calls afterwards affect the newly generated project. Args: project_dir: Absolute directory path to put your Django project. project_name: Name of your Django project. database_user: The name of the database user. By default it is "postgres". This is required for Django app to access database. database_password: The database password to set. cloud_sql_proxy_port: The port being forwarded by cloud sql proxy. """ os.environ['DATABASE_USER'] = database_user os.environ['DATABASE_PASSWORD'] = database_password if cloud_sql_proxy_port: os.environ['CLOUD_SQL_PROXY_PORT'] = str(cloud_sql_proxy_port) sys.path.append(project_dir) os.environ['DJANGO_SETTINGS_MODULE'] = '{}.cloud_settings'.format( project_name) try: django.setup() except Exception as e: raise crash_handling.UserError( 'Not able to import Django settings file.') from e
def test_ignore_user_error(self, mock_open_browser, *unused_mocks): test_io = io.TestIO() test_io.answers.append('Y') # Agree to open browser to create an issue error = KeyError('Test KeyError') with self.assertRaises(KeyError): try: raise crash_handling.UserError() from error except Exception as e: crash_handling.handle_crash(e, 'command_fake', test_io) mock_open_browser.assert_not_called()
def migrate_database(self, project_dir: str, project_id: str, instance_name: str, cloud_sql_proxy_path: str = 'cloud_sql_proxy', region: str = 'us-west1', port: Optional[int] = 5432): """Migrate to Cloud SQL database. This function should be called after we do the following: 1. Generated the Django project source files. 2. Setup Django environment so that it is using configuration files of the newly generated project. 3. Created the Cloud SQL instance and database user. Args: project_dir: Absolute path of the Django project directory. project_id: GCP project id. instance_name: Name of the Cloud SQL instance where the database you want to migrate is in. cloud_sql_proxy_path: The command to run your cloud sql proxy. region: Where the Cloud SQL instance is in. port: The port being forwarded by cloud sql proxy. """ with self.with_cloud_sql_proxy(project_id, instance_name, cloud_sql_proxy_path, region, port): try: # The environment variable must exist. This is the prerequisite # of calling this function settings_module = os.environ['DJANGO_SETTINGS_MODULE'] # "makemigrations" will generate migration files based on # definitions in models.py. makemigrations_args = [ 'django-admin', 'makemigrations', '='.join(['--pythonpath', project_dir]), '='.join(['--settings', settings_module]) ] subprocess.check_call(makemigrations_args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # "migrate" will modify cloud sql database. migrate_args = [ 'django-admin', 'migrate', '='.join(['--pythonpath', project_dir]), '='.join(['--settings', settings_module]) ] subprocess.check_call(migrate_args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except Exception as e: raise crash_handling.UserError( 'Not able to migrate database.') from e
def enable_service_sync(self, project_id: str, service: str): """Enable a service for the given project. Args: project_id: GCP project id. service: Name of the service to be enabled. For example, "drive.googleapis.com" Raises: EnableServiceError: When it fails to enable a service. """ service_name = '/'.join(['projects', project_id, 'services', service]) request = self._service_usage_service.services().enable( name=service_name) try: response = request.execute(num_retries=5) except errors.HttpError as e: if e.resp.status == 400: tos = 'terms of service' if tos in str(e): url = 'https://console.developers.google.com/terms/cloud' msg = ('Please accept the terms of service in the Google' 'Cloud Console @ {}'.format(url)) raise crash_handling.UserError(msg) # For all errors that are not related to ToS we want to raise raise e # When the api call succeed, the response is a Service object. # See # https://cloud.google.com/service-usage/docs/reference/rest/v1/services/get if 'name' not in response: raise EnableServiceError( 'unexpected response enabling service "{}": {}'.format( service_name, response)) while True: request = self._service_usage_service.services().get( name=service_name) response = request.execute(num_retries=5) # Response format: # https://cloud.google.com/service-usage/docs/reference/rest/v1/Service if response['state'] == 'ENABLED': return elif response['state'] == 'DISABLED': time.sleep(2) continue else: # In 'STATE_UNSPECIFIED' state. raise EnableServiceError( 'unexpected service status after enabling: {!r}: [{!r}]'. format(response['status'], response))
def create_super_user(self, superuser_name: str, superuser_email: str, superuser_password: str, project_id: str, instance_name: str, cloud_sql_proxy_path: str = 'cloud_sql_proxy', region: str = 'us-west1', port: Optional[int] = 5432): """Create a super user in the cloud sql database. This function should be called after we did the following: 1. Generated the Django project source files. 2. Setup Django environment so that it is using configuration files of the newly generated project. 3. Created the Cloud SQL instance and database user. 4. Migrated database. Otherwise the schema for superuser does not exist. Args: superuser_name: Name of the super user you want to create. superuser_email: Email of the super user you want to create. superuser_password: Password of the super user you want to create. project_id: GCP project id. instance_name: The Cloud SQL instance name in which you want to create the super user. cloud_sql_proxy_path: The command to run your cloud sql proxy. region: Where the Cloud SQL instance is in. port: The port being forwarded by cloud sql proxy. """ with self.with_cloud_sql_proxy(project_id, instance_name, cloud_sql_proxy_path, region, port): # This can only be imported after django.setup() is called try: from django.contrib.auth.models import User # Check whether the super user we want to create exist or not # If a superuser with the same name already exist, we will skip # creation users = User.objects.filter(username=superuser_name) for user in users: if user.is_superuser: return User.objects.create_superuser( username=superuser_name, email=superuser_email, password=superuser_password) except Exception as e: raise crash_handling.UserError( 'Not able to create super user.') from e
def migrate_database(self, project_id: str, instance_name: str, cloud_sql_proxy_path: str = 'cloud_sql_proxy', region: str = 'us-west1', port: Optional[int] = 5432): """Migrate to Cloud SQL database. This function should be called after we do the following: 1. Generated the Django project source files. 2. Setup Django environment so that it is using configuration files of the newly generated project. 3. Created the Cloud SQL instance and database user. Args: project_id: GCP project id. instance_name: Name of the Cloud SQL instance where the database you want to migrate is in. cloud_sql_proxy_path: The command to run your cloud sql proxy. region: Where the Cloud SQL instance is in. port: The port being forwarded by cloud sql proxy. """ with self.with_cloud_sql_proxy(project_id, instance_name, cloud_sql_proxy_path, region, port): try: # "makemigrations" will generate migration files based on # definitions in models.py. management.call_command('makemigrations', verbosity=0, interactive=False) # "migrate" will modify cloud sql database. management.call_command('migrate', verbosity=0, interactive=False) except Exception as e: raise crash_handling.UserError( 'Not able to migrate database.') from e