Skip to content

Commit

Permalink
feat: implement determine-restore-eligibility
Browse files Browse the repository at this point in the history
This new cluster action inspects the contents of the backup snapshot of
a certain application and returns its IMAGE_URL and the list of nodes
eligible for restore.
  • Loading branch information
DavidePrincipi committed Dec 13, 2024
1 parent 34d4438 commit 3f9e730
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python3

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import agent
import sys
import os
import json
import tempfile
import cluster.modules

request = json.load(sys.stdin)
repository = request['repository']
path = request['path']
snapshot = request.get("snapshot", "latest")
# Write privileges are required for Redis repository metadata caching:
rdb = agent.redis_connect(privileged=True)
# Write the output to a named temporary file, to parse it with the
# existing read_envfile() function
with tempfile.NamedTemporaryFile() as fenv:
agent.run_restic(rdb, repository, path, ["--workdir=/srv"], ["dump", snapshot, "state/environment"], text=True, encoding='utf-8', stdout=fenv, check=True)
fenv.seek(0)
original_environment = agent.read_envfile(fenv.name)
module_source, _ = original_environment['IMAGE_URL'].rsplit(':', 1)
# Reduce the list to one element, matching the original source:
available = list(filter(lambda omod: omod["source"] == module_source, cluster.modules.list_available(rdb, skip_core_modules = False)))
if not available:
agent.set_status('validation-failed')
json.dump([{'field':'none', 'parameter':'none','value': '', 'error':'module_not_available'}], fp=sys.stdout)
sys.exit(2)
cluster.modules.decorate_with_installed(rdb, available)
cluster.modules.decorate_with_install_destinations(rdb, available)
json.dump({
"image_url": original_environment['IMAGE_URL'],
"install_destinations": available[0]["install_destinations"],
}, fp=sys.stdout)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "determine-restore-eligibility input",
"$id": "http://schema.nethserver.org/cluster/determine-restore-eligibility-input.json",
"description": "Input schema of the determine-restore-eligibility action",
"examples": [
{
"repository": "48ce000a-79b7-5fe6-8558-177fd70c27b4",
"path": "dokuwiki/dokuwiki1@f5d24fcd-819c-4b1d-98ad-a1b2ebcee8cf",
"snapshot": "a6b8317eef"
}
],
"type": "object",
"required": [
"repository",
"path",
"snapshot"
],
"properties": {
"repository": {
"title": "Destination ID",
"description": "Backup destination identifier",
"type": "string",
"minLength": 1
},
"path": {
"title": "Backup repository path",
"description": "Path of the Restic backup repository in the destination",
"type": "string",
"minLength": 1
},
"snapshot": {
"title": "Restic snapshot ID",
"type": "string"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "determine-restore-eligibility output",
"$id": "http://schema.nethserver.org/cluster/determine-restore-eligibility-output.json",
"description": "Output schema of the determine-restore-eligibility action",
"examples": [
{
"image_url": "ghcr.io/nethserver/ejabberd:1.0.1",
"install_destinations": [
{
"node_id": 1,
"instances": 0,
"eligible": true,
"reject_reason": null
},
{
"node_id": 2,
"instances": 1,
"eligible": false,
"reject_reason": {
"message": "max_per_node_limit",
"parameter": "1"
}
}
]
}
],
"type": "object",
"required": [
"image_url",
"install_destinations"
],
"properties": {
"image_url": {
"type":"string"
},
"install_destinations": {
"description": "Describe for each node of the cluster if the node is eligible or not to install a new module instance. If not, a reject reason is returned.",
"type": "array",
"items": {
"type": "object",
"required": [
"node_id",
"instances",
"eligible",
"reject_reason"
],
"properties": {
"node_id": {
"type": "integer",
"description": "Node identifier"
},
"instances": {
"type": "integer",
"description": "Number of module instances currently installed on the node"
},
"eligible": {
"type": "boolean",
"description": "True if another instance of the module can be installed on the node"
},
"reject_reason": {
"type": [
"object",
"null"
],
"descripton": "If it is an object, it tells why the node is not eligible to host a module instance",
"properties": {
"message": {
"type": "string"
},
"parameter": {
"type": "string"
}
},
"required": [
"message",
"parameter"
]
}
}
}
}
}
}

0 comments on commit 3f9e730

Please sign in to comment.