|
| 1 | +var AWS_ACCESS_KEY = process.env.AWS_ACCESS_KEY |
| 2 | +var AWS_SECRET_KEY = process.env.AWS_SECRET_KEY |
| 3 | +var ENVIRONMENT = process.env.ENVIRONMENT |
| 4 | + |
| 5 | +var assign = require('101/assign') |
| 6 | +var AWS = require('aws-sdk') |
| 7 | +var Dockerode = require('dockerode') |
| 8 | +var find = require('101/find') |
| 9 | +var fs = require('fs') |
| 10 | +var join = require('path').join |
| 11 | +var Promise = require('bluebird') |
| 12 | +var Swarmerode = require('swarmerode') |
| 13 | + |
| 14 | +AWS.config.update({ |
| 15 | + accessKeyId: AWS_ACCESS_KEY, |
| 16 | + secretAccessKey: AWS_SECRET_KEY, |
| 17 | + region: 'us-west-2' |
| 18 | +}) |
| 19 | +var ec2 = new AWS.EC2() |
| 20 | +var cloudwatch = new AWS.CloudWatch() |
| 21 | + |
| 22 | +function promiseWhile (condition, action) { |
| 23 | + function loop (data) { |
| 24 | + if (condition(data)) { return Promise.resolve(data) } |
| 25 | + return action(data).then(loop) |
| 26 | + } |
| 27 | + return loop |
| 28 | +} |
| 29 | + |
| 30 | +Dockerode = Swarmerode(Dockerode) |
| 31 | + |
| 32 | +var certs = {} |
| 33 | +try { |
| 34 | + // DOCKER_CERT_PATH is docker's default thing it checks - may as well use it |
| 35 | + var certPath = process.env.DOCKER_CERT_PATH |
| 36 | + certs.ca = fs.readFileSync(join(certPath, '/ca.pem')) |
| 37 | + certs.cert = fs.readFileSync(join(certPath, '/cert.pem')) |
| 38 | + certs.key = fs.readFileSync(join(certPath, '/key.pem')) |
| 39 | +} catch (e) { |
| 40 | + console.error({ err: e }, 'cannot load certificates for docker!!') |
| 41 | + // use all or none - so reset certs here |
| 42 | + certs = {} |
| 43 | +} |
| 44 | + |
| 45 | +var docker = new Dockerode(assign({ |
| 46 | + host: process.env.SWARM_HOSTNAME, |
| 47 | + port: process.env.SWARM_PORT, |
| 48 | + timeout: 2 * 60 * 1000 |
| 49 | +}, certs)) |
| 50 | + |
| 51 | +var UNITS = { |
| 52 | + 'B': 'Bytes', |
| 53 | + 'KiB': 'Kilobytes', |
| 54 | + 'MiB': 'Megabytes', |
| 55 | + 'GiB': 'Gigabytes' |
| 56 | +} |
| 57 | + |
| 58 | +var FACTOR = { |
| 59 | + Bytes: 1000 * 1000 * 1000, |
| 60 | + Kilobytes: 1000 * 1000, |
| 61 | + Megabytes: 1000, |
| 62 | + Gigabytes: 1 |
| 63 | +} |
| 64 | + |
| 65 | +var FILTER_PARAMS = { |
| 66 | + Filters: [ |
| 67 | + { Name: 'tag:role', Values: ['dock'] }, |
| 68 | + { |
| 69 | + Name: 'instance.group-name', |
| 70 | + Values: [ `${ENVIRONMENT}-dock` ] |
| 71 | + } |
| 72 | + ] |
| 73 | +} |
| 74 | + |
| 75 | +Promise.props({ |
| 76 | + docks: getDocks(), |
| 77 | + swarmHosts: getSwarmInfo() |
| 78 | +}) |
| 79 | + .then(function (data) { |
| 80 | + return Promise.each(data.swarmHosts, function (h) { |
| 81 | + var d = find(data.docks, function (dock) { |
| 82 | + return dock.PrivateIpAddress === h.Host |
| 83 | + }) |
| 84 | + if (!d) { |
| 85 | + console.error('could not find match:', h.host) |
| 86 | + return |
| 87 | + } |
| 88 | + var org = find(d.Tags, function (t) { return t.Key === 'org' }) |
| 89 | + if (!org) { |
| 90 | + console.error('could not find org tag') |
| 91 | + return |
| 92 | + } |
| 93 | + var orgID = org.Value |
| 94 | + var postData = { |
| 95 | + Namespace: 'Runnable/Swarm', |
| 96 | + MetricData: [ |
| 97 | + { |
| 98 | + MetricName: 'Swarm Reserved Memory', |
| 99 | + Dimensions: [{ |
| 100 | + Name: 'InstanceId', |
| 101 | + Value: d.InstanceId |
| 102 | + }, { |
| 103 | + Name: 'AutoScalingGroupName', |
| 104 | + Value: `asg-production-${ENVIRONMENT}-${orgID}` |
| 105 | + }], |
| 106 | + Value: h.Value, |
| 107 | + Unit: h.Unit |
| 108 | + }, { |
| 109 | + MetricName: 'Swarm Reserved Memory', |
| 110 | + Dimensions: [{ |
| 111 | + Name: 'AutoScalingGroupName', |
| 112 | + Value: `asg-production-${ENVIRONMENT}-${orgID}` |
| 113 | + }], |
| 114 | + Value: h.Value, |
| 115 | + Unit: h.Unit |
| 116 | + } |
| 117 | + ] |
| 118 | + } |
| 119 | + console.log(JSON.stringify(postData)) |
| 120 | + return Promise.fromCallback(function (cb) { |
| 121 | + cloudwatch.putMetricData(postData, cb) |
| 122 | + }) |
| 123 | + .then(function () { console.log('posted') }) |
| 124 | + }) |
| 125 | + }) |
| 126 | + .catch(function (err) { |
| 127 | + console.error('ERROR:', err.stack || err.message || err) |
| 128 | + }) |
| 129 | + |
| 130 | +function getDocks () { |
| 131 | + return Promise.resolve({ instances: [] }) |
| 132 | + .then(promiseWhile( |
| 133 | + function (data) { return data.done }, |
| 134 | + function (data) { |
| 135 | + const opts = assign({}, FILTER_PARAMS) |
| 136 | + if (data.NextToken) { opts.NextToken = data.NextToken } |
| 137 | + return Promise.fromCallback(function (cb) { |
| 138 | + ec2.describeInstances(opts, cb) |
| 139 | + }) |
| 140 | + .then(function (awsData) { |
| 141 | + awsData.Reservations.forEach(function (r) { |
| 142 | + r.Instances.forEach(function (i) { |
| 143 | + data.instances.push(i) |
| 144 | + }) |
| 145 | + }) |
| 146 | + data.NextToken = awsData.NextToken |
| 147 | + data.done = !awsData.NextToken |
| 148 | + return data |
| 149 | + }) |
| 150 | + } |
| 151 | + )) |
| 152 | + .then(function (data) { return data.instances }) |
| 153 | +} |
| 154 | + |
| 155 | +function getSwarmInfo () { |
| 156 | + return Promise.fromCallback(function (cb) { |
| 157 | + docker.swarmInfo(cb) |
| 158 | + }) |
| 159 | + .then(function (info) { |
| 160 | + return Object.keys(info.parsedSystemStatus.ParsedNodes).map(function (key) { |
| 161 | + var node = info.parsedSystemStatus.ParsedNodes[key] |
| 162 | + var usedMemory = node.ReservedMem.split('/').shift().trim() |
| 163 | + var availableMemory = node.ReservedMem.split('/').pop().trim() |
| 164 | + var usedMemoryValue = parseFloat(usedMemory.split(' ').shift()) |
| 165 | + var usedMemoryUnits = UNITS[usedMemory.split(' ').pop()] |
| 166 | + var availableMemoryValue = parseFloat(availableMemory.split(' ').shift()) |
| 167 | + var availableMemoryUnits = UNITS[availableMemory.split(' ').pop()] |
| 168 | + |
| 169 | + var usedMemoryGiB = usedMemoryValue / FACTOR[usedMemoryUnits] |
| 170 | + var availableMemoryGiB = availableMemoryValue / FACTOR[availableMemoryUnits] |
| 171 | + |
| 172 | + var percentage = (usedMemoryGiB / availableMemoryGiB) * 100 |
| 173 | + |
| 174 | + return { |
| 175 | + Host: node.Host.split(':').shift(), |
| 176 | + Value: percentage, |
| 177 | + Unit: 'Percent' |
| 178 | + } |
| 179 | + }) |
| 180 | + }) |
| 181 | +} |
0 commit comments