@ossy/deployment-tools 3.0.9 → 3.4.0
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/CHANGELOG.md +17 -0
- package/README.md +156 -118
- package/jest.config.js +5 -0
- package/package.json +6 -4
- package/src/config/platform-config.js +16 -16
- package/src/ecs/platform-ecs-services.js +209 -0
- package/src/ecs/platform-ecs-services.spec.js +163 -0
- package/src/edge/platform-edge-domains.js +101 -0
- package/src/edge/platform-edge-domains.spec.js +87 -0
- package/src/index.js +45 -1
- package/src/infrastructure/dns-stack.js +47 -3
- package/src/infrastructure/dns-stack.spec.js +15 -0
- package/src/infrastructure/platform-ci-stack.js +151 -0
- package/src/infrastructure/platform-ecs-stack.js +349 -0
- package/src/infrastructure/platform-edge-stack.js +213 -0
- package/src/infrastructure/platform-secrets-stack.js +103 -0
- package/src/infrastructure/platform-stage.js +32 -5
- package/src/infrastructure/storage-static-stack.js +4 -5
- package/src/secrets/platform-secret-services.js +32 -0
- package/src/secrets/platform-secret-services.spec.js +40 -0
- package/src/secrets/sync-platform-secrets.js +136 -0
- package/src/services/platform-services.js +94 -0
- package/src/services/platform-services.spec.js +80 -0
- package/src/template/platform-template.js +5 -5
- package/src/infrastructure/container-deployment-target/aws-profile.js +0 -25
- package/src/infrastructure/container-deployment-target/caddy.service.js +0 -117
- package/src/infrastructure/container-deployment-target/container-deployment-target.js +0 -222
- package/src/infrastructure/container-deployment-target/container-service.js +0 -162
- package/src/infrastructure/container-deployment-target/index.js +0 -3
- package/src/infrastructure/container-deployment-target/ossy-api.service.js +0 -54
- package/src/infrastructure/container-deployment-target/ossy-runtime.service.js +0 -49
- package/src/infrastructure/container-deployment-target/user-data-commands.js +0 -31
- package/src/infrastructure/deployment-target-stack.js +0 -53
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {'http'|'tcp'} ServiceType
|
|
3
|
-
*
|
|
4
|
-
* @typedef {Object} HttpServiceConfig
|
|
5
|
-
* @property {'http'} [type] - defaults to 'http'
|
|
6
|
-
* @property {string} name - unique name, used for container name and systemd unit
|
|
7
|
-
* @property {string} domain - domain this service handles (Caddy block + Route53)
|
|
8
|
-
* @property {string} image - Docker image to run
|
|
9
|
-
*
|
|
10
|
-
* @typedef {Object} TcpServiceConfig
|
|
11
|
-
* @property {'tcp'} type - marks this as a raw TCP/UDP service (no HTTP/Caddy)
|
|
12
|
-
* @property {string} name - unique name, used for container name and systemd unit
|
|
13
|
-
* @property {string} image - Docker image to run
|
|
14
|
-
* @property {number[]} ports - ports to expose directly (same inside and outside container)
|
|
15
|
-
* @property {'tcp'|'udp'|'both'} [protocol] - defaults to 'tcp'
|
|
16
|
-
*
|
|
17
|
-
* @typedef {HttpServiceConfig|TcpServiceConfig} ContainerServiceConfig
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const BASE_HTTP_PORT = 3002 // 3000 = platform runtime, 3001 = API
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// HTTP service — reverse-proxied through Caddy, container listens on port 3000
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
class HttpContainerService {
|
|
27
|
-
constructor({ name, domain, image }, httpIndex) {
|
|
28
|
-
this.name = name
|
|
29
|
-
this.domain = domain
|
|
30
|
-
this.image = image
|
|
31
|
-
this.hostPort = BASE_HTTP_PORT + httpIndex
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get systemdUnitFile() {
|
|
35
|
-
return `
|
|
36
|
-
[Unit]
|
|
37
|
-
Description=Container service: ${this.name} (${this.domain})
|
|
38
|
-
After=network.target docker.service
|
|
39
|
-
Requires=docker.service
|
|
40
|
-
|
|
41
|
-
[Service]
|
|
42
|
-
EnvironmentFile=/etc/environment
|
|
43
|
-
ExecStartPre=-/usr/bin/docker rm -f ${this.name}
|
|
44
|
-
ExecStartPre=/usr/bin/docker pull ${this.image}
|
|
45
|
-
ExecStart=/usr/bin/docker run --name ${this.name} \\
|
|
46
|
-
--network ossy-network \\
|
|
47
|
-
-p ${this.hostPort}:3000 \\
|
|
48
|
-
--env-file /etc/environment \\
|
|
49
|
-
${this.image}
|
|
50
|
-
ExecStop=/usr/bin/docker stop ${this.name}
|
|
51
|
-
Restart=on-failure
|
|
52
|
-
RestartSec=5
|
|
53
|
-
|
|
54
|
-
[Install]
|
|
55
|
-
WantedBy=multi-user.target
|
|
56
|
-
`
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get caddyBlock() {
|
|
60
|
-
return `
|
|
61
|
-
${this.domain} {
|
|
62
|
-
tls {
|
|
63
|
-
dns route53 {
|
|
64
|
-
max_retries 10
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
reverse_proxy localhost:${this.hostPort}
|
|
68
|
-
}`
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Ports to open in the EC2 security group. HTTP services use Caddy on 80/443 — no extra rules needed. */
|
|
72
|
-
get securityGroupPorts() {
|
|
73
|
-
return []
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
get unitName() { return `${this.name}.service` }
|
|
77
|
-
install() { return [`sudo tee /etc/systemd/system/${this.unitName} > /dev/null << 'EOF'\n${this.systemdUnitFile}\nEOF`] }
|
|
78
|
-
enable() { return [`sudo systemctl enable ${this.name}`] }
|
|
79
|
-
start() { return [`sudo systemctl start ${this.name}`] }
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
// TCP/UDP service — bypasses Caddy, ports exposed directly on the host.
|
|
84
|
-
// Used for game servers and any other raw socket services.
|
|
85
|
-
// ---------------------------------------------------------------------------
|
|
86
|
-
|
|
87
|
-
class TcpContainerService {
|
|
88
|
-
constructor({ name, image, ports, protocol = 'tcp' }) {
|
|
89
|
-
this.name = name
|
|
90
|
-
this.image = image
|
|
91
|
-
this.ports = ports
|
|
92
|
-
this.protocol = protocol // 'tcp' | 'udp' | 'both'
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
get systemdUnitFile() {
|
|
96
|
-
const portMappings = this.ports.map(p => `-p ${p}:${p}`).join(' \\\n ')
|
|
97
|
-
return `
|
|
98
|
-
[Unit]
|
|
99
|
-
Description=Container service: ${this.name} (${this.protocol.toUpperCase()} ${this.ports.join(', ')})
|
|
100
|
-
After=network.target docker.service
|
|
101
|
-
Requires=docker.service
|
|
102
|
-
|
|
103
|
-
[Service]
|
|
104
|
-
EnvironmentFile=/etc/environment
|
|
105
|
-
ExecStartPre=-/usr/bin/docker rm -f ${this.name}
|
|
106
|
-
ExecStartPre=/usr/bin/docker pull ${this.image}
|
|
107
|
-
ExecStart=/usr/bin/docker run --name ${this.name} \\
|
|
108
|
-
--network ossy-network \\
|
|
109
|
-
${portMappings} \\
|
|
110
|
-
--env-file /etc/environment \\
|
|
111
|
-
${this.image}
|
|
112
|
-
ExecStop=/usr/bin/docker stop ${this.name}
|
|
113
|
-
Restart=on-failure
|
|
114
|
-
RestartSec=5
|
|
115
|
-
|
|
116
|
-
[Install]
|
|
117
|
-
WantedBy=multi-user.target
|
|
118
|
-
`
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** TCP services bypass Caddy — no Caddy block. */
|
|
122
|
-
get caddyBlock() { return null }
|
|
123
|
-
|
|
124
|
-
/** Each port needs an explicit security group ingress rule. */
|
|
125
|
-
get securityGroupPorts() {
|
|
126
|
-
return this.ports.flatMap(port => {
|
|
127
|
-
if (this.protocol === 'both') return [{ port, protocol: 'tcp' }, { port, protocol: 'udp' }]
|
|
128
|
-
return [{ port, protocol: this.protocol }]
|
|
129
|
-
})
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
get unitName() { return `${this.name}.service` }
|
|
133
|
-
install() { return [`sudo tee /etc/systemd/system/${this.unitName} > /dev/null << 'EOF'\n${this.systemdUnitFile}\nEOF`] }
|
|
134
|
-
enable() { return [`sudo systemctl enable ${this.name}`] }
|
|
135
|
-
start() { return [`sudo systemctl start ${this.name}`] }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
// Factory
|
|
140
|
-
// ---------------------------------------------------------------------------
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Build service instances from the `services` array in platforms.json.
|
|
144
|
-
* HTTP services auto-assign host ports starting at 3002 (counted separately
|
|
145
|
-
* from TCP services so reordering TCP entries doesn't shift HTTP ports).
|
|
146
|
-
*
|
|
147
|
-
* @param {ContainerServiceConfig[]} services
|
|
148
|
-
* @returns {(HttpContainerService|TcpContainerService)[]}
|
|
149
|
-
*/
|
|
150
|
-
const fromConfig = (services = []) => {
|
|
151
|
-
let httpIndex = 0
|
|
152
|
-
return services.map(s => {
|
|
153
|
-
if (s.type === 'tcp') return new TcpContainerService(s)
|
|
154
|
-
return new HttpContainerService(s, httpIndex++)
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
module.exports = {
|
|
159
|
-
HttpContainerService,
|
|
160
|
-
TcpContainerService,
|
|
161
|
-
fromConfig
|
|
162
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
const systemdServiceFile = `
|
|
2
|
-
[Unit]
|
|
3
|
-
Description=Ossy API
|
|
4
|
-
After=network.target docker.service
|
|
5
|
-
Requires=docker.service
|
|
6
|
-
|
|
7
|
-
[Service]
|
|
8
|
-
EnvironmentFile=/etc/environment
|
|
9
|
-
ExecStartPre=-/usr/bin/docker rm -f ossy-api
|
|
10
|
-
ExecStartPre=/usr/bin/docker pull ghcr.io/ossy-se/api:latest
|
|
11
|
-
ExecStart=/usr/bin/docker run --name ossy-api \\
|
|
12
|
-
--network ossy-network \\
|
|
13
|
-
-p 3001:3000 \\
|
|
14
|
-
-e DB_URL=\${DB_URL} \\
|
|
15
|
-
-e DB_NAME=\${DB_NAME} \\
|
|
16
|
-
-e TOKEN_SECRET=\${TOKEN_SECRET} \\
|
|
17
|
-
-e MEDIA_REPOSITORY=\${MEDIA_REPOSITORY} \\
|
|
18
|
-
-e MEDIA_CDN_DOMAIN_NAME=\${MEDIA_CDN_DOMAIN_NAME} \\
|
|
19
|
-
-e AWS_ACCESS_KEY_ID=\${AWS_ACCESS_KEY_ID} \\
|
|
20
|
-
-e AWS_SECRET_ACCESS_KEY=\${AWS_SECRET_ACCESS_KEY} \\
|
|
21
|
-
ghcr.io/ossy-se/api:latest
|
|
22
|
-
ExecStop=/usr/bin/docker stop ossy-api
|
|
23
|
-
Restart=on-failure
|
|
24
|
-
RestartSec=5
|
|
25
|
-
|
|
26
|
-
[Install]
|
|
27
|
-
WantedBy=multi-user.target
|
|
28
|
-
`
|
|
29
|
-
|
|
30
|
-
// The image is pulled automatically on every start via ExecStartPre, so the
|
|
31
|
-
// service always runs the latest published version. To redeploy: systemctl restart ossy-api
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @class
|
|
35
|
-
*/
|
|
36
|
-
class OssyApiService {
|
|
37
|
-
|
|
38
|
-
static install() {
|
|
39
|
-
return [`sudo tee /etc/systemd/system/ossy-api.service > /dev/null << 'EOF'\n${systemdServiceFile}\nEOF`]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
static enable() {
|
|
43
|
-
return ['sudo systemctl enable ossy-api']
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
static start() {
|
|
47
|
-
return ['sudo systemctl start ossy-api']
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = {
|
|
53
|
-
OssyApiService
|
|
54
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
const systemdServiceFile = `
|
|
2
|
-
[Unit]
|
|
3
|
-
Description=Ossy Runtime
|
|
4
|
-
After=network.target docker.service
|
|
5
|
-
Requires=docker.service
|
|
6
|
-
|
|
7
|
-
[Service]
|
|
8
|
-
EnvironmentFile=/etc/environment
|
|
9
|
-
ExecStartPre=-/usr/bin/docker rm -f ossy-runtime
|
|
10
|
-
ExecStartPre=/usr/bin/docker pull ghcr.io/ossy-se/platform:latest
|
|
11
|
-
ExecStart=/usr/bin/docker run --name ossy-runtime \\
|
|
12
|
-
--network ossy-network \\
|
|
13
|
-
-p 3000:3000 \\
|
|
14
|
-
-e OSSY_API_KEY=\${OSSY_API_KEY} \\
|
|
15
|
-
-e OSSY_API_URL=http://ossy-api:3001/api/v0 \\
|
|
16
|
-
ghcr.io/ossy-se/platform:latest
|
|
17
|
-
ExecStop=/usr/bin/docker stop ossy-runtime
|
|
18
|
-
Restart=on-failure
|
|
19
|
-
RestartSec=5
|
|
20
|
-
|
|
21
|
-
[Install]
|
|
22
|
-
WantedBy=multi-user.target
|
|
23
|
-
`
|
|
24
|
-
|
|
25
|
-
// The image is pulled automatically on every start via ExecStartPre, so the
|
|
26
|
-
// service always runs the latest published version. To redeploy: systemctl restart ossy-runtime
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @class
|
|
30
|
-
*/
|
|
31
|
-
class OssyRuntimeService {
|
|
32
|
-
|
|
33
|
-
static install() {
|
|
34
|
-
return [`sudo tee /etc/systemd/system/ossy-runtime.service > /dev/null << 'EOF'\n${systemdServiceFile}\nEOF`]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static enable() {
|
|
38
|
-
return ['sudo systemctl enable ossy-runtime']
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
static start() {
|
|
42
|
-
return ['sudo systemctl start ossy-runtime']
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
module.exports = {
|
|
48
|
-
OssyRuntimeService
|
|
49
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const getInstallDocker = () => [
|
|
2
|
-
'apt-get remove docker docker-engine docker.io containerd runc',
|
|
3
|
-
`apt-get install \
|
|
4
|
-
apt-transport-https \
|
|
5
|
-
ca-certificates \
|
|
6
|
-
curl \
|
|
7
|
-
gnupg-agent \
|
|
8
|
-
software-properties-common -y`,
|
|
9
|
-
'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -',
|
|
10
|
-
`sudo add-apt-repository \
|
|
11
|
-
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
|
|
12
|
-
$(lsb_release -cs) \
|
|
13
|
-
stable"`,
|
|
14
|
-
'sudo apt-get update',
|
|
15
|
-
'sudo apt-get install docker-ce docker-ce-cli containerd.io -y'
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
const getInstallNodeJs = () => [
|
|
19
|
-
'curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -',
|
|
20
|
-
'sudo apt-get -y install nodejs'
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
const getInstallNpm = () => [
|
|
24
|
-
'sudo apt install npm'
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
module.exports = {
|
|
28
|
-
getInstallDocker,
|
|
29
|
-
getInstallNodeJs,
|
|
30
|
-
getInstallNpm
|
|
31
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-new */
|
|
2
|
-
const { CfnOutput, Stack } = require('aws-cdk-lib')
|
|
3
|
-
const { ContainerDeploymentTarget } = require('./container-deployment-target')
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* DeploymentTargetStackProps
|
|
7
|
-
* @typedef {Object} DeploymentTargetStackProps
|
|
8
|
-
* @property {PlatformConfig} config - platform config
|
|
9
|
-
* @property {import('aws-cdk-lib/aws-s3').IBucket} bucket - static assets bucket (from StorageStaticStack)
|
|
10
|
-
* @property {string} cdnDomainName - CloudFront domain name (from StorageStaticStack)
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* DeploymentTargetStack provisions the EC2 instance (with Elastic IP) that runs the
|
|
15
|
-
* platform services. The S3 bucket lives in StorageStaticStack so that EC2 replacements
|
|
16
|
-
* never trigger CloudFormation cross-stack export conflicts on storage resources.
|
|
17
|
-
*
|
|
18
|
-
* MEDIA_REPOSITORY and MEDIA_CDN_DOMAIN_NAME are derived from the actual bucket and CDN
|
|
19
|
-
* resources rather than being hardcoded in platforms.json.
|
|
20
|
-
*/
|
|
21
|
-
class DeploymentTargetStack extends Stack {
|
|
22
|
-
constructor(scope, id, props) {
|
|
23
|
-
super(scope, id, props)
|
|
24
|
-
|
|
25
|
-
if (!props?.config) {
|
|
26
|
-
throw ('[DeploymentTargetStack] No config provided')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const containerDeploymentTarget = new ContainerDeploymentTarget(this, 'ContainerDeploymentTarget', {
|
|
30
|
-
config: {
|
|
31
|
-
...props.config,
|
|
32
|
-
env: {
|
|
33
|
-
...props.config.env,
|
|
34
|
-
MEDIA_REPOSITORY: props.bucket.bucketName,
|
|
35
|
-
MEDIA_CDN_DOMAIN_NAME: `https://${props.cdnDomainName}`
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
bucket: props.bucket
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
this.containerDeploymentTargetPublicIp = containerDeploymentTarget.instancePublicIp
|
|
42
|
-
|
|
43
|
-
new CfnOutput(this, 'ContainerDeploymentTargetPublicIp', {
|
|
44
|
-
value: containerDeploymentTarget.instancePublicIp,
|
|
45
|
-
description: 'Public IP of the EC2 instance (Elastic IP)',
|
|
46
|
-
exportName: `${props.config.platformName}-ContainerDeploymentTargetPublicIp`
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = {
|
|
52
|
-
DeploymentTargetStack
|
|
53
|
-
}
|