Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
Support multiple backends & minor fixes (#255)
Browse files Browse the repository at this point in the history
* fix(props): Extend code to support individual props files & support multiple backend envs

* fix(multiEnv): committing WIP - half-working test script for generating new props from existing props

* fix(video-staging): Added logic for generating unique props from existing video resource

* fix(build): Added logic to build props from existing resource when build is invoked

* fix(tokenGen): Fix CloudFront Token Gen Lambda (as per pull #247)

* fix(tokenGen): Added env interpolation to tokenGen GQL schema (as per pr #219)

* fix(gqlSchema): Capitalize GQL models for Admin-UI compliance/friendliness

* fix(tagging): Added resource-level tagging in accordance with pr #205
  • Loading branch information
armenr authored Jun 4, 2021
1 parent ffd86fb commit 990015c
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 19 deletions.
50 changes: 48 additions & 2 deletions commands/video/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ module.exports = {
name: subcommand,
run: async (context) => {
const { amplify } = context;
const targetDir = amplify.pathManager.getBackendDirPath();
const amplifyMeta = amplify.getProjectMeta();
const targetDir = amplify.pathManager.getBackendDirPath();
const projectDetails = context.amplify.getProjectDetails();
const newEnvName = projectDetails.localEnvInfo.envName;
const resourceFilesBaseDir = `${targetDir}/video/${shared.resourceName}/`;
const resourceFilesList = fs.readdirSync(resourceFilesBaseDir);

if (!(category in amplifyMeta) || Object.keys(amplifyMeta[category]).length === 0) {
context.print.error(`You have no ${category} projects.`);
Expand All @@ -35,7 +39,49 @@ module.exports = {
return;
}

const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${shared.resourceName}/props.json`));
// Check if env-specific props file already exists
const hasOwnEnvProps = resourceFilesList.includes(`${newEnvName}-props.json`);

// Check if ANY props file exist for a different env in this project || returns array
const hasAnyEnvProps = resourceFilesList.find(fileName => fileName.includes('-props.json'));

// If this env doesn't have its own props AND there is an existing amplify-video resource
if (!hasOwnEnvProps && hasAnyEnvProps) {
// take the first props file you find and copy that!
const propsFilenameToCopy = resourceFilesList.filter(propsFileName => propsFileName.includes('-props.json'))[0];

// extract substring for the existing env's name we're going to copy over
const envNameToReplace = propsFilenameToCopy.substr(0, propsFilenameToCopy.indexOf('-'));

// read JSON from the existing env's props file
const existingPropsToMutate = JSON.parse(fs.readFileSync(`${resourceFilesBaseDir}/${propsFilenameToCopy}`));

const searchAndReplaceProps = () => {
const newPropsObj = {};
// eslint-disable-next-line no-restricted-syntax
for (const [key, value] of Object.entries(existingPropsToMutate.contentDeliveryNetwork)) {
// look for any string values that contain existing env's name
if (typeof value === 'string' && value.includes(`${envNameToReplace}`)) {
// replace with new env name
const newValue = value.replace(new RegExp(envNameToReplace, 'g'), `${newEnvName}`);
newPropsObj[key] = newValue;
} else {
// copy existing values that do not match replacement conditions aka "generic props"
newPropsObj[key] = value;
}
}
return newPropsObj;
};

// merge new props and existing generic props
const newPropsToSave = Object.assign(
existingPropsToMutate, { contentDeliveryNetwork: searchAndReplaceProps() },
);

fs.writeFileSync(`${resourceFilesBaseDir}/${newEnvName}-props.json`, JSON.stringify(newPropsToSave, null, 4));
}

const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${shared.resourceName}/${projectDetails.localEnvInfo.envName}-props.json`));

return buildTemplates(context, props);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ Resources:
ServiceToken: !Ref pFunctionArn
name: !Ref pProjectName
type: !Ref pQuality
latencyMode: !Ref pLatencyMode
latencyMode: !Ref pLatencyMode
tags: {"amplify-video" : "amplify-video"}
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@
"rDistribution" : {
"Type": "AWS::CloudFront::Distribution",
"Properties": {
"Tags" : [{
"Key" : "amplify-video",
"Value" : "amplify-video"
}],
"DistributionConfig": {
"Origins": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ Resources:
rCloudFrontDist:
Type: AWS::CloudFront::Distribution
Properties:
Tags:
- Key: amplify-video
Value: amplify-video
DistributionConfig:
DefaultCacheBehavior:
ForwardedValues:
Expand All @@ -40,8 +43,8 @@ Resources:
TrustedKeyGroups:
- !Ref rCloudFrontKeyGroup
<% } %>
Origins:
-
Origins:
-
DomainName: !Ref pBucketUrl
Id: vodS3Origin
S3OriginConfig:
Expand Down Expand Up @@ -72,4 +75,4 @@ Outputs:
<% } %>
oCFDomain:
Value: !GetAtt rCloudFrontDist.DomainName
Description: Domain for our videos
Description: Domain for our videos
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ async function createJob(eventObject) {
UserMetadata: {},
Role: process.env.MC_ROLE,
Settings: jobSettings,
Tags: { 'amplify-video': 'amplify-video' },
};
await mcClient.createJob(jobParams).promise();
}
6 changes: 4 additions & 2 deletions provider-utils/awscloudformation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ let serviceMetadata;
async function addResource(context, service, options) {
serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const targetDir = context.amplify.pathManager.getBackendDirPath();
const projectDetails = context.amplify.getProjectDetails();
const { serviceWalkthroughFilename, defaultValuesFilename } = serviceMetadata;
const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`;
const { serviceQuestions } = require(serviceWalkthroughSrc);
Expand All @@ -23,21 +24,22 @@ async function addResource(context, service, options) {
if (result.parameters !== undefined) {
await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/parameters.json`, JSON.stringify(result.parameters, null, 4));
}
await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/props.json`, JSON.stringify(result, null, 4));
await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/${projectDetails.localEnvInfo.envName}-props.json`, JSON.stringify(result, null, 4));
await buildTemplates(context, result);
}

async function updateResource(context, service, options, resourceName) {
serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const targetDir = context.amplify.pathManager.getBackendDirPath();
const projectDetails = context.amplify.getProjectDetails();
const { serviceWalkthroughFilename, defaultValuesFilename } = serviceMetadata;
const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`;
const { serviceQuestions } = require(serviceWalkthroughSrc);
const result = await serviceQuestions(context, options, defaultValuesFilename, resourceName);
if (result.parameters !== undefined) {
await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/parameters.json`, JSON.stringify(result.parameters, null, 4));
}
await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/props.json`, JSON.stringify(result, null, 4));
await fs.writeFileSync(`${targetDir}/video/${result.shared.resourceName}/${projectDetails.localEnvInfo.envName}-props.json`, JSON.stringify(result, null, 4));
await buildTemplates(context, result);
context.print.success(`Successfully updated ${result.shared.resourceName}`);
}
Expand Down
10 changes: 5 additions & 5 deletions provider-utils/awscloudformation/schemas/schema.graphql.ejs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


type vodAsset @model (subscriptions: {level: public})
type VodAsset @model (subscriptions: {level: public})
@auth(
rules: [
<% if (locals.permissions && locals.permissions.permissionSchema.includes("any")) { -%>
Expand All @@ -21,11 +21,11 @@ type vodAsset @model (subscriptions: {level: public})
description:String!

#DO NOT EDIT
video:videoObject @connection
}
video:VideoObject @connection
}

#DO NOT EDIT
type videoObject @model
type VideoObject @model
@auth(
rules: [
<% if (locals.permissions && locals.permissions.permissionSchema.includes("any")) { -%>
Expand All @@ -45,4 +45,4 @@ type videoObject @model
<% if (contentDeliveryNetwork.signedKey) { -%>
token: String @function(name: "<%= contentDeliveryNetwork.functionNameSchema %>")
<% } -%>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc
const defaultLocation = path.resolve(`${__dirname}/../default-values/${defaultValuesFilename}`);
let resource = {};
const targetDir = amplify.pathManager.getBackendDirPath();
const projectDetails = context.amplify.getProjectDetails();
let advancedAnswers = {};
let mediaLiveAnswers = {};
let mediaPackageAnswers;
Expand All @@ -24,7 +25,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc

// TODO: find a way to use default in new question files
try {
const oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/props.json`));
const oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/${projectDetails.localEnvInfo.envName}-props.json`));
Object.assign(defaults, oldValues);
} catch (err) {
// Do nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc
props.shared = nameDict;
// TODO: find a way of using default values from new question
try {
oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/props.json`));
oldValues = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/${projectDetails.localEnvInfo.envName}-props.json`));
Object.assign(defaults, oldValues);
} catch (err) {
// Do nothing
Expand Down Expand Up @@ -340,7 +340,7 @@ async function createCDN(context, props, options, aws, oldValues) {
const uuid = Math.random().toString(36).substring(2, 6)
+ Math.random().toString(36).substring(2, 6);
const secretName = `${props.shared.resourceName}-${projectDetails.localEnvInfo.envName}-pem-${uuid}`.slice(0, 63);
const rPublicName = `rCloudFrontPublicKey${uuid}`.slice(0, 63);
const rPublicName = `rCloudFrontPublicKey${projectDetails.localEnvInfo.envName}${uuid}`.slice(0, 63);
const publicKeyName = `${props.shared.resourceName}-${projectDetails.localEnvInfo.envName}-publickey-${uuid}`.slice(0, 63);
const smClient = new aws.SecretsManager({ apiVersion: '2017-10-17' });
const createSecretParams = {
Expand Down
3 changes: 2 additions & 1 deletion provider-utils/awscloudformation/utils/livestream-obs.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ async function setupOBS(context, resourceName) {
async function createConfig(context, projectConfig, projectName) {
// check for obs installation!
let profileDir = '';
const projectDetails = context.amplify.getProjectDetails();
if (process.platform === 'darwin') {
profileDir = `${process.env.HOME}/Library/Application Support/obs-studio/basic/profiles/`;
} else if (process.platform === 'win32') {
Expand All @@ -45,7 +46,7 @@ async function createConfig(context, projectConfig, projectName) {
generateServiceLive(profileDir, projectConfig.output.oMediaLivePrimaryIngestUrl);
} else if (projectConfig.serviceType === 'ivs') {
const targetDir = context.amplify.pathManager.getBackendDirPath();
const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${projectName}/props.json`));
const props = JSON.parse(fs.readFileSync(`${targetDir}/video/${projectName}/${projectDetails.localEnvInfo.envName}-props.json`));
generateINIIVS(projectName, profileDir, props);
generateServiceIVS(profileDir, projectConfig.output);
}
Expand Down
53 changes: 51 additions & 2 deletions provider-utils/awscloudformation/utils/video-staging.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,57 @@ async function pushTemplates(context) {
async function build(context, resourceName, projectType, props) {
const { amplify } = context;
const targetDir = amplify.pathManager.getBackendDirPath();
const projectDetails = context.amplify.getProjectDetails();

const newEnvName = projectDetails.localEnvInfo.envName;
const resourceFilesBaseDir = `${targetDir}/video/${resourceName}/`;
const resourceFilesList = fs.readdirSync(resourceFilesBaseDir);


// Check if env-specific props file already exists
const hasOwnEnvProps = resourceFilesList.includes(`${newEnvName}-props.json`);

// Check if ANY props file exist for a different env in this project || returns array
const hasAnyEnvProps = resourceFilesList.find(fileName => fileName.includes('-props.json'));

// If this env doesn't have its own props AND there is an existing amplify-video resource
if (!hasOwnEnvProps && hasAnyEnvProps) {
// take the first props file you find and copy that!
const propsFilenameToCopy = resourceFilesList.filter(propsFileName => propsFileName.includes('-props.json'))[0];

// extract substring for the existing env's name we're going to copy over
const envNameToReplace = propsFilenameToCopy.substr(0, propsFilenameToCopy.indexOf('-'));

// read JSON from the existing env's props file
const existingPropsToMutate = JSON.parse(fs.readFileSync(`${resourceFilesBaseDir}/${propsFilenameToCopy}`));

const searchAndReplaceProps = () => {
const newPropsObj = {};
// eslint-disable-next-line no-restricted-syntax
for (const [key, value] of Object.entries(existingPropsToMutate.contentDeliveryNetwork)) {
// look for any string values that contain existing env's name
if (typeof value === 'string' && value.includes(`${envNameToReplace}`)) {
// replace with new env name
const newValue = value.replace(new RegExp(envNameToReplace, 'g'), `${newEnvName}`);
newPropsObj[key] = newValue;
} else {
// copy existing values that do not match replacement conditions aka "generic props"
newPropsObj[key] = value;
}
}
return newPropsObj;
};

// merge new props and existing generic props
const newPropsToSave = Object.assign(
existingPropsToMutate, { contentDeliveryNetwork: searchAndReplaceProps() },
);

fs.writeFileSync(`${resourceFilesBaseDir}/${newEnvName}-props.json`, JSON.stringify(newPropsToSave, null, 4));
}

if (!props) {
props = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/props.json`));
props = JSON.parse(fs.readFileSync(`${targetDir}/video/${resourceName}/${projectDetails.localEnvInfo.envName}-props.json`));
}
if (projectType === 'video-on-demand') {
props = getVODEnvVars(context, props, resourceName);
Expand Down Expand Up @@ -95,7 +144,7 @@ function getVODEnvVars(context, props, resourceName) {
delete props.shared.bucket;
delete props.shared.bucketInput;
delete props.shared.bucketOutput;
fs.writeFileSync(`${targetDir}/video/${resourceName}/props.json`, JSON.stringify(props, null, 4));
fs.writeFileSync(`${targetDir}/video/${resourceName}/${amplifyProjectDetails.localEnvInfo.envName}-props.json`, JSON.stringify(props, null, 4));
}
const envVars = amplifyProjectDetails.teamProviderInfo[currentEnvInfo]
.categories.video[resourceName];
Expand Down
47 changes: 47 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
const fs = require('fs');

const newEnvName = 'tonia';
const resourceDirectoryFiles = fs.readdirSync(
`${__dirname}/`,
);

// Check if env-specific props file already exists
const hasOwnEnvProps = resourceDirectoryFiles.includes(`${newEnvName}-props.json`);

// Check if ANY env props exist
const hasAnyEnvProps = resourceDirectoryFiles.find(item => item.includes('-props.json'));

// console.log('hasOwnEnv', hasOwnEnvProps);
// console.log('hasAnyEnvProps', hasAnyEnvProps);

if (!hasOwnEnvProps) {
if (hasAnyEnvProps) {
// take the first props file you find and copy that!
const propsFilenameToCopy = resourceDirectoryFiles.filter(propsFileName => propsFileName.includes('-props.json'))[0];
const envNameToReplace = propsFilenameToCopy.substr(0, propsFilenameToCopy.indexOf('-'));
const propsToMutate = JSON.parse(fs.readFileSync(`${__dirname}/${propsFilenameToCopy}`));

const searchAndReplaceProps = () => {
const newPropsObj = {};
for (const [key, value] of Object.entries(propsToMutate.contentDeliveryNetwork)) {
if (typeof value === 'string' && value.includes(`${envNameToReplace}`)) {
const newValue = value.replace(new RegExp(envNameToReplace, 'g'), `${newEnvName}`);
newPropsObj[key] = newValue;
} else {
newPropsObj[key] = value;
}
}
return newPropsObj;
};

const newPropsToSave = Object.assign(
propsToMutate, { contentDeliveryNetwork: searchAndReplaceProps() },
);

console.log('IM GONNA SAVE THIS, ', newPropsToSave);

fs.writeFileSync(`${__dirname}/${newEnvName}-props.json`, JSON.stringify(newPropsToSave, null, 4));
}
}

0 comments on commit 990015c

Please sign in to comment.