@pulumi/awsx
Version:
[](https://github.com/pulumi/pulumi-awsx/actions) [](https://slack.pulumi.com) [;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Metric = void 0;
exports.mergeDimensions = mergeDimensions;
const aws = __importStar(require("@pulumi/aws"));
const pulumi = __importStar(require("@pulumi/pulumi"));
const widgets_simple_1 = require("./widgets_simple");
const utils = __importStar(require("../utils"));
/**
* Metrics are the fundamental concept in CloudWatch. A metric represents a time-ordered set of data
* points that are published to CloudWatch. Think of a metric as a variable to monitor, and the data
* points as representing the values of that variable over time. For example, the CPU usage of a
* particular EC2 instance is one metric provided by Amazon EC2. The data points themselves can come
* from any application or business activity from which you collect data.
*
* AWS services send metrics to CloudWatch, and you can send your own custom metrics to CloudWatch.
* You can add the data points in any order, and at any rate you choose. You can retrieve statistics
* about those data points as an ordered set of time-series data.
*
* Metrics exist only in the region in which they are created. Metrics cannot be deleted, but they
* automatically expire after 15 months if no new data is published to them. Data points older than
* 15 months expire on a rolling basis; as new data points come in, data older than 15 months is
* dropped.
*
* Metrics are uniquely defined by a name, a namespace, and zero or more dimensions. Each data point
* in a metric has a time stamp, and (optionally) a unit of measure. You can retrieve statistics
* from CloudWatch for any metric.
*
* see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Metric
* for more details.
*/
class Metric {
/**
* Optional resource this is a metric for. Used only for parenting purposes when making new alarms.
*/
resource;
/**
* The namespace for this metric. See docs for the
* [list of namespaces](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/aws-namespaces.html).
* See docs for
* [supported metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html).
*/
namespace;
/**
* The name for this metric. See docs for
* [supported metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html).
*/
name;
/**
* The dimensions for this metric. For the list of available dimensions see the AWS documentation
* [here](http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html).
*/
dimensions;
/**
* The period in seconds over which the specified [statistic] is applied. Must be in multiples
* of 60. Periods are defined in numbers of seconds, and valid values for period are 1, 5, 10,
* 30, or any multiple of 60. For example, to specify a period of six minutes, use 360 as the
* period value. You can adjust how the data is aggregated by varying the length of the period.
* A period can be as short as one second or as long as one day (86,400 seconds). The default
* value is 60 seconds.
*
* See https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html
* for more details.
*/
period;
/**
* The statistic to apply to the alarm's associated metric. Either of the following is
* supported: `SampleCount`, `Average`, `Sum`, `Minimum`, `Maximum`.
*
* Defaults to [Average] if [statistic] and [extendedStatistic] is unspecified.
*/
statistic;
/**
* The percentile statistic for the metric associated with the alarm. Specify a value between
* [0.0] and [100].
*/
extendedStatistic;
/**
* The unit for this metric.
*
* See https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html
*/
unit;
/**
* The six-digit HTML hex color code to be used for this metric.
*
* Only used if this metric is displayed in a [Dashboard] with a [MetricWidget].
*/
color;
/**
* The label to display for this metric in the graph legend. If this is not specified, the
* metric is given an autogenerated label that distinguishes it from the other metrics in the
* widget.
*
* Only used if this metric is displayed in a [Dashboard] with a [MetricWidget].
*/
label;
/**
* Set this to true to have the metric appear in the graph, or false to have it be hidden. The
* default is true.
*
* Only used if this metric is displayed in a [Dashboard] with a [MetricWidget].
*/
visible;
/**
* Where on the graph to display the y-axis for this metric. The default is left.
*
* Only used if this metric is displayed in a [Dashboard] with a [MetricWidget].
*/
yAxis;
/**
* The id of this metric. This id can be used as part of a math expression.
*/
id;
/**
* @param resource Optional resource this is a metric for. This is only used for parenting
* purposes. i.e. if an [Alarm] is created from this [Metric], then [resource] will be used as
* the parent of the alarm by default.
*/
constructor(args, resource) {
this.resource = resource;
this.name = pulumi.output(args.name);
if (args.dimensions) {
this.dimensions = pulumi.output(args.dimensions);
}
this.namespace = pulumi.output(args.namespace);
this.period = utils.ifUndefined(args.period, 300).apply(p => validatePeriod(p));
this.statistic = pulumi.all([args.statistic, args.extendedStatistic])
.apply(([statistic, extendedStatistic]) => validateStatistics(statistic, extendedStatistic));
this.extendedStatistic = pulumi.output(args.extendedStatistic).apply(es => validateExtendedStatistic(es));
this.unit = pulumi.output(args.unit);
// Only for metrics that are placed in dashboards.
this.color = pulumi.output(args.color);
this.label = pulumi.output(args.label);
this.visible = utils.ifUndefined(args.visible, true);
this.yAxis = utils.ifUndefined(args.yAxis, "left");
this.id = pulumi.output(args.id);
}
with(change) {
if (!change) {
return this;
}
let result = this;
result = hasOwnProperty(change, "dimensions") ? result.withDimensions(change.dimensions) : result;
result = hasOwnProperty(change, "period") ? result.withPeriod(change.period) : result;
result = hasOwnProperty(change, "statistic") ? result.withStatistic(change.statistic) : result;
result = hasOwnProperty(change, "extendedStatistic") ? result.withExtendedStatistic(change.extendedStatistic) : result;
result = hasOwnProperty(change, "unit") ? result.withUnit(change.unit) : result;
result = hasOwnProperty(change, "color") ? result.withColor(change.color) : result;
result = hasOwnProperty(change, "label") ? result.withLabel(change.label) : result;
result = hasOwnProperty(change, "visible") ? result.withVisible(change.visible) : result;
result = hasOwnProperty(change, "yAxis") ? result.withYAxis(change.yAxis) : result;
result = hasOwnProperty(change, "id") ? result.withId(change.id) : result;
return result;
}
features() {
return {
namespace: this.namespace,
name: this.name,
dimensions: this.dimensions,
period: this.period,
statistic: this.statistic,
extendedStatistic: this.extendedStatistic,
unit: this.unit,
};
}
spread() {
return {
resource: this.resource,
namespace: this.namespace,
name: this.name,
dimensions: this.dimensions,
period: this.period,
statistic: this.statistic,
extendedStatistic: this.extendedStatistic,
unit: this.unit,
color: this.color,
label: this.label,
visible: this.visible,
yAxis: this.yAxis,
id: this.id
};
}
/**
* Produces a new [Metric] instances with the specific [dimensions] of this instance overwritten
* with the [dimensions] pass in as arguments. Because this is a merging, to unset a particular
* dimension, pass in an explicit value of `{ name: undefined }`. To clear all dimensions, pass
* in `undefined` for the entire argument.
*/
withDimensions(dimensions) {
return new Metric({
...this.spread(),
dimensions: mergeDimensions(this.dimensions, dimensions),
}, this.resource);
}
withPeriod(period) {
return new Metric({ ...this.spread(), period }, this.resource);
}
withUnit(unit) {
return new Metric({ ...this.spread(), unit }, this.resource);
}
withColor(color) {
return new Metric({ ...this.spread(), color }, this.resource);
}
withLabel(label) {
return new Metric({ ...this.spread(), label }, this.resource);
}
withVisible(visible) {
return new Metric({ ...this.spread(), visible }, this.resource);
}
withYAxis(yAxis) {
return new Metric({ ...this.spread(), yAxis }, this.resource);
}
withId(id) {
return new Metric({ ...this.spread(), id }, this.resource);
}
withStatistic(statistic) {
// If they're supplying a statistic, then we want to clear out extendedStatistic.
return new Metric({
...this.spread(),
statistic,
extendedStatistic: pulumi.all([statistic, this.extendedStatistic])
.apply(([statistic, extendedStatistic]) => statistic !== undefined ? undefined : extendedStatistic),
}, this.resource);
}
withExtendedStatistic(extendedStatistic) {
// If they're supplying an extendedStatistic, then we want to clear out statistic.
return new Metric({
...this.spread(),
statistic: pulumi.all([this.statistic, extendedStatistic])
.apply(([statistic, extendedStatistic]) => extendedStatistic !== undefined ? undefined : statistic),
extendedStatistic,
}, this.resource);
}
createAlarm(name, args, opts = {}) {
const comparisonOperator = utils.ifUndefined(args.comparisonOperator, "GreaterThanOrEqualToThreshold");
return new aws.cloudwatch.MetricAlarm(name, {
...args,
comparisonOperator,
actionsEnabled: utils.ifUndefined(args.actionsEnabled, true),
alarmDescription: computeDescription(this, args, comparisonOperator),
treatMissingData: utils.ifUndefined(args.treatMissingData, "missing"),
dimensions: this.dimensions,
extendedStatistic: this.extendedStatistic.apply(s => s === undefined ? undefined : `p${s}`),
metricName: this.name,
namespace: this.namespace,
period: this.period,
statistic: this.statistic,
unit: this.unit.apply(u => u),
}, { parent: this.resource, ...opts });
}
/** For internal use only. */
addWidgetJson(metrics) {
// Each single metric in the metrics array has the following format:
// [Namespace, MetricName, [{DimensionName,DimensionValue}...] [Rendering Properties Object] ]
// See: https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/CloudWatch-Dashboard-Body-Structure.html#CloudWatch-Dashboard-Properties-Metrics-Array-Format
const op = pulumi.all([this.spread(), (0, widgets_simple_1.statisticString)(this.spread())]).apply(([uw, stat]) => {
const result = [];
if (uw.period % 60 !== 0) {
throw new Error(`Dashboard metric period must be a multiple of 60: ${uw.period}`);
}
result.push(uw.namespace);
result.push(uw.name);
// note that dimensions are just added added directly into the array, there's no
// sub-array or other structure to hold them.
if (uw.dimensions) {
for (const key in uw.dimensions) {
if (uw.dimensions.hasOwnProperty(key)) {
result.push(key);
result.push(uw.dimensions[key]);
}
}
}
const renderingProps = {
stat,
color: uw.color,
label: uw.label,
period: uw.period,
visible: uw.visible,
yAxis: uw.yAxis,
id: uw.id
};
result.push(renderingProps);
return result;
});
metrics.push(op);
}
}
exports.Metric = Metric;
/** @internal */
function mergeDimensions(oldDimensions, newDimensions) {
if (!newDimensions) {
// they're explicitly clearing out all dimensions.
return undefined;
}
return pulumi.all([oldDimensions, newDimensions]).apply(([oldDimensions, newDimensions]) => {
if (!oldDimensions) {
// no old dimensions, can just use all the new dimensions passed in.
return newDimensions;
}
// have both old and new. need to overwrite all the old dimensions with whatever is in new.
const result = { ...oldDimensions };
for (const name in newDimensions) {
if (newDimensions.hasOwnProperty(name)) {
result[name] = newDimensions[name];
}
}
return result;
});
}
function hasOwnProperty(obj, key) {
return obj.hasOwnProperty(key);
}
function computeDescription(metric, args, comparisonOperator) {
return pulumi.all([metric, args, comparisonOperator])
.apply(([metric, args, comparisonOperator]) => {
if (args.alarmDescription !== undefined) {
return args.alarmDescription;
}
const name = metric.label || metric.name;
const op = operatorDescription(comparisonOperator);
const period = args.evaluationPeriods === 1
? `for 1 datapoint`
: `for ${args.evaluationPeriods} datapoints`;
const time = args.evaluationPeriods * metric.period;
const timeDesc = time % 60 === 0
? `within ${time / 60} minutes`
: `within ${time} seconds`;
return `${name} ${op} ${args.threshold} ${period} ${timeDesc}`;
});
}
function operatorDescription(op) {
switch (op) {
case "GreaterThanOrEqualToThreshold": return ">=";
case "GreaterThanThreshold": return ">";
case "LessThanThreshold": return "<";
case "LessThanOrEqualToThreshold": return "<=";
default: throw new Error(`Unexpected comparison operator: ${op}`);
}
}
function validatePeriod(period) {
// valid values for period are 1, 5, 10, 30, or any multiple of 60
if (period !== 1 && period !== 5 && period !== 10 && period !== 30 && period % 60 !== 0) {
throw new Error("Valid values for [args.period] are 1, 5, 10, 30, or any multiple of 60");
}
return period;
}
function validateStatistics(statistic, extendedStatistic) {
if (statistic === undefined && extendedStatistic === undefined) {
return "Average";
}
if (statistic !== undefined && extendedStatistic !== undefined) {
throw new Error("Only provide one of [args.statistic] and [args.extendedStatistic]");
}
return statistic;
}
function validateExtendedStatistic(extendedStatistic) {
if (extendedStatistic !== undefined) {
if (extendedStatistic < 0 || extendedStatistic > 100) {
throw new Error("[args.extendedStatistic] must be between 0 and 100.");
}
}
return extendedStatistic;
}
//# sourceMappingURL=metric.js.map