mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-25 02:00:19 -06:00
224 lines
9.3 KiB
Python
224 lines
9.3 KiB
Python
# ##########################################################################
|
|
# #
|
|
# # pgAdmin 4 - PostgreSQL Tools
|
|
# #
|
|
# # Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
|
# # This software is released under the PostgreSQL Licence
|
|
# #
|
|
# ##########################################################################
|
|
import json
|
|
import os
|
|
import time
|
|
|
|
from utils.io import debug, error, output
|
|
from utils.misc import get_my_ip, get_random_id
|
|
from providers._abstract import AbsProvider
|
|
|
|
from googleapiclient import discovery
|
|
from googleapiclient.errors import HttpError
|
|
from google.auth.transport.requests import Request
|
|
from google.oauth2.credentials import Credentials
|
|
|
|
|
|
class GoogleProvider(AbsProvider):
|
|
def __init__(self):
|
|
self._credentials_json = None
|
|
self._credentials = None
|
|
self._cloud_resource_manager_api_version = 'v1'
|
|
self._sqladmin_api_version = 'v1'
|
|
self._compute_api_version = 'v1'
|
|
self._scopes = ['https://www.googleapis.com/auth/cloud-platform',
|
|
'https://www.googleapis.com/auth/sqlservice.admin']
|
|
self._database_password = None
|
|
|
|
# Get credentials from environment
|
|
if 'GOOGLE_CREDENTIALS' in os.environ:
|
|
self._credentials_json = \
|
|
json.loads(os.environ['GOOGLE_CREDENTIALS'])
|
|
|
|
if 'GOOGLE_DATABASE_PASSWORD' in os.environ:
|
|
self._database_password = os.environ['GOOGLE_DATABASE_PASSWORD']
|
|
|
|
def init_args(self, parsers):
|
|
""" Create the command line parser for this provider """
|
|
self.parser = parsers.add_parser('google',
|
|
help='Google Cloud PostgreSQL',
|
|
epilog='Credentials are read from '
|
|
'the environment.')
|
|
|
|
# Create the command sub-parser
|
|
parsers = self.parser.add_subparsers(help='Google commands',
|
|
dest='command')
|
|
|
|
# Create the create instance command parser
|
|
parser_create_instance = parsers.add_parser('create-instance',
|
|
help='create a new '
|
|
'instance')
|
|
|
|
parser_create_instance.add_argument('--region', help='name of the '
|
|
'Google region')
|
|
|
|
parser_create_instance.add_argument('--project', required=True,
|
|
help='name of the project in which'
|
|
' instance to be created')
|
|
|
|
parser_create_instance.add_argument('--name', required=True,
|
|
help='name of the instance')
|
|
|
|
parser_create_instance.add_argument('--db-password', required=False,
|
|
help='password for the database')
|
|
|
|
parser_create_instance.add_argument('--db-version', required=False,
|
|
default='POSTGRES_14',
|
|
help='version of PostgreSQL to '
|
|
'deploy (default: POSTGRES_14)')
|
|
|
|
parser_create_instance.add_argument('--instance-type', required=True,
|
|
help='machine type for the '
|
|
'instance nodes, e.g. '
|
|
'db-f1-micro')
|
|
|
|
parser_create_instance.add_argument('--storage-size', type=int,
|
|
required=True,
|
|
help='storage size in GB')
|
|
|
|
parser_create_instance.add_argument('--storage-type', default='PD_SSD',
|
|
help='storage type for the data '
|
|
'database (default: SSD)')
|
|
|
|
parser_create_instance.add_argument('--high-availability',
|
|
default=False)
|
|
|
|
parser_create_instance.add_argument('--public-ip', default='127.0.0.1',
|
|
help='Public IP '
|
|
'(default: 127.0.0.1)')
|
|
|
|
parser_create_instance.add_argument('--availability-zone',
|
|
help='name of the availability '
|
|
'zone')
|
|
|
|
parser_create_instance.add_argument('--secondary-availability-zone',
|
|
help='name of the secondary '
|
|
'availability zone')
|
|
|
|
##########################################################################
|
|
# Google Helper functions
|
|
##########################################################################
|
|
def _get_credentials(self, scopes):
|
|
self._credentials = Credentials.from_authorized_user_info(
|
|
self._credentials_json, scopes)
|
|
if not self._credentials or not self._credentials.valid:
|
|
if self._credentials and self._credentials.expired and \
|
|
self._credentials.refresh_token and \
|
|
self._credentials.has_scopes(scopes):
|
|
self._credentials.refresh(Request())
|
|
return self._credentials
|
|
else:
|
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
flow = InstalledAppFlow.from_client_config(self._client_config,
|
|
scopes)
|
|
self._credentials = flow.run_local_server()
|
|
return self._credentials
|
|
|
|
@staticmethod
|
|
def get_authorized_network_list(ip):
|
|
authorized_networks = []
|
|
ip = ip.split(',')
|
|
for i in ip:
|
|
authorized_networks.append({
|
|
'value': i,
|
|
'name': 'pgcloud client {}'.format(i),
|
|
'kind': 'sql#aclEntry'
|
|
})
|
|
return authorized_networks
|
|
|
|
def _create_google_postgresql_instance(self, args):
|
|
credentials = self._get_credentials(self._scopes)
|
|
service = discovery.build('sqladmin', 'v1beta4',
|
|
credentials=credentials)
|
|
high_availability = \
|
|
'REGIONAL' if eval(args.high_availability) else 'ZONAL'
|
|
|
|
db_password = self._database_password \
|
|
if self._database_password is not None else args.db_password
|
|
|
|
ip = args.public_ip if args.public_ip else '{}/32'.format(get_my_ip())
|
|
authorized_networks = self.get_authorized_network_list(ip)
|
|
|
|
database_instance_body = {
|
|
'databaseVersion': args.db_version,
|
|
'instanceType': 'CLOUD_SQL_INSTANCE',
|
|
'project': args.project,
|
|
'name': args.name,
|
|
'region': args.region,
|
|
'gceZone': args.availability_zone,
|
|
'secondaryGceZone': args.secondary_availability_zone,
|
|
"rootPassword": db_password,
|
|
'settings': {
|
|
'tier': args.instance_type,
|
|
'availabilityType': high_availability,
|
|
'dataDiskType': args.storage_type,
|
|
'dataDiskSizeGb': args.storage_size,
|
|
'ipConfiguration': {
|
|
"authorizedNetworks": authorized_networks,
|
|
'ipv4Enabled': True
|
|
},
|
|
}
|
|
}
|
|
operation = None
|
|
try:
|
|
debug('Creating Google instance: {}...'.format(args.name))
|
|
req = service.instances().insert(project=args.project,
|
|
body=database_instance_body)
|
|
res = req.execute()
|
|
operation = res['name']
|
|
|
|
except HttpError as err:
|
|
if err.status_code == 409:
|
|
error('Google SQL instance {} already exists.'.
|
|
format(args.name))
|
|
else:
|
|
error(str(err))
|
|
except Exception as e:
|
|
error(str(e))
|
|
|
|
# Wait for completion
|
|
instance_provisioning = True
|
|
log_msg = 10000
|
|
while instance_provisioning:
|
|
req = service.operations().get(project=args.project,
|
|
operation=operation)
|
|
res = req.execute()
|
|
status = res['status']
|
|
if status != 'PENDING' and status != 'RUNNING':
|
|
instance_provisioning = False
|
|
else:
|
|
time.sleep(5)
|
|
log_msg -= 1
|
|
|
|
if log_msg % 15 == 0:
|
|
debug('Creating Google instance: {}...'.format(args.name))
|
|
|
|
req = service.instances().get(project=args.project, instance=args.name)
|
|
res = req.execute()
|
|
return res
|
|
##########################################################################
|
|
# User commands
|
|
##########################################################################
|
|
|
|
def cmd_create_instance(self, args):
|
|
""" Create an Google instance"""
|
|
instance_data = self._create_google_postgresql_instance(args)
|
|
data = {'instance': {
|
|
'Hostname': instance_data['ipAddresses'][0]['ipAddress'],
|
|
'Port': 5432,
|
|
'Database': 'postgres',
|
|
'Username': 'postgres',
|
|
}}
|
|
output(data)
|
|
|
|
|
|
def load():
|
|
""" Loads the current provider """
|
|
return GoogleProvider()
|