LambdaFunctionResource.java

// Generated by delombok at Tue Apr 06 14:11:35 UTC 2021
/*
 * This file is part of the pl.wrzasq.lambda.
 *
 * @license http://mit-license.org/ The MIT license
 * @copyright 2019 - 2020 © by Rafał Wrzeszcz - Wrzasq.pl.
 */
package pl.wrzasq.lambda.macro.lambda.function.template;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import pl.wrzasq.commons.aws.cloudformation.macro.TemplateUtils;

/**
 * Model for handled resource.
 */
public class LambdaFunctionResource {
    /**
     * Resource type identifier.
     */
    public static final String LAMBDA_RESOURCE_TYPE = "WrzasqPl::Lambda::Function";
    /**
     * Serverless resource type identifier.
     */
    public static final String SERVERLESS_RESOURCE_TYPE = "WrzasqPl::Serverless::Function";
    /**
     * Metrics namespace.
     */
    private static final String METRICS_NAMESPACE = "WrzasqPl/Lambda";
    /**
     * Default retention period for logs (in days).
     */
    private static final Number DEFAULT_LOGS_RETENTION_DAYS = 7;
    /**
     * Default alarm check period (in seconds).
     */
    private static final Number DEFAULT_ALERT_PERIOD = 300;
    /**
     * Value used for counting metrics (each metric record is single occurrence).
     */
    private static final String COUNTER_METRIC_VALUE = "1";
    /**
     * LogGroupName property name.
     */
    private static final String PROPERTY_LOG_GROUP_NAME = "LogGroupName";
    /**
     * LogsRetentionInDays property name.
     */
    private static final String PROPERTY_LOGS_RETENTION_IN_DAYS = "LogsRetentionInDays";
    /**
     * Namespace property name.
     */
    private static final String PROPERTY_NAMESPACE = "Namespace";
    /**
     * MetricName property name.
     */
    private static final String PROPERTY_METRIC_NAME = "MetricName";
    /**
     * Statistic property name.
     */
    private static final String PROPERTY_STATISTIC = "Statistic";
    /**
     * ComparisonOperator property name.
     */
    private static final String PROPERTY_COMPARISON_OPERATOR = "ComparisonOperator";
    /**
     * Threshold property name.
     */
    private static final String PROPERTY_THRESHOLD = "Threshold";
    /**
     * EvaluationPeriods property name.
     */
    private static final String PROPERTY_EVALUATION_PERIODS = "EvaluationPeriods";
    /**
     * Period property name.
     */
    private static final String PROPERTY_PERIOD = "Period";
    /**
     * TreatMissingData property name.
     */
    private static final String PROPERTY_TREAT_MISSING_DATA = "TreatMissingData";
    /**
     * MetricValue property name.
     */
    private static final String PROPERTY_METRIC_VALUE = "MetricValue";
    /**
     * MetricNamespace property name.
     */
    private static final String PROPERTY_METRIC_NAMESPACE = "MetricNamespace";
    /**
     * MetricTransformations property name.
     */
    private static final String PROPERTY_METRIC_TRANSFORMATIONS = "MetricTransformations";
    /**
     * FilterPattern property name.
     */
    private static final String PROPERTY_FILTER_PATTERN = "FilterPattern";
    /**
     * Resource name.
     */
    private String logicalId;
    /**
     * Resource mode (`Lambda` or `Serverless`).
     */
    private String mode;

    /**
     * Builds definition of physical resources.
     *
     * @param properties Properties for our custom resource.
     * @return Definitions of all resources.
     */
    public Map<String, Object> buildDefinitions(Map<String, Object> properties) {
        var resources = new HashMap<String, Object>();
        // generate all sub-resources
        this.createLogGroup(resources, properties);
        this.createMemoryMetricFilter(resources);
        this.createErrorsMetricFilter(resources, properties);
        this.createErrorsAlarm(resources, properties);
        this.createWarningsMetricFilter(resources, properties);
        // leave all properties as function properties
        this.createLambdaFunction(resources, properties);
        return resources;
    }

    /**
     * Creates log group resource definition.
     *
     * @param resources Resources container.
     * @param properties Resource properties.
     */
    private void createLogGroup(Map<String, Object> resources, Map<String, Object> properties) {
        // default setup
        var resourceProperties = new HashMap<String, Object>();
        resourceProperties.put(LambdaFunctionResource.PROPERTY_LOG_GROUP_NAME, TemplateUtils.sub(String.format("/aws/lambda/${%s}", this.logicalId)));
        TemplateUtils.popProperty(properties, LambdaFunctionResource.PROPERTY_LOGS_RETENTION_IN_DAYS, value -> resourceProperties.put("RetentionInDays", value), LambdaFunctionResource.DEFAULT_LOGS_RETENTION_DAYS);
        this.generateResource(resources, "LogGroup", "Logs::LogGroup", resourceProperties);
    }

    /**
     * Creates memory metric filter resource definition.
     *
     * @param resources Resources container.
     */
    private void createMemoryMetricFilter(Map<String, Object> resources) {
        this.createMetricFilter(resources, "MemoryMetricFilter", "Memory", "$max_memory_used", "[label=\"REPORT\", " + "..., " + "memory_label=\"Used:\", " + "max_memory_used, unit=\"MB\", " + "xray_label=\"XRAY\", " + "trace_label=\"TraceId:\", " + "traced, " + "segment_label=\"SegmentId:\", " + "segment, " + "sampled_label=\"Sampled:\", " + "sampled_value]");
    }

    /**
     * Creates errors metric filter resource definition.
     *
     * @param resources Resources container.
     * @param properties Resource properties.
     */
    private void createErrorsMetricFilter(Map<String, Object> resources, Map<String, Object> properties) {
        TemplateUtils.popProperty(properties, "ErrorsFilterPattern", filter -> this.createMetricFilter(resources, "ErrorsMetricFilter", "Errors", LambdaFunctionResource.COUNTER_METRIC_VALUE, filter), "ERROR -LOG_ERROR");
    }

    /**
     * Creates errors alarm resource definition.
     *
     * @param resources Resources container.
     * @param properties Resource properties.
     */
    private void createErrorsAlarm(Map<String, Object> resources, Map<String, Object> properties) {
        var resourceProperties = new HashMap<String, Object>();
        resourceProperties.put(LambdaFunctionResource.PROPERTY_NAMESPACE, LambdaFunctionResource.METRICS_NAMESPACE);
        resourceProperties.put(LambdaFunctionResource.PROPERTY_METRIC_NAME, TemplateUtils.sub(String.format("${%s}-Errors", this.logicalId)));
        resourceProperties.put(LambdaFunctionResource.PROPERTY_STATISTIC, "Sum");
        resourceProperties.put(LambdaFunctionResource.PROPERTY_COMPARISON_OPERATOR, "GreaterThanThreshold");
        resourceProperties.put(LambdaFunctionResource.PROPERTY_THRESHOLD, 0);
        resourceProperties.put(LambdaFunctionResource.PROPERTY_EVALUATION_PERIODS, 1);
        resourceProperties.put(LambdaFunctionResource.PROPERTY_PERIOD, LambdaFunctionResource.DEFAULT_ALERT_PERIOD);
        resourceProperties.put(LambdaFunctionResource.PROPERTY_TREAT_MISSING_DATA, "notBreaching");
        TemplateUtils.popProperty(properties, "ErrorsAlarmActions", value -> resourceProperties.put("AlarmActions", value), null);
        this.generateResource(resources, "ErrorsAlarm", "CloudWatch::Alarm", resourceProperties);
    }

    /**
     * Creates warnings metric filter resource definition.
     *
     * @param resources Resources container.
     * @param properties Resource properties.
     */
    private void createWarningsMetricFilter(Map<String, Object> resources, Map<String, Object> properties) {
        TemplateUtils.popProperty(properties, "WarningsFilterPattern", filter -> this.createMetricFilter(resources, "WarningsMetricFilter", "Warnings", LambdaFunctionResource.COUNTER_METRIC_VALUE, filter), "WARN");
    }

    /**
     * Generic method for metric filter creation.
     *
     * @param resources Resources container.
     * @param resourceName Resource logical ID.
     * @param metricNameSuffix Metric name postfix.
     * @param metricValue Metric value.
     * @param filterPattern Metric log filter pattern.
     */
    private void createMetricFilter(Map<String, Object> resources, String resourceName, String metricNameSuffix, String metricValue, Object filterPattern) {
        var transformation = new HashMap<>();
        transformation.put(LambdaFunctionResource.PROPERTY_METRIC_NAMESPACE, LambdaFunctionResource.METRICS_NAMESPACE);
        transformation.put(LambdaFunctionResource.PROPERTY_METRIC_NAME, TemplateUtils.sub(String.format("${%s}-%s", this.logicalId, metricNameSuffix)));
        transformation.put(LambdaFunctionResource.PROPERTY_METRIC_VALUE, metricValue);
        var resourceProperties = new HashMap<String, Object>();
        resourceProperties.put(LambdaFunctionResource.PROPERTY_LOG_GROUP_NAME, TemplateUtils.ref(this.getLogGroupLogicalId()));
        resourceProperties.put(LambdaFunctionResource.PROPERTY_METRIC_TRANSFORMATIONS, Collections.singletonList(transformation));
        // note that this is not a string - it may be defined as a CloudFormation function call!
        resourceProperties.put(LambdaFunctionResource.PROPERTY_FILTER_PATTERN, filterPattern);
        this.generateResource(resources, resourceName, "Logs::MetricFilter", resourceProperties);
    }

    /**
     * Creates function resource definition.
     *
     * @param resources Resources container.
     * @param properties Resource properties.
     */
    private void createLambdaFunction(Map<String, Object> resources, Map<String, Object> properties) {
        this.generateResource(resources, "", String.format("%s::Function", this.mode), properties);
    }

    /**
     * Builds logical ID of LogGroup resource.
     *
     * @return LogGroup logical ID.
     */
    public String getLogGroupLogicalId() {
        return String.format("%sLogGroup", this.logicalId);
    }

    /**
     * Adds resource to collection.
     *
     * @param resources Resources container.
     * @param suffix Resource logical ID suffix.
     * @param type CloudFormation resource type.
     * @param properties Resource configuration.
     */
    private void generateResource(Map<String, Object> resources, String suffix, String type, Map<String, Object> properties) {
        resources.put(String.format("%s%s", this.logicalId, suffix), TemplateUtils.generateResource(type, properties, null));
    }

    @SuppressWarnings("all")
    @lombok.Generated
    public LambdaFunctionResource(final String logicalId, final String mode) {
        this.logicalId = logicalId;
        this.mode = mode;
    }
}