cloudmason 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ name: Node.js Package
2
+
3
+ on:
4
+ pull_request:
5
+ types:
6
+ - closed
7
+ branches:
8
+ - 'release/**'
9
+
10
+ jobs:
11
+ if_merged:
12
+ if: github.event.pull_request.merged == true
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v3
16
+ - uses: actions/setup-node@v3
17
+ with:
18
+ node-version: 16
19
+ registry-url: https://registry.npmjs.org/
20
+ - run: npm run build
21
+ env:
22
+ branch: ${{ github.base_ref }}
23
+ build: ${{ github.run_number }}
24
+ - run: npm ci
25
+ - run: npm publish
26
+ env:
27
+ NODE_AUTH_TOKEN: ${{secrets.npm_token}}
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Introduction
2
+ TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
3
+
4
+ ## Sub Introduction
5
+
6
+ ### Sub2 Introduction
7
+
8
+
9
+ # Getting Started
10
+ TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
11
+ 1. Installation process
12
+ 2. Software dependencies
13
+ 3. Latest releases
14
+ 4. API references
15
+
16
+ # Build and Test
17
+ TODO: Describe and show how to build your code and run the tests.
18
+
19
+ # Contribute
20
+ TODO: Explain how other users and developers can contribute to make your code better.
21
+
22
+ If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
23
+ - [ASP.NET Core](https://github.com/aspnet/Home)
24
+ - [Visual Studio Code](https://github.com/Microsoft/vscode)
25
+ - [Chakra Core](https://github.com/Microsoft/ChakraCore)
package/build.js ADDED
@@ -0,0 +1,21 @@
1
+ const fs = require('fs');
2
+
3
+ const pckg = JSON.parse(fs.readFileSync('./package.json'));
4
+
5
+ const branch = process.env.branch;
6
+ const build = process.env.build;
7
+
8
+ console.log(`Branch: ${branch}`);
9
+ console.log(`Build: ${build}`);
10
+
11
+ const branchParts = branch.split('/');
12
+ const lastPart = branchParts[branchParts.length - 1];
13
+ const majorVersion = lastPart.replaceAll(/[^0-9\.]/g, '');
14
+
15
+ const version = `${majorVersion}.${build}`;
16
+
17
+ pckg.version = version;
18
+
19
+ console.log(`Version: ${version}`);
20
+
21
+ fs.writeFileSync('./package.json', JSON.stringify(pckg));
@@ -0,0 +1,29 @@
1
+ const CF = require('./helpers/cf');
2
+ const Params = require('./helpers/params');
3
+
4
+
5
+ exports.delete_instance = async function(args){
6
+ // Get App
7
+ const app = await Params.getApp(args.app);
8
+ if (!app){
9
+ console.log('Err: No app named ' + args.app);
10
+ throw new Error('Err: No app named ' + args.app)
11
+ }
12
+ // Get Instance
13
+ const targetInstance = app.instances.find(ins=>{ return ins.name.toLowerCase() == args.i.toLowerCase() });
14
+ if (!targetInstance){ console.log(`No instance of ${args.app} named ${args.i}`); throw new Error('Invalid Instance')}
15
+
16
+ // Delete Stack
17
+ const stackName = (`${args.app}-${args.i}`).toLowerCase();
18
+ console.log(`Deleting ${args.app} instance ${args.i} in ${targetInstance.region}`)
19
+ const delOK = await CF.delete(stackName, targetInstance.region);
20
+ if (delOK){
21
+ console.log('Delete Successful')
22
+ } else {
23
+ return;
24
+ }
25
+
26
+ // Update Params
27
+ await Params.deleteInstance(args.app,args.i);
28
+ return true;
29
+ }
@@ -0,0 +1,28 @@
1
+ const { S3Client,PutObjectCommand } = require("@aws-sdk/client-s3");
2
+ const fs = require('fs');
3
+ const path = require('path')
4
+ const Params = require('./helpers/params');
5
+ const S3 = require('./helpers/s3');
6
+
7
+
8
+ exports.main = async function(args){
9
+ // Check for existing app
10
+ const existingApp = await Params.getApp(args.name);
11
+ if (!existingApp){
12
+ console.log('Err: App does not exist ' + args.name);
13
+ throw new Error('App does not exist')
14
+ }
15
+
16
+
17
+ // Upload Stack
18
+ console.log(`Uploading ${args.type} to ${process.env.orgBucket}`)
19
+ const stackKey = `apps/${args.name.toLowerCase()}/default_stack.yaml`
20
+ await S3.uploadInfraText(stackKey,stackText);
21
+
22
+ // Update app config
23
+ console.log('Adding app params');
24
+ const nodev = args.node || '';
25
+ const pyv = args.py || '';
26
+ await Params.addApp(args.name,args.type,stackKey,nodev,pyv);
27
+ console.log(`Added ${args.name} with ${args.type} stack`)
28
+ }
@@ -0,0 +1,118 @@
1
+ const { CloudFormationClient, CreateStackCommand,UpdateStackCommand, DeleteStackCommand, ValidateTemplateCommand,DescribeStacksCommand } = require('@aws-sdk/client-cloudformation');
2
+
3
+ const fs = require('fs');
4
+ const path = require('path')
5
+
6
+
7
+ exports.deployOrgStack = async function(stackName,stackType,region,params,tag){
8
+ // Read Stack
9
+ const stackPath = path.resolve(__dirname,'stacks',`${stackType.toLowerCase()}.yaml`);
10
+ if (!fs.existsSync(stackPath)){
11
+ console.log('Invalid stack type:' + stackType);
12
+ throw { message: 'Invalid stack type', at: 'deployStack'}
13
+ }
14
+ const stackYML = fs.readFileSync(stackPath,'utf-8');
15
+ console.log
16
+ // Build Params
17
+ const cfParams = Object.keys(params).map(k=>{ return { ParameterKey: k, ParameterValue: params[k] } })
18
+ const cfTags = Object.keys(tag).map(k=>{ return { Key: k, Value: tag[k] } })
19
+ // Deploy Stack
20
+ const client = new CloudFormationClient({ region });
21
+ const input = {
22
+ StackName: stackName,
23
+ TemplateBody: stackYML,
24
+ Capabilities: [
25
+ "CAPABILITY_IAM" || "CAPABILITY_NAMED_IAM" || "CAPABILITY_AUTO_EXPAND",
26
+ ],
27
+ Parameters: cfParams,
28
+ Tags: cfTags
29
+ };
30
+ try{
31
+ const result = await client.send(new CreateStackCommand(input));
32
+ return result.StackId;
33
+ } catch (e){
34
+ if (/AlreadyExistsException/.test(e)){
35
+ console.log('Stack exists ' + stackName);
36
+ return false;
37
+ } else {
38
+ throw new Error(e.message)
39
+ }
40
+ }
41
+ }
42
+
43
+ exports.deployS3Stack = async function(stackName,s3Url,params,tag,region){
44
+ // Build Params
45
+ const cfParams = Object.keys(params).map(k=>{ return { ParameterKey: k, ParameterValue: params[k] } })
46
+ const cfTags = Object.keys(tag).map(k=>{ return { Key: k, Value: tag[k] } })
47
+ // Deploy Stack
48
+ const client = new CloudFormationClient({ region });
49
+ const input = {
50
+ StackName: stackName,
51
+ TemplateURL: s3Url,
52
+ Capabilities: [
53
+ "CAPABILITY_IAM" || "CAPABILITY_NAMED_IAM" || "CAPABILITY_AUTO_EXPAND",
54
+ ],
55
+ Parameters: cfParams,
56
+ Tags: cfTags
57
+ };
58
+
59
+ const result = await client.send(new CreateStackCommand(input));
60
+ return result.StackId;
61
+ }
62
+
63
+ exports.updateStack = async function(stackName,s3Url,params,region){
64
+ const client = new CloudFormationClient({ region });
65
+ const cfParams = Object.keys(params).map(k=>{ return { ParameterKey: k, ParameterValue: params[k] } })
66
+ console.log(s3Url)
67
+ const cmd = {
68
+ StackName: stackName,
69
+ TemplateURL: s3Url,
70
+ Parameters: cfParams,
71
+ Capabilities: ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
72
+ };
73
+ const command = new UpdateStackCommand(cmd);
74
+ const response = await client.send(command);
75
+ return true;
76
+ }
77
+
78
+ exports.validateStack = async function(stack){
79
+ const client = new CloudFormationClient({ region: process.env.orgRegion });
80
+
81
+ const command = new ValidateTemplateCommand({
82
+ TemplateBody: stack,
83
+ });
84
+
85
+ try{
86
+ const data = await client.send(command);
87
+ return { ok: true, data: data }
88
+ } catch (e){
89
+ return {ok: false, data: e}
90
+ }
91
+ }
92
+
93
+ exports.stackExists = async function(stackName,region){
94
+ const client = new CloudFormationClient({ region });
95
+ const command = new DescribeStacksCommand({ StackName: stackName });
96
+ try {
97
+
98
+ const response = await client.send(command);
99
+ return true;
100
+ } catch (error) {
101
+ if (error.name === 'ValidationError') {
102
+ return false;
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+
108
+ exports.delete = async function(stackName,region){
109
+ const client = new CloudFormationClient({ region });
110
+ try {
111
+ const command = new DeleteStackCommand({ StackName: stackName });
112
+ const response = await client.send(command);
113
+ return true;
114
+ } catch (error) {
115
+ console.error("Error deleting the stack:", error);
116
+ return false;
117
+ }
118
+ }
@@ -0,0 +1,40 @@
1
+ const { EC2Client, RunInstancesCommand,CreateImageCommand,TerminateInstancesCommand,DescribeInstanceStatusCommand,DeregisterImageCommand,DescribeImagesCommand,CopyImageCommand } = require("@aws-sdk/client-ec2");
2
+
3
+ exports.findAMI = async function(image_name,region){
4
+ const client = new EC2Client({ region });
5
+ const input = { // DescribeImagesRequest
6
+ Filters: [ // FilterList
7
+ { // Filter
8
+ Name: "name",
9
+ Values: [ // ValueStringList
10
+ image_name,
11
+ ],
12
+ },
13
+ ],
14
+ IncludeDeprecated: true,
15
+ DryRun: false,
16
+ MaxResults: 6
17
+ };
18
+ const command = new DescribeImagesCommand(input);
19
+ const response = await client.send(command);
20
+ const images = response.Images;
21
+ if (!images[0]){
22
+ console.log('No existing image with name:' + image_name)
23
+ return false;
24
+ };
25
+ return images[0].ImageId;
26
+ }
27
+
28
+ exports.copyAMI = async function(image_name,src_ami,src_region,dest_region){
29
+
30
+ const destinationEc2Client = new EC2Client({ region: dest_region });
31
+
32
+ const copyImageCommand = new CopyImageCommand({
33
+ SourceRegion: src_region,
34
+ SourceImageId: src_ami,
35
+ Name: image_name,
36
+ });
37
+
38
+ const { ImageId } = await destinationEc2Client.send(copyImageCommand);
39
+ return ImageId;
40
+ }
@@ -0,0 +1,178 @@
1
+ const { SSMClient, GetParameterCommand,PutParameterCommand,GetParametersByPathCommand } = require("@aws-sdk/client-ssm");
2
+
3
+ // ORG PARAMS
4
+ exports.getOrgConfig = async function(){
5
+ if (!process.env.orgRegion){ throw new Error('Region not set') }
6
+ const ssmClient = new SSMClient({ region: process.env.orgRegion }); // Set your preferred region
7
+ const pathPrefix = "/infra/";
8
+ const parameters = [];
9
+ let nextToken;
10
+
11
+ do {
12
+ const response = await ssmClient.send(new GetParametersByPathCommand({
13
+ Path: pathPrefix,
14
+ NextToken: nextToken
15
+ }));
16
+
17
+ if (response.Parameters) {
18
+ parameters.push(...response.Parameters);
19
+ }
20
+
21
+ nextToken = response.NextToken;
22
+
23
+ } while (nextToken);
24
+ const params = {}
25
+ parameters.forEach(p=>{
26
+ const k = p.Name.split('/')[p.Name.split('/').length-1]
27
+ params[k] = p.Value
28
+ })
29
+ return params;
30
+ }
31
+
32
+ // APP PARAMS
33
+ exports.listApps = async function(){
34
+ if (!process.env.orgRegion){ throw new Error('Region not set') }
35
+
36
+ const ssmClient = new SSMClient({ region: process.env.orgRegion }); // Set your preferred region
37
+ const pathPrefix = "/infra/apps/";
38
+ const parameters = [];
39
+ let nextToken;
40
+
41
+ do {
42
+ const response = await ssmClient.send(new GetParametersByPathCommand({
43
+ Path: pathPrefix,
44
+ NextToken: nextToken
45
+ }));
46
+
47
+ if (response.Parameters) {
48
+ parameters.push(...response.Parameters);
49
+ }
50
+
51
+ nextToken = response.NextToken;
52
+
53
+ } while (nextToken);
54
+ const params = parameters.map(p=>{ return JSON.parse(p.Value) })
55
+ return params;
56
+ }
57
+
58
+ exports.getApp = async function(appName){
59
+ if (!process.env.orgRegion){ throw new Error('Region not set') }
60
+ const app = await readParam(
61
+ `/infra/apps/${appName.toLowerCase()}`,
62
+ process.env.orgRegion
63
+ )
64
+ return app ? JSON.parse(app) : null;
65
+ }
66
+
67
+ exports.addApp = async function(appName,appType,stackKey,nodeV,pyV){
68
+ const appData = {
69
+ name: appName,
70
+ stack: appType,
71
+ nodeV: nodeV,
72
+ pyV: pyV,
73
+ stackKey: stackKey,
74
+ versions: {
75
+ // build,ami,updated
76
+ },
77
+ instances: []
78
+ }
79
+ const paramPath = `/infra/apps/${appName.toLowerCase()}`;
80
+ await writeParam(paramPath,JSON.stringify(appData),process.env.orgRegion);
81
+ }
82
+
83
+ // VERSION PARAMS
84
+ exports.updateAppV = async function(appName,version,vParams){
85
+ if (!process.env.orgRegion){ throw new Error('Region not set') }
86
+ if (!vParams.baseAMI_Name || !vParams.baseAMI_Id || !vParams.updated){ throw new Error('Missing version param' + vParams)}
87
+ const appKey = `/infra/apps/${appName.toLowerCase()}`
88
+ const appStr = await readParam(appKey,process.env.orgRegion);
89
+ const app = JSON.parse(appStr);
90
+
91
+ app.versions[version] = vParams;
92
+ await writeParam(appKey,JSON.stringify(app),process.env.orgRegion);
93
+ }
94
+
95
+ // INSTANCE PARAMS
96
+ exports.addInstance = async function(appName,instanceName,params){
97
+ if (!process.env.orgRegion){ throw new Error('Region not set') }
98
+ const appKey = `/infra/apps/${appName.toLowerCase()}`
99
+ const appStr = await readParam(appKey,process.env.orgRegion);
100
+ const app = JSON.parse(appStr);
101
+
102
+ const ei = app.instances.find(ins=>{ return ins.name.toLowerCase() == instanceName.toLowerCase()});
103
+ if (ei){
104
+ throw new Error('Instance exists')
105
+ }
106
+ app.instances.push(params);
107
+ await writeParam(appKey,JSON.stringify(app),process.env.orgRegion)
108
+ }
109
+
110
+ exports.editInstance = async function(appName,instanceName,version,cfParams){
111
+ if (!process.env.orgRegion){ throw new Error('Region not set') }
112
+ const appKey = `/infra/apps/${appName.toLowerCase()}`
113
+ const appStr = await readParam(appKey,process.env.orgRegion);
114
+ const app = JSON.parse(appStr);
115
+
116
+ const ei = app.instances.find(ins=>{ return ins.name.toLowerCase() == instanceName.toLowerCase()});
117
+ if (!ei){ throw new Error('Instance not found') }
118
+ ei.version = version;
119
+ ei.cfParams = cfParams;
120
+ await writeParam(appKey,JSON.stringify(app),process.env.orgRegion)
121
+ }
122
+
123
+ exports.deleteInstance = async function(appName,instanceName){
124
+ if (!process.env.orgRegion){ throw new Error('Region not set') }
125
+ const appKey = `/infra/apps/${appName.toLowerCase()}`
126
+ const appStr = await readParam(appKey,process.env.orgRegion);
127
+ const app = JSON.parse(appStr);
128
+
129
+ const ei = app.instances.find(ins=>{ return ins.name.toLowerCase() == instanceName.toLowerCase()});
130
+ if (!ei){ throw new Error('Instance not found') }
131
+ app.instances = app.instances.filter(ins=>{ return ins.name.toLowerCase() !== instanceName.toLowerCase()});
132
+ await writeParam(appKey,JSON.stringify(app),process.env.orgRegion)
133
+ }
134
+
135
+
136
+
137
+
138
+
139
+ //////////////////////
140
+ async function writeParam(name,value,region){
141
+ const ssmClient = new SSMClient({ region }); // specify the appropriate AWS region
142
+ if (value.length > 1000){
143
+ console.log('WARNING: Long parameter ' + value.length)
144
+ }
145
+ // Creating the command object with the parameter details
146
+ const command = new PutParameterCommand({
147
+ Name: name,
148
+ Value: value,
149
+ Type: 'String', // Specify the parameter type: 'String', 'StringList', or 'SecureString'
150
+ Overwrite: true, // Specify whether to overwrite an existing parameter
151
+ });
152
+
153
+ const response = await ssmClient.send(command);
154
+ return true;
155
+ }
156
+
157
+ async function readParam(name,region){
158
+ const ssmClient = new SSMClient({ region });
159
+
160
+ // Create a command object with the parameter name
161
+ const command = new GetParameterCommand({
162
+ Name: name,
163
+ WithDecryption: true
164
+ });
165
+
166
+ try {
167
+ // Send the command to SSM and wait for the result
168
+ const response = await ssmClient.send(command);
169
+ return response.Parameter.Value;
170
+ } catch (error) {
171
+ // Handle any errors
172
+ if (/ParameterNotFound/.test(error)){
173
+ return null;
174
+ } else {
175
+ throw error;
176
+ }
177
+ }
178
+ }
@@ -0,0 +1,68 @@
1
+ const { S3Client,PutObjectCommand,CopyObjectCommand,HeadObjectCommand } = require("@aws-sdk/client-s3");
2
+ const fs = require('fs');
3
+
4
+
5
+ exports.uploadInfraFile = async function(fileKey,localPath){
6
+ const s3Path = fileKey;
7
+ if (!fs.existsSync(localPath)){
8
+ throw new Error('File does not exist:' + localPath)
9
+ }
10
+ const fileStream = fs.createReadStream(localPath);
11
+ // Upload Stack
12
+ const client = new S3Client({region: process.env.orgRegion});
13
+ const input = {
14
+ Body: fileStream,
15
+ Bucket: process.env.orgBucket,
16
+ Key: s3Path.toLowerCase()
17
+ };
18
+ const poCommand = new PutObjectCommand(input);
19
+ const poResponse = await client.send(poCommand);
20
+ return poResponse;
21
+ }
22
+
23
+ exports.uploadInfraText = async function(fileKey,fileText){
24
+ // Upload Stack
25
+ const client = new S3Client({region: process.env.orgRegion});
26
+ const input = {
27
+ Body: fileText,
28
+ Bucket: process.env.orgBucket,
29
+ Key: fileKey.toLowerCase()
30
+ };
31
+ const poCommand = new PutObjectCommand(input);
32
+ const poResponse = await client.send(poCommand);
33
+ return poResponse;
34
+ }
35
+
36
+ exports.infraFileExists = async function(fileKey){
37
+ const client = new S3Client({region: process.env.orgRegion});
38
+
39
+ const input = {
40
+ Bucket: process.env.orgBucket,
41
+ Key: fileKey.toLowerCase()
42
+ };
43
+
44
+ const command = new HeadObjectCommand(input)
45
+ try{
46
+ const response = await client.send(command);
47
+ return true;
48
+ } catch (e){
49
+ if (/NotFound/.test(e)){
50
+ return false;
51
+ } else {
52
+ console.log('Uncaught Error:',e)
53
+ throw e;
54
+ }
55
+ }
56
+ }
57
+
58
+
59
+ exports.copyInfraFile = async function(srcKey,destKey){
60
+ const client = new S3Client({ region: process.env.orgRegion });
61
+ const params = {
62
+ Bucket: process.env.orgBucket,
63
+ CopySource: `${process.env.orgBucket}/${srcKey}`,
64
+ Key: destKey.toLowerCase(),
65
+ };
66
+ await client.send(new CopyObjectCommand(params));
67
+ return true;
68
+ }