Skip to content

Commit 7983b03

Browse files
authored
Merge pull request #113 from grahamc/rds-subnet-groups
RDS: Support VPC
2 parents 34307d3 + a6c8d9c commit 7983b03

16 files changed

+399
-55
lines changed

nixops_aws/ec2_utils.py

+40-22
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
from boto.exception import BotoServerError
1313
from botocore.exceptions import ClientError
1414
from boto.pyami.config import Config
15-
import botocore
16-
from typing import Tuple
15+
from typing import Tuple, TYPE_CHECKING, Iterable, Any, Optional
16+
17+
if TYPE_CHECKING:
18+
import mypy_boto3_rds
1719

1820

1921
def fetch_aws_secret_key(access_key_id) -> Tuple[str, str]:
@@ -119,16 +121,33 @@ def connect_vpc(region, access_key_id):
119121
return conn
120122

121123

124+
def connect_rds_boto3(region, access_key_id) -> "mypy_boto3_rds.RDSClient":
125+
assert region
126+
(access_key_id, secret_access_key) = fetch_aws_secret_key(access_key_id)
127+
client = boto3.session.Session().client(
128+
"rds",
129+
region_name=region,
130+
aws_access_key_id=access_key_id,
131+
aws_secret_access_key=secret_access_key,
132+
)
133+
return client
134+
135+
122136
def get_access_key_id():
123137
return os.environ.get("EC2_ACCESS_KEY") or os.environ.get("AWS_ACCESS_KEY_ID")
124138

125139

126-
def retry(f, error_codes=[], logger=None):
140+
def retry(
141+
f, error_codes: Optional[Iterable[Any]] = None, logger=None, num_retries: int = 7
142+
):
127143
"""
128144
Retry function f up to 7 times. If error_codes argument is empty list, retry on all EC2 response errors,
129145
otherwise, only on the specified error codes.
130146
"""
131147

148+
if error_codes is None:
149+
error_codes = []
150+
132151
def handle_exception(e):
133152
if hasattr(e, "error_code"):
134153
err_code = e.error_code
@@ -137,8 +156,12 @@ def handle_exception(e):
137156
err_code = e.response["Error"]["Code"]
138157
err_msg = e.response["Error"]["Message"]
139158

140-
if i == num_retries or (error_codes != [] and err_code not in error_codes):
141-
raise e
159+
if err_code == "RequestLimitExceeded":
160+
return False
161+
162+
if error_codes and err_code not in error_codes:
163+
return True
164+
142165
if logger is not None:
143166
logger.log(
144167
"got (possibly transient) EC2 error code '{0}': {1}. retrying...".format(
@@ -147,8 +170,9 @@ def handle_exception(e):
147170
)
148171

149172
def handle_boto3_exception(e):
150-
if i == num_retries:
151-
raise e
173+
if error_codes and getattr(e, "response", {}).get("code") not in error_codes:
174+
return True
175+
152176
if logger is not None:
153177
if hasattr(e, "response"):
154178
logger.log(
@@ -157,29 +181,23 @@ def handle_boto3_exception(e):
157181
)
158182
)
159183

184+
def should_abort(e):
185+
if isinstance(e, (SQSError, EC2ResponseError, BotoServerError)):
186+
return handle_exception(e)
187+
elif isinstance(e, ClientError):
188+
return handle_boto3_exception(e)
189+
160190
i = 0
161-
num_retries = 7
162191
while i <= num_retries:
163192
i += 1
164193
next_sleep = 5 + random.random() * (2 ** i)
165194

166195
try:
167196
return f()
168-
except EC2ResponseError as e:
169-
handle_exception(e)
170-
except SQSError as e:
171-
handle_exception(e)
172-
except ClientError as e:
173-
handle_boto3_exception(e)
174-
except BotoServerError as e:
175-
if e.error_code == "RequestLimitExceeded":
176-
num_retries += 1
177-
else:
178-
handle_exception(e)
179-
except botocore.exceptions.ClientError as e:
180-
handle_exception(e)
181197
except Exception as e:
182-
raise e
198+
if num_retries == i or should_abort(e):
199+
raise e
200+
num_retries += 1
183201

184202
time.sleep(next_sleep)
185203

nixops_aws/nix/cloudwatch-log-group.nix

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ with lib;
1414

1515
accessKeyId = mkOption {
1616
type = types.str;
17+
default = "";
1718
description = "The AWS Access Key ID.";
1819
};
1920

nixops_aws/nix/cloudwatch-log-stream.nix

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ with lib;
1414

1515
accessKeyId = mkOption {
1616
type = types.str;
17+
default = "";
1718
description = "The AWS Access Key ID.";
1819
};
1920

nixops_aws/nix/default.nix

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
ebsVolumes = evalResources ./ebs-volume.nix (zipAttrs resourcesByType.ebsVolumes or []);
1919
elasticIPs = evalResources ./elastic-ip.nix (zipAttrs resourcesByType.elasticIPs or []);
2020
rdsDbInstances = evalResources ./ec2-rds-dbinstance.nix (zipAttrs resourcesByType.rdsDbInstances or []);
21+
rdsSubnetGroups = evalResources ./rds-subnet-group.nix (zipAttrs resourcesByType.rdsSubnetGroups or []);
2122
rdsDbSecurityGroups = evalResources ./ec2-rds-dbsecurity-group.nix (zipAttrs resourcesByType.rdsDbSecurityGroups or []);
2223
route53RecordSets = evalResources ./route53-recordset.nix (zipAttrs resourcesByType.route53RecordSets or []);
2324
elasticFileSystems = evalResources ./elastic-file-system.nix (zipAttrs resourcesByType.elasticFileSystems or []);

nixops_aws/nix/ec2-rds-dbinstance.nix

+18
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ with import ./lib.nix lib;
8181
description = "The endpoint address of the database instance. This is set by NixOps.";
8282
};
8383

84+
subnetGroup = mkOption {
85+
default = null;
86+
type = types.nullOr (types.either types.str (resource "rds-subnet-group"));
87+
apply = x: if builtins.isNull x then null else if builtins.isString x then x else "res-" + x._name;
88+
description = ''
89+
RDS Subnet Group to place this database in.
90+
'';
91+
};
92+
8493
securityGroups = mkOption {
8594
default = [ "default" ];
8695
type = types.listOf (types.either types.str (resource "ec2-rds-security-group"));
@@ -90,6 +99,15 @@ with import ./lib.nix lib;
9099
'';
91100
};
92101

102+
vpcSecurityGroups = mkOption {
103+
default = null;
104+
type = types.nullOr (types.listOf (types.either types.str (resource "ec2-security-group")));
105+
apply = map (x: if builtins.isString x then x else "res-" + x._name);
106+
description = ''
107+
List of VPC security groups to authorize on this DBInstance.
108+
'';
109+
};
110+
93111
};
94112

95113
config._type = "ec2-rds-dbinstance";

nixops_aws/nix/elastic-file-system-mount-target.nix

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ with import ./lib.nix lib;
1414

1515
accessKeyId = mkOption {
1616
type = types.str;
17+
default = "";
1718
description = "The AWS Access Key ID.";
1819
};
1920

nixops_aws/nix/elastic-file-system.nix

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ with lib;
1313

1414
accessKeyId = mkOption {
1515
type = types.str;
16+
default = "";
1617
description = "The AWS Access Key ID.";
1718
};
1819

nixops_aws/nix/iam-role.nix

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ with lib;
1414

1515
accessKeyId = mkOption {
1616
type = types.str;
17+
default = "";
1718
description = "The AWS Access Key ID.";
1819
};
1920

nixops_aws/nix/rds-subnet-group.nix

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{ config, lib, uuid, name, ... }:
2+
3+
with import ./lib.nix lib;
4+
with lib;
5+
with import ./lib.nix lib;
6+
7+
{
8+
9+
options = {
10+
11+
name = mkOption {
12+
default = "nixops-${uuid}-${name}";
13+
type = types.str;
14+
description = "Name of the RDS subnet group.";
15+
};
16+
17+
description = mkOption {
18+
default = "NixOps-provisioned subnet group ${name}";
19+
type = types.str;
20+
description = "Informational description of the subnet group.";
21+
};
22+
23+
accessKeyId = mkOption {
24+
default = "";
25+
type = types.str;
26+
description = "The AWS Access Key ID.";
27+
};
28+
29+
region = mkOption {
30+
example = "us-east-1";
31+
type = types.str;
32+
description = ''
33+
AWS region in which the instance is to be deployed.
34+
'';
35+
};
36+
37+
subnetIds = mkOption {
38+
default = [];
39+
example = [ "subnet-00000000" ];
40+
type = types.listOf (types.either types.str (resource "vpc-subnet"));
41+
apply = map (x: if builtins.isString x then x else "res-" + x._name + "." + x._type);
42+
description = ''
43+
The subnets inside a VPC to launch the databases in.
44+
'';
45+
};
46+
};
47+
48+
config._type = "rds-subnet-group";
49+
}

nixops_aws/nix/s3-bucket.nix

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ with lib;
1919

2020
accessKeyId = mkOption {
2121
type = types.str;
22+
default = "";
2223
description = "The AWS Access Key ID.";
2324
};
2425

nixops_aws/nix/sns-topic.nix

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ with lib;
1919

2020
accessKeyId = mkOption {
2121
type = types.str;
22+
default = "";
2223
description = "The AWS Access Key ID.";
2324
};
2425

nixops_aws/nix/sqs-queue.nix

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ with lib;
1919

2020
accessKeyId = mkOption {
2121
type = types.str;
22+
default = "";
2223
description = "The AWS Access Key ID.";
2324
};
2425

0 commit comments

Comments
 (0)