@ossy/deployment-tools 0.0.19 → 0.0.21

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/deployment-tools",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "Collection of scripts and tools to aid deployment of containers and static files",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -12,6 +12,9 @@
12
12
  "license": "ISC",
13
13
  "bin": "dist/index.js",
14
14
  "dependencies": {
15
+ "@actions/core": "^1.10.0",
16
+ "@aws-sdk/client-sqs": "^3.186.0",
17
+ "@aws-sdk/client-sts": "^3.188.0",
15
18
  "arg": "^5.0.2",
16
19
  "express": "^4.18.1",
17
20
  "nanoid": "^4.0.0",
@@ -0,0 +1,24 @@
1
+ import * as core from '@actions/core'
2
+ import { STSClient, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts'
3
+
4
+ export class AwsCredentialsClient {
5
+
6
+ static getTemporaryCredentials(awsAccountId, region, roleToAssume = 'github-ci-role') {
7
+ const stsClient = new STSClient({ region })
8
+
9
+ return core.getIDToken('sts.amazonaws.com')
10
+ .then(webIdentityToken => stsClient.send(new AssumeRoleWithWebIdentityCommand({
11
+ RoleArn: `arn:aws:iam::${awsAccountId}:role/${roleToAssume}`,
12
+ RoleSessionName: 'GitHubActions',
13
+ DurationSeconds: 5 * 60,
14
+ WebIdentityToken: webIdentityToken
15
+ })))
16
+ .then(responseData => ({
17
+ accessKeyId: responseData.Credentials.AccessKeyId,
18
+ secretAccessKey: responseData.Credentials.SecretAccessKey,
19
+ sessionToken: responseData.Credentials.SessionToken
20
+ }))
21
+
22
+ }
23
+
24
+ }
@@ -49,7 +49,7 @@ export class CaddyClient {
49
49
  http: {
50
50
  servers: {
51
51
  [this.serverName]: {
52
- listen: [':443'],
52
+ listen: [':80', ':443'],
53
53
  routes: [
54
54
  {
55
55
  match: [Matchers.host(`*.${config.environmentName}.${config.domain}`)],
package/src/config.js CHANGED
@@ -1,4 +1,9 @@
1
+ export const platformName = process.env.DEPLOYMENT_TOOLS_PLATFORM_NAME
1
2
  export const domain = process.env.DEPLOYMENT_TOOLS_DOMAIN || 'localhost'
2
3
  export const environmentName = process.env.DEPLOYMENT_TOOLS_ENVIRONMENT_NAME || 'dev'
3
4
  export const ciSubDomain = process.env.DEPLOYMENT_TOOLS_CI_SUB_DOMAIN || 'ci'
4
5
  export const port = process.env.DEPLOYMENT_TOOLS_PORT || 3000
6
+ export const awsAccountId = process.env.AWS_ACCOUNT_ID
7
+ export const awsRegion = process.env.AWS_REGION
8
+
9
+ export const deploymentQueueUrl = `https://sqs.${awsRegion}.amazonaws.com/${awsAccountId}/${platformName}-${environmentName}`
@@ -1,32 +1,36 @@
1
1
  import { resolve } from 'path'
2
2
  import { readFileSync } from 'fs'
3
- import fetch from 'node-fetch'
3
+ import { AwsCredentialsClient } from './aws-credentials-client.js'
4
+ import { DeploymentQueueClient } from './deployment-manager-client.js'
4
5
 
5
- export class ContainerManagerCommands {
6
+ export class ContainerManagerClient {
6
7
 
8
+ //eslint-disable-next-line max-params
7
9
  static deploy(
10
+ username,
11
+ authentication,
8
12
  targetEnvironment = 'local-dev',
9
13
  pathToDeploymentPlatforms,
10
14
  pathToOssyFile
11
15
  ) {
12
- const webApiCommands = new ContainerManagerCommands()
13
- return webApiCommands.deploy(
16
+ const containerManagerClient = new ContainerManagerClient()
17
+ return containerManagerClient.deploy(
18
+ username,
19
+ authentication,
14
20
  targetEnvironment,
15
21
  pathToDeploymentPlatforms,
16
22
  pathToOssyFile
17
23
  )
18
24
  }
19
25
 
20
- constructor() {
21
-
22
- }
26
+ constructor() {}
23
27
 
24
28
  getDeployments(pathToOssyFile) {
25
29
  const ossyfile = JSON.parse(readFileSync(resolve(pathToOssyFile), 'utf8'))
26
30
  return Promise.resolve(ossyfile.deployments || [])
27
31
  }
28
32
 
29
- async getDeploymentPlatforms(pathToDeploymentPlatforms) {
33
+ getDeploymentPlatforms(pathToDeploymentPlatforms) {
30
34
 
31
35
  const localDevPlatform = {
32
36
  name: 'dev',
@@ -36,9 +40,8 @@ export class ContainerManagerCommands {
36
40
 
37
41
  if (!pathToDeploymentPlatforms) return [localDevPlatform]
38
42
 
39
- const imports = await import(resolve(pathToDeploymentPlatforms))
40
- const deploymentPlatforms = Object.values(imports)
41
- return deploymentPlatforms || [localDevPlatform]
43
+ const deploymentPlatforms = JSON.parse(readFileSync(resolve(pathToDeploymentPlatforms), 'utf8'))
44
+ return Promise.resolve(deploymentPlatforms || [localDevPlatform])
42
45
  }
43
46
 
44
47
  getDeploymentTargetURL(targetEnvironment, deploymentPlatform, deployment) {
@@ -65,7 +68,7 @@ export class ContainerManagerCommands {
65
68
  deploy(
66
69
  username,
67
70
  authentication,
68
- targetEnvironment = 'dev',
71
+ targetEnvironment,
69
72
  pathToDeploymentPlatforms,
70
73
  pathToOssyFile
71
74
  ) {
@@ -99,17 +102,14 @@ export class ContainerManagerCommands {
99
102
  authentication: authentication
100
103
  }
101
104
 
102
- console.log('body', body)
103
-
104
- const ciUrl = targetEnvironment === 'prod'
105
- ? `http://${platform.ciSubDomain}.${platform.domain}/deploy`
106
- : `http://${platform.ciSubDomain}.${targetEnvironment}.${platform.domain}/deploy`
105
+ return AwsCredentialsClient.getTemporaryCredentials(platform.awsAccountId, platform.awsRegion)
106
+ .then(credentials => {
107
+ const deploymentQueueClient = DeploymentQueueClient.new({ credentials })
108
+ return deploymentQueueClient.sendMessage(body)
109
+ .then(() => console.log('[ContainerManagerCommands] Deployment request has been sent'))
110
+ .catch(error => console.log('[ContainerManagerCommands] Could not send deployment request', error))
107
111
 
108
- fetch(ciUrl, {
109
- method: 'POST',
110
- headers: { 'Content-Type': 'application/json' },
111
- body: JSON.stringify(body)
112
- })
112
+ })
113
113
  })
114
114
  })
115
115
  .catch(error => console.log(error))
@@ -1,6 +1,7 @@
1
1
  import express from 'express'
2
2
  import { CaddyClient } from './caddy-client.js'
3
3
  import { DockerClient } from './docker-client.js'
4
+ import { DeploymentQueueClient } from './deployment-manager-client.js'
4
5
  import * as config from './config.js'
5
6
 
6
7
  export class ContainerManagerServer {
@@ -10,17 +11,18 @@ export class ContainerManagerServer {
10
11
  }
11
12
 
12
13
  constructor() {
14
+ this.server = express()
13
15
  this.dockerClient = DockerClient.new()
14
16
  this.caddyClient = CaddyClient.new()
15
- this.server = express()
17
+ this.deploymentQueueClient = DeploymentQueueClient.new()
16
18
 
17
- this.server.use(express.json())
19
+ this.applyDefaultCaddyConfig()
20
+ this.setupServerEndpoints()
21
+ this.pollForDeploymentRequests()
22
+ }
18
23
 
19
- this.caddyClient.applyDefaultServerConfig()
20
- .then(() => this.caddyClient.deploy({
21
- domain: `${config.ciSubDomain}.${config.environmentName}.${config.domain}`,
22
- targetPort: config.port
23
- }))
24
+ setupServerEndpoints() {
25
+ this.server.use(express.json())
24
26
 
25
27
  this.server.get('/', (req, res) => {
26
28
  res.redirect('/status')
@@ -30,19 +32,34 @@ export class ContainerManagerServer {
30
32
  res.json('Server is live')
31
33
  })
32
34
 
33
- this.server.post('/deploy', (req, res) => {
34
- console.log('/deploy body', req.body)
35
- this.deploy(req, res)
36
- })
37
-
38
35
  this.server.listen(config.port, () => {
39
36
  console.log(`Web API is live on port ${config.port}`)
40
37
  })
41
38
  }
42
39
 
43
- deploy(req, res) {
40
+ applyDefaultCaddyConfig() {
41
+ this.caddyClient.applyDefaultServerConfig()
42
+ .then(() => this.caddyClient.deploy({
43
+ domain: `${config.ciSubDomain}.${config.environmentName}.${config.domain}`,
44
+ targetPort: config.port
45
+ }))
46
+ }
47
+
48
+ pollForDeploymentRequests() {
49
+ const FIVE_MINUTES = 3000
50
+
51
+ const handleIcomingMessages = () => this.deploymentQueueClient.receiveMessages()
52
+ .then(data => data.Messages.map(message => {
53
+ console.log('[ContainerManagerServer] handleIcomingMessages')
54
+ this.deploy(message)
55
+ this.deploymentQueueClient.deleteMessage(message.ReceiptHandle)
56
+ }))
57
+ .catch(error => console.log('[ContainerManagerServer] handleIcomingMessages error', error))
58
+
59
+ setInterval(handleIcomingMessages, FIVE_MINUTES)
60
+ }
44
61
 
45
- console.log('req.body: ', req.body)
62
+ deploy(deploymentRequestMessage) {
46
63
 
47
64
  const {
48
65
  registry,
@@ -53,7 +70,7 @@ export class ContainerManagerServer {
53
70
  image,
54
71
  domain,
55
72
  env
56
- } = req.body
73
+ } = deploymentRequestMessage
57
74
 
58
75
  this.dockerClient.deploy({
59
76
  image,
@@ -67,7 +84,6 @@ export class ContainerManagerServer {
67
84
 
68
85
  domain && this.caddyClient.deploy({ domain, targetPort: hostPort })
69
86
 
70
- res.json('Recieved deployment request')
71
87
  }
72
88
 
73
89
  }
@@ -0,0 +1,57 @@
1
+ import {
2
+ SQSClient,
3
+ SendMessageCommand,
4
+ DeleteMessageCommand,
5
+ ReceiveMessageCommand
6
+ } from '@aws-sdk/client-sqs'
7
+ import * as config from './config.js'
8
+
9
+ export class DeploymentQueueClient {
10
+
11
+ static new({ queueUrl, region, credentials } = {}) {
12
+
13
+ const deploymentQueueClientDefaultConfig = {
14
+ queueUrl: queueUrl || config.deploymentQueueUrl,
15
+ region: region || config.awsRegion,
16
+ credentials
17
+ }
18
+
19
+ return new DeploymentQueueClient(deploymentQueueClientDefaultConfig)
20
+ }
21
+
22
+ constructor({ queueUrl, region, credentials }) {
23
+ this.queueUrl = queueUrl
24
+ this.sqs = new SQSClient({ region, credentials })
25
+ }
26
+
27
+ sendMessage(messageBody) {
28
+ const command = SendMessageCommand({
29
+ QueueUrl: this.queueUrl,
30
+ MessageBody: JSON.stringify(messageBody)
31
+ })
32
+
33
+ return this.sqs.send(command)
34
+ .then(() => console.log('Success'))
35
+ .catch(err => console.log(err))
36
+ }
37
+
38
+ receiveMessages() {
39
+ const command = ReceiveMessageCommand({ QueueUrl: this.queueUrl })
40
+
41
+ return this.sqs.send(command)
42
+ .catch(error => console.log('Error', error))
43
+ }
44
+
45
+ deleteMessage(receiptHandle) {
46
+ const command = DeleteMessageCommand({
47
+ QueueUrl: this.queueUrl,
48
+ ReceiptHandle: receiptHandle
49
+ })
50
+
51
+ return this.sqs.send(command)
52
+ .then(() => console.log('success'))
53
+ .catch(error => console.log('Error', error))
54
+
55
+ }
56
+
57
+ }
package/src/index.js CHANGED
@@ -3,7 +3,7 @@ import { platform } from 'os'
3
3
  import { exec } from 'child_process'
4
4
  import arg from 'arg'
5
5
  import { ContainerManagerServer } from './container-manager-server.js'
6
- import { ContainerManagerCommands } from './container-manager-commands.js'
6
+ import { ContainerManagerClient } from './container-manager-client.js'
7
7
 
8
8
  const [_, __, command, ...restArgs] = process.argv
9
9
 
@@ -40,7 +40,7 @@ const deployHandler = () => {
40
40
  '-u': '--username',
41
41
 
42
42
  '--authentication': String,
43
- '--p': '--authentication',
43
+ '--a': '--authentication',
44
44
 
45
45
  '--target-env': String,
46
46
  '-t': '--target-env',
@@ -54,7 +54,7 @@ const deployHandler = () => {
54
54
 
55
55
  console.log('args', args)
56
56
 
57
- ContainerManagerCommands.deploy(
57
+ ContainerManagerClient.deploy(
58
58
  args['--username'],
59
59
  args['--authentication'],
60
60
  args['--target-env'],