Skip to content

Instantly share code, notes, and snippets.

@rainder
Created July 27, 2017 12:15
Show Gist options
  • Save rainder/ed3e28e49ba809d6964fcec5577cd172 to your computer and use it in GitHub Desktop.
Save rainder/ed3e28e49ba809d6964fcec5577cd172 to your computer and use it in GitHub Desktop.
kubernetes cluster backup script
#!/usr/bin/env node
'use strict';
/**
*
* Usage example
*
* To dump cluster configuration to the file use
*
* `node ./k8s-backup.js [--context=...] > dump.json`
*
* To restore use
*
* `kubectl apply [--context=...] -f dump.json`
*/
const childProcess = require('child_process');
const util = require('util');
const exec = util.promisify(childProcess.exec);
const EXEC_OPTIONS = { maxBuffer: Infinity };
const OBJECTS_TO_BACKUP = [
'namespaces',
'limitranges',
'serviceaccounts',
'clusterrolebindings',
'clusterroles',
'rolebindings',
'roles',
'configmaps',
'secrets',
'storageclasses',
'services',
'ingresses',
'daemonsets',
'deployments',
'replicationcontrollers',
'statefulsets',
'horizontalpodautoscalers',
'poddisruptionbudgets',
// 'thirdpartyresources',
];
return run().catch((err) => console.error(err.stack));
/**
*
* @returns {Promise.<void>}
*/
async function run() {
const command = [
'kubectl',
'get',
OBJECTS_TO_BACKUP.join(','),
'--export=true',
'-o json',
'--all-namespaces',
...getArgs(),
].join(' ');
const json = await exec(command, EXEC_OPTIONS).then((result) => {
if (result.stderr) {
console.error(result.stderr);
process.exit(1);
}
return JSON.parse(result.stdout);
});
cleanUp(json);
printStats(json);
const jsonString = JSON.stringify(json, 0, 2);
process.stdout.write(jsonString);
console.error(`\nBytes written: ${jsonString.length}`);
};
/**
*
* @param json
*/
function cleanUp(json) {
json.items = json.items.filter((item) => {
return item.metadata.namespace !== 'kube-system' && item.metadata.namespace !== 'kube-public';
});
json.items.forEach((item) => {
Reflect.deleteProperty(item, 'status');
Reflect.deleteProperty(item.metadata, 'creationTimestamp');
Reflect.deleteProperty(item.metadata, 'uid');
Reflect.deleteProperty(item.metadata, 'resourceVersion');
if (item.metadata.annotations) {
Reflect.deleteProperty(item.metadata.annotations, 'kubectl.kubernetes.io/last-applied-configuration');
}
if (item.kind === 'Service') {
Reflect.deleteProperty(item.spec, 'clusterIP');
}
});
}
/**
*
* @param json
*/
function printStats(json) {
const objects = new Map();
console.error(
'Namespaces:',
Array.from(new Set(json.items.map(item => item.metadata.namespace))).filter(i => i).join(', '),
);
json.items.forEach(({kind}) => {
objects.set(kind, (objects.get(kind) || 0) + 1);
});
console.error('');
console.error(Array.from(objects.entries()).map(i => i.join(': ')).join('\n'));
}
/**
*
* @returns {Array}
*/
function getArgs() {
const result = [];
const commands = {
'--context': (value) => result[result.length] = `--context=${value}`,
};
process.argv.slice(2).forEach((arg) => {
const [key, value] = arg.split('=');
if (commands[key] === undefined) {
console.error(`Unknown argument specified: ${key}`);
process.exit(1);
}
commands[key](value);
});
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment