Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ability to rollback to a previous Baremetal deploy #5361

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/docs/deploy/baremetal.md
Expand Up @@ -394,6 +394,24 @@ yarn rw deploy baremetal --no-migrate

Run `yarn rw deploy baremetal --help` for the full list of flags. You can set them as `--migrate=false` or use the `--no-migrate` variant.

## Rollback

If you deploy and find something has gone horribly wrong, you can rollback your deploy to the previous release:

```bash
yarn rw deploy baremetal --rollback
```

You can even rollback multiple deploys, up to the total number you still have denoted with the `keepReleases` option:

```bash
yarn rw deploy baremetal --rollback 3
```

Note that this will *not* rollback your database—if you had a release that changed the database, that updated database will still be in effect, but with the previous version of the web and api sides. Trying to undo database migrations is a very difficult proposition and isn't even possible in many cases.

Make sure to thoroughly test releases that change the database before doing it for real!

## Maintenance Page

If you find that you have a particular complex deploy, one that may involve incompatible database changes with the current codebase, or want to make sure that database changes don't occur while in the middle of a deploy, you can put up a maintenance page:
Expand Down
130 changes: 116 additions & 14 deletions packages/cli/src/commands/deploy/baremetal.js
Expand Up @@ -96,6 +96,11 @@ export const builder = (yargs) => {
help: 'Put up a maintenance page by replacing the content of web/dist/index.html with the content of web/src/maintenance.html',
})

yargs.option('rollback', {
describe: 'Add/remove the maintenance page',
help: 'Rollback [count] number of releases',
})

// TODO: Allow option to pass --sides and only deploy select sides instead of all, always

yargs.epilogue(
Expand Down Expand Up @@ -133,6 +138,8 @@ const sshExec = async (ssh, sshOptions, task, path, command, args) => {
)
process.exit(1)
}

return result
}

export const verifyServerConfig = (config) => {
Expand Down Expand Up @@ -200,6 +207,93 @@ const maintenanceTasks = (status, ssh, sshOptions, serverConfig) => {
}
}

const rollbackTasks = (count, ssh, sshOptions, serverConfig) => {
let rollbackCount = 1

if (parseInt(count) === count) {
rollbackCount = count
}

const tasks = [
{
title: `Rolling back ${rollbackCount} release(s)...`,
task: async (_ctx, task) => {
const currentLink = (
await sshExec(ssh, sshOptions, task, serverConfig.path, 'readlink', [
'-f',
'current',
])
).stdout
.split('/')
.pop()
const dirs = (
await sshExec(ssh, sshOptions, task, serverConfig.path, 'ls', ['-t'])
).stdout
.split('\n')
.filter((dirs) => !dirs.match(/current/))

const deployedIndex = dirs.indexOf(currentLink)
const rollbackIndex = deployedIndex + rollbackCount

if (dirs[rollbackIndex]) {
console.info('Setting symlink')
await symlinkCurrentCommand(
dirs[rollbackIndex],
ssh,
sshOptions,
task,
serverConfig.path
)
} else {
throw new Error(
`Cannot rollback ${rollbackCount} release(s): ${
dirs.length - dirs.indexOf(currentLink) - 1
} previous release(s) available`
)
}
},
},
]

for (const processName of serverConfig.processNames) {
tasks.push({
title: `Restarting ${processName} process...`,
task: async (_ctx, task) => {
await restartProcessCommand(
processName,
ssh,
sshOptions,
task,
serverConfig.path
)
},
})
}

return tasks
}

const symlinkCurrentCommand = async (dir, ssh, sshOptions, task, path) => {
return await sshExec(ssh, sshOptions, task, path, 'ln', [
SYMLINK_FLAGS,
dir,
CURRENT_RELEASE_SYMLINK_NAME,
])
}

const restartProcessCommand = async (
processName,
ssh,
sshOptions,
task,
path
) => {
return await sshExec(ssh, sshOptions, task, path, 'pm2', [
'restart',
processName,
])
}

const deployTasks = (yargs, ssh, sshOptions, serverConfig) => {
const deployBranch =
yargs.branch || serverConfig.branch || DEFAULT_BRANCH_NAME
Expand Down Expand Up @@ -283,32 +377,33 @@ const deployTasks = (yargs, ssh, sshOptions, serverConfig) => {
tasks.push({
title: `Symlinking current release...`,
task: async (_ctx, task) => {
await sshExec(ssh, sshOptions, task, serverConfig.path, 'ln', [
SYMLINK_FLAGS,
await symlinkCurrentCommand(
yargs.releaseDir,
CURRENT_RELEASE_SYMLINK_NAME,
])
ssh,
sshOptions,
task,
serverConfig.path
)
},
skip: () => !yargs.update,
})

// start/restart monitoring processes
for (const process of serverConfig.processNames) {
for (const processName of serverConfig.processNames) {
if (yargs.firstRun) {
tasks.push({
title: `Starting ${process} process for the first time...`,
title: `Starting ${processName} process for the first time...`,
task: async (_ctx, task) => {
await sshExec(ssh, sshOptions, task, serverConfig.path, 'pm2', [
'start',
path.join(CURRENT_RELEASE_SYMLINK_NAME, 'ecosystem.config.js'),
'--only',
process,
processName,
])
},
skip: () => !yargs.restart,
})
tasks.push({
title: `Saving ${process} state for future startup...`,
title: `Saving ${processName} state for future startup...`,
task: async (_ctx, task) => {
await sshExec(ssh, sshOptions, task, serverConfig.path, 'pm2', [
'save',
Expand All @@ -318,12 +413,15 @@ const deployTasks = (yargs, ssh, sshOptions, serverConfig) => {
})
} else {
tasks.push({
title: `Restarting ${process} process...`,
title: `Restarting ${processName} process...`,
task: async (_ctx, task) => {
await sshExec(ssh, sshOptions, task, serverConfig.path, 'pm2', [
'restart',
process,
])
await restartProcessCommand(
processName,
ssh,
sshOptions,
task,
serverConfig.path
)
},
skip: () => !yargs.restart,
})
Expand Down Expand Up @@ -396,6 +494,10 @@ const commands = (yargs, ssh) => {
tasks = tasks.concat(
maintenanceTasks(yargs.maintenance, ssh, sshOptions, serverConfig)
)
} else if (yargs.rollback) {
tasks = tasks.concat(
rollbackTasks(yargs.rollback, ssh, sshOptions, serverConfig)
)
} else {
tasks = tasks.concat(deployTasks(yargs, ssh, sshOptions, serverConfig))
}
Expand Down