Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41,453 changes: 41,445 additions & 8 deletions API.md

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions assets/BitmapWidgetRenderingSupport/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Ignoring some checks since the function is in pure JS

/* eslint-disable */
const aws = require('aws-sdk');

const DOCS = `
## Display a CloudWatch bitmap graph
Displays CloudWatch metrics as a bitmap, for faster display of metrics.

### Widget parameters
Param | Description
---|---
**graph** | The graph definition. Use the parameters from the **Source** tab in CloudWatch Console's **Metrics** page.
**useMonitorPortal** (default = true) | Flag indicating whether we want to use MonitorPortal to render the graph. False will switch to CloudWatch API.

### Example parameters
\`\`\` yaml
useMonitorPortal: boolean
graph:
view: timeSeries
metrics:
- [ AWS/Lambda, Invocations ]
region: ${process.env.AWS_REGION}
\`\`\`
`;

exports.handler = async (event: any) => {
async function renderUsingCloudWatch(graph: any, width: number, height: number) {
const params = {MetricWidget: JSON.stringify(graph)};
const region = graph.region;
const customBackoff = (retryCount: number) => {
// Keep retrying with a random delay, long enough to overcome throttling from CW
const delay = 300 + Math.floor(Math.random() * 500);
console.log(`retry number ${retryCount} with a delay of ${delay} ms`);
return delay;
}
const clientOptions = {
region,
// Keep retrying until the Lambda times out
maxRetries: 99,
retryDelayOptions: {customBackoff},
httpOptions: {
connectTimeout: 3 * 1000,
timeout: 3 * 1000,
},
};
const cloudwatch = new aws.CloudWatch(clientOptions);
const image = await cloudwatch.getMetricWidgetImage(params).promise();
const base64Image = Buffer.from(image.MetricWidgetImage).toString('base64');
return `<img width="${width}" height="${height}" loading="lazy" src="data:image/png;base64,${base64Image}"/>`;
}

if (event.describe) {
return DOCS;
}

const widgetContext = event.widgetContext;
const timeRange = widgetContext.timeRange.zoom || widgetContext.timeRange;
const start = new Date(timeRange.start).toISOString();
const end = new Date(timeRange.end).toISOString();
const width = widgetContext.width;
const height = widgetContext.height;
const graph = Object.assign(event.graph, {start, end, width, height});

return renderUsingCloudWatch(graph, width, height);
};
82 changes: 82 additions & 0 deletions assets/SecretsManagerMetricsPublisher/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import aws = require("aws-sdk");

const region = process.env.AWS_REGION;
const millisPerDay = 1000 * 60 * 60 * 24;

const clientOptions = {
region,
maxRetries: 5,
httpOptions: {
connectTimeout: 3 * 1000,
timeout: 3 * 1000,
},
};

const cloudwatch = new aws.CloudWatch(clientOptions);
const sm = new aws.SecretsManager(clientOptions);

function daysSince(date: any, now = Date.now()) {
const millis = now - date.getTime();
const days = millis / millisPerDay;
return Math.floor(days);
}

exports.handler = async (event: any, context: any) => {
console.debug("event:", JSON.stringify(event));
console.debug("context:", JSON.stringify(context));

console.info(`retrieving secret: ${event.secretId}`);
const secret = await sm
.describeSecret({
SecretId: event.secretId,
})
.promise();

console.debug("found secret: ", JSON.stringify(secret));
if (!secret.Name || !secret.CreatedDate) {
throw new Error("invalid secret response");
}

// use retrieved secret name in case secretId was an arn
const secretName = secret.Name;
const lastChangedDate = secret.LastChangedDate ?? secret.CreatedDate;
const lastRotatedDate = secret.LastRotatedDate ?? secret.CreatedDate;
const now = Date.now();

const metricData = [
{
MetricName: "DaysSinceLastChange",
Dimensions: [
{
Name: "SecretName",
Value: secretName,
},
],
Unit: "Count",
Value: daysSince(lastChangedDate, now),
},
{
MetricName: "DaysSinceLastRotation",
Dimensions: [
{
Name: "SecretName",
Value: secretName,
},
],
Unit: "Count",
Value: daysSince(lastRotatedDate, now),
},
];

const params = {
Namespace: "SecretsManager",
MetricData: metricData,
};

console.debug("putMetricData params: ", JSON.stringify(params));

console.info(`publishing metrics for secret: ${event.secretId}`);
await cloudwatch.putMetricData(params).promise();

return Promise.resolve();
};
Loading