@ossy/deployment-tools 0.0.25 → 0.0.26

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.
@@ -1,24 +0,0 @@
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: 15 * 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
- }
package/src/config.js DELETED
@@ -1,22 +0,0 @@
1
- export const resolveConfiguration = (deploymentPlatform = {}) => {
2
- const platformName = deploymentPlatform.name || process.env.DEPLOYMENT_TOOLS_PLATFORM_NAME
3
- const domain = deploymentPlatform.domain || process.env.DEPLOYMENT_TOOLS_DOMAIN || 'localhost'
4
- const environmentName = process.env.DEPLOYMENT_TOOLS_ENVIRONMENT_NAME || 'dev'
5
- const ciSubDomain = deploymentPlatform.ciSubDomain || process.env.DEPLOYMENT_TOOLS_CI_SUB_DOMAIN || 'ci'
6
- const port = process.env.DEPLOYMENT_TOOLS_PORT || 3000
7
- const awsAccountId = deploymentPlatform.awsAccountId || process.env.AWS_ACCOUNT_ID
8
- const awsRegion = deploymentPlatform.awsRegion || process.env.AWS_REGION
9
-
10
- const deploymentQueueUrl = `https://sqs.${awsRegion}.amazonaws.com/${awsAccountId}/${platformName}-${environmentName}`
11
-
12
- return {
13
- platformName,
14
- domain,
15
- environmentName,
16
- ciSubDomain,
17
- port,
18
- awsAccountId,
19
- awsRegion,
20
- deploymentQueueUrl
21
- }
22
- }
@@ -1,115 +0,0 @@
1
- import { resolve } from 'path'
2
- import { readFileSync } from 'fs'
3
- import { AwsCredentialsClient } from './aws-credentials-client.js'
4
- import { DeploymentQueueClient } from './deployment-queue-client.js'
5
-
6
- export class ContainerManagerClient {
7
-
8
- //eslint-disable-next-line max-params
9
- static deploy(
10
- username,
11
- authentication,
12
- targetEnvironment = 'local-dev',
13
- pathToDeploymentPlatforms,
14
- pathToOssyFile
15
- ) {
16
- const containerManagerClient = new ContainerManagerClient()
17
- return containerManagerClient.deploy(
18
- username,
19
- authentication,
20
- targetEnvironment,
21
- pathToDeploymentPlatforms,
22
- pathToOssyFile
23
- )
24
- }
25
-
26
- constructor() {}
27
-
28
- getDeployments(pathToOssyFile) {
29
- const ossyfile = JSON.parse(readFileSync(resolve(pathToOssyFile), 'utf8'))
30
- return Promise.resolve(ossyfile.deployments || [])
31
- }
32
-
33
- getDeploymentPlatforms(pathToDeploymentPlatforms) {
34
-
35
- const localDevPlatform = {
36
- name: 'dev',
37
- domain: 'localhost',
38
- supportedDeploymentTypes: ['CONTAINER']
39
- }
40
-
41
- if (!pathToDeploymentPlatforms) return [localDevPlatform]
42
-
43
- const deploymentPlatforms = JSON.parse(readFileSync(resolve(pathToDeploymentPlatforms), 'utf8'))
44
- return Promise.resolve(deploymentPlatforms || [localDevPlatform])
45
- }
46
-
47
- getDeploymentTargetURL(targetEnvironment, deploymentPlatform, deployment) {
48
- let domain = targetEnvironment !== 'prod'
49
- ? `${targetEnvironment}.`
50
- : ''
51
-
52
- domain = deployment.subdomain
53
- ? `${domain}${deployment.subdomain}.${deploymentPlatform.domain}`
54
- : `${domain}${deploymentPlatform.domain}`
55
-
56
- return domain
57
- }
58
-
59
- getEnvironmentVariables(targetEnvironment, deployment) {
60
- const envs = deployment.env || {}
61
- return {
62
- ...(envs.shared || {}),
63
- ...(envs[targetEnvironment] || {})
64
- }
65
- }
66
-
67
- //eslint-disable-next-line max-params
68
- deploy(
69
- username,
70
- authentication,
71
- targetEnvironment,
72
- pathToDeploymentPlatforms,
73
- pathToOssyFile
74
- ) {
75
- return Promise.all([
76
- this.getDeploymentPlatforms(pathToDeploymentPlatforms),
77
- this.getDeployments(pathToOssyFile)
78
- ])
79
- .then(([platforms, deployments]) => {
80
- deployments.map(deployment => {
81
- const platform = platforms.find(platform => platform.name === deployment.targetDeploymentPlatform)
82
-
83
- if (!platform) {
84
- return Promise.reject(
85
- `Could not find a deployment platform with the name ${deployment.targetDeploymentPlatform}`
86
- )
87
- }
88
-
89
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0
90
-
91
- const body = {
92
- image: deployment.image,
93
- hostPort: deployment.hostPort,
94
- containerPort: deployment.containerPort,
95
- domain: this.getDeploymentTargetURL(targetEnvironment, platform, deployment),
96
- env: this.getEnvironmentVariables(targetEnvironment, deployment),
97
- registry: deployment.registry,
98
- username: username,
99
- authentication: authentication
100
- }
101
-
102
- return AwsCredentialsClient.getTemporaryCredentials(platform.awsAccountId, platform.awsRegion)
103
- .then(credentials => {
104
- const deploymentQueueClient = DeploymentQueueClient.new({ deploymentPlatform: platform, credentials })
105
- return deploymentQueueClient.sendMessage(body)
106
- .then(() => console.log('[ContainerManagerCommands] Deployment request has been sent'))
107
- .catch(error => console.log('[ContainerManagerCommands] Could not send deployment request', error))
108
-
109
- })
110
- })
111
- })
112
- .catch(error => console.log(error))
113
- }
114
-
115
- }
@@ -1,91 +0,0 @@
1
- import express from 'express'
2
- import { CaddyClient } from './caddy-client.js'
3
- import { DockerClient } from './docker-client.js'
4
- import { DeploymentQueueClient } from './deployment-queue-client.js'
5
- import { resolveConfiguration } from './config.js'
6
-
7
- const config = resolveConfiguration()
8
-
9
- export class ContainerManagerServer {
10
-
11
- static start() {
12
- return new ContainerManagerServer()
13
- }
14
-
15
- constructor() {
16
- this.server = express()
17
- this.dockerClient = DockerClient.new()
18
- this.caddyClient = CaddyClient.new()
19
- this.deploymentQueueClient = DeploymentQueueClient.new()
20
-
21
- this.applyDefaultCaddyConfig()
22
- this.setupServerEndpoints()
23
- this.pollForDeploymentRequests()
24
- }
25
-
26
- setupServerEndpoints() {
27
- this.server.use(express.json())
28
-
29
- this.server.get('/', (req, res) => {
30
- res.redirect('/status')
31
- })
32
-
33
- this.server.get('/status', (req, res) => {
34
- res.json('Server is live')
35
- })
36
-
37
- this.server.listen(config.port, () => {
38
- console.log(`Web API is live on port ${config.port}`)
39
- })
40
- }
41
-
42
- applyDefaultCaddyConfig() {
43
- this.caddyClient.applyDefaultServerConfig()
44
- .then(() => this.caddyClient.deploy({
45
- domain: `${config.ciSubDomain}.${config.environmentName}.${config.domain}`,
46
- targetPort: config.port
47
- }))
48
- }
49
-
50
- pollForDeploymentRequests() {
51
- const FIVE_MINUTES = 3000
52
-
53
- const handleIcomingMessages = () => this.deploymentQueueClient.receiveMessages()
54
- .then(data => data.Messages.map(message => {
55
- console.log('[ContainerManagerServer] handleIcomingMessages')
56
- this.deploy(message)
57
- this.deploymentQueueClient.deleteMessage(message.ReceiptHandle)
58
- }))
59
- .catch(error => console.log('[ContainerManagerServer] handleIcomingMessages error', error))
60
-
61
- setInterval(handleIcomingMessages, FIVE_MINUTES)
62
- }
63
-
64
- deploy(deploymentRequestMessage) {
65
-
66
- const {
67
- registry,
68
- username,
69
- authentication,
70
- containerPort,
71
- hostPort,
72
- image,
73
- domain,
74
- env
75
- } = deploymentRequestMessage
76
-
77
- this.dockerClient.deploy({
78
- image,
79
- registry,
80
- username,
81
- authentication,
82
- containerPort,
83
- hostPort,
84
- env
85
- })
86
-
87
- domain && this.caddyClient.deploy({ domain, targetPort: hostPort })
88
-
89
- }
90
-
91
- }
@@ -1,51 +0,0 @@
1
- import {
2
- SQSClient,
3
- SendMessageCommand,
4
- DeleteMessageCommand,
5
- ReceiveMessageCommand
6
- } from '@aws-sdk/client-sqs'
7
- import { resolveConfiguration } from './config.js'
8
-
9
- export class DeploymentQueueClient {
10
-
11
- static new(config) {
12
- return new DeploymentQueueClient(config)
13
- }
14
-
15
- constructor({ deploymentPlatform, credentials } = {}) {
16
- const { region, deploymentQueueUrl: queueUrl } = resolveConfiguration(deploymentPlatform)
17
- this.queueUrl = queueUrl
18
- this.sqs = new SQSClient({ region, credentials })
19
- }
20
-
21
- sendMessage(messageBody) {
22
- const command = new SendMessageCommand({
23
- QueueUrl: this.queueUrl,
24
- MessageBody: JSON.stringify(messageBody)
25
- })
26
-
27
- return this.sqs.send(command)
28
- .then(() => console.log('Success'))
29
- .catch(err => console.log(err))
30
- }
31
-
32
- receiveMessages() {
33
- const command = new ReceiveMessageCommand({ QueueUrl: this.queueUrl })
34
-
35
- return this.sqs.send(command)
36
- .catch(error => console.log('Error', error))
37
- }
38
-
39
- deleteMessage(receiptHandle) {
40
- const command = new DeleteMessageCommand({
41
- QueueUrl: this.queueUrl,
42
- ReceiptHandle: receiptHandle
43
- })
44
-
45
- return this.sqs.send(command)
46
- .then(() => console.log('success'))
47
- .catch(error => console.log('Error', error))
48
-
49
- }
50
-
51
- }
@@ -1,80 +0,0 @@
1
- import { exec } from 'child_process'
2
- import fs from 'fs'
3
- import { nanoid } from 'nanoid'
4
- import path from 'path'
5
- import { fileURLToPath } from 'url'
6
-
7
- const __filename = fileURLToPath(import.meta.url)
8
- const __dirname = path.dirname(__filename)
9
-
10
- export class DockerClient {
11
-
12
- static new() {
13
- return new DockerClient()
14
- }
15
-
16
- constructor() {
17
- this.networkName = 'deployment-tools'
18
- exec(`sudo docker network create ${this.networkName}`)
19
- }
20
-
21
- stopContainer({ image }) {
22
- const name = image.replaceAll('/', '_')
23
- return `sudo docker stop ${name} ||`
24
- }
25
-
26
- startContainer({ image, containerPort, hostPort, registry, env }) {
27
- const name = image.replaceAll('/', '_')
28
- const imageUrl = !!registry ? `${registry}/${image}` : image
29
- const envsAsString = Object.entries(env || {}).reduce((envs, [name, value]) => `${envs} --env ${name}=${value}`, '')
30
- return `sudo docker run -d -p ${hostPort}:${containerPort} --name=${name} --network=${this.networkName} --network-alias=${name} --rm ${envsAsString} ${imageUrl}`
31
- }
32
-
33
- resolveCredentials({ registry, username, authentication }) {
34
- return (username || authentication)
35
- ? `sudo docker login ${registry} -u ${username} -p ${authentication}`
36
- : 'echo No credentials provided, assuming image is publicly hosted'
37
- }
38
-
39
- deploy({
40
- image,
41
- registry,
42
- username,
43
- authentication,
44
- containerPort,
45
- hostPort,
46
- env
47
- }) {
48
- return new Promise(resolve => {
49
- const dockerCommandScript = `'#!/bin/bash'
50
- ${this.stopContainer({ image })}
51
- ${this.resolveCredentials({ registry, username, authentication })}
52
- ${this.startContainer({ image, containerPort, hostPort, registry, env })}`
53
-
54
- const deploymentId = nanoid()
55
-
56
- const FilePaths = {
57
- DeploymentScript: `${__dirname}/${deploymentId}.sh`
58
- }
59
-
60
- fs.writeFileSync(FilePaths.DeploymentScript, dockerCommandScript)
61
- const command = exec(`bash ${FilePaths.DeploymentScript}`)
62
-
63
- command.stdout.on('data', (data) => {
64
- console.log(`[DockerClient]: ${data}`)
65
- })
66
-
67
- command.stderr.on('data', (data) => {
68
- console.error(`[DockerClient]: ${data}`)
69
- })
70
-
71
- command.on('close', (code) => {
72
- console.log(`[DockerClient] command exited with code ${code}`)
73
- fs.unlinkSync(FilePaths.DeploymentScript)
74
- resolve()
75
- })
76
- })
77
-
78
- }
79
-
80
- }
package/src/index.js DELETED
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env node
2
- import { platform } from 'os'
3
- import { exec } from 'child_process'
4
- import arg from 'arg'
5
- import { ContainerManagerServer } from './container-manager-server.js'
6
- import { ContainerManagerClient } from './container-manager-client.js'
7
-
8
- const [_, __, command, ...restArgs] = process.argv
9
-
10
- const startHandler = () => {
11
- console.log('Running start command')
12
-
13
- const Platforms = {
14
- windows: 'win32',
15
- mac: 'darwin'
16
- }
17
-
18
- if ([Platforms.windows].includes(platform())) {
19
- return console.error('Deployment tools do not support this os')
20
- }
21
-
22
- ContainerManagerServer.start()
23
- }
24
-
25
- const stopHandler = () => {
26
- console.log('Running stop command')
27
- exec('systemctl stop deployment-tools.service')
28
- }
29
-
30
- const statusHandler = () => {
31
- console.log('Running status command')
32
- exec('systemctl status deployment-tools.service')
33
- }
34
-
35
- const deployHandler = () => {
36
- console.log('Running deploy command')
37
-
38
- const args = arg({
39
- '--username': String,
40
- '-u': '--username',
41
-
42
- '--authentication': String,
43
- '--a': '--authentication',
44
-
45
- '--target-env': String,
46
- '-t': '--target-env',
47
-
48
- '--ossyfile': String,
49
- '-o': '--ossyfile',
50
-
51
- '--platforms': String,
52
- '-p': '--platforms'
53
- }, { argv: restArgs })
54
-
55
- console.log('args', args)
56
-
57
- ContainerManagerClient.deploy(
58
- args['--username'],
59
- args['--authentication'],
60
- args['--target-env'],
61
- args['--platforms'],
62
- args['--ossyfile']
63
- )
64
- }
65
-
66
- const Commands = {
67
- start: startHandler,
68
- stop: stopHandler,
69
- status: statusHandler,
70
- deploy: deployHandler
71
- }
72
-
73
- const commandHandler = Commands[command]
74
-
75
- if (!commandHandler) {
76
- console.log('Command not implemented, did you spell it correctly?')
77
- }
78
-
79
- commandHandler()