Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 42 additions & 19 deletions web/pgadmin/misc/cloud/azure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@
import os


from azure.mgmt.rdbms.postgresql_flexibleservers import \
PostgreSQLManagementClient
from azure.identity import AzureCliCredential, DeviceCodeCredential,\
AuthenticationRecord
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.subscription import SubscriptionClient
from azure.mgmt.rdbms.postgresql_flexibleservers.models import \
NameAvailabilityRequest

MODULE_NAME = 'azure'


Expand Down Expand Up @@ -265,6 +256,34 @@ def clear_session():
return make_json_response(success=1)


def _azure_sdk():
"""Defer heavy Azure SDK imports until required by user actions.
Repeat calls are cheap via sys.modules caching.
"""
from types import SimpleNamespace
from azure.identity import (
AzureCliCredential, DeviceCodeCredential, AuthenticationRecord
)
from azure.mgmt.rdbms.postgresql_flexibleservers import (
PostgreSQLManagementClient
)
from azure.mgmt.rdbms.postgresql_flexibleservers.models import (
NameAvailabilityRequest
)
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.subscription import SubscriptionClient

return SimpleNamespace(
AzureCliCredential=AzureCliCredential,
DeviceCodeCredential=DeviceCodeCredential,
AuthenticationRecord=AuthenticationRecord,
PostgreSQLManagementClient=PostgreSQLManagementClient,
ResourceManagementClient=ResourceManagementClient,
SubscriptionClient=SubscriptionClient,
NameAvailabilityRequest=NameAvailabilityRequest,
)


class Azure:
def __init__(self, interactive_browser_credential, tenant_id=None,
session_token=None, region='eastus'):
Expand Down Expand Up @@ -367,7 +386,8 @@ def _get_azure_credentials(self):

def _azure_cli_auth(self):
if self._cli_credentials is None:
self._cli_credentials = AzureCliCredential()
sdk = _azure_sdk()
self._cli_credentials = sdk.AzureCliCredential()
self.list_subscriptions()
return self._cli_credentials

Expand All @@ -380,8 +400,9 @@ def _azure_interactive_auth_prompt_callback(
session['azure']['azure_auth_code'] = azure_auth_code

def _azure_interactive_auth(self):
sdk = _azure_sdk()
if self.authentication_record_json is None:
_interactive_credential = DeviceCodeCredential(
_interactive_credential = sdk.DeviceCodeCredential(
tenant_id=self._tenant_id,
timeout=180,
prompt_callback=self._azure_interactive_auth_prompt_callback,
Expand All @@ -392,9 +413,9 @@ def _azure_interactive_auth(self):
_auth_record = _interactive_credential.authenticate()
self.authentication_record_json = _auth_record.serialize()
else:
deserialized_auth_record = AuthenticationRecord.deserialize(
deserialized_auth_record = sdk.AuthenticationRecord.deserialize(
self.authentication_record_json)
_interactive_credential = DeviceCodeCredential(
_interactive_credential = sdk.DeviceCodeCredential(
tenant_id=self._tenant_id,
timeout=180,
prompt_callback=self._azure_interactive_auth_prompt_callback,
Expand All @@ -411,15 +432,16 @@ def _get_azure_client(self, type):
return self._clients[type]

_, _credentials = self._get_azure_credentials()
sdk = _azure_sdk()

if type == 'postgresql':
client = PostgreSQLManagementClient(_credentials,
self.subscription_id)
client = sdk.PostgreSQLManagementClient(_credentials,
self.subscription_id)
elif type == 'resource':
client = ResourceManagementClient(_credentials,
self.subscription_id)
client = sdk.ResourceManagementClient(_credentials,
self.subscription_id)
elif type == 'subscription':
client = SubscriptionClient(_credentials)
client = sdk.SubscriptionClient(_credentials)

self._clients[type] = client
return self._clients[type]
Expand All @@ -430,8 +452,9 @@ def check_cluster_name_availability(self, cluster_name):
:param cluster_name
"""
postgresql_client = self._get_azure_client('postgresql')
sdk = _azure_sdk()
res = postgresql_client.check_name_availability.execute(
NameAvailabilityRequest(
sdk.NameAvailabilityRequest(
name=cluster_name,
type='Microsoft.DBforPostgreSQL/flexibleServers'))
res = res.__dict__
Expand Down
58 changes: 37 additions & 21 deletions web/pgadmin/misc/cloud/google/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@
# fall back to google-auth cleanly. See issue #10110.
sys.modules.setdefault('oauth2client', None)

from googleapiclient import discovery
from googleapiclient.errors import HttpError
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

MODULE_NAME = 'google'
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # Required for Oauth2

Expand Down Expand Up @@ -374,6 +369,17 @@ def clear_google_session():
session.pop('google')


def _google_sdk():
"""Defer heavy Google API client imports until required by user actions.
Repeat calls are cheap via sys.modules caching.
"""
from types import SimpleNamespace
from googleapiclient import discovery
from googleapiclient.errors import HttpError

return SimpleNamespace(discovery=discovery, HttpError=HttpError)


class Google:
def __init__(self, client_config=None):
# Google cloud sql api versions
Expand Down Expand Up @@ -468,6 +474,8 @@ def get_auth_url(self, host_url):
self._verification_error = None
try:
self._redirect_url = host_url + 'google/callback'
# Defer InstalledAppFlow (heavy import, user action only, cached)
from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_config(
client_config=self._client_config, scopes=self._scopes,
redirect_uri=self._redirect_url)
Expand All @@ -491,6 +499,8 @@ def callback(self, flask_request):
if session['state'] != flask_request.args.get('state', None):
self._verification_successful = False,
self._verification_error = 'Invalid state parameter'
# Defer InstalledAppFlow (heavy import, user action only, cached)
from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_config(
client_config=self._client_config, scopes=self._scopes,
redirect_uri=self._redirect_url)
Expand Down Expand Up @@ -532,6 +542,8 @@ def _get_credentials(self, scopes):
if self._credentials and self._credentials.expired and \
self._credentials.refresh_token and \
self._credentials.has_scopes(scopes):
# Defer Request (heavy import, user action only, cached)
from google.auth.transport.requests import Request
self._credentials.refresh(Request())
return self._credentials
return self._credentials
Expand All @@ -543,17 +555,18 @@ def get_projects(self):
"""
projects = []
error = None
sdk = _google_sdk()
credentials = self._get_credentials(self._scopes)
service = discovery.build('cloudresourcemanager',
self._cloud_resource_manager_api_version,
credentials=credentials)
service = sdk.discovery.build('cloudresourcemanager',
self._cloud_resource_manager_api_version,
credentials=credentials)
try:
req = service.projects().list()
res = req.execute()
for project in res.get('projects', []):
projects.append({'label': project['projectId'],
'value': project['projectId']})
except HttpError as e:
except sdk.HttpError as e:
error = e.reason
except Exception as e:
error = str(e)
Expand All @@ -566,10 +579,11 @@ def get_regions(self, project):
:return:
"""
self._project_id = project
sdk = _google_sdk()
credentials = self._get_credentials(self._scopes)
service = discovery.build('compute',
self._compute_api_version,
credentials=credentials)
service = sdk.discovery.build('compute',
self._compute_api_version,
credentials=credentials)
error = None
try:
req = service.regions().list(project=project)
Expand All @@ -582,7 +596,7 @@ def get_regions(self, project):
region_zones = list(
map(lambda region: region.split('/')[-1], region_zones))
self._availability_zones[region_name] = region_zones
except HttpError as e:
except sdk.HttpError as e:
error = e.reason
except Exception as e:
error = str(e)
Expand Down Expand Up @@ -611,10 +625,11 @@ def get_instance_types(self, project, region):
high_mem = []
instance_types = {}
error = None
sdk = _google_sdk()
credentials = self._get_credentials(self._scopes)
service = discovery.build('sqladmin',
self._sqladmin_api_version,
credentials=credentials)
service = sdk.discovery.build('sqladmin',
self._sqladmin_api_version,
credentials=credentials)
try:
req = service.tiers().list(project=project)
res = req.execute()
Expand Down Expand Up @@ -645,7 +660,7 @@ def get_instance_types(self, project, region):
instance_types = {'standard': standard_instances,
'highmem': high_mem,
'shared': shared_instances}
except HttpError as e:
except sdk.HttpError as e:
error = e.reason
except Exception as e:
error = str(e)
Expand All @@ -659,10 +674,11 @@ def get_database_versions(self):
pg_database_versions = []
database_versions = []
error = None
sdk = _google_sdk()
credentials = self._get_credentials(self._scopes)
service = discovery.build('sqladmin',
self._sqladmin_api_version,
credentials=credentials)
service = sdk.discovery.build('sqladmin',
self._sqladmin_api_version,
credentials=credentials)
try:
req = service.flags().list()
res = req.execute()
Expand All @@ -673,7 +689,7 @@ def get_database_versions(self):
label = (version.title().split('_')[0])[0:7] \
+ 'SQL ' + version.split('_')[1]
database_versions.append({'label': label, 'value': version})
except HttpError as e:
except sdk.HttpError as e:
error = e.reason
except Exception as e:
error = str(e)
Expand Down
6 changes: 4 additions & 2 deletions web/pgadmin/misc/cloud/rds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
# AWS RDS Cloud Deployment Implementation

import requests
import boto3
import json
from boto3.session import Session
from flask_babel import gettext
from flask import session, current_app, request
from pgadmin.user_login_check import pga_login_required
Expand Down Expand Up @@ -152,6 +150,8 @@ def get_regions():
"""GET Regions for AWS."""
try:
clear_aws_session()
# Defer boto3.session (heavy import, user action only, cached)
from boto3.session import Session
_session = Session()
res = _session.get_available_regions('rds')
regions = []
Expand Down Expand Up @@ -192,6 +192,8 @@ def _get_aws_client(self, type):
if type in self._clients:
return self._clients[type]

# Defer boto3 (heavy import, user action only, cached)
import boto3
session = boto3.Session(
aws_access_key_id=self._access_key,
aws_secret_access_key=self._secret_key,
Expand Down
Loading