Fixed following issues related to cloud deployment:
1) The Mumbai region issue has been resolved 2) Display name of regions has been modified appropriately 3) The password field has been validated the same way as AWS 4) Added support for a list of IP addresses in the public IP address range field.pull/74/head
parent
dd4849070a
commit
ffc1c6c3b7
|
@ -137,17 +137,18 @@ class RdsProvider(AbsProvider):
|
||||||
""" Create a new security group for the instance """
|
""" Create a new security group for the instance """
|
||||||
ec2 = self._get_aws_client('ec2', args)
|
ec2 = self._get_aws_client('ec2', args)
|
||||||
ip = args.public_ip if args.public_ip else get_my_ip()
|
ip = args.public_ip if args.public_ip else get_my_ip()
|
||||||
|
ip = ip.split(',')
|
||||||
|
|
||||||
# Deploy the security group
|
# Deploy the security group
|
||||||
try:
|
try:
|
||||||
name = 'pgacloud_{}_{}_{}'.format(args.name,
|
name = 'pgacloud_{}_{}_{}'.format(args.name,
|
||||||
ip.replace('.', '-'),
|
ip[0].replace('.', '-'),
|
||||||
get_random_id())
|
get_random_id())
|
||||||
debug(args, 'Creating security group: {}...'.format(name))
|
debug(args, 'Creating security group: {}...'.format(name))
|
||||||
output({'Creating': 'Creating security group: {}...'.format(name)})
|
output({'Creating': 'Creating security group: {}...'.format(name)})
|
||||||
response = ec2.create_security_group(
|
response = ec2.create_security_group(
|
||||||
Description='Inbound access for {} to RDS instance {}'.format(
|
Description='Inbound access for {} to RDS instance {}'.format(
|
||||||
ip, args.name),
|
ip[0], args.name),
|
||||||
GroupName=name
|
GroupName=name
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -158,9 +159,17 @@ class RdsProvider(AbsProvider):
|
||||||
def _add_ingress_rule(self, args, security_group):
|
def _add_ingress_rule(self, args, security_group):
|
||||||
""" Add a local -> PostgreSQL ingress rule to a security group """
|
""" Add a local -> PostgreSQL ingress rule to a security group """
|
||||||
ec2 = self._get_aws_client('ec2', args)
|
ec2 = self._get_aws_client('ec2', args)
|
||||||
ip = args.public_ip if args.public_ip else '{}/32'.format(get_my_ip())
|
ip = args.public_ip if args.public_ip else\
|
||||||
|
'{}/32'.format(get_my_ip())
|
||||||
port = args.db_port or 5432
|
port = args.db_port or 5432
|
||||||
|
IpRanges = []
|
||||||
|
|
||||||
|
ip = ip.split(',')
|
||||||
|
for i in ip:
|
||||||
|
IpRanges.append({
|
||||||
|
'CidrIp': i,
|
||||||
|
'Description': 'pgcloud client {}'.format(i)
|
||||||
|
})
|
||||||
try:
|
try:
|
||||||
output({'Adding': 'Adding ingress rule for: {}...'.format(ip)})
|
output({'Adding': 'Adding ingress rule for: {}...'.format(ip)})
|
||||||
debug(args,
|
debug(args,
|
||||||
|
@ -172,12 +181,7 @@ class RdsProvider(AbsProvider):
|
||||||
'FromPort': port,
|
'FromPort': port,
|
||||||
'ToPort': port,
|
'ToPort': port,
|
||||||
'IpProtocol': 'tcp',
|
'IpProtocol': 'tcp',
|
||||||
'IpRanges': [
|
'IpRanges': IpRanges
|
||||||
{
|
|
||||||
'CidrIp': ip,
|
|
||||||
'Description': 'pgcloud client {}'.format(ip)
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,6 +25,7 @@ from pgadmin.model import db, Server, Process
|
||||||
from pgadmin.misc.cloud.utils.rds import RDS, verify_aws_credentials,\
|
from pgadmin.misc.cloud.utils.rds import RDS, verify_aws_credentials,\
|
||||||
get_aws_db_instances, get_aws_db_versions, clear_aws_session,\
|
get_aws_db_instances, get_aws_db_versions, clear_aws_session,\
|
||||||
get_aws_regions
|
get_aws_regions
|
||||||
|
from pgadmin.misc.cloud.utils import get_my_ip
|
||||||
|
|
||||||
from config import root
|
from config import root
|
||||||
|
|
||||||
|
@ -81,7 +82,8 @@ class CloudModule(PgAdminModule):
|
||||||
'cloud.get_aws_db_instances',
|
'cloud.get_aws_db_instances',
|
||||||
'cloud.update_cloud_server',
|
'cloud.update_cloud_server',
|
||||||
'cloud.update_cloud_process',
|
'cloud.update_cloud_process',
|
||||||
'cloud.get_aws_regions']
|
'cloud.get_aws_regions',
|
||||||
|
'cloud.get_host_ip']
|
||||||
|
|
||||||
|
|
||||||
# Create blueprint for CloudModule class
|
# Create blueprint for CloudModule class
|
||||||
|
@ -108,6 +110,13 @@ def script():
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/get_host_ip/',
|
||||||
|
methods=['GET'], endpoint='get_host_ip')
|
||||||
|
@login_required
|
||||||
|
def get_host_ip():
|
||||||
|
return make_json_response(data=get_my_ip())
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/verify_credentials/',
|
@blueprint.route('/verify_credentials/',
|
||||||
methods=['POST'], endpoint='verify_credentials')
|
methods=['POST'], endpoint='verify_credentials')
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -285,7 +294,8 @@ def _create_server(data):
|
||||||
maintenance_db=data.get('db'),
|
maintenance_db=data.get('db'),
|
||||||
username=data.get('username'),
|
username=data.get('username'),
|
||||||
ssl_mode='prefer',
|
ssl_mode='prefer',
|
||||||
cloud_status=data.get('cloud_status')
|
cloud_status=data.get('cloud_status'),
|
||||||
|
connect_timeout=30,
|
||||||
)
|
)
|
||||||
|
|
||||||
db.session.add(server)
|
db.session.add(server)
|
||||||
|
|
|
@ -68,8 +68,22 @@ export default function CloudWizard({ nodeInfo, nodeData }) {
|
||||||
const [cloudDBCred, setCloudDBCred] = React.useState({});
|
const [cloudDBCred, setCloudDBCred] = React.useState({});
|
||||||
const [cloudDBDetails, setCloudDBDetails] = React.useState({});
|
const [cloudDBDetails, setCloudDBDetails] = React.useState({});
|
||||||
const [callRDSAPI, setCallRDSAPI] = React.useState({});
|
const [callRDSAPI, setCallRDSAPI] = React.useState({});
|
||||||
|
const [hostIP, setHostIP] = React.useState('127.0.0.1/32');
|
||||||
const axiosApi = getApiInstance();
|
const axiosApi = getApiInstance();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
let _url = url_for('cloud.get_host_ip') ;
|
||||||
|
axiosApi.get(_url)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.data) {
|
||||||
|
setHostIP(res.data.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
Alertify.error(gettext(`Error while getting the host ip: ${error.response.data.errormsg}`));
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (callRDSAPI == 2) {
|
if (callRDSAPI == 2) {
|
||||||
const cloudDBInstanceSchema = new CloudInstanceDetailsSchema({
|
const cloudDBInstanceSchema = new CloudInstanceDetailsSchema({
|
||||||
|
@ -111,6 +125,7 @@ export default function CloudWizard({ nodeInfo, nodeData }) {
|
||||||
server_groups: ()=>getNodeListById(pgAdmin.Browser.Nodes['server_group'], nodeInfo, nodeData),
|
server_groups: ()=>getNodeListById(pgAdmin.Browser.Nodes['server_group'], nodeInfo, nodeData),
|
||||||
}, {
|
}, {
|
||||||
gid: nodeInfo['server_group']._id,
|
gid: nodeInfo['server_group']._id,
|
||||||
|
hostIP: hostIP,
|
||||||
});
|
});
|
||||||
setCloudInstanceDetailsInstance(cloudDBInstanceSchema);
|
setCloudInstanceDetailsInstance(cloudDBInstanceSchema);
|
||||||
}
|
}
|
||||||
|
@ -151,7 +166,7 @@ export default function CloudWizard({ nodeInfo, nodeData }) {
|
||||||
return isError;
|
return isError;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateCloudStep2 = (cloudInstanceDetails) => {
|
const validateCloudStep2 = (cloudInstanceDetails, host_ip) => {
|
||||||
let isError = false;
|
let isError = false;
|
||||||
if (isEmptyString(cloudInstanceDetails.aws_name) ||
|
if (isEmptyString(cloudInstanceDetails.aws_name) ||
|
||||||
isEmptyString(cloudInstanceDetails.aws_db_version) || isEmptyString(cloudInstanceDetails.aws_instance_type) ||
|
isEmptyString(cloudInstanceDetails.aws_db_version) || isEmptyString(cloudInstanceDetails.aws_instance_type) ||
|
||||||
|
@ -162,7 +177,7 @@ export default function CloudWizard({ nodeInfo, nodeData }) {
|
||||||
if(cloudInstanceDetails.aws_storage_type == 'io1' && isEmptyString(cloudInstanceDetails.aws_storage_IOPS)) {
|
if(cloudInstanceDetails.aws_storage_type == 'io1' && isEmptyString(cloudInstanceDetails.aws_storage_IOPS)) {
|
||||||
isError = true;
|
isError = true;
|
||||||
}
|
}
|
||||||
if (isEmptyString(cloudInstanceDetails.aws_public_ip)) cloudInstanceDetails.aws_public_ip = '127.0.0.1/32';
|
if (isEmptyString(cloudInstanceDetails.aws_public_ip)) cloudInstanceDetails.aws_public_ip = host_ip;
|
||||||
return isError;
|
return isError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -222,7 +237,7 @@ export default function CloudWizard({ nodeInfo, nodeData }) {
|
||||||
isError = validateCloudStep1(cloudDBCred);
|
isError = validateCloudStep1(cloudDBCred);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
isError = validateCloudStep2(cloudInstanceDetails);
|
isError = validateCloudStep2(cloudInstanceDetails, hostIP);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
isError = validateCloudStep3(cloudDBDetails);
|
isError = validateCloudStep3(cloudDBDetails);
|
||||||
|
|
|
@ -27,6 +27,7 @@ define('pgadmin.misc.cloud', [
|
||||||
return pgBrowser.Cloud;
|
return pgBrowser.Cloud;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create an Object Cloud of pgBrowser class
|
// Create an Object Cloud of pgBrowser class
|
||||||
pgBrowser.Cloud = {
|
pgBrowser.Cloud = {
|
||||||
init: function() {
|
init: function() {
|
||||||
|
|
|
@ -17,7 +17,7 @@ class CloudInstanceDetailsSchema extends BaseUISchema {
|
||||||
super({
|
super({
|
||||||
oid: undefined,
|
oid: undefined,
|
||||||
aws_name: '',
|
aws_name: '',
|
||||||
aws_public_ip: '127.0.0.1/32',
|
aws_public_ip: initValues.hostIP,
|
||||||
...initValues
|
...initValues
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class CloudInstanceDetailsSchema extends BaseUISchema {
|
||||||
}, {
|
}, {
|
||||||
id: 'aws_public_ip', label: gettext('Public IP range'), type: 'text',
|
id: 'aws_public_ip', label: gettext('Public IP range'), type: 'text',
|
||||||
mode: ['create'],
|
mode: ['create'],
|
||||||
helpMessage: gettext('IP Address range for permitting the inbound traffic. Ex: 127.0.0.1/32'),
|
helpMessage: gettext('IP Address range for permitting the inbound traffic. Ex: 127.0.0.1/32, add multiple ip addresses/ranges by comma separated.'),
|
||||||
}, {
|
}, {
|
||||||
type: 'nested-fieldset', label: gettext('Version & Instance'),
|
type: 'nested-fieldset', label: gettext('Version & Instance'),
|
||||||
mode: ['create'],
|
mode: ['create'],
|
||||||
|
@ -127,9 +127,18 @@ class DatabaseSchema extends BaseUISchema {
|
||||||
&& data.aws_db_password != data.aws_db_confirm_password) {
|
&& data.aws_db_password != data.aws_db_confirm_password) {
|
||||||
setErrMsg('aws_db_confirm_password', gettext('Passwords do not match.'));
|
setErrMsg('aws_db_confirm_password', gettext('Passwords do not match.'));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
if (!isEmptyString(data.aws_db_confirm_password) && data.aws_db_confirm_password.length < 8) {
|
||||||
|
setErrMsg('aws_db_confirm_password', gettext('Password must be 8 characters or more.'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (data.aws_db_confirm_password.includes('\'') || data.aws_db_confirm_password.includes('"') ||
|
||||||
|
data.aws_db_confirm_password.includes('@') || data.aws_db_confirm_password.includes('/')) {
|
||||||
|
setErrMsg('aws_db_confirm_password', gettext('Invalid passowrd.'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get idAttribute() {
|
get idAttribute() {
|
||||||
|
@ -152,6 +161,7 @@ class DatabaseSchema extends BaseUISchema {
|
||||||
}, {
|
}, {
|
||||||
id: 'aws_db_password', label: gettext('Password'), type: 'password',
|
id: 'aws_db_password', label: gettext('Password'), type: 'password',
|
||||||
mode: ['create'], noEmpty: true,
|
mode: ['create'], noEmpty: true,
|
||||||
|
helpMessage: gettext('At least 8 printable ASCII characters. Can not contain any of the following: / \(slash\), \'\(single quote\), "\(double quote\) and @ \(at sign\).')
|
||||||
}, {
|
}, {
|
||||||
id: 'aws_db_confirm_password', label: gettext('Confirm password'),
|
id: 'aws_db_confirm_password', label: gettext('Confirm password'),
|
||||||
type: 'password',
|
type: 'password',
|
||||||
|
@ -202,6 +212,7 @@ export class InstanceSchema extends BaseUISchema {
|
||||||
if (source[0] == 'aws_db_instance_class') {
|
if (source[0] == 'aws_db_instance_class') {
|
||||||
return {reload_instances: false};
|
return {reload_instances: false};
|
||||||
} else {
|
} else {
|
||||||
|
state.instanceData = [];
|
||||||
return {reload_instances: true};
|
return {reload_instances: true};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# ##########################################################################
|
||||||
|
# #
|
||||||
|
# # pgAdmin 4 - PostgreSQL Tools
|
||||||
|
# #
|
||||||
|
# # Copyright (C) 2013 - 2022, The pgAdmin Development Team
|
||||||
|
# # This software is released under the PostgreSQL Licence
|
||||||
|
# #
|
||||||
|
# ##########################################################################
|
||||||
|
|
||||||
|
import urllib3
|
||||||
|
|
||||||
|
|
||||||
|
def get_my_ip():
|
||||||
|
""" Return the public IP of this host """
|
||||||
|
http = urllib3.PoolManager()
|
||||||
|
try:
|
||||||
|
external_ip = http.request.urlopen(
|
||||||
|
'https://ident.me').read().decode('utf8')
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
external_ip = http.request.urlopen(
|
||||||
|
'https://ifconfig.me/ip').read().decode('utf8')
|
||||||
|
except Exception:
|
||||||
|
external_ip = '127.0.0.1'
|
||||||
|
|
||||||
|
return external_ip
|
|
@ -13,24 +13,28 @@
|
||||||
AWS_REGIONS = {
|
AWS_REGIONS = {
|
||||||
'us-gov-east-1': 'AWS GovCloud (US-East)',
|
'us-gov-east-1': 'AWS GovCloud (US-East)',
|
||||||
'us-gov-west-1': 'AWS GovCloud (US-West)',
|
'us-gov-west-1': 'AWS GovCloud (US-West)',
|
||||||
|
'af-south-1': 'Africa (Cape Town)',
|
||||||
'ap-east-1': 'Asia Pacific (Hong Kong)',
|
'ap-east-1': 'Asia Pacific (Hong Kong)',
|
||||||
'ap-south-': 'Asia Pacific (Mumbai)',
|
'ap-south-1': 'Asia Pacific (Mumbai)',
|
||||||
'ap-northeast-3': 'Asia Pacific (Osaka-Local)',
|
'ap-northeast-3': 'Asia Pacific (Osaka)',
|
||||||
'ap-northeast-2': 'Asia Pacific (Seoul)',
|
'ap-northeast-2': 'Asia Pacific (Seoul)',
|
||||||
'ap-southeast-1': 'Asia Pacific (Singapore)',
|
'ap-southeast-1': 'Asia Pacific (Singapore)',
|
||||||
'ap-southeast-2': 'Asia Pacific (Sydney)',
|
'ap-southeast-2': 'Asia Pacific (Sydney)',
|
||||||
|
'ap-southeast-3': 'Asia Pacific (Jakarta)',
|
||||||
'ap-northeast-1': 'Asia Pacific (Tokyo)',
|
'ap-northeast-1': 'Asia Pacific (Tokyo)',
|
||||||
'ca-central-1': 'Canada (Central)',
|
'ca-central-1': 'Canada (Central)',
|
||||||
'cn-north-1': 'China (Beijing)',
|
'cn-north-1': 'China (Beijing)',
|
||||||
'cn-northwest-1': 'China (Ningxia)',
|
'cn-northwest-1': 'China (Ningxia)',
|
||||||
'eu-central-1': 'EU (Frankfurt)',
|
'eu-central-1': 'Europe (Frankfurt)',
|
||||||
'eu-west-1': 'EU (Ireland)',
|
'eu-west-1': 'Europe (Ireland)',
|
||||||
'eu-west-2': 'EU (London)',
|
'eu-west-2': 'Europe (London)',
|
||||||
'eu-west-3': 'EU (Paris)',
|
'eu-west-3': 'Europe (Paris)',
|
||||||
'eu-north-1': 'EU (Stockholm)',
|
'eu-north-1': 'Europe (Stockholm)',
|
||||||
|
'eu-south-1': 'Europe (Milan)',
|
||||||
'sa-east-1': 'South America (Sao Paulo)',
|
'sa-east-1': 'South America (Sao Paulo)',
|
||||||
'us-east-1': 'US East (N. Virginia)',
|
'us-east-1': 'US East (N. Virginia)',
|
||||||
'us-east-2': 'US East (Ohio)',
|
'us-east-2': 'US East (Ohio)',
|
||||||
'us-west-1': 'US West (N. California)',
|
'us-west-1': 'US West (N. California)',
|
||||||
'us-west-2': 'US West (Oregon)',
|
'us-west-2': 'US West (Oregon)',
|
||||||
|
'me-south-1': 'Middle East (Bahrain)',
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue