This repository has been archived by the owner on Apr 19, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding support to new AWS Service (#127)
* IVS Support
- Loading branch information
Showing
13 changed files
with
850 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
provider-utils/awscloudformation/cloudformation-templates/ivs-helpers/IVS-Channel.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
Description: S3 Workflow | ||
|
||
Parameters: | ||
pProjectName: | ||
Type: String | ||
Description: ProjectName | ||
AllowedPattern: "[a-zA-Z][a-zA-Z0-9-_]*" | ||
Default: DefaultName | ||
pFunctionArn: | ||
Type: String | ||
Description: Name of function | ||
Default: arn-default | ||
Outputs: | ||
oVideoChannelArn: | ||
Value: !GetAtt rIVSChannel.arn | ||
oVideoOutput: | ||
Value: !GetAtt rIVSChannel.playbackUrl | ||
oVideoInputURL: | ||
Value: !GetAtt rIVSChannel.ingestURL | ||
oVideoInputKey: | ||
Value: !GetAtt rIVSChannel.streamKeyValue | ||
|
||
Resources: | ||
rIVSChannel: | ||
Type: "Custom::StarfruitChannel" | ||
Properties: | ||
ServiceToken: !Ref pFunctionArn | ||
name: !Ref pProjectName | ||
API: VideoChannel |
70 changes: 70 additions & 0 deletions
70
provider-utils/awscloudformation/cloudformation-templates/ivs-helpers/IVS-Custom.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
Description: IVS Shim Template | ||
|
||
Parameters: | ||
pS3: | ||
Type: String | ||
Description: Store template and lambda package | ||
AllowedPattern: "[a-zA-Z][a-zA-Z0-9-_]*" | ||
Default: amazonbooth | ||
pFunctionName: | ||
Type: String | ||
Description: Name of function | ||
Default: default | ||
pFunctionHash: | ||
Type: String | ||
Description: FunctionHash | ||
Default: default | ||
|
||
Outputs: | ||
oIVSLambda: | ||
Value: !GetAtt rIVSChannelLambda.Arn | ||
Description: Arn for Lambda function | ||
|
||
Resources: | ||
rIVSChannelLambda: | ||
Type: AWS::Lambda::Function | ||
Properties: | ||
FunctionName: !Ref pFunctionName | ||
Description: A shim for IVS support. Can do anything that the API can do | ||
Handler: index.handler | ||
Role: !GetAtt rIVSRole.Arn | ||
Runtime: nodejs12.x | ||
Timeout: 30 | ||
Code: | ||
S3Bucket: !Ref pS3 | ||
S3Key: !Sub | ||
- ivs-helpers/IVSShim-${hash}.zip | ||
- { hash: !Ref pFunctionHash } | ||
|
||
rIVSRole: | ||
Type: AWS::IAM::Role | ||
Properties: | ||
AssumeRolePolicyDocument: | ||
Version: 2012-10-17 | ||
Statement: | ||
- | ||
Effect: Allow | ||
Principal: | ||
Service: | ||
- lambda.amazonaws.com | ||
Action: | ||
- sts:AssumeRole | ||
Policies: | ||
- | ||
PolicyName: !Sub "${AWS::StackName}-internal-trigger-role" | ||
PolicyDocument: | ||
Statement: | ||
- | ||
Effect: Allow | ||
Action: | ||
- ivs:* | ||
Resource: "*" | ||
- | ||
Effect: Allow | ||
Action: | ||
- logs:CreateLogGroup | ||
- logs:CreateLogStream | ||
- logs:DescribeLogStreams | ||
- logs:PutLogEvents | ||
Resource: | ||
- arn:aws:logs:*:*:* |
156 changes: 156 additions & 0 deletions
156
...s/awscloudformation/cloudformation-templates/ivs-helpers/LambdaFunctions/IVSShim/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
/* eslint-disable strict */ | ||
/* eslint-disable global-require */ | ||
/* eslint-disable */ | ||
const crypto = require('crypto'); | ||
const rp = require('request-promise-native'); | ||
|
||
const SigV4Utils = { | ||
sign(key, msg) { | ||
return crypto.createHmac('sha256', key).update(msg).digest().toString('hex'); | ||
}, | ||
sha256(msg) { | ||
return crypto.createHash('sha256').update(msg, 'utf8').digest().toString('hex'); | ||
}, | ||
getSignatureKey(key, dateStamp, regionName, serviceName) { | ||
const kDate = crypto.createHmac('sha256', `AWS4${key}`).update(dateStamp).digest(); | ||
const kRegion = crypto.createHmac('sha256', kDate).update(regionName).digest(); | ||
const kService = crypto.createHmac('sha256', kRegion).update(serviceName).digest(); | ||
const kSigning = crypto.createHmac('sha256', kService).update('aws4_request').digest(); | ||
return kSigning; | ||
}, | ||
}; | ||
|
||
exports.handler = async (event, context) => { | ||
const config = event.ResourceProperties; | ||
let responseData = {}; | ||
switch (event.RequestType) { | ||
case 'Create': | ||
responseData = await createIVS(config); | ||
break; | ||
case 'Delete': | ||
responseData = await deleteIVS(event, config); | ||
break; | ||
case 'Update': | ||
responseData = await updateIVS(event, config); | ||
break; | ||
default: | ||
console.log(`${event.RequestType} is not supported. No changes are applied`); | ||
} | ||
const response = await sendResponse(event, context, 'SUCCESS', responseData); | ||
console.log('CFN STATUS:: ', response, responseData); | ||
}; | ||
|
||
async function createIVS(config) { | ||
if (config.API === 'VideoChannel') { | ||
const flattenResults = {}; | ||
const results = await signAndRequest('POST', '/CreateChannel', config); | ||
flattenResults.arn = results.channel.arn; | ||
flattenResults.playbackUrl = results.channel.playbackUrl; | ||
flattenResults.streamKeyValue = results.streamKey.value; | ||
flattenResults.ingestURL = results.channel.ingestEndpoint; | ||
return flattenResults; | ||
} | ||
return { error: "API doesn't exists" }; | ||
} | ||
|
||
async function updateIVS(config) { | ||
console.log(config); | ||
return {}; | ||
} | ||
|
||
async function deleteIVS(event, config) { | ||
if (config.API === 'VideoChannel') { | ||
const deleteRequest = { | ||
arn: event.PhysicalResourceId, | ||
}; | ||
const results = await signAndRequest('POST', '/DeleteChannel', deleteRequest); | ||
if (results) { | ||
results.arn = event.PhysicalResourceId; | ||
results.message = 'Delete was successful'; | ||
return results; | ||
} | ||
deleteRequest.message = 'Delete was successful'; | ||
return deleteRequest; | ||
} | ||
} | ||
|
||
async function signAndRequest(method, uriRaw, config) { | ||
const service = 'ivs'; | ||
const region = process.env.AWS_REGION; | ||
const accessKey = process.env.AWS_ACCESS_KEY_ID; | ||
const secretKey = process.env.AWS_SECRET_ACCESS_KEY; | ||
const token = encodeURIComponent(process.env.AWS_SESSION_TOKEN); | ||
const algorithm = 'AWS4-HMAC-SHA256'; | ||
const host = 'ivs.us-west-2.amazonaws.com'; | ||
const canonicalUri = uriRaw; | ||
|
||
const now = new Date(); | ||
const amzdate = `${now.toISOString().replace(/[-:]/g, '').split('.')[0]}Z`; | ||
const dateStamp = amzdate.split('T')[0]; | ||
const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`; | ||
let canonicalQuerystring = 'X-Amz-Algorithm=AWS4-HMAC-SHA256'; | ||
canonicalQuerystring += `&X-Amz-Credential=${encodeURIComponent(`${accessKey}/${credentialScope}`)}`; | ||
canonicalQuerystring += `&X-Amz-Date=${amzdate}`; | ||
canonicalQuerystring += '&X-Amz-Expires=86400'; | ||
canonicalQuerystring += `&X-Amz-Security-Token=${token}`; | ||
canonicalQuerystring += '&X-Amz-SignedHeaders=host'; | ||
delete config.ServiceToken; | ||
delete config.API; | ||
|
||
const canonicalHeaders = `host:${host}\n`; | ||
const payloadHash = SigV4Utils.sha256(JSON.stringify(config)); | ||
const canonicalRequest = `${method}\n${canonicalUri}\n${canonicalQuerystring}\n${canonicalHeaders}\nhost\n${payloadHash}`; | ||
|
||
const stringToSign = `${algorithm}\n${amzdate}\n${credentialScope}\n${SigV4Utils.sha256(canonicalRequest)}`; | ||
const signingKey = SigV4Utils.getSignatureKey(secretKey, dateStamp, region, service); | ||
const signature = SigV4Utils.sign(signingKey, stringToSign); | ||
|
||
canonicalQuerystring += `&X-Amz-Signature=${signature}`; | ||
const requestURL = `${host}${canonicalUri}?${canonicalQuerystring}`; | ||
const options = { | ||
method, | ||
uri: `https://${requestURL}`, | ||
body: config, | ||
json: true, | ||
}; | ||
const urlReturn = await rp(options); | ||
console.log(urlReturn); | ||
|
||
return urlReturn; | ||
} | ||
|
||
async function sendResponse(event, context, responseStatus, responseData) { | ||
const responseBodyDictionary = { | ||
Status: responseStatus, | ||
Reason: `See the details in CloudWatch Log Stream: ${context.logStreamName}`, | ||
StackId: event.StackId, | ||
RequestId: event.RequestId, | ||
LogicalResourceId: event.LogicalResourceId, | ||
Data: responseData, | ||
}; | ||
if (responseData !== undefined) { | ||
responseBodyDictionary.PhysicalResourceId = responseData.arn; | ||
responseBodyDictionary.Data = responseData; | ||
} | ||
|
||
const responseBody = JSON.stringify(responseBodyDictionary); | ||
|
||
console.log('RESPONSE BODY:\n', responseBody); | ||
|
||
const options = { | ||
method: 'PUT', | ||
uri: event.ResponseURL, | ||
body: responseBody, | ||
headers: { | ||
'content-type': '', | ||
'content-length': responseBody.length, | ||
}, | ||
}; | ||
const urlReturn = await rp(options); | ||
console.log('urlReturn: ', urlReturn); | ||
|
||
console.log('options: ', options); | ||
|
||
console.log('SENDING RESPONSE...\n'); | ||
return urlReturn; | ||
} |
Oops, something went wrong.