Commit b5aa723c by John Jarvis

Merge pull request #1293 from edx/jarv/db-clone-update

Jarv/db clone update
parents 1edeb1d5 9d790b06
{
"AWSTemplateFormatVersion":"2010-09-09",
"Description":"Separate VPC for database clones and replicas.",
"Parameters":{
"EnvironmentTag":{
"Type":"String",
"Description":"A tag value applied to the hosts in the VPC indicating which environment to use during the configuration phase, e.g., stage, prod, sandbox",
"Default":"prod"
},
"DeploymentTag":{
"Type":"String",
"Description":"A tag value applied to the hosts in the VPC indicating which deployment this is, e.g., edx, edge, <university>, <org>",
"Default":"edx"
},
"KeyName":{
"Type":"String",
"Description":"Name of an existing EC2 KeyPair to enable SSH access to the web server",
"Default":"deployment-201407"
},
"ClassB":{
"Default":"1",
"Description":"The second octet of the Class B to be allocated for this VPC. 10.?.xxx.xxx",
"Type":"Number",
"MinValue":"0",
"MaxValue":"255",
"ConstraintDescription":"ClassB value must be between 0 and 255."
}
},
"Mappings":{
"SubnetConfig":{
"VPC": { "CIDR":".0.0/16" },
"Data01": { "CIDR":".50.0/24" },
"Data02": { "CIDR":".51.0/24" }
},
"MapRegionsToAvailZones":{
"us-east-1": { "AZone2":"us-east-1d", "AZone0":"us-east-1b", "AZone1":"us-east-1c" },
"us-west-1": { "AZone0":"us-west-1a", "AZone2":"us-west-1b", "AZone1":"us-west-1c" },
"us-west-2": { "AZone0":"us-west-2a", "AZone1":"us-west-2b", "AZone2":"us-west-2c" },
"eu-west-1": { "AZone0":"eu-west-1a", "AZone1":"eu-west-1b", "AZone2":"eu-west-1c" },
"sa-east-1": { "AZone0":"sa-east-1a", "AZone1":"sa-east-1b", "AZone2":"sa-east-1c" },
"ap-southeast-1": { "AZone0":"ap-southeast-1a", "AZone1":"ap-southeast-1b", "AZone2":"ap-southeast-1c" },
"ap-southeast-2": { "AZone0":"ap-southeast-2a", "AZone1":"ap-southeast-2b", "AZone2":"ap-southeast-2c" },
"ap-northeast-1": { "AZone0":"ap-northeast-1a", "AZone1":"ap-northeast-1b", "AZone2":"ap-northeast-1c" }
}
},
"Resources":{
"EdxVPC":{
"Type":"AWS::EC2::VPC",
"Properties":{
"EnableDnsSupport" : "true",
"EnableDnsHostnames" : "true",
"CidrBlock": { "Fn::Join": ["", ["10.", { "Ref": "ClassB" }, { "Fn::FindInMap": [ "SubnetConfig", "VPC", "CIDR"]}]]},
"InstanceTenancy":"default"
}
},
"Data01":{
"Type":"AWS::EC2::Subnet",
"Properties":{
"VpcId":{
"Ref":"EdxVPC"
},
"CidrBlock":{
"Fn::Join": ["", [
"10.", { "Ref": "ClassB"},
{"Fn::FindInMap":[
"SubnetConfig",
"Data01",
"CIDR"
]}
]]
},
"AvailabilityZone":{
"Fn::FindInMap":[
"MapRegionsToAvailZones",
{ "Ref":"AWS::Region" },
"AZone0"
]
},
"Tags":[
{
"Key":"Name",
"Value":"Subnet-for-sanitized-dbs"
}
]
}
},
"Data02":{
"Type":"AWS::EC2::Subnet",
"Properties":{
"VpcId":{
"Ref":"EdxVPC"
},
"CidrBlock":{
"Fn::Join": ["", [
"10.", { "Ref": "ClassB"},
{"Fn::FindInMap":[
"SubnetConfig",
"Data02",
"CIDR"
]}
]]
},
"AvailabilityZone":{
"Fn::FindInMap":[
"MapRegionsToAvailZones",
{ "Ref":"AWS::Region" },
"AZone1"
]
},
"Tags":[
{
"Key":"Name",
"Value":"Subnet-for-non-sanitized-clones"
}
]
}
},
"PrivateRouteTable":{
"Type":"AWS::EC2::RouteTable",
"Properties":{
"VpcId":{
"Ref":"EdxVPC"
},
"Tags":[
{
"Key":"Application",
"Value":{
"Ref":"AWS::StackId"
}
},
{
"Key":"Network",
"Value":"Private"
}
]
}
},
"PrivateSubnetRouteTableAssociationData01":{
"Type":"AWS::EC2::SubnetRouteTableAssociation",
"Properties":{
"SubnetId":{
"Ref":"Data01"
},
"RouteTableId":{
"Ref":"PrivateRouteTable"
}
}
},
"PrivateSubnetRouteTableAssociationData02":{
"Type":"AWS::EC2::SubnetRouteTableAssociation",
"Properties":{
"SubnetId":{
"Ref":"Data02"
},
"RouteTableId":{
"Ref":"PrivateRouteTable"
}
}
},
"PrivateNetworkAcl":{
"Type":"AWS::EC2::NetworkAcl",
"Properties":{
"VpcId":{
"Ref":"EdxVPC"
},
"Tags":[
{
"Key":"Application",
"Value":{
"Ref":"AWS::StackId"
}
},
{
"Key":"Network",
"Value":"Private"
}
]
}
},
"InboundPrivateNetworkAclEntry":{
"Type":"AWS::EC2::NetworkAclEntry",
"Properties":{
"NetworkAclId":{
"Ref":"PrivateNetworkAcl"
},
"RuleNumber":"100",
"Protocol":"6",
"RuleAction":"allow",
"Egress":"false",
"CidrBlock":"0.0.0.0/0",
"PortRange":{
"From":"0",
"To":"65535"
}
}
},
"OutBoundPrivateNetworkAclEntry":{
"Type":"AWS::EC2::NetworkAclEntry",
"Properties":{
"NetworkAclId":{
"Ref":"PrivateNetworkAcl"
},
"RuleNumber":"100",
"Protocol":"6",
"RuleAction":"allow",
"Egress":"true",
"CidrBlock":"0.0.0.0/0",
"PortRange":{
"From":"0",
"To":"65535"
}
}
},
"PrivateSubnetNetworkAclAssociationData01":{
"Type":"AWS::EC2::SubnetNetworkAclAssociation",
"Properties":{
"SubnetId":{
"Ref":"Data01"
},
"NetworkAclId":{
"Ref":"PrivateNetworkAcl"
}
}
},
"PrivateSubnetNetworkAclAssociationData02":{
"Type":"AWS::EC2::SubnetNetworkAclAssociation",
"Properties":{
"SubnetId":{
"Ref":"Data02"
},
"NetworkAclId":{
"Ref":"PrivateNetworkAcl"
}
}
},
"EdxDataSecurityGroup":{
"Type":"AWS::EC2::SecurityGroup",
"Properties":{
"GroupDescription":"Open up access to the data subnet",
"VpcId":{
"Ref":"EdxVPC"
},
"SecurityGroupIngress":[
{
"IpProtocol":"tcp",
"FromPort":"3306",
"ToPort":"3306",
"CidrIp":"0.0.0.0/0"
},
{
"IpProtocol":"tcp",
"FromPort":"27017",
"ToPort":"27017",
"CidrIp":"0.0.0.0/0"
}
]
}
},
"EdxDBSubnetGroup":{
"Type":"AWS::RDS::DBSubnetGroup",
"Properties":{
"DBSubnetGroupDescription":"Subnets available for the RDS DB Instance",
"SubnetIds":[
{
"Ref":"Data01"
},
{
"Ref":"Data02"
}
]
}
},
"DBSecurityGroup":{
"Type":"AWS::RDS::DBSecurityGroup",
"Properties":{
"EC2VpcId":{
"Ref":"EdxVPC"
},
"GroupDescription":"Data access"
}
}
}
}
#!/usr/bin/env python #!/usr/bin/env python -u
import boto import boto
import boto.route53 import boto.route53
import boto.route53.record import boto.route53.record
...@@ -37,25 +37,6 @@ RDS_SIZES = [ ...@@ -37,25 +37,6 @@ RDS_SIZES = [
'db.m2.4xlarg', 'db.m2.4xlarg',
] ]
# These are the groups for the different
# stack names that will be assigned once
# the corresponding db is cloned
SG_GROUPS = {
'stage-edx': 'sg-d2f623b7',
}
# This group must already be created
# and allows for full access to port
# 3306 from within the vpc.
# This group is assigned temporarily
# for cleaning the db
SG_GROUPS_FULL = {
'stage-edx': 'sg-0abf396f',
}
def parse_args(args=sys.argv[1:]): def parse_args(args=sys.argv[1:]):
stack_names = all_stack_names() stack_names = all_stack_names()
...@@ -64,9 +45,12 @@ def parse_args(args=sys.argv[1:]): ...@@ -64,9 +45,12 @@ def parse_args(args=sys.argv[1:]):
for db in rds.describe_db_instances()['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']] for db in rds.describe_db_instances()['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']]
parser = ArgumentParser(description=description, formatter_class=RawTextHelpFormatter) parser = ArgumentParser(description=description, formatter_class=RawTextHelpFormatter)
parser.add_argument('-s', '--stack-name', choices=stack_names, parser.add_argument('--vpc', default=None, action="store_true",
default=None, help='this is for a vpc')
help='Stack name for where you want this RDS instance launched') parser.add_argument('--security-group', default=None,
help='security group name that should be assigned to the new RDS instance (vpc only!)')
parser.add_argument('--subnet', default=None,
help='subnet that should be used for the RDS instance (vpc only!)')
parser.add_argument('-t', '--type', choices=RDS_SIZES, parser.add_argument('-t', '--type', choices=RDS_SIZES,
default='db.m1.small', help='RDS size to create instances of') default='db.m1.small', help='RDS size to create instances of')
parser.add_argument('-d', '--db-source', choices=dbs, parser.add_argument('-d', '--db-source', choices=dbs,
...@@ -86,8 +70,8 @@ def parse_args(args=sys.argv[1:]): ...@@ -86,8 +70,8 @@ def parse_args(args=sys.argv[1:]):
parser.add_argument('--dump', action="store_true", parser.add_argument('--dump', action="store_true",
default=False, default=False,
help="create a sql dump after launching it into the vpc") help="create a sql dump after launching it into the vpc")
parser.add_argument('--secret-var-file', parser.add_argument('-s', '--secret-var-files', action="append", required=True,
help="using a secret var file run ansible against the host to update db users") help="use one or more secret var files to run ansible against the host to update db users")
return parser.parse_args(args) return parser.parse_args(args)
...@@ -99,10 +83,11 @@ def wait_on_db_status(db_name, region='us-east-1', wait_on='available', aws_id=N ...@@ -99,10 +83,11 @@ def wait_on_db_status(db_name, region='us-east-1', wait_on='available', aws_id=N
if len(statuses) > 1: if len(statuses) > 1:
raise Exception("More than one instance returned for {0}".format(db_name)) raise Exception("More than one instance returned for {0}".format(db_name))
if statuses[0]['DBInstanceStatus'] == wait_on: if statuses[0]['DBInstanceStatus'] == wait_on:
print("Status is: {}".format(wait_on))
break break
sys.stdout.write(".") sys.stdout.write("status is {}..\n".format(statuses[0]['DBInstanceStatus']))
sys.stdout.flush() sys.stdout.flush()
time.sleep(2) time.sleep(10)
return return
if __name__ == '__main__': if __name__ == '__main__':
...@@ -119,22 +104,21 @@ if __name__ == '__main__': ...@@ -119,22 +104,21 @@ if __name__ == '__main__':
use_latest_restorable_time=True, use_latest_restorable_time=True,
db_instance_class=args.type, db_instance_class=args.type,
) )
if args.stack_name: if args.vpc:
subnet_name = rds_subnet_group_name_for_stack_name(args.stack_name) restore_args['db_subnet_group_name'] = args.subnet
restore_args['db_subnet_group_name'] = subnet_name
rds.restore_db_instance_to_point_in_time(**restore_args) rds.restore_db_instance_to_point_in_time(**restore_args)
wait_on_db_status(restore_dbid) wait_on_db_status(restore_dbid)
print("Getting db host")
db_host = rds.describe_db_instances(restore_dbid)['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['Endpoint']['Address'] db_host = rds.describe_db_instances(restore_dbid)['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['Endpoint']['Address']
if args.password or args.stack_name:
modify_args = dict( modify_args = dict(
apply_immediately=True apply_immediately=True
) )
if args.password: if args.password:
modify_args['master_user_password'] = args.password modify_args['master_user_password'] = args.password
if args.stack_name:
modify_args['vpc_security_group_ids'] = [SG_GROUPS[args.stack_name], SG_GROUPS_FULL[args.stack_name]] if args.vpc:
modify_args['vpc_security_group_ids'] = [args.security_group]
else: else:
# dev-edx is the default security group for dbs that # dev-edx is the default security group for dbs that
# are not in the vpc, it allows connections from the various # are not in the vpc, it allows connections from the various
...@@ -142,8 +126,11 @@ if __name__ == '__main__': ...@@ -142,8 +126,11 @@ if __name__ == '__main__':
modify_args['db_security_groups'] = ['dev-edx'] modify_args['db_security_groups'] = ['dev-edx']
# Update the db immediately # Update the db immediately
print("Updating db instance: {}".format(modify_args))
rds.modify_db_instance(restore_dbid, **modify_args) rds.modify_db_instance(restore_dbid, **modify_args)
print("Waiting 15 seconds before checking to see if db is available")
time.sleep(15)
wait_on_db_status(restore_dbid)
if args.clean_wwc: if args.clean_wwc:
# Run the mysql clean sql file # Run the mysql clean sql file
sanitize_cmd = """mysql -u root -p{root_pass} -h{db_host} wwc < {sanitize_wwc_sql_file} """.format( sanitize_cmd = """mysql -u root -p{root_pass} -h{db_host} wwc < {sanitize_wwc_sql_file} """.format(
...@@ -162,12 +149,16 @@ if __name__ == '__main__': ...@@ -162,12 +149,16 @@ if __name__ == '__main__':
print("Running {}".format(sanitize_cmd)) print("Running {}".format(sanitize_cmd))
os.system(sanitize_cmd) os.system(sanitize_cmd)
if args.secret_var_file: if args.secret_var_files:
extra_args = ""
for secret_var_file in args.secret_var_files:
extra_args += " -e@{}".format(secret_var_file)
db_cmd = """cd {play_path} && ansible-playbook -c local -i 127.0.0.1, update_edxapp_db_users.yml """ \ db_cmd = """cd {play_path} && ansible-playbook -c local -i 127.0.0.1, update_edxapp_db_users.yml """ \
"""-e @{secret_var_file} -e "edxapp_db_root_user=root edxapp_db_root_pass={root_pass} """ \ """{extra_args} -e "edxapp_db_root_user=root edxapp_db_root_pass={root_pass} """ \
"""EDXAPP_MYSQL_HOST={db_host}" """.format( """EDXAPP_MYSQL_HOST={db_host}" """.format(
root_pass=args.password, root_pass=args.password,
secret_var_file=args.secret_var_file, extra_args=extra_args,
db_host=db_host, db_host=db_host,
play_path=play_path) play_path=play_path)
print("Running {}".format(db_cmd)) print("Running {}".format(db_cmd))
...@@ -181,6 +172,3 @@ if __name__ == '__main__': ...@@ -181,6 +172,3 @@ if __name__ == '__main__':
db_host=db_host) db_host=db_host)
print("Running {}".format(dns_cmd)) print("Running {}".format(dns_cmd))
os.system(dns_cmd) os.system(dns_cmd)
if args.stack_name:
rds.modify_db_instance(restore_dbid, vpc_security_group_ids=[SG_GROUPS[args.stack_name]])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment