Last active
October 31, 2021 15:27
-
-
Save kdorff/2f208b85ef465dbc34dff14d02a55d57 to your computer and use it in GitHub Desktop.
Groovy script to perform diff against the last two autorestic snapshots for a list of backends.
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
#!/usr/bin/env groovy | |
import groovy.yaml.YamlSlurper | |
/** | |
* | |
* Script to perform a diff from latest:latest to latest on the | |
* list of autorestic backends passed in as command line arguments. | |
* | |
* REQUIREMENTS | |
* * restic (tested with 0.12.1) | |
* * autorestic (tested with 1.3.0, configured and working, with at least two snapshots) | |
* * java 1.8+ (tested with openjdk 1.8-292) | |
* * groovy 3.0.0+ (tested with 3.0.9, I install via sdkman) | |
* | |
* example .autorestic.yml | |
* --------------------------- | |
* backends: | |
* local-docker-compose: | |
* type: local | |
* path: /local-restic-repo-folder/docker-compose/ | |
* key: "local_restic_repo_key" | |
* remote-docker-compose: | |
* type: b2 | |
* path: bucketname:/docker-compose/ | |
* key: "remote_restic_repo_key" | |
* env: | |
* b2_account_id: my_b2_account_id | |
* b2_account_key: my_b2_account_key | |
* locations: | |
* docker-compose: | |
* from: /folder/containing/data/to/backup/ | |
* to: | |
* - local-docker-compose | |
* - remote-docker-compose | |
* hooks: | |
* after: | |
* - /root/autorestic-scripts/last_diff.groovy local-docker-compose remote-docker-compose | |
*/ | |
class last_diff { | |
/** | |
* configuration: where to find the restic binary. | |
*/ | |
static String RESTIC_BIN = '/usr/bin/restic' | |
/** | |
* configuration: where to find the autorestic config file. | |
*/ | |
static String AUTORESTIC_CONFIG = '/root/.autorestic.yml' | |
/** | |
* The autorestic config File. | |
*/ | |
File autoresticConfigFile = new File(AUTORESTIC_CONFIG) | |
/** | |
* the backends to diff. | |
*/ | |
List<String> backendsToDiff = [] | |
/** | |
* Kick it all off. | |
*/ | |
static void main(String[] args) { | |
new last_diff(args).run() | |
} | |
/** | |
* Constructor. Parse args and such. | |
*/ | |
last_diff(String[] args) { | |
backendsToDiff.addAll(args) | |
if (!autoresticConfigFile.exists()) { | |
System.err.println("The autorestic config file doesn't exist ${AUTORESTIC_CONFIG}") | |
System.exit(-1) | |
} | |
} | |
/** | |
* Primary execution method. | |
*/ | |
void run() { | |
def config = readAutoResticConfig() | |
backendsToDiff.eachWithIndex { backendToDiff, i -> | |
if (i) { | |
println "" | |
} | |
def backendConfig = config.backends."${backendToDiff}" | |
List<String> env = configureExecEnv(backendConfig) | |
diffBackend(backendToDiff, env) | |
} | |
} | |
/** | |
* Try to diff from latest:latest to latest for one backend. | |
*/ | |
void diffBackend(String backend, List<String> env) { | |
// | |
// Get the list of snapshots | |
// | |
def cmd = "${RESTIC_BIN} snapshots" | |
// println "Executing ${cmd} with ${env}" | |
def snapshotsProc = cmd.execute(env, null) | |
def snapshotsBuffer = new StringBuffer() | |
// Capture the output | |
snapshotsProc.waitForProcessOutput(snapshotsBuffer, System.err) | |
if (snapshotsProc.exitValue() != 0) { | |
System.err.println "!! Command ${cmd} failed with exit code ${snapshotsProc.exitValue()}" | |
return | |
} | |
List<String> snapshots = snapshotsBuffer.toString().split('[\n\r]') | |
if (snapshots.size() < 6) { | |
System.err.println("Not enough snaphots found.") | |
System.err.println("The snapshots command returned ${snapshots}") | |
return | |
} | |
String[] diffFromParts = snapshots[-4].split(' +') | |
String diffFromId = diffFromParts[0] | |
String diffFromTime = diffFromParts[1] + ' ' + diffFromParts[2] | |
String[] diffToParts = snapshots[-3].split(' +') | |
String diffToId = diffToParts[0] | |
String diffToTime = diffToParts[1] + ' ' + diffToParts[2] | |
println "++" | |
println "++ Diffs for ${backend}" | |
println "++ From ${diffFromTime} (${diffFromId})" | |
println "++ To ${diffToTime} (${diffToId})" | |
println "++" | |
// | |
// Diff from latest:latest to latest | |
// | |
cmd = "${RESTIC_BIN} diff ${diffFromId} ${diffToId}" | |
// println "Executing ${cmd} with ${env}" | |
def diffProc = cmd.execute(env, null) | |
// Stream the output for the diff | |
diffProc.waitForProcessOutput(System.out, System.err) | |
if (diffProc.exitValue() != 0) { | |
System.err.println "!! Command ${cmd} failed with exit code ${diffProc.exitValue()}" | |
return | |
} | |
} | |
/** | |
* Parse the autorestic configuration file. | |
*/ | |
def readAutoResticConfig() { | |
return new YamlSlurper().parse(autoresticConfigFile) | |
} | |
/** | |
* Create the appropriate environment variables for | |
* restic to be able to run against the current | |
* backend. | |
*/ | |
List<String> configureExecEnv(backendConfig) { | |
List<String> env = [] | |
System.getenv().each { k, v -> | |
// Include the current environment variables | |
if (v) { | |
env.add("${k}=${v}") | |
} | |
} | |
switch (backendConfig.type) { | |
case 'local': | |
env.addAll( | |
"RESTIC_REPOSITORY=${backendConfig.path}", | |
"RESTIC_PASSWORD=${backendConfig.key}", | |
) | |
break; | |
case 'b2': | |
env.addAll( | |
"RESTIC_REPOSITORY=b2:${backendConfig.path}", | |
"RESTIC_PASSWORD=${backendConfig.key}", | |
) | |
break; | |
default: | |
System.err.println("Unsupported backend type ${backendConfig.type}") | |
System.exit(-2) | |
} | |
for (String envEntry in backendConfig.env) { | |
// Include environment variables defined on the autorestic backend | |
String[] envParts = envEntry.split('=', 2) | |
// autorestic may rewrite the YML with env names in lower case. fix that. | |
env.add "${envParts[0].toUpperCase()}=${envParts[1]}" | |
} | |
return env | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment