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
29 changes: 29 additions & 0 deletions spire/templates/apps-100A.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,35 @@ Resources:
TemplateURL: !Sub ${TemplateUrlPrefix}/dovetail-marketing-form-handler.yml
TimeoutInMinutes: 10

DovetailStreamRecorderStack:
Type: AWS::CloudFormation::Stack
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
Properties:
Parameters:
NestedChangeSetScrubbingResourcesState: !Ref NestedChangeSetScrubbingResourcesState
EnvironmentType: !Ref EnvironmentType
EnvironmentTypeAbbreviation: !Ref EnvironmentTypeAbbreviation
AwsOrganizationId: !Ref AwsOrganizationId
RegionMode: !Ref RegionMode
RootStackName: !Ref RootStackName
RootStackId: !Ref RootStackId
CodeS3Bucket: !Ref DeploymentPackageBucketName
CodeS3ObjectKey: !Sub /prx/${EnvironmentTypeAbbreviation}/Spire/Dovetail-Stream_Recorder/pkg/s3-object-key
StreamRecorderConfigUrl: !Sub /prx/${EnvironmentTypeAbbreviation}/Spire/Dovetail-Stream_Recorder/config-url
StreamRecorderOxbowSnsTopicArns: !Sub /prx/${EnvironmentTypeAbbreviation}/Spire/Dovetail-Stream_Recorder/${AWS::Region}/oxbow-sns-topic-arns
Comment thread
farski marked this conversation as resolved.
Tags:
- { Key: prx:meta:tagging-version, Value: "2021-04-07" }
- { Key: prx:cloudformation:stack-name, Value: !Ref AWS::StackName }
- { Key: prx:cloudformation:stack-id, Value: !Ref AWS::StackId }
- { Key: prx:cloudformation:root-stack-name, Value: !Ref RootStackName }
- { Key: prx:cloudformation:root-stack-id, Value: !Ref RootStackId }
- { Key: prx:ops:environment, Value: !Ref EnvironmentType }
- { Key: prx:dev:family, Value: Dovetail }
- { Key: prx:dev:application, Value: Stream Recorder }
TemplateURL: !Sub ${TemplateUrlPrefix}/dovetail-stream-recorder.yml
TimeoutInMinutes: 5

DovetailTrafficStack:
Type: AWS::CloudFormation::Stack
Condition: IsStaging # Staging only
Expand Down
244 changes: 244 additions & 0 deletions spire/templates/apps/dovetail-stream-recorder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# stacks/apps/dovetail-stream-recorder.yml
# 100A
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Description: >-
Creates a Lambda function that orchestrates audio stream recordings

Parameters:
kMetricFilterNamespace:
Type: String
Default: PRX/Dovetail/StreamRecorder
#######
NestedChangeSetScrubbingResourcesState: { Type: String }
EnvironmentType: { Type: String }
EnvironmentTypeAbbreviation: { Type: String }
AwsOrganizationId: { Type: String }
RegionMode: { Type: String }
RootStackName: { Type: String }
RootStackId: { Type: String }
CodeS3Bucket: { Type: String }
CodeS3ObjectKey: { Type: AWS::SSM::Parameter::Value<String> }
StreamRecorderConfigUrl: { Type: AWS::SSM::Parameter::Value<String> }
StreamRecorderOxbowSnsTopicArns: { Type: AWS::SSM::Parameter::Value<String> }

Conditions:
EnableNestedChangeSetScrubbingResources: !Equals [!Ref NestedChangeSetScrubbingResourcesState, Enabled]

Resources:
NestedChangeSetScrubber: { Type: AWS::SNS::Topic, Condition: EnableNestedChangeSetScrubbingResources }

StreamRecorderBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
LifecycleConfiguration:
Rules:
- ExpirationInDays: 14
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- { Key: prx:meta:tagging-version, Value: "2021-04-07" }
- { Key: prx:cloudformation:stack-name, Value: !Ref AWS::StackName }
- { Key: prx:cloudformation:stack-id, Value: !Ref AWS::StackId }
- { Key: prx:cloudformation:root-stack-name, Value: !Ref RootStackName }
- { Key: prx:cloudformation:root-stack-id, Value: !Ref RootStackId }
- { Key: prx:ops:environment, Value: !Ref EnvironmentType }
- { Key: prx:dev:family, Value: Dovetail }
- { Key: prx:dev:application, Value: Stream Recorder }
StreamRecorderBucketPolicy:
Type: AWS::S3::BucketPolicy
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
Bucket: !Ref StreamRecorderBucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Condition:
StringEquals:
aws:PrincipalOrgID: !Ref AwsOrganizationId
Effect: Allow
Principal:
AWS: "*"
Resource: !Sub ${StreamRecorderBucket.Arn}/*
Sid: AllowOrganizationObjectRead
Comment thread
cavis marked this conversation as resolved.
Version: "2012-10-17"

StreamRecorderFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri:
Bucket: !Ref CodeS3Bucket
Key: !Ref CodeS3ObjectKey
Description: !Sub ${EnvironmentType} Stream Recorder
Environment:
Variables:
BUFFER_START: "300"
BUFFER_END: "300"
CONFIG_URL: !Ref StreamRecorderConfigUrl
OXBOW_SNS_TOPICS: !Ref StreamRecorderOxbowSnsTopicArns
Comment thread
cavis marked this conversation as resolved.
OXBOW_STARTUP_TIME: "120"
OXBOW_WIP_TIME: "30"
S3_BUCKET: !Ref StreamRecorderBucket
Events:
ScheduleTrigger:
Properties:
Schedule: !Sub rate(1 minute)
# TODO: remove after testing redundant recording callbacks
Enabled: !Equals [!Ref RegionMode, Primary]
Type: Schedule
Handler: index.handler
MemorySize: 256
Runtime: nodejs24.x
Policies:
- Statement:
- Action: s3:ListBucket
Effect: Allow
Resource: !GetAtt StreamRecorderBucket.Arn
- Action:
- s3:GetObject
- s3:GetObjectAcl
- s3:GetObjectTagging
- s3:PutObject
- s3:PutObjectAcl
- s3:PutObjectTagging
Effect: Allow
Resource: !Sub ${StreamRecorderBucket.Arn}/*
Comment thread
cavis marked this conversation as resolved.
Version: "2012-10-17"
Tags:
prx:meta:tagging-version: "2021-04-07"
prx:cloudformation:stack-name: !Ref AWS::StackName
prx:cloudformation:stack-id: !Ref AWS::StackId
prx:cloudformation:root-stack-name: !Ref RootStackName
prx:cloudformation:root-stack-id: !Ref RootStackId
prx:ops:environment: !Ref EnvironmentType
prx:dev:family: Dovetail
prx:dev:application: Stream Recorder
Timeout: 60
StreamRecorderFunctionLogGroup:
Type: AWS::Logs::LogGroup
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
LogGroupName: !Sub /aws/lambda/${StreamRecorderFunction}
RetentionInDays: 14
Tags:
- { Key: prx:meta:tagging-version, Value: "2021-04-07" }
- { Key: prx:cloudformation:stack-name, Value: !Ref AWS::StackName }
- { Key: prx:cloudformation:stack-id, Value: !Ref AWS::StackId }
- { Key: prx:cloudformation:root-stack-name, Value: !Ref RootStackName }
- { Key: prx:cloudformation:root-stack-id, Value: !Ref RootStackId }
- { Key: prx:ops:environment, Value: !Ref EnvironmentType }
- { Key: prx:dev:family, Value: Dovetail }
- { Key: prx:dev:application, Value: Stream Recorder }

StreamRecorderElevatedErrorAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub WARN [Stream-Recorder] Lambda function <${EnvironmentTypeAbbreviation}> INVOCATIONS ERRORS (${RootStackName})
AlarmDescription: !Sub >-
${EnvironmentType} Stream Recorder Lambda function is failing, so it
may not be capturing audio streams!
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: FunctionName
Value: !Ref StreamRecorderFunction
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 60
Statistic: Sum
Tags:
- { Key: prx:meta:tagging-version, Value: "2021-04-07" }
- { Key: prx:cloudformation:stack-name, Value: !Ref AWS::StackName }
- { Key: prx:cloudformation:stack-id, Value: !Ref AWS::StackId }
- { Key: prx:cloudformation:root-stack-name, Value: !Ref RootStackName }
- { Key: prx:cloudformation:root-stack-id, Value: !Ref RootStackId }
- { Key: prx:ops:environment, Value: !Ref EnvironmentType }
- { Key: prx:dev:family, Value: Dovetail }
- { Key: prx:dev:application, Value: Stream Recorder }
Threshold: 0
TreatMissingData: notBreaching

StreamRecorderFunctionWarnLevelLogMetricFilter:
Type: AWS::Logs::MetricFilter
Properties:
FilterPattern: '{ $._logLevel = "warn" }'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know there's prior art for these log-level-based alarms, but I always sort of wonder why we don't just emit specific CloudWatch metrics and alarm on those. If things are getting to a point in the code where we are able to decide to log warn vs. error, we probably can equally decide what metric/value to publish, and then these alarms become a lot more meaningful.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Emit metrics" meaning... via the aws sdk?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the downsides are:

  • One more dependency to install, permission, and stub out in the tests
  • I'd still want the log output anyways, even if we also directly send the CW event

LogGroupName: !Ref StreamRecorderFunctionLogGroup
MetricTransformations:
- MetricName: !Sub warns_${StreamRecorderFunction}
MetricNamespace: !Ref kMetricFilterNamespace
MetricValue: "1"
StreamRecorderFunctionLoggedWarnAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub WARN [Stream-Recorder] Lambda function <${EnvironmentTypeAbbreviation}> LOGGED WARNINGS (${RootStackName})
AlarmDescription: !Sub >-
${EnvironmentType} Stream Recorder Lambda function has logged some warnings.
ComparisonOperator: GreaterThanThreshold
EvaluationPeriods: 1
MetricName: !Sub warns_${StreamRecorderFunction}
Namespace: !Ref kMetricFilterNamespace
Period: 60
Statistic: Sum
Tags:
- { Key: prx:meta:tagging-version, Value: "2021-04-07" }
- { Key: prx:cloudformation:stack-name, Value: !Ref AWS::StackName }
- { Key: prx:cloudformation:stack-id, Value: !Ref AWS::StackId }
- { Key: prx:cloudformation:root-stack-name, Value: !Ref RootStackName }
- { Key: prx:cloudformation:root-stack-id, Value: !Ref RootStackId }
- { Key: prx:ops:environment, Value: !Ref EnvironmentType }
- { Key: prx:ops:cloudwatch-log-group-name, Value: !Ref StreamRecorderFunctionLogGroup }
- { Key: prx:dev:family, Value: Dovetail }
- { Key: prx:dev:application, Value: Stream Recorder }
Threshold: 0
TreatMissingData: notBreaching

StreamRecorderFunctionErrorLevelLogMetricFilter:
Type: AWS::Logs::MetricFilter
Properties:
FilterPattern: '{ $._logLevel = "error" }'
LogGroupName: !Ref StreamRecorderFunctionLogGroup
MetricTransformations:
- MetricName: !Sub errors_${StreamRecorderFunction}
MetricNamespace: !Ref kMetricFilterNamespace
MetricValue: "1"
StreamRecorderFunctionLoggedErrorAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub ERROR [Stream-Recorder] Lambda function <${EnvironmentTypeAbbreviation}> LOGGED ERRORS (${RootStackName})
AlarmDescription: !Sub >-
${EnvironmentType} Stream Recorder Lambda function has logged some errors.
ComparisonOperator: GreaterThanThreshold
EvaluationPeriods: 1
MetricName: !Sub errors_${StreamRecorderFunction}
Namespace: !Ref kMetricFilterNamespace
Period: 60
Statistic: Sum
Tags:
- { Key: prx:meta:tagging-version, Value: "2021-04-07" }
- { Key: prx:cloudformation:stack-name, Value: !Ref AWS::StackName }
- { Key: prx:cloudformation:stack-id, Value: !Ref AWS::StackId }
- { Key: prx:cloudformation:root-stack-name, Value: !Ref RootStackName }
- { Key: prx:cloudformation:root-stack-id, Value: !Ref RootStackId }
- { Key: prx:ops:environment, Value: !Ref EnvironmentType }
- { Key: prx:ops:cloudwatch-log-group-name, Value: !Ref StreamRecorderFunctionLogGroup }
- { Key: prx:dev:family, Value: Dovetail }
- { Key: prx:dev:application, Value: Stream Recorder }
Threshold: 0
TreatMissingData: notBreaching

Outputs:
StreamRecorderFunctionArn:
Value: !GetAtt StreamRecorderFunction.Arn
StreamRecorderBucketArn:
Value: !GetAtt StreamRecorderBucket.Arn
StreamRecorderBucketRegionalDomainName:
Value: !GetAtt StreamRecorderBucket.RegionalDomainName