sumomo 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 32e4cbb699706aa8e81613f2e781aeb4d9868daf
4
- data.tar.gz: 7d83d874b32195063ebb437ad9062da01ce72833
3
+ metadata.gz: cb1aedf8b2967520988740daf5e85b6c068302b4
4
+ data.tar.gz: 7fa8031ebf8b52c3ffbe9450d0ed3f7c7672a1c8
5
5
  SHA512:
6
- metadata.gz: b233a9eaae20d275c787adb1dde73ad5ca6e532c7aed417b8c02e662208537d7393cfaefcc4f7bc769ee4003fd1a5dcc8e2be58588f29fe1c7f3ce51edc45de4
7
- data.tar.gz: a2bdea1719464211ec9046a0f30cd8702ae0fdac12eac107c00954b358bd4ec34f18457f9195438f129c84441a8a9bbb4742e66c4a7b804bc4ce4d65b927f1c8
6
+ metadata.gz: 83a0194a2aeb0a63ee35bed97c7521546511ab9fa7677d5e282c1bbeb5caed9f3091a70f63df81ae4dcb574732ee2a10059ede53e99491078a79c42914d0ea3c
7
+ data.tar.gz: 775a27622a8deb089fb94921644566baa1937b1beba30811055ba21f4ebb117fa4b45a9d951b65bc92bd4a3ce50f9ea8414cf0a95b924b462108f4e6b89c5f26
data/README.md CHANGED
@@ -20,7 +20,21 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- TODO: Write usage instructions here
23
+ This gem lets you use all of the syntax in the [momo](https://github.com/davidsiaw/momo) and more!
24
+
25
+ You need to setup your AWS credentials using `aws configure`.
26
+
27
+ ```
28
+ require "sumomo"
29
+
30
+ Sumomo::update_stack(name: "mystack", region: "ap-northeast-1") do
31
+ x = hidden_value "meow"
32
+
33
+ output "Haha", x
34
+ end
35
+
36
+ Sumomo::wait_for_stack(name: "kurosawa", region: "ap-northeast-1")
37
+ ```
24
38
 
25
39
  ## Development
26
40
 
@@ -30,5 +44,5 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
30
44
 
31
45
  ## Contributing
32
46
 
33
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sumomo.
47
+ Bug reports and pull requests are welcome on GitHub at https://github.com/davidsiaw/sumomo.
34
48
 
@@ -0,0 +1,151 @@
1
+ var aws = require("aws-sdk");
2
+
3
+ Cloudformation = {}
4
+ Cloudformation.SUCCESS = "SUCCESS";
5
+ Cloudformation.FAILED = "FAILED";
6
+ Cloudformation.status_sent = false;
7
+ Cloudformation.send = function(request, context, responseStatus, responseData, reason, physicalResourceId) {
8
+
9
+ var responseBody = JSON.stringify({
10
+ Status: responseStatus,
11
+ Reason: reason + " Log Stream: " + context.logStreamName,
12
+ PhysicalResourceId: physicalResourceId || context.logStreamName,
13
+ StackId: request.StackId,
14
+ RequestId: request.RequestId,
15
+ LogicalResourceId: request.LogicalResourceId,
16
+ Data: responseData
17
+ });
18
+
19
+ console.log("Response body:\n", responseBody);
20
+
21
+ var https = require("https");
22
+ var url = require("url");
23
+
24
+ var parsedUrl = url.parse(request.ResponseURL);
25
+ var options = {
26
+ hostname: parsedUrl.hostname,
27
+ port: 443,
28
+ path: parsedUrl.path,
29
+ method: "PUT",
30
+ headers: {
31
+ "content-type": "",
32
+ "content-length": responseBody.length
33
+ }
34
+ };
35
+
36
+ var request = https.request(options, function(response) {
37
+ console.log("Status code: " + response.statusCode);
38
+ console.log("Status message: " + response.statusMessage);
39
+ context.done();
40
+ });
41
+
42
+ request.on("error", function(error) {
43
+ console.log("send(..) failed executing https.request(..): " + error);
44
+ context.done();
45
+ });
46
+
47
+ request.write(responseBody);
48
+ request.end();
49
+ Cloudformation.status_sent = true;
50
+ }
51
+
52
+ function Storage(bucket, prefix, region)
53
+ {
54
+ var s3 = new aws.S3({region: region});
55
+
56
+ this.put = function(key, value, onComplete, onError) {
57
+
58
+ s3.putObject({
59
+ Bucket: bucket,
60
+ Key: "data/" + prefix + "/" + key,
61
+ Body: value
62
+ }, function(err, data) {
63
+ if (err)
64
+ {
65
+ if (onError)
66
+ {
67
+ onError(err);
68
+ }
69
+ else
70
+ {
71
+ throw(err);
72
+ }
73
+ }
74
+ else
75
+ {
76
+ if (onComplete)
77
+ {
78
+ onComplete();
79
+ }
80
+ }
81
+ })
82
+ }
83
+
84
+ this.get = function(key, onComplete, onError) {
85
+
86
+ s3.getObject({
87
+ Bucket: bucket,
88
+ Key: "data/" + prefix + "/" + key
89
+ }, function(err, data) {
90
+ if (err)
91
+ {
92
+ if (onError)
93
+ {
94
+ onError(err);
95
+ }
96
+ else
97
+ {
98
+ throw(err);
99
+ }
100
+ }
101
+ else
102
+ {
103
+ if (onComplete)
104
+ {
105
+ onComplete(data.Body.toString());
106
+ }
107
+ }
108
+ })
109
+ }
110
+
111
+ return this;
112
+ }
113
+
114
+ Global = {}
115
+
116
+ process.on('uncaughtException', function(err) {
117
+ console.log("Sending the following error to cloudformation: ");
118
+ console.log(err);
119
+ Cloudformation.send(Global.request, Global.context, Cloudformation.FAILED, {}, err);
120
+ });
121
+
122
+ process.on('exit', function() {
123
+ if (!Cloudformation.status_sent)
124
+ {
125
+ console.log("No status sent to cloudformation, assuming FAILED");
126
+ Cloudformation.send(Global.request, Global.context, Cloudformation.FAILED, {}, "No status sent to cloudformation");
127
+ }
128
+ });
129
+
130
+ exports.handler = function(request, context)
131
+ {
132
+ console.log("Request:");
133
+ console.log(request);
134
+ console.log("Context:");
135
+ console.log(context);
136
+
137
+ Global.request = request;
138
+ Global.context = context;
139
+ var store = Storage(request.ResourceProperties.Bucket, request.LogicalResourceId, request.ResourceProperties.Region);
140
+
141
+ try
142
+ {
143
+ {{ CODE }}
144
+ }
145
+ catch (err)
146
+ {
147
+ console.log("Sending the following error to cloudformation: ");
148
+ console.log(err);
149
+ Cloudformation.send(request, context, Cloudformation.FAILED, {}, err);
150
+ }
151
+ }
@@ -0,0 +1,106 @@
1
+
2
+ var typeToArch = {
3
+ "t1.micro" : "PV64" ,
4
+ "t2.nano" : "HVM64",
5
+ "t2.micro" : "HVM64",
6
+ "t2.small" : "HVM64",
7
+ "t2.medium" : "HVM64",
8
+ "t2.large" : "HVM64",
9
+ "m1.small" : "PV64" ,
10
+ "m1.medium" : "PV64" ,
11
+ "m1.large" : "PV64" ,
12
+ "m1.xlarge" : "PV64" ,
13
+ "m2.xlarge" : "PV64" ,
14
+ "m2.2xlarge" : "PV64" ,
15
+ "m2.4xlarge" : "PV64" ,
16
+ "m3.medium" : "HVM64",
17
+ "m3.large" : "HVM64",
18
+ "m3.xlarge" : "HVM64",
19
+ "m3.2xlarge" : "HVM64",
20
+ "m4.large" : "HVM64",
21
+ "m4.xlarge" : "HVM64",
22
+ "m4.2xlarge" : "HVM64",
23
+ "m4.4xlarge" : "HVM64",
24
+ "m4.10xlarge" : "HVM64",
25
+ "c1.medium" : "PV64" ,
26
+ "c1.xlarge" : "PV64" ,
27
+ "c3.large" : "HVM64",
28
+ "c3.xlarge" : "HVM64",
29
+ "c3.2xlarge" : "HVM64",
30
+ "c3.4xlarge" : "HVM64",
31
+ "c3.8xlarge" : "HVM64",
32
+ "c4.large" : "HVM64",
33
+ "c4.xlarge" : "HVM64",
34
+ "c4.2xlarge" : "HVM64",
35
+ "c4.4xlarge" : "HVM64",
36
+ "c4.8xlarge" : "HVM64",
37
+ "g2.2xlarge" : "HVMG2",
38
+ "g2.8xlarge" : "HVMG2",
39
+ "r3.large" : "HVM64",
40
+ "r3.xlarge" : "HVM64",
41
+ "r3.2xlarge" : "HVM64",
42
+ "r3.4xlarge" : "HVM64",
43
+ "r3.8xlarge" : "HVM64",
44
+ "i2.xlarge" : "HVM64",
45
+ "i2.2xlarge" : "HVM64",
46
+ "i2.4xlarge" : "HVM64",
47
+ "i2.8xlarge" : "HVM64",
48
+ "d2.xlarge" : "HVM64",
49
+ "d2.2xlarge" : "HVM64",
50
+ "d2.4xlarge" : "HVM64",
51
+ "d2.8xlarge" : "HVM64",
52
+ "hi1.4xlarge" : "HVM64",
53
+ "hs1.8xlarge" : "HVM64",
54
+ "cr1.8xlarge" : "HVM64",
55
+ "cc2.8xlarge" : "HVM64"
56
+ }
57
+
58
+ var archToAMINamePattern = {
59
+ "PV64": "amzn-ami-pv*.x86_64-ebs",
60
+ "HVM64": "amzn-ami-hvm*.x86_64-gp2",
61
+ "HVMG2": "amzn-ami-graphics-hvm-*x86_64-ebs*"
62
+ };
63
+
64
+ var ec2 = new aws.EC2({region: request.ResourceProperties.Region});
65
+
66
+ var describeImagesParams = {
67
+ Filters: [{ Name: "name", Values: [archToAMINamePattern[typeToArch[request.ResourceProperties.InstanceType]]]}],
68
+ Owners: [request.ResourceProperties.Architecture == "HVMG2" ? "679593333241" : "amazon"]
69
+ };
70
+
71
+ // Check if the image is a beta or rc image. The Lambda function won't return any of those images.
72
+ function isBeta(imageName) {
73
+ return imageName.toLowerCase().indexOf("beta") > -1 || imageName.toLowerCase().indexOf(".rc") > -1;
74
+ }
75
+
76
+ // Get AMI IDs with the specified name pattern and owner
77
+ ec2.describeImages(describeImagesParams, function(err, describeImagesResult)
78
+ {
79
+ if (err)
80
+ {
81
+ console.log(err, err.stack); // an error occurred
82
+ Cloudformation.send(request, context, Cloudformation.FAILED, {}, JSON.stringify(err));
83
+ }
84
+ else
85
+ {
86
+ var response = {}
87
+ var id = "NONE";
88
+ var images = describeImagesResult.Images;
89
+ // Sort images by name in decscending order. The names contain the AMI version, formatted as YYYY.MM.Ver.
90
+ images.sort(function(x, y) { return y.Name.localeCompare(x.Name); });
91
+ for (var j = 0; j < images.length; j++)
92
+ {
93
+ if (isBeta(images[j].Name)) continue;
94
+ id = images[j].ImageId;
95
+ response["Name"] = images[j].Name;
96
+ response["ImageType"] = images[j].ImageType;
97
+ response["CreationDate"] = images[j].CreationDate;
98
+ response["Description"] = images[j].Description;
99
+ response["RootDeviceType"] = images[j].RootDeviceType;
100
+ response["RootDeviceName"] = images[j].RootDeviceName;
101
+ response["VirtualizationType"] = images[j].VirtualizationType;
102
+ break;
103
+ }
104
+ Cloudformation.send(request, context, Cloudformation.SUCCESS, response, "Success", id);
105
+ }
106
+ });
@@ -0,0 +1,89 @@
1
+ // return number of zones
2
+ // Zones = comma separated availability zone names
3
+
4
+ physicalId = "0"
5
+ response = {}
6
+
7
+ function send_success()
8
+ {
9
+ if (request.RequestType == "Create")
10
+ {
11
+ Cloudformation.send(request, context, Cloudformation.SUCCESS, response, "Success", physicalId);
12
+ }
13
+
14
+ if (request.RequestType == "Update")
15
+ {
16
+ Cloudformation.send(request, context, Cloudformation.SUCCESS, response, "Success", physicalId);
17
+ }
18
+
19
+ if (request.RequestType == "Delete")
20
+ {
21
+ Cloudformation.send(request, context, Cloudformation.SUCCESS, {}, "Success");
22
+ }
23
+ }
24
+
25
+ function get_spot_prices(instance, zones, number)
26
+ {
27
+ if (zones.length == 0)
28
+ {
29
+ send_success();
30
+ }
31
+ else
32
+ {
33
+ ec2.describeSpotPriceHistory({
34
+ AvailabilityZone: zones[0],
35
+ InstanceTypes: [instance],
36
+ MaxResults: 10
37
+ }, function(err, data) {
38
+ if (err)
39
+ {
40
+ console.log(err, err.stack); // an error occurred
41
+ Cloudformation.send(request, context, Cloudformation.FAILED, {}, JSON.stringify(err));
42
+ }
43
+ else
44
+ {
45
+ console.log("HISTORY")
46
+ console.log(JSON.stringify(data.SpotPriceHistory))
47
+ var price = data.SpotPriceHistory[0].SpotPrice
48
+ if (response["ZoneSpotPrices"])
49
+ {
50
+ response["ZoneSpotPrices"] += ","
51
+ }
52
+ response["ZoneSpotPrices"] += price;
53
+ response["ZoneSpotPrice" + number] = price;
54
+ zones.splice(0,1);
55
+ get_spot_prices(instance, zones, number+1);
56
+ }
57
+ });
58
+ }
59
+ }
60
+
61
+ var ec2 = new aws.EC2({region: request.ResourceProperties.Region});
62
+ ec2.describeAvailabilityZones({}, function(err, data) {
63
+ if (err)
64
+ {
65
+ console.log(err, err.stack); // an error occurred
66
+ Cloudformation.send(request, context, Cloudformation.FAILED, {}, JSON.stringify(err));
67
+ }
68
+ else
69
+ {
70
+ var zones = data.AvailabilityZones.map(function(x) {return x.ZoneName;});
71
+
72
+ physicalId = String(zones.length);
73
+ response["Zones"] = zones.join();
74
+ for(var i=0; i<zones.length; i++)
75
+ {
76
+ response["Zone"+i] = zones[i];
77
+ }
78
+
79
+ if (request.ResourceProperties.GetSpotPriceFor)
80
+ {
81
+ response["ZoneSpotPrices"] = ""
82
+ get_spot_prices(request.ResourceProperties.GetSpotPriceFor, zones, 1);
83
+ }
84
+ else
85
+ {
86
+ send_success();
87
+ }
88
+ }
89
+ });
@@ -0,0 +1,231 @@
1
+ // Key: Cloudflare API key
2
+ // Email: Cloudflare API email
3
+ // Domain: Cloudflare domain
4
+ // Entry: DNS entry text
5
+ // CNAME: The CNAME record
6
+ // return Record ID of the CNAME record
7
+
8
+ var querystring = require('querystring');
9
+ var https = require('https');
10
+
11
+ key = request.ResourceProperties.Key
12
+ email = request.ResourceProperties.Email
13
+ domain = request.ResourceProperties.Domain
14
+ entry = request.ResourceProperties.Entry
15
+ cname = request.ResourceProperties.Cname
16
+
17
+ function get(path, query, on_complete)
18
+ {
19
+ var query_string = querystring.stringify(query);
20
+
21
+ var get_options = {
22
+ host: 'api.cloudflare.com',
23
+ port: '443',
24
+ path: '/client/v4' + path + "/?" + query_string,
25
+ method: 'GET',
26
+ headers: {
27
+ 'X-Auth-Key': key,
28
+ 'X-Auth-Email': email
29
+ }
30
+ };
31
+
32
+ var req = https.request(get_options, function(res) {
33
+ //console.log('statusCode: ', res.statusCode);
34
+ //console.log('headers: ', res.headers);
35
+ var body = "";
36
+ res.setEncoding('utf8');
37
+ res.on('data', function(d) {
38
+ body += d;
39
+ });
40
+ res.on('end', function() {
41
+ on_complete(JSON.parse(body));
42
+ });
43
+ req.on('error', function(e) {
44
+ console.error(e);
45
+ });
46
+ });
47
+ req.end();
48
+ }
49
+
50
+ function http_request(method, path, data, on_complete)
51
+ {
52
+ post_data = JSON.stringify(data);
53
+
54
+ var post_options = {
55
+ host: 'api.cloudflare.com',
56
+ port: '443',
57
+ path: '/client/v4' + path,
58
+ method: method,
59
+ headers: {
60
+ 'Content-Type': 'application/json',
61
+ 'Content-Length': Buffer.byteLength(post_data),
62
+ 'X-Auth-Key': key,
63
+ 'X-Auth-Email': email
64
+ }
65
+ };
66
+
67
+ var req = https.request(post_options, function(res) {
68
+ //console.log('statusCode: ', res.statusCode);
69
+ //console.log('headers: ', res.headers);
70
+ var body = "";
71
+ res.setEncoding('utf8');
72
+ res.on('data', function(d) {
73
+ body += d;
74
+ });
75
+ res.on('end', function() {
76
+ on_complete(JSON.parse(body));
77
+ });
78
+ req.on('error', function(e) {
79
+ console.error(e);
80
+ });
81
+ });
82
+ req.write(post_data);
83
+ req.end();
84
+ }
85
+
86
+ function post(path, data, on_complete)
87
+ {
88
+ http_request("POST", path, data, on_complete)
89
+ }
90
+
91
+ function del(path, data, on_complete)
92
+ {
93
+ http_request("DELETE", path, data, on_complete)
94
+ }
95
+
96
+ function get_zone_id(zone_name, on_complete)
97
+ {
98
+ get("/zones", {name: zone_name}, function (data)
99
+ {
100
+ on_complete(data["result"][0]["id"])
101
+ });
102
+ }
103
+
104
+ function get_dns_record_named(zone_id, name, on_complete)
105
+ {
106
+ get("/zones/"+zone_id+"/dns_records", {name: name}, function (data)
107
+ {
108
+ on_complete(data["result"])
109
+ });
110
+ }
111
+
112
+ function create_cname_dns_record(zone_id, type, name, content, on_complete)
113
+ {
114
+ post("/zones/"+zone_id+"/dns_records", {
115
+ type: type,
116
+ name: name,
117
+ content: content
118
+ }, function(data) {
119
+ on_complete(data)
120
+ });
121
+ };
122
+
123
+ function delete_cname_dns_record(zone_id, record_id, on_complete)
124
+ {
125
+ del("/zones/"+zone_id+"/dns_records/"+record_id, {}, function(data) {
126
+ on_complete(data)
127
+ });
128
+ };
129
+
130
+
131
+ console.log('REQUEST RECEIVED:\n', JSON.stringify(request));
132
+
133
+ function send_success(record_id)
134
+ {
135
+ Cloudformation.send(request, context, Cloudformation.SUCCESS, {}, "Success", record_id);
136
+ }
137
+
138
+ function send_failure(reason)
139
+ {
140
+ Cloudformation.send(request, context, Cloudformation.FAILED, {}, reason);
141
+ }
142
+
143
+ function save_data(domain_id, record_id, on_complete, on_error)
144
+ {
145
+ store.put("domain_id", domain_id, function() {
146
+ store.put("record_id", record_id, on_complete, on_error);
147
+ }, on_error);
148
+ }
149
+
150
+ function load_data(on_complete, on_error)
151
+ {
152
+ store.get("domain_id", function(domain_id) {
153
+ store.get("record_id", function(record_id) {
154
+ on_complete(domain_id, record_id);
155
+ }, on_error);
156
+ }, on_error);
157
+ }
158
+
159
+ if (request.RequestType == "Create") {
160
+
161
+ get_zone_id(domain, function(id)
162
+ {
163
+ get_dns_record_named(id, entry, function(data)
164
+ {
165
+ if (data.length == 0)
166
+ {
167
+ create_cname_dns_record(id, "CNAME", entry, cname, function(data) {
168
+ console.log("CREATE RECORD: " + JSON.stringify(data));
169
+ if (data.result)
170
+ {
171
+ save_data(id, data.result.id,
172
+ function() {
173
+ send_success(data.result.id)
174
+ },
175
+ function() {
176
+ send_failure("Failed to save data")
177
+ }
178
+ );
179
+ }
180
+ else
181
+ {
182
+ send_failure(JSON.stringify(data));
183
+ }
184
+ });
185
+ }
186
+ else
187
+ {
188
+ send_failure("Entry " + entry + " already exists");
189
+ }
190
+ console.log(data);
191
+ });
192
+ });
193
+
194
+ return;
195
+ }
196
+
197
+ if (request.RequestType == "Update") {
198
+
199
+ load_data(function(domain_id, record_id) {
200
+
201
+ delete_cname_dns_record(domain_id, record_id, function() {
202
+ create_cname_dns_record(domain_id, "CNAME", entry, cname, function(data) {
203
+ console.log(data);
204
+ save_data(domain_id, data.result.id,
205
+ function() {
206
+ send_success(data.result.id)
207
+ },
208
+ function() {
209
+ send_failure("Failed to save data")
210
+ }
211
+ );
212
+ });
213
+ });
214
+
215
+ }, send_failure.bind("Failed to load data"));
216
+
217
+
218
+ return;
219
+ }
220
+
221
+ if (request.RequestType == "Delete") {
222
+
223
+ load_data(function(domain_id, record_id) {
224
+
225
+ delete_cname_dns_record(domain_id, record_id, function() {
226
+ send_success(record_id)
227
+ });
228
+
229
+ }, send_failure.bind("Failed to load data"));
230
+ return;
231
+ }