-
Notifications
You must be signed in to change notification settings - Fork 0
/
ec2-modifyvolume.js
154 lines (134 loc) · 5.71 KB
/
ec2-modifyvolume.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* global input, http, output, JXON */
// Inputs:
// VolumeId (required)
// CurrentSize (required)
// ScaleFactor (required)
// AWSRegion (required)
// AWSAccessKey (required)
// AWSSecretKey (required)
// AWSSessionToken
//
// Outputs:
// ErrorMessage
// OKToProceed
// NewSize
//
// Endpoints:
// It is necessary to create an endpoint for each region used. The endpoint name
// must be of the form ec2.<region>.amazonaws.com and have a base URL of
// https://ec2.<region>.amazonaws.com. No authentication.
const jsSHA = require('jsSHA');
const hmacSha256 = (signingKey, stringToSign, type="HEX") => {
var sha_ob = new jsSHA("SHA-256", "TEXT");
sha_ob.setHMACKey(signingKey, type);
sha_ob.update(stringToSign);
return sha_ob.getHMAC("HEX");
};
function hashSha256(stringToHash) {
var sha_ob = new jsSHA('SHA-256', "TEXT");
sha_ob.update(stringToHash);
return sha_ob.getHash("HEX");
}
function prependLeadingZeroes(n) {
if (n <= 9) {
return "0" + n;
}
return n.toString();
}
function getSignatureKey(key, dateStamp, regionName, serviceName) {
var kDate = hmacSha256(`AWS4${key}`, dateStamp, "TEXT");
var kRegion = hmacSha256(kDate, regionName);
var kService = hmacSha256(kRegion, serviceName);
var kSigning = hmacSha256(kService, "aws4_request");
return kSigning;
}
function buildHeader(access_key, secret_key, region, request_parameters) {
const method = "GET";
const service = "ec2";
const host = service+"."+region+".amazonaws.com";
const t = new Date();
const datestamp = `${t.getFullYear()}${prependLeadingZeroes(t.getMonth()+1)}${prependLeadingZeroes(t.getDate())}`;
// 4-digit year, 2-digit month, 2-digit date, T, 2-digit hour, 2-digit minutes, 2-digit seconds, Z
const amzdate = datestamp+"T"+prependLeadingZeroes(t.getHours())+prependLeadingZeroes(t.getMinutes())+prependLeadingZeroes(t.getSeconds())+"Z";
const canonical_uri = "/";
const canonical_querystring = request_parameters;
const canonical_headers = `host:${host}\nx-amz-date:${amzdate}\n`;
const signed_headers = 'host;x-amz-date';
// Calculate the hash of the payload which, for GET, is empty
const payload_hash = hashSha256("");
const canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash;
const algorithm = 'AWS4-HMAC-SHA256';
const credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request';
const string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashSha256(canonical_request);
const signing_key = getSignatureKey(secret_key, datestamp, region, service);
const signature = hmacSha256(signing_key, string_to_sign).toString('hex');
const authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature;
var requestHeaders = {
"host": host,
"x-amz-date": amzdate,
"Content-type": "application/json",
"Authorization": authorization_header
};
return [
requestHeaders,
host
];
}
function executeEc2Action(access_key, secret_key, region, request_parameters) {
const blob = buildHeader(access_key, secret_key, region, request_parameters);
var requestHeaders = blob[0];
const host = blob[1];
if (input.AWSSessionToken) {
requestHeaders["X-Amz-Security-Token"] = input.AWSSessionToken;
}
var ec2Request = http.request({
endpoint: host,
path: "/?"+request_parameters,
method: 'GET',
headers: requestHeaders
});
return ec2Request.write();
}
try {
const access_key = input.AWSAccessKey;
const secret_key = input.AWSSecretKey;
const region = input.AWSRegion;
const volume_id = input.VolumeId;
const volume_size = input.CurrentSize * input.ScaleFactor;
var ec2Response = executeEc2Action(access_key, secret_key, region, "Action=ModifyVolume&Size="+volume_size+"&Version=2016-11-15&VolumeId="+volume_id);
if (ec2Response.statusCode === 400) {
var json_error = JXON.parse(ec2Response.body);
if (json_error.response.errors.error.code === "VolumeModificationRateExceeded") {
throw new Error(json_error.response.errors.error.message);
} else {
throw new Error(ec2Response.body);
}
}
if (ec2Response.statusCode !== 200) {
throw new Error(ec2Response.body);
}
var json_response = JXON.parse(ec2Response.body);
// Make sure that the modificiation state is as expected (modifying) and then
// wait for it to change to not modifying (i.e. optimizing, completed or failed).
var state = json_response.modifyvolumeresponse.volumemodification.modificationstate;
if (state !== "modifying") {
throw new Error("Got unexpected modification state of '"+state+"' after expanding volume");
}
while (state === "modifying") {
let loopResponse = executeEc2Action(access_key, secret_key, region, "Action=DescribeVolumesModifications&Version=2016-11-15&VolumeId="+volume_id);
if (loopResponse.statusCode !== 200) {
throw new Error(loopResponse.body);
}
let jsonLoop = JXON.parse(loopResponse.body);
let modItem = jsonLoop.describevolumesmodificationsresponse.volumemodificationset.item;
state = modItem.modificationstate;
if (state === "failed") {
throw new Error("Expansion of volume has failed");
}
}
output.OKToProceed = true;
output.NewSize = volume_size;
} catch (error) {
output.ErrorMessage = error.message;
output.OKToProceed = false;
}