-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c9927e2
Showing
15 changed files
with
5,490 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.github | ||
node_modules | ||
dist | ||
tests | ||
.gitignore | ||
babel.config.js | ||
LICENSE | ||
README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: ci | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
pull_request: | ||
branches: | ||
- '*' | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: yarn | ||
- run: yarn test | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: yarn | ||
- run: yarn build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
FROM mhart/alpine-node:12 | ||
|
||
WORKDIR /app | ||
|
||
COPY package.json yarn.lock ./ | ||
|
||
RUN yarn --frozen-lockfile | ||
|
||
COPY . ./ | ||
|
||
RUN yarn build \ | ||
&& rm -rf node_modules \ | ||
&& yarn --frozen-lockfile --prod | ||
|
||
FROM mhart/alpine-node:slim-12 | ||
|
||
WORKDIR /app | ||
|
||
COPY --from=0 /app/dist ./ | ||
|
||
COPY --from=0 /app/node_modules ./node_modules | ||
|
||
ENV NODE_ENV=production | ||
|
||
CMD [ "node", "index.js" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2020 Tim Cheung <[email protected]> | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# docker-csgo-updater | ||
|
||
<p> | ||
<a href="https://github.com/timche/docker-csgo-updater"> | ||
<img alt="GitHub CI" src="https://github.com/timche/docker-csgo-updater/workflows/ci/badge.svg" /> | ||
</a> | ||
<a href="https://hub.docker.com/r/timche/csgo-updater"> | ||
<img alt="Docker Image Version (latest semver)" src="https://img.shields.io/docker/v/timche/csgo-updater" /> | ||
</a> | ||
<a href="https://hub.docker.com/r/timche/csgo-updater"> | ||
<img alt="Docker Image Size (latest semver)" src="https://img.shields.io/docker/image-size/timche/csgo-updater" /> | ||
</a> | ||
<a href="https://hub.docker.com/r/timche/csgo-updater"> | ||
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/timche/csgo-updater" /> | ||
</a> | ||
<a href="https://hub.docker.com/r/timche/csgo-updater"> | ||
<img alt="Docker Stars" src="https://img.shields.io/docker/stars/timche/csgo-updater" /> | ||
</a> | ||
</p> | ||
|
||
> Automatically restart [timche/csgo](https://github.com/timche/docker-csgo) based containers to update CS:GO servers when an update is available | ||
## How to Use This Image | ||
|
||
``` | ||
$ docker run -d \ | ||
--name csgo-updater \ | ||
-v /var/run/docker.sock:/var/run/docker.sock \ | ||
timche/csgo-updater | ||
``` | ||
|
||
### Environment Variables | ||
|
||
##### `UPDATER_CONTAINER_IMAGE` | ||
|
||
Default: `timche/csgo` | ||
|
||
The Docker containers running the specified image name csgo-updater will watch. | ||
|
||
##### `UPDATER_POLL_INTERVAL` | ||
|
||
Default: `60` | ||
|
||
The poll interval (in seconds) csgo-updater will poll for new containers. | ||
|
||
## How It Works | ||
|
||
csgo-updater is attaching to the stdout of the containers and will restart them when their CS:GO server process is logging `MasterRequestRestart`, which is a request from the Steam Master Server to tell the CS:GO server that an update is available and the server should restart. | ||
|
||
To restart, csgo-updater will send `SIGINT` to the container, which is not immediately killing the CS:GO server process but instead the process will check if the server is empty or will wait for the server to be empty and then shut it down to stop the container. After that, csgo-updater will start the container again and [the CS:GO server will be updated before starting the server](https://github.com/timche/docker-csgo#updating-the-server). | ||
|
||
**Note:** If the CS:GO server container has a restart policy set, the policy won't restart the container in this case, because csgo-updater is stopping the container manually. See [Docker restart policy details](https://docs.docker.com/config/containers/start-containers-automatically/#restart-policy-details). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module.exports = { | ||
presets: [ | ||
['@babel/preset-env', { targets: { node: 'current' } }], | ||
'@babel/preset-typescript' | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{ | ||
"scripts": { | ||
"start": "node dist/index.js", | ||
"dev": "ts-node-dev src/index.ts", | ||
"build": "tsc", | ||
"test": "jest" | ||
}, | ||
"dependencies": { | ||
"delay": "^4.3.0", | ||
"dockerode": "^3.0.2", | ||
"pino": "^6.3.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.8.4", | ||
"@babel/preset-env": "^7.8.4", | ||
"@babel/preset-typescript": "^7.8.3", | ||
"@sindresorhus/tsconfig": "^0.6.0", | ||
"@types/dockerode": "^2.5.31", | ||
"@types/jest": "^25.1.2", | ||
"@types/node": "^12.12.14", | ||
"@types/pino": "^6.0.1", | ||
"babel-jest": "^25.1.0", | ||
"husky": "^3.1.0", | ||
"jest": "^25.1.0", | ||
"prettier": "^1.19.1", | ||
"pretty-quick": "^2.0.1", | ||
"ts-node-dev": "^1.0.0-pre.44", | ||
"typescript": "^3.7.3" | ||
}, | ||
"prettier": { | ||
"semi": false, | ||
"singleQuote": true | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "pretty-quick --staged" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const { UPDATER_CONTAINER_IMAGE = 'timche/csgo' } = process.env | ||
|
||
const csgoImageNameRegExp = new RegExp( | ||
`${UPDATER_CONTAINER_IMAGE}$|${UPDATER_CONTAINER_IMAGE}:.+` | ||
) | ||
|
||
export function isCSGOImage(imageName: string) { | ||
return csgoImageNameRegExp.test(imageName) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import delay from 'delay' | ||
import * as pino from 'pino' | ||
import logger from './logger' | ||
import watchContainers from './watchContainers' | ||
|
||
const { | ||
UPDATER_POLL_INTERVAL = 60 // Seconds | ||
} = process.env | ||
|
||
process.on( | ||
'uncaughtException', | ||
pino.final(logger, (err, finalLogger) => { | ||
finalLogger.error(err, 'uncaughtException') | ||
process.exit(1) | ||
}) | ||
) | ||
|
||
process.on( | ||
// TS overload bug: | ||
// Argument of type '"unhandledRejection"' is not assignable to parameter of type 'Signals'. | ||
// @ts-ignore | ||
'unhandledRejection', | ||
pino.final(logger, (err, finalLogger) => { | ||
finalLogger.error(err, 'unhandledRejection') | ||
process.exit(1) | ||
}) | ||
) | ||
|
||
async function dockerCSGOUpdater() { | ||
while (true) { | ||
watchContainers() | ||
await delay(Number(UPDATER_POLL_INTERVAL) * 1000) | ||
} | ||
} | ||
|
||
dockerCSGOUpdater() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import * as pino from 'pino' | ||
|
||
const logger = pino() | ||
|
||
export default logger |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import * as Docker from 'dockerode' | ||
import { isCSGOImage } from './helpers' | ||
import logger from './logger' | ||
|
||
const docker = new Docker() | ||
|
||
let watchingContainers: string[] = [] | ||
|
||
async function watchContainer(containerInfo: Docker.ContainerInfo) { | ||
try { | ||
const container = docker.getContainer(containerInfo.Id) | ||
const containerName = containerInfo.Names[0] | ||
const containerId = container.id | ||
|
||
let updating = false | ||
|
||
watchingContainers = watchingContainers.concat(containerId) | ||
|
||
const attachContainer = async () => { | ||
logger.info(`Watching ${containerName} (${containerId}) ...`) | ||
|
||
const stdoutStream = await container.attach({ | ||
stream: true, | ||
stdout: true | ||
}) | ||
|
||
stdoutStream.on('error', logger.error) | ||
|
||
const checkForUpdate = async (data: Buffer) => { | ||
try { | ||
const stdout = data.toString() | ||
|
||
if (stdout.includes('MasterRequestRestart')) { | ||
updating = true | ||
|
||
logger.info( | ||
`Update available for ${containerName} (${container.id}), restarting with SIGINT ...` | ||
) | ||
|
||
stdoutStream.removeListener('data', checkForUpdate) | ||
|
||
await Promise.all([ | ||
container.kill({ signal: 'SIGINT' }), | ||
container.wait() | ||
]) | ||
|
||
updating = false | ||
|
||
logger.debug(`Starting ${containerName} (${container.id}) ...`) | ||
|
||
await container.start() | ||
|
||
logger.debug(`${containerName} (${container.id}) started`) | ||
|
||
await attachContainer() | ||
} | ||
} catch (error) { | ||
logger.error(error) | ||
} | ||
} | ||
|
||
stdoutStream.on('data', checkForUpdate) | ||
|
||
stdoutStream.on('close', async () => { | ||
try { | ||
stdoutStream.removeAllListeners() | ||
|
||
logger.debug(`${containerName} (${containerId}) stopped`) | ||
|
||
if (!updating) { | ||
logger.debug(`Stopped watching ${containerName} (${containerId})`) | ||
|
||
watchingContainers = watchingContainers.filter( | ||
watchingContainerId => watchingContainerId !== containerId | ||
) | ||
} | ||
} catch (error) { | ||
logger.error(error) | ||
} | ||
}) | ||
} | ||
|
||
await attachContainer() | ||
} catch (error) { | ||
logger.error(error) | ||
} | ||
} | ||
|
||
async function getUnwatchedContainers() { | ||
const containers = await docker.listContainers() | ||
|
||
return containers.filter( | ||
({ Id: id, Image: image }) => | ||
isCSGOImage(image) && !watchingContainers.includes(id) | ||
) | ||
} | ||
|
||
export default async function watchContainers() { | ||
try { | ||
logger.debug('Looking for unwatched containers ...') | ||
|
||
const unwatchedContainers = await getUnwatchedContainers() | ||
|
||
unwatchedContainers.forEach(containerInfo => watchContainer(containerInfo)) | ||
} catch (error) { | ||
logger.error(error) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { isCSGOImage } from '../src/helpers' | ||
|
||
describe('isCSGOImageName', () => { | ||
test('returns true', () => { | ||
expect(isCSGOImage('timche/csgo')).toBe(true) | ||
expect(isCSGOImage('timche/csgo:pug-practice')).toBe(true) | ||
}) | ||
|
||
test('returns false', () => { | ||
expect(isCSGOImage('timche/csgo-foo')).toBe(false) | ||
expect(isCSGOImage('foo/bar')).toBe(false) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"extends": "@sindresorhus/tsconfig", | ||
"compilerOptions": { | ||
"outDir": "dist", | ||
"declaration": false | ||
}, | ||
"exclude": ["tests"] | ||
} |
Oops, something went wrong.