Compare commits

..

17 Commits

Author SHA1 Message Date
AaronDewes
5ea1d44188 Fix key
Previously, the human-readable name was compared to the app id
2022-06-04 08:00:23 +00:00
AaronDewes
b149564490 Fix the add-https script
This script allows configuring SSL using the built-in nginx. Previously, it read the port from the .env, which could cause issues sometimes. It also contained workarounds for specific apps. This should fix that too.
2022-06-04 07:57:43 +00:00
Aaron Dewes
86c17c365e
Remove leftovers from karen v1 (#42) 2022-06-01 21:14:49 -04:00
AaronDewes
5b2b5a4541 Merge branch 'stable' into beta 2022-06-01 16:44:19 +00:00
AaronDewes
6e74290691 Merge branch 'stable' into beta 2022-05-24 05:39:59 +00:00
AaronDewes
62d51aa807 0.0.4 rc1 2022-05-22 14:34:09 +00:00
AaronDewes
781299fa1a Update manager 2022-05-22 13:10:26 +00:00
AaronDewes
4eb9819cf9 Remove leftovers from WIP karen v2 2022-05-22 13:08:07 +00:00
AaronDewes
9a6501a80e Merge remote-tracking branch 'origin/karen-v2' into beta 2022-05-22 13:07:12 +00:00
AaronDewes
d45da547d6 Merge branch 'stable' into beta 2022-05-22 13:06:30 +00:00
Aaron Dewes
80ead94dbd
Remove unused import 2022-05-20 10:12:53 +02:00
Aaron Dewes
a5e74fa1d2
Remove log 2022-05-19 19:03:55 +02:00
AaronDewes
f6dae9c646 Add karen v2 2022-05-19 10:41:21 +00:00
AaronDewes
3cec30340b Generate cache files for compose generator 2022-05-16 09:10:52 +00:00
AaronDewes
611d4a166b Add Karen v2 demo 2022-05-16 07:19:50 +00:00
AaronDewes
56946a9812 Put the latest beta into info.json 2022-05-16 06:11:47 +00:00
AaronDewes
6b29a76d81 Switch app store to beta branch 2022-05-16 06:11:07 +00:00
34 changed files with 159 additions and 993 deletions

4
.gitignore vendored
View File

@ -22,7 +22,7 @@ statuses/*
app-data app-data
apps apps
electrs/* electrs/*
fulcrum/* fulcrumx/*
nginx/* nginx/*
redis/* redis/*
docker-compose.override.yml docker-compose.override.yml
@ -43,7 +43,7 @@ db/citadel-seed/*
!db/.gitkeep !db/.gitkeep
!nginx/.gitkeep !nginx/.gitkeep
!redis/.gitkeep !redis/.gitkeep
!fulcrum/.gitkeep !fulcrumx/.gitkeep
!**/*.license !**/*.license
services/installed.json services/installed.json

View File

@ -3,9 +3,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# A collection of fully FLOSS app definitions and FLOSS apps for Citadel. # A collection of fully FLOSS app definitions and FLOSS apps for Citadel.
https://github.com/runcitadel/apps v3-stable https://github.com/runcitadel/apps v3-beta
# Some apps modified version of Umbrel apps, and their app definitions aren't FLOSS yet. # Some apps modified version of Umbrel apps, and their app definitions aren't FLOSS yet.
# Include them anyway, but as a separate repo. # Include them anyway, but as a separate repo.
# Add a # to the line below to disable the repo and only use FLOSS apps. # Add a # to the line below to disable the repo and only use FLOSS apps.
https://github.com/runcitadel/apps-nonfree v3-stable https://github.com/runcitadel/apps-nonfree v3-beta

View File

@ -47,7 +47,7 @@ def getContainerHiddenService(
if isinstance(container.hiddenServicePorts, int): if isinstance(container.hiddenServicePorts, int):
return getHiddenServiceString( return getHiddenServiceString(
"{} {}".format(metadata.name, container.name), "{} {}".format(metadata.name, container.name),
metadata.id if isMainContainer else "{}-{}".format(metadata.id, container.name), metadata.id,
container.hiddenServicePorts, container.hiddenServicePorts,
containerIp, containerIp,
container.hiddenServicePorts, container.hiddenServicePorts,
@ -55,7 +55,7 @@ def getContainerHiddenService(
elif isinstance(container.hiddenServicePorts, list): elif isinstance(container.hiddenServicePorts, list):
return getHiddenServiceMultiPort( return getHiddenServiceMultiPort(
"{} {}".format(metadata.name, container.name), "{} {}".format(metadata.name, container.name),
metadata.id if isMainContainer else "{}-{}".format(metadata.id, container.name), metadata.id,
containerIp, containerIp,
container.hiddenServicePorts, container.hiddenServicePorts,
) )
@ -77,14 +77,14 @@ def getContainerHiddenService(
else: else:
additionalHiddenServices[key] = value additionalHiddenServices[key] = value
for key, value in additionalHiddenServices.items(): for key, value in additionalHiddenServices.items():
otherHiddenServices += "\n"
if isinstance(value, int): if isinstance(value, int):
otherHiddenServices += "# {} {} {} Hidden Service\nHiddenServiceDir /var/lib/tor/app-{}-{}\n".format( otherHiddenServices += "# {} {} {} Hidden Service\nHiddenServiceDir /var/lib/tor/app-{}-{}\n".format(
metadata.name, container.name, key, metadata.id, key metadata.name, container.name, key, metadata.id, container.name
) )
otherHiddenServices += "HiddenServicePort {} {}:{}".format( otherHiddenServices += "HiddenServicePort {} {}:{}".format(
value, containerIp, value value, containerIp, value
) )
otherHiddenServices += "\n"
elif isinstance(value, list): elif isinstance(value, list):
otherHiddenServices += getHiddenServiceMultiPort( otherHiddenServices += getHiddenServiceMultiPort(
"{} {}".format(metadata.name, key), "{}-{}".format(metadata.id, key), containerIp, value "{} {}".format(metadata.name, key), "{}-{}".format(metadata.id, key), containerIp, value

View File

@ -85,12 +85,11 @@ def createComposeConfigFromV3(app: dict, nodeRoot: str):
newApp = configureIps(newApp, networkingFile, envFile) newApp = configureIps(newApp, networkingFile, envFile)
# This is validated earlier # This is validated earlier
for container in newApp.containers: for container in newApp.containers:
for tcpPort in container.requiredPorts: container.ports = container.requiredPorts
container.ports.append("{}:{}".format(tcpPort, tcpPort))
del container.requiredPorts del container.requiredPorts
for container in newApp.containers: for container in newApp.containers:
for udpPort in container.requiredUdpPorts: for udpPort in container.requiredUdpPorts:
container.ports.append("{}:{}/udp".format(udpPort, udpPort)) container.ports.append("{}/udp".format(udpPort))
del container.requiredUdpPorts del container.requiredUdpPorts
newApp = configureMainPort(newApp, nodeRoot) newApp = configureMainPort(newApp, nodeRoot)
newApp = configureHiddenServices(newApp, nodeRoot) newApp = configureHiddenServices(newApp, nodeRoot)

View File

@ -77,8 +77,6 @@ def configureMainPort(app: AppStage2, nodeRoot: str) -> AppStage3:
# If it doesn't contain a :, it's the port itself # If it doesn't contain a :, it's the port itself
if mainPort == False: if mainPort == False:
mainPort = mainContainer.ports[0] mainPort = mainContainer.ports[0]
if mainPort.find(":") != -1:
mainPort = mainPort.split(":")[1]
else: else:
mainContainer.ports = [portToAppend] mainContainer.ports = [portToAppend]

View File

@ -118,6 +118,26 @@ def getUserData():
userData = json.load(f) userData = json.load(f)
return userData return userData
def checkUpdateAvailable(name: str) -> bool:
latestAppYml = yaml.safe_load(getAppYml(name))
with open(os.path.join(appsDir, name, "app.yml"), "r") as f:
originalAppYml = yaml.safe_load(f)
if not "metadata" in latestAppYml or not "version" in latestAppYml["metadata"] or not "metadata" in originalAppYml or not "version" in originalAppYml["metadata"]:
print("App {} is not valid".format(name))
return False
return semver.compare(latestAppYml["metadata"]["version"], originalAppYml["metadata"]["version"]) > 0
def getAvailableUpdates():
availableUpdates = []
apps = findAndValidateApps(appsDir)
for app in apps:
try:
if checkUpdateAvailable(app):
availableUpdates.append(app)
except Exception:
print("Warning: Can't check app {} yet".format(app), file=sys.stderr)
return availableUpdates
def startInstalled(): def startInstalled():
# If userfile doesn't exist, just do nothing # If userfile doesn't exist, just do nothing
userData = {} userData = {}
@ -344,59 +364,3 @@ def updateRepos():
shutil.rmtree(tempDir) shutil.rmtree(tempDir)
with open(os.path.join(appsDir, "sourceMap.json"), "w") as f: with open(os.path.join(appsDir, "sourceMap.json"), "w") as f:
json.dump(sourceMap, f) json.dump(sourceMap, f)
def getAvailableUpdates():
availableUpdates = {}
repos = []
ignoreApps = []
with open(sourcesList) as f:
repos = f.readlines()
try:
with open(updateIgnore) as f:
ignoreApps = f.readlines()
except: pass
# For each repo, clone the repo to a temporary dir, checkout the branch,
# and overwrite the current app dir with the contents of the temporary dir/apps/app
# Set this to ignoreApps. Normally, it keeps track of apps already installed from repos higher in the list,
# but apps specified in updateignore have the highest priority
alreadyDefined = [s.strip() for s in ignoreApps]
for repo in repos:
repo = repo.strip()
if repo == "":
continue
# Also ignore comments
if repo.startswith("#"):
continue
# Split the repo into the git url and the branch
repo = repo.split(" ")
if len(repo) != 2:
print("Error: Invalid repo format in " + sourcesList, file=sys.stderr)
exit(1)
gitUrl = repo[0]
branch = repo[1]
# Clone the repo to a temporary dir
tempDir = tempfile.mkdtemp()
# Git clone with a depth of 1 to avoid cloning the entire repo
# Don't print anything to stdout, as we don't want to see the git clone output
subprocess.run("git clone --depth 1 --branch {} {} {}".format(branch, gitUrl, tempDir), shell=True, stdout=subprocess.DEVNULL)
# Overwrite the current app dir with the contents of the temporary dir/apps/app
for app in os.listdir(os.path.join(tempDir, "apps")):
try:
# if the app is already installed (or a simple file instead of a valid app), skip it
if app in alreadyDefined or not os.path.isdir(os.path.join(tempDir, "apps", app)):
continue
with open(os.path.join(appsDir, app, "app.yml"), "r") as f:
originalAppYml = yaml.safe_load(f)
with open(os.path.join(tempDir, "apps", app, "app.yml"), "r") as f:
latestAppYml = yaml.safe_load(f)
if semver.compare(latestAppYml["metadata"]["version"], originalAppYml["metadata"]["version"]) > 0:
availableUpdates[app] = {
"updateFrom": originalAppYml["metadata"]["version"],
"updateTo": latestAppYml["metadata"]["version"]
}
except Exception:
print("Warning: Can't check app {} (yet)".format(app), file=sys.stderr)
# Remove the temporary dir
shutil.rmtree(tempDir)
return availableUpdates

View File

@ -7,18 +7,18 @@ import yaml
from jsonschema import validate from jsonschema import validate
import yaml import yaml
scriptDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
with open(os.path.join(scriptDir, 'app-standard-v1.yml'), 'r') as f:
schemaVersion1 = yaml.safe_load(f)
with open(os.path.join(scriptDir, 'app-standard-v2.yml'), 'r') as f:
schemaVersion2 = yaml.safe_load(f)
with open(os.path.join(scriptDir, 'app-standard-v3.yml'), 'r') as f:
schemaVersion3 = yaml.safe_load(f)
# Validates app data # Validates app data
# Returns true if valid, false otherwise # Returns true if valid, false otherwise
scriptDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
def validateApp(app: dict): def validateApp(app: dict):
with open(os.path.join(scriptDir, 'app-standard-v1.yml'), 'r') as f:
schemaVersion1 = yaml.safe_load(f)
with open(os.path.join(scriptDir, 'app-standard-v2.yml'), 'r') as f:
schemaVersion2 = yaml.safe_load(f)
with open(os.path.join(scriptDir, 'app-standard-v3.yml'), 'r') as f:
schemaVersion3 = yaml.safe_load(f)
if 'version' in app and str(app['version']) == "1": if 'version' in app and str(app['version']) == "1":
try: try:
validate(app, schemaVersion1) validate(app, schemaVersion1)

View File

@ -1 +0,0 @@
../cli/citadel

View File

@ -13,7 +13,7 @@ CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)"
result=$(docker compose \ result=$(docker compose \
--file "${CITADEL_ROOT}/docker-compose.yml" \ --file "${CITADEL_ROOT}/docker-compose.yml" \
--env-file "${CITADEL_ROOT}/.env" \ --env-file "${CITADEL_ROOT}/.env" \
exec lightning lncli "$@") exec lnd lncli "$@")
# We need to echo with quotes to preserve output formatting # We need to echo with quotes to preserve output formatting
echo "$result" echo "$result"

View File

@ -1,487 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
set -euo pipefail
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)"
CLI_NAME="$(basename $0)"
CLI_VERSION="0.0.1"
CLI_DIR="$(dirname "$(readlink -f "$0")")"
SERVICE_NAME="citadel-startup"
EDITOR="${EDITOR:-micro}"
source $CLI_DIR/utils/functions.sh
source $CLI_DIR/utils/multiselect.sh
source $CLI_DIR/utils/spinner.sh
source $CLI_DIR/utils/helpers.sh
if [ -z ${1+x} ]; then
command=""
else
command="$1"
fi
# Check Citadel Status
if [[ "$command" = "status" ]]; then
POSITIONAL_ARGS=()
long=false
while [[ $# -gt 0 ]]; do
case $1 in
-l | --long)
long=true
shift
;;
-* | --*)
echo "Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}"
free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'
df -h | awk '$NF=="/"{printf "Disk Usage: %d/%dGB (%s)\n", $3,$2,$5}'
top -bn1 | grep load | awk '{printf "CPU Load: %.2f\n", $(NF-2)}'
echo
if $long; then
docker container ls --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
else
docker container ls --format "table {{.Names}}\t{{.Status}}"
fi
if [[ $(pgrep -f karen) ]]; then
printf "\nKaren is listening.\n"
else
printf "\nERROR: Karen is not listening.\n"
fi
exit
fi
# Update Citadel
if [[ "$command" = "update" ]]; then
POSITIONAL_ARGS=()
branch=$(get_update_channel)
force=false
while [[ $# -gt 0 ]]; do
case $1 in
-b | --branch)
branch="$2"
shift # past argument
shift # past value
;;
--force)
force=true
shift # past argument
;;
-* | --*)
echo "Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
if $force; then
sudo rm -f $CITADEL_ROOT/statuses/update-in-progress
fi
sudo $CITADEL_ROOT/scripts/update/update --repo runcitadel/core#$branch
exit
fi
# Start Citadel
if [[ "$command" = "start" ]]; then
if $(is_managed_by_systemd); then
if $(is_service_active); then
echo 'Citadel is already running.'
else
sudo systemctl start citadel-startup
fi
else
sudo $CITADEL_ROOT/scripts/start
fi
exit
fi
# Stop Citadel
if [[ "$command" = "stop" ]]; then
active=$(is_service_active)
if $(is_managed_by_systemd) && $active; then
if $(is_service_active); then
sudo systemctl stop citadel-startup
else
echo 'Citadel is not running.'
fi
else
sudo $CITADEL_ROOT/scripts/stop
fi
exit
fi
# Restart Citadel
if [[ "$command" = "restart" ]]; then
shift
# TODO: enable restarting services
if [ ! -z ${1+x} ]; then
echo "Too many arguments."
echo "Usage: \`$CLI_NAME $command\`"
exit 1
fi
if $(is_managed_by_systemd); then
sudo systemctl restart $SERVICE_NAME
else
sudo $CITADEL_ROOT/scripts/stop
sudo $CITADEL_ROOT/scripts/start
fi
exit
fi
# Reboot the system
if [[ "$command" = "reboot" ]]; then
$CLI_NAME stop || true
sudo reboot
exit
fi
# Shutdown the system
if [[ "$command" = "shutdown" ]]; then
$CLI_NAME stop || true
sudo shutdown now
exit
fi
# List all installed services apps
if [[ "$command" = "list" ]]; then
echo 'karen'
docker ps --format "{{.Names}}"
exit
fi
# Run a command inside a container
if [[ "$command" = "run" ]]; then
shift
if [ -z ${1+x} ]; then
echo "Specify an app or service."
echo "Usage: \`$CLI_NAME $command <service> \"<command>\"\`"
exit 1
fi
if [ -z ${2+x} ]; then
echo "Specify a command to run."
echo "Usage: \`$CLI_NAME $command <service> \"<command>\"\`"
exit 1
fi
docker exec -t $1 sh -c "$2" || {
echo "To see all installed services & apps use \`$CLI_NAME list\`"
echo "Usage: \`$CLI_NAME $command <service> \"<command>\"\`"
exit 1
}
exit
fi
# Configure Citadel
if [[ "$command" = "set" ]]; then
shift
if [ -z ${1+x} ]; then
echo "Missing subcommand."
echo "Usage: \`$CLI_NAME $command <subcommand>\`"
exit 1
else
subcommand="$1"
fi
# Switch update channel
if [[ "$subcommand" = "update-channel" ]]; then
if [ -z ${2+x} ]; then
echo "Specify an update channel to switch to."
echo "Usage: \`$CLI_NAME $subcommand <stable|beta|c-lightning>\`"
exit 1
fi
case $2 in "stable" | "beta" | "c-lightning")
# continue
;;
*)
echo "Not a valid update channel: \"$2\""
exit 1
;;
esac
sudo $CITADEL_ROOT/scripts/set-update-channel $2
$CLI_NAME update
exit
fi
# Switch Bitcoin/Electrum implementation
if [[ "$subcommand" = "implementation" ]] || [[ "$subcommand" = "impl" ]]; then
shift
sudo $CITADEL_ROOT/services/manage.py set $@
$CLI_NAME restart
exit
fi
# Switch Bitcoin network
if [[ "$subcommand" = "network" ]]; then
shift
if [ -z ${1+x} ]; then
echo "Specify a network to switch to."
echo "Usage: \`$CLI_NAME $subcommand <mainnet|signet|testnet|regtest>\`"
else
case $1 in
"mainnet" | "testnet" | "signet" | "regtest")
sudo $CITADEL_ROOT/scripts/stop
sudo OVERWRITE_NETWORK=$1 $CITADEL_ROOT/scripts/configure
sudo $CITADEL_ROOT/scripts/start
;;
*)
echo "Not a valid value for network"
exit 1
;;
esac
fi
exit
fi
echo "\"$subcommand\" is not a valid subcommand."
exit 1
fi
# App commands
if [[ "$command" = "app" ]]; then
shift
sudo $CITADEL_ROOT/scripts/app $@
exit
fi
# Edit common app configuration files
if [[ "$command" = "configure" ]]; then
if [ -z ${2+x} ]; then
echo "Specify an app or service to configure."
echo "Usage: \`$CLI_NAME $command <service>\`"
exit 1
fi
POSITIONAL_ARGS=()
persist=false
while [[ $# -gt 0 ]]; do
case $1 in
--persist)
persist=true
shift # past argument
;;
-* | --*)
echo "Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
# These service and app configs are already persisted
# TODO: add more apps
if [[ "$2" = "nextcloud" ]]; then
edit_file --priviledged $CITADEL_ROOT/app-data/nextcloud/data/nextcloud/config/config.php
prompt_apply_config nextcloud-web-1 false
exit
fi
if [[ "$2" = "nginx" ]]; then
edit_file $CITADEL_ROOT/nginx/nginx.conf
prompt_apply_config nginx false
exit
fi
if $persist; then
echo "NOTE: As of now persisted config changes will not be kept when updating Citadel."
else
echo "NOTE: Some changes to this configuration file may be overwritten the next time you start Citadel."
echo "To persist the changes run the command again with \`$CLI_NAME configure $2 --persist\`"
fi
read -p "Continue? [Y/n] " should_continue
echo
if [[ $should_continue =~ [Nn]$ ]]; then
exit
fi
# Service and app configs below may be overwritten
# TODO: check which implementation is running
# and do "bitcoin" / "lightning" / "electrum"
if [[ "$2" = "bitcoin" ]]; then
if $persist; then
edit_file $CITADEL_ROOT/templates/bitcoin-sample.conf
prompt_apply_config bitcoin true
else
edit_file $CITADEL_ROOT/bitcoin/bitcoin.conf
prompt_apply_config bitcoin false
fi
exit
fi
if [[ "$2" = "lnd" ]]; then
if $persist; then
edit_file $CITADEL_ROOT/templates/lnd-sample.conf
prompt_apply_config lightning true
else
edit_file $CITADEL_ROOT/lnd/lnd.conf
prompt_apply_config lightning false
fi
exit
fi
if [[ "$2" = "electrs" ]]; then
edit_file $CITADEL_ROOT/templates/electrs-sample.toml
prompt_apply_config electrum true
exit
fi
if [[ "$2" = "fulcrum" ]]; then
if $persist; then
edit_file $CITADEL_ROOT/templates/fulcrum-sample.conf
prompt_apply_config electrum true
else
edit_file $CITADEL_ROOT/fulcrum/fulcrum.conf
prompt_apply_config electrum false
fi
exit
fi
echo "No service or app \"$2\" not found."
exit 1
fi
# Show logs for apps & services
if [[ "$command" = "logs" ]]; then
shift
POSITIONAL_ARGS=()
follow=false
while [[ $# -gt 0 ]]; do
case $1 in
-f | --follow)
follow=true
shift # past argument
;;
-n | --tail)
number_of_lines="$2"
shift # past argument
shift # past value
;;
-* | --*)
echo "Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
# Set default number_of_lines if not set by user
if [ -z ${number_of_lines+x} ]; then
if [[ ${#POSITIONAL_ARGS[@]} == 0 ]] || [[ ${#POSITIONAL_ARGS[@]} == 1 ]]; then
number_of_lines=40
else
number_of_lines=10
fi
fi
if [ -z ${1+x} ] || [[ "$1" = "karen" ]]; then
if [[ ${#POSITIONAL_ARGS[@]} == 2 ]]; then
echo "Karen logs cannot be viewed together with other services."
echo "Usage: \`$CLI_NAME $command karen\`"
exit 1
fi
tail $($follow && echo "-f") -n $number_of_lines $CITADEL_ROOT/logs/karen.log
exit
fi
if [[ ${#POSITIONAL_ARGS[@]} == 1 ]]; then
docker logs $($follow && echo "-f") --tail $number_of_lines $@ || {
echo "To see all installed services & apps use \`$CLI_NAME list\`"
echo "Usage: \`$CLI_NAME $command <service>\`"
exit 1
}
else
# TODO: can only show logs for services in docker-compose.yml
docker compose logs $($follow && echo "-f") --tail $number_of_lines $@ || {
echo "To see all installed services & apps use \`$CLI_NAME list\`"
echo "Usage: \`$CLI_NAME $command <service>\`"
exit 1
}
fi
exit
fi
# Debug Citadel
if [[ "$command" = "debug" ]]; then
shift
sudo $CITADEL_ROOT/scripts/debug $@
exit
fi
# Show version information for this CLI
if [[ "$command" = "--version" ]] || [[ "$command" = "-v" ]]; then
citadel_version=$(jq -r '.version' $CITADEL_ROOT/info.json)
echo "Citadel v$citadel_version"
echo "citadel-cli v$CLI_VERSION"
exit
fi
# Show usage information for this CLI
if [[ "$command" = "--help" ]] || [[ "$command" = "-h" ]]; then
show_help
exit
fi
# If we get here it means no valid command was supplied
# Show help and exit
show_help
exit

View File

@ -1,111 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
show_help() {
cat <<EOF
${CLI_NAME}-cli v${CLI_VERSION}
Manage your Citadel.
Usage: ${CLI_NAME} <command> [options]
Flags:
-h, --help Show this help message
-v, --version Show version information for this CLI
Commands:
status Check the status of all services
start Start the Citadel service
stop Stop the Citadel service safely
restart Restart the Citadel service
reboot Reboot the system
shutdown Shutdown the system
update Update Citadel
list List all installed services apps
run <service> "<command>" Run a command inside a container
set <command> Switch between Bitcoin & Lightning implementations
app <command> Install, update or restart apps
configure <service> Edit service & app configuration files
logs <service> Show logs for an app or service
debug View logs for troubleshooting
EOF
}
is_managed_by_systemd() {
if systemctl --all --type service | grep -q "$SERVICE_NAME"; then
echo true
else
echo false
fi
}
is_service_active() {
service_status=$(systemctl is-active $SERVICE_NAME)
if [[ "$service_status" = "active" ]]; then
echo true
else
echo false
fi
}
edit_file() {
if [[ $1 = "--priviledged" ]]; then
echo "Editing this file requires elevated priviledges."
if ! sudo test -f $2; then
echo "File not found."
exit 1
fi
if sudo test -w $2; then
sudo $EDITOR $2
else
echo "File not writable."
fi
else
if ! test -f $1; then
echo "File not found."
exit 1
fi
if test -w $1; then
$EDITOR $1
else
echo "File not writable."
fi
fi
}
get_update_channel() {
update_channel_line=$(cat $CITADEL_ROOT/.env | grep UPDATE_CHANNEL)
update_channel=(${update_channel_line//=/ })
if [ -z ${update_channel[1]+x} ]; then
# fall back to stable
echo "stable"
else
echo ${update_channel[1]}
fi
}
prompt_apply_config() {
service=$1
persisted=$2
read -p "Do you want to apply the changes now? [y/N] " should_restart
echo
if [[ $should_restart =~ [Yy]$ ]]; then
if $persisted; then
sudo $CITADEL_ROOT/scripts/configure
fi
printf "\nRestarting service \"$service\"...\n"
docker restart $service
echo "Done."
else
if $persisted; then
echo "To apply the changes, restart Citadel by running \`$CLI_NAME restart\`."
else
echo "To apply the changes, restart service \"$service\" by running \`docker restart $service\`."
fi
fi
}

View File

@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
trim() {
local var="$*"
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"
printf '%s' "$var"
}

View File

@ -1,123 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
function multiselect() {
# little helpers for terminal print control and key input
ESC=$(printf "\033")
cursor_blink_on() { printf "$ESC[?25h"; }
cursor_blink_off() { printf "$ESC[?25l"; }
cursor_to() { printf "$ESC[$1;${2:-1}H"; }
print_inactive() { printf "$2 $1 "; }
print_active() { printf "$2 $ESC[7m $1 $ESC[27m"; }
get_cursor_row() {
IFS=';' read -sdR -p $'\E[6n' ROW COL
echo ${ROW#*[}
}
local return_value=$1
local -n options=$2
local -n defaults=$3
local selected=()
for ((i = 0; i < ${#options[@]}; i++)); do
if [[ ${defaults[i]} = "true" ]]; then
selected+=("true")
else
selected+=("false")
fi
printf "\n"
done
# determine current screen position for overwriting the options
local lastrow=$(get_cursor_row)
local startrow=$(($lastrow - ${#options[@]}))
# ensure cursor and input echoing back on upon a ctrl+c during read -s
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
cursor_blink_off
key_input() {
local key
IFS= read -rsn1 key 2>/dev/null >&2
if [[ $key = "" ]]; then echo enter; fi
if [[ $key = $'\x20' ]]; then echo space; fi
if [[ $key = "k" ]]; then echo up; fi
if [[ $key = "j" ]]; then echo down; fi
if [[ $key = $'\x1b' ]]; then
read -rsn2 key
if [[ $key = [A || $key = k ]]; then echo up; fi
if [[ $key = [B || $key = j ]]; then echo down; fi
fi
}
toggle_option() {
local option=$1
if [[ ${selected[option]} == true ]]; then
selected[option]=false
else
selected[option]=true
fi
}
print_options() {
# print options by overwriting the last lines
local idx=0
for option in "${options[@]}"; do
local prefix="[ ]"
if [[ ${selected[idx]} == true ]]; then
prefix="[\e[38;5;46m✔\e[0m]"
fi
cursor_to $(($startrow + $idx))
if [ $idx -eq $1 ]; then
print_active "$option" "$prefix"
else
print_inactive "$option" "$prefix"
fi
idx=$((idx + 1))
done
}
get_result() {
local result=()
for ((i = 0; i < ${#options[@]}; i++)); do
if ${selected[i]}; then
result+=(${options[i]})
fi
done
echo "${result[@]}"
}
local active=0
while true; do
print_options $active
# user key control
case $(key_input) in
space) toggle_option $active ;;
enter)
print_options -1
break
;;
up)
active=$((active - 1))
if [ $active -lt 0 ]; then active=$((${#options[@]} - 1)); fi
;;
down)
active=$((active + 1))
if [ $active -ge ${#options[@]} ]; then active=0; fi
;;
esac
done
# cursor position back to normal
cursor_to $lastrow
printf "\n"
cursor_blink_on
eval $return_value='("$(get_result)")'
}

View File

@ -1,87 +0,0 @@
#!/bin/bash
# Author: Tasos Latsas
# spinner.sh
#
# Display an awesome 'spinner' while running your long shell commands
#
# Do *NOT* call _spinner function directly.
# Use {start,stop}_spinner wrapper functions
# usage:
# 1. source this script in your's
# 2. start the spinner:
# start_spinner [display-message-here]
# 3. run your command
# 4. stop the spinner:
# stop_spinner [your command's exit status]
#
# Also see: test.sh
function _spinner() {
# $1 start/stop
#
# on start: $2 display message
# on stop : $2 process exit status
# $3 spinner function pid (supplied from stop_spinner)
local on_success="DONE"
local on_fail="FAIL"
local white="\e[1;37m"
local green="\e[1;32m"
local red="\e[1;31m"
local nc="\e[0m"
case $1 in
start)
# display message with some space
echo -ne "${2} "
# start spinner
i=1
sp='\|/-'
delay=${SPINNER_DELAY:-0.15}
while :; do
printf "\b${sp:i++%${#sp}:1}"
sleep $delay
done
;;
stop)
if [[ -z ${3} ]]; then
echo "spinner is not running.."
exit 1
fi
kill $3 >/dev/null 2>&1
# inform the user upon success or failure
echo -en "\b["
if [[ $2 -eq 0 ]]; then
echo -en "${green}${on_success}${nc}"
else
echo -en "${red}${on_fail}${nc}"
fi
echo -e "]"
;;
*)
echo "invalid argument, try {start/stop}"
exit 1
;;
esac
}
function start_spinner() {
# $1 : msg to display
_spinner "start" "${1}" &
# set global spinner pid
_sp_pid=$!
disown
}
function stop_spinner() {
# $1 : command exit status
_spinner "stop" $1 $_sp_pid
unset _sp_pid
}

View File

@ -2,7 +2,7 @@ version: '3.8'
services: services:
tor: tor:
container_name: tor container_name: tor
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
user: toruser user: toruser
restart: on-failure restart: on-failure
volumes: volumes:
@ -15,7 +15,7 @@ services:
ipv4_address: $TOR_PROXY_IP ipv4_address: $TOR_PROXY_IP
app-tor: app-tor:
container_name: app-tor container_name: app-tor
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
user: toruser user: toruser
restart: on-failure restart: on-failure
volumes: volumes:
@ -26,7 +26,7 @@ services:
ipv4_address: $APPS_TOR_IP ipv4_address: $APPS_TOR_IP
app-2-tor: app-2-tor:
container_name: app-2-tor container_name: app-2-tor
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
user: toruser user: toruser
restart: on-failure restart: on-failure
volumes: volumes:
@ -37,7 +37,7 @@ services:
ipv4_address: $APPS_2_TOR_IP ipv4_address: $APPS_2_TOR_IP
app-3-tor: app-3-tor:
container_name: app-3-tor container_name: app-3-tor
image: lncm/tor:0.4.7.8@sha256:aab30ebb496aa25934d6096951d8b200347c3c3ce5db3493695229efa2601f7b image: lncm/tor:0.4.7.7@sha256:3c4ae833d2fefbea7d960f833a1e89fc9b2069a6e5f360109b5ddc9334ac0227
user: toruser user: toruser
restart: on-failure restart: on-failure
volumes: volumes:
@ -65,7 +65,7 @@ services:
ipv4_address: $NGINX_IP ipv4_address: $NGINX_IP
bitcoin: bitcoin:
container_name: bitcoin container_name: bitcoin
image: ghcr.io/runcitadel/bitcoinknots:main@sha256:5fbee0f6f0d09d42aacc11c373ffe6162210c42ce21e6eba294e547e3ad80219 image: nolim1t/bitcoinknots:v22.0.knots20211108@sha256:a475da2b2ecda55fcc65ea23e1a36c58b2c10549f1c3d3bb3c31c7dda1127354
depends_on: depends_on:
- tor - tor
volumes: volumes:
@ -79,7 +79,7 @@ services:
ipv4_address: $BITCOIN_IP ipv4_address: $BITCOIN_IP
lightning: lightning:
container_name: lightning container_name: lightning
image: lightninglabs/lnd:v0.15.0-beta@sha256:d227a9db0727ff56020c8d6604c8c369757123d238ab6ce679579c2dd0d0d259 image: lightninglabs/lnd:v0.14.3-beta@sha256:6a2234b0aad4caed3d993736816b198d6228f32c59b27ba2218d5ebf516ae905
user: 1000:1000 user: 1000:1000
depends_on: depends_on:
- tor - tor
@ -100,7 +100,7 @@ services:
ipv4_address: $LND_IP ipv4_address: $LND_IP
dashboard: dashboard:
container_name: dashboard container_name: dashboard
image: ghcr.io/runcitadel/dashboard:v0.0.15@sha256:a2cf5ad79367fb083db0f61e5a296aafee655c99af0c228680644c248ec674a5 image: ghcr.io/runcitadel/dashboard:v0.0.12@sha256:102a44b938765be8224c63785949b90bf796670f01aef4e282d58a4c26a42595
restart: on-failure restart: on-failure
stop_grace_period: 1m30s stop_grace_period: 1m30s
networks: networks:
@ -108,7 +108,7 @@ services:
ipv4_address: $DASHBOARD_IP ipv4_address: $DASHBOARD_IP
manager: manager:
container_name: manager container_name: manager
image: ghcr.io/runcitadel/manager:v0.0.15@sha256:9fb5a86d9e40a04f93d5b6110d43a0f9a5c4ad6311a843b5442290013196a5ce image: ghcr.io/runcitadel/manager:v0.0.13@sha256:3ced2643e12253fea46abb48a69dcd999d69d3d86ed9956ae0298d4f6be4f06d
depends_on: depends_on:
- tor - tor
- redis - redis
@ -162,7 +162,7 @@ services:
ipv4_address: $MANAGER_IP ipv4_address: $MANAGER_IP
middleware: middleware:
container_name: middleware container_name: middleware
image: ghcr.io/runcitadel/middleware:v0.0.11@sha256:e472da8cbfa67d9a9dbf321334fe65cdf20a0f9b6d6bab33fdf07210f54e7002 image: ghcr.io/runcitadel/middleware:v0.0.10@sha256:afd6e2b6f5ba27cde32f6f6d630ddc6dd46d1072871f7834d7424d0554d0f53d
depends_on: depends_on:
- manager - manager
- bitcoin - bitcoin

View File

@ -6,4 +6,4 @@
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
"${CITADEL_ROOT}/scripts/set-update-channel" "${1}" "${CITADEL_ROOT}/scripts/set-update-channel" beta

View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
"${CITADEL_ROOT}/scripts/set-update-channel" stable

View File

@ -1,6 +1,6 @@
{ {
"version": "0.0.7", "version": "0.0.4-rc.1",
"name": "Citadel 0.0.7", "name": "Citadel 0.0.4 Release Candidate 1",
"requires": ">=0.0.1", "requires": ">=0.0.1",
"notes": "While we are busy with the next huge update, you may need to wait longer for updates. This update updates Bitcoin Knots and LND to their latest versions to ensure apps can utilize their latest features. In addition, this update includes the Citadel CLI. More information on that will be published soon." "notes": "This update fixes multiple bugs in the 0.0.4 release."
} }

View File

@ -53,11 +53,17 @@ with open(registry_file) as file:
port = None port = None
for app in registry: for app in registry:
if app['name'] == service: if app['id'] == service:
if "internalPort" in app: if "internalPort" in app:
port = app['internalPort'] port = app['internalPort']
else:
port = app['port']
break break
if port is None:
print("No port found!")
exit(1)
original_config = crossplane.parse(nginx_config_file) original_config = crossplane.parse(nginx_config_file)
parsedConfig = original_config["config"][0]["parsed"] parsedConfig = original_config["config"][0]["parsed"]
@ -70,14 +76,6 @@ ip_alt_env_var = "APP_{}_MAIN_IP".format(
service.upper().replace("-", "_") service.upper().replace("-", "_")
) )
port_env_var = "APP_{}_WEB_PORT".format(
service.upper().replace("-", "_")
)
port_alt_env_var = "APP_{}_MAIN_PORT".format(
service.upper().replace("-", "_")
)
env = parse_dotenv(os.path.join(node_root, '.env')) env = parse_dotenv(os.path.join(node_root, '.env'))
ip=None ip=None
@ -89,21 +87,6 @@ else:
print("Error: No IP found for {}".format(service)) print("Error: No IP found for {}".format(service))
exit(1) exit(1)
if port == None:
if port_env_var in env:
port = env[port_env_var]
elif port_alt_env_var in env:
port = env[port_alt_env_var]
else:
print("Error: No port found for {}".format(service))
exit(1)
if service == "btcpay-server":
port = 1234
if service == "lnme":
port = 1323
actual_url="http://{}:{}".format(ip, port) actual_url="http://{}:{}".format(ip, port)

View File

@ -1,7 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com # SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
# SPDX-FileCopyrightText: 2022 Citadel and contributors. https://runcitadel.space
# #
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
@ -99,19 +98,39 @@ tar \
# --verbose \ # --verbose \
# --gzip # --gzip
# Check if a file exists on Firebase by checking if
# "https://firebasestorage.googleapis.com/v0/b/citadel-user-backups.appspot.com/o/backups%2F<THE_FILE_NAME>?alt=media"
# returns a 200 response code.
#
check_if_exists() {
curl -s -o /dev/null -w "%{http_code}" \
-X GET \
"https://firebasestorage.googleapis.com/v0/b/citadel-user-backups.appspot.com/o/backups%2F${1}?alt=media"
}
# Upload a file to Firebase Cloud Storage by uloading its name + a random ID.
# Before uploading, we need to check if a file with the same name already exists, and if it does, change the random ID.
# For example, if we want to upload a file named "<THE_FILE_NAME>" with the content AA, we'd use this command to upload:
# curl -X POST "https://firebasestorage.googleapis.com/v0/b/citadel-user-backups.appspot.com/o/backups%2F<THE_FILE_NAME>?alt=media" -d "AA" -H "Content-Type: text/plain"
# To check if a file exists, we can check if this endpoint returns an error 404:
# curl "https://firebasestorage.googleapis.com/v0/b/citadel-user-backups.appspot.com/o/backups%2F<THE_FILE_NAME>?alt=media"
# To download a file, we can the same endpoint
upload_file() { upload_file() {
local file_to_send="${1}" local file_to_send="${1}"
local backup_id="${2}" local file_name="${2}"
local upload_data=$(jq --null-input \ # A random ID to avoid collisions
--arg name "$backup_id" \ local random_id="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 60 ; echo '')"
--arg data "$(base64 $file_to_send)" \ # Check if the file already exists
'{"name": $name, "data": $data}') # While a file with the same name exists, we'll try to upload it with a different ID
while [[ $(check_if_exists "${file_name}-${random_id}") == "200" ]]; do
random_id="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 60 ; echo '')"
done
# Upload the file
curl -X POST \ curl -X POST \
"https://account.runcitadel.space/api/upload" \ "https://firebasestorage.googleapis.com/v0/b/citadel-user-backups.appspot.com/o/backups%2F${file_name}-${random_id}?alt=media" \
-d "${upload_data}" \ -d @"${file_to_send}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/octet-stream" \
--socks5 "localhost:${TOR_PROXY_PORT}" \ > /dev/null
> /dev/null
} }
if [[ $BITCOIN_NETWORK == "testnet" ]]; then if [[ $BITCOIN_NETWORK == "testnet" ]]; then

2
scripts/configure vendored
View File

@ -334,7 +334,7 @@ templates_to_build = {
"./templates/bitcoin-sample.conf": "./bitcoin/bitcoin.conf", "./templates/bitcoin-sample.conf": "./bitcoin/bitcoin.conf",
"./templates/.env-sample": "./.env", "./templates/.env-sample": "./.env",
"./templates/electrs-sample.toml": "./electrs/electrs.toml", "./templates/electrs-sample.toml": "./electrs/electrs.toml",
"./templates/fulcrum-sample.conf": "./fulcrum/fulcrum.conf", "./templates/fulcrumx-sample.conf": "./fulcrumx/fulcrumx.conf",
"./templates/nginx-sample.conf": "./nginx/nginx.conf" "./templates/nginx-sample.conf": "./nginx/nginx.conf"
} }

View File

@ -21,17 +21,7 @@ function get_memory_usage() {
function mem_usage_to_percent() { function mem_usage_to_percent() {
local mem_usage="$1" local mem_usage="$1"
local total_mem="$(free -m | awk 'NR==2 {print $2}')" local total_mem="$(free -m | awk 'NR==2 {print $2}')"
echo "$(awk "BEGIN {printf \"%.1f\", ${mem_usage/,/.} / ${total_mem/,/.} * 100}")" echo "$(awk "BEGIN {printf \"%.1f\", $mem_usage / $total_mem * 100}")"
}
function app_mem_usage() {
# For every container of the app, get the mem usage, save it, and at the end, print the total mem usage of the app
local mem_usage=0
for container in $(get_app_containers "$1"); do
# Use awk to add, it supports floating point numbers
mem_usage=$(awk "BEGIN {printf \"%.2f\", $mem_usage + $(get_memory_usage "$container")}")
done
echo "${1}: $mem_usage%"
} }
get_total_used_mem_raw() { get_total_used_mem_raw() {
@ -45,7 +35,7 @@ get_total_used_mem() {
# To get the containers of the app, list every container whose name starts with the name of the app # To get the containers of the app, list every container whose name starts with the name of the app
get_app_containers () { get_app_containers () {
local app_name="$1" local app_name="$1"
"${CITADEL_ROOT}/scripts/app" compose "${app_name}" ps | awk '{print $1}' | grep -v 'NAME' "${CITADEL_ROOT}/scripts/app" compose "${app_name}" ps | awk '{print $1}' | grep -v 'Name\|-----'
} }
# Get the memory usage of the whole system, excluding docker containers # Get the memory usage of the whole system, excluding docker containers
@ -58,17 +48,22 @@ get_system_memory_usage() {
} }
main() { main() {
echo "total: $(get_total_used_mem)%" echo "total: $(get_total_used_mem)%"&
echo "system: $(get_system_memory_usage)%"
for service in bitcoin lightning electrum tor; do for service in bitcoin lightning electrum tor; do
echo "${service}: $(get_memory_usage "$service")%" & echo "${service}: $(get_memory_usage "$service")%" &
done done
for app in $("${CITADEL_ROOT}/scripts/app" ls-installed); do for app in $("${CITADEL_ROOT}/scripts/app" ls-installed); do
app_mem_usage "${app}" & # For every container of the app, get the mem usage, save it, and at the end, print the total mem usage of the app
local mem_usage=0
for container in $(get_app_containers "$app"); do
# Use awk to add, it supports floating point numbers
mem_usage=$(awk "BEGIN {printf \"%.2f\", $mem_usage + $(get_memory_usage "$container")}")
done
wait
echo "${app}: $mem_usage%"
done done
echo "system: $(get_system_memory_usage)%"
wait wait
} }
echo "Calculating memory usage..."
echo "This may take a while, please wait..."
main | sort --key 2 --numeric-sort --reverse main | sort --key 2 --numeric-sort --reverse

View File

@ -90,11 +90,11 @@ echo
echo "Starting status monitors..." echo "Starting status monitors..."
echo echo
pkill -f ./scripts/status-monitor || true pkill -f ./scripts/status-monitor || true
./scripts/status-monitor memory 300 &>> "${CITADEL_LOGS}/status-monitor.log" & ./scripts/status-monitor memory 60 &>> "${CITADEL_LOGS}/status-monitor.log" &
./scripts/status-monitor storage 60 &>> "${CITADEL_LOGS}/status-monitor.log" & ./scripts/status-monitor storage 60 &>> "${CITADEL_LOGS}/status-monitor.log" &
./scripts/status-monitor temperature 15 &>> "${CITADEL_LOGS}/status-monitor.log" & ./scripts/status-monitor temperature 15 &>> "${CITADEL_LOGS}/status-monitor.log" &
./scripts/status-monitor uptime 15 &>> "${CITADEL_LOGS}/status-monitor.log" & ./scripts/status-monitor uptime 15 &>> "${CITADEL_LOGS}/status-monitor.log" &
./scripts/status-monitor app-updates 600 &>> "${CITADEL_LOGS}/status-monitor.log" & ./scripts/status-monitor app-updates 300 &>> "${CITADEL_LOGS}/status-monitor.log" &
echo "Starting backup monitor..." echo "Starting backup monitor..."
echo echo

View File

@ -4,9 +4,6 @@
# #
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# To prevent tools we use from outputting in another language
LANG=C
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
memory_total_bytes() { memory_total_bytes() {
@ -26,7 +23,7 @@ get_app_memory_use() {
local app_memory=0 local app_memory=0
local app_containers=$("${CITADEL_ROOT}/scripts/app" compose "${app}" ps | awk '{print $1}' | grep -v 'NAME') local app_containers=$("${CITADEL_ROOT}/app/app-manager.py" compose "${app}" ps | awk '{print $1}' | grep -v 'Name\|-----')
for container in $app_containers; do for container in $app_containers; do
local container_memory=$(get_container_memory_use "${container}") local container_memory=$(get_container_memory_use "${container}")
app_memory=$(awk "BEGIN {print ${app_memory}+${container_memory}}") app_memory=$(awk "BEGIN {print ${app_memory}+${container_memory}}")
@ -46,7 +43,7 @@ used=$(memory_used_bytes)
json=$(echo $json | jq --arg used "${used}" '. + {used: $used|tonumber}') json=$(echo $json | jq --arg used "${used}" '. + {used: $used|tonumber}')
cumulative_app_memory="0" cumulative_app_memory="0"
for app in $( "${CITADEL_ROOT}/scripts/app" ls-installed); do for app in $( "${CITADEL_ROOT}/app/app-manager.py" ls-installed); do
app_memory=$(get_app_memory_use "${app}") app_memory=$(get_app_memory_use "${app}")
cumulative_app_memory=$(($cumulative_app_memory+$app_memory)) cumulative_app_memory=$(($cumulative_app_memory+$app_memory))
json=$(echo $json | jq --arg app "${app}" --arg app_memory "${app_memory}" '.breakdown |= .+ [{id: $app, used: $app_memory|tonumber}]') json=$(echo $json | jq --arg app "${app}" --arg app_memory "${app_memory}" '.breakdown |= .+ [{id: $app, used: $app_memory|tonumber}]')

View File

@ -12,4 +12,4 @@ nginx/*
services/installed.yml services/installed.yml
apps/sourceMap.json apps/sourceMap.json
apps/.updateignore apps/.updateignore
fulcrum/* fulcrumx/*

View File

@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
bitcoin:
container_name: bitcoin
image: ghcr.io/runcitadel/bitcoin-custom:main@sha256:d0af506f8dc92a434e845305ac4252d0601b699c4b3bc4443073a0a2e237f3a0
depends_on:
- tor
volumes:
- ${PWD}/bitcoin:/data/.bitcoin
restart: on-failure
stop_grace_period: 1m
ports:
- $BITCOIN_P2P_PORT:$BITCOIN_P2P_PORT
networks:
default:
ipv4_address: $BITCOIN_IP

View File

@ -4,7 +4,7 @@
bitcoin: bitcoin:
container_name: bitcoin container_name: bitcoin
image: lncm/bitcoind:v23.0@sha256:57317c90d89156a30327fe1b8e51b836e0fd1a8ba13721eb2e75e6b35a570e26 image: lncm/bitcoind:v22.0@sha256:37a1adb29b3abc9f972f0d981f45e41e5fca2e22816a023faa9fdc0084aa4507
depends_on: depends_on:
- tor - tor
volumes: volumes:

View File

@ -4,7 +4,7 @@
bitcoin: bitcoin:
container_name: bitcoin container_name: bitcoin
image: ghcr.io/runcitadel/bitcoinknots:main@sha256:5fbee0f6f0d09d42aacc11c373ffe6162210c42ce21e6eba294e547e3ad80219 image: nolim1t/bitcoinknots:v22.0.knots20211108@sha256:a475da2b2ecda55fcc65ea23e1a36c58b2c10549f1c3d3bb3c31c7dda1127354
depends_on: depends_on:
- tor - tor
volumes: volumes:

View File

@ -4,12 +4,12 @@
electrum: electrum:
container_name: electrum container_name: electrum
image: cculianu/fulcrum:latest@sha256:c0543f8b8a5bf6b0c447d8525d6b4360a6c07532f7741f19cc2c179968e71848 image: ghcr.io/runcitadel/fulcrumx:latest@sha256:a74abdfe8397f02482faed6bd828477c452df071129f66ad6596d0ab8d29cf39
working_dir: /data working_dir: /data
volumes: volumes:
- ${PWD}/bitcoin:/bitcoin:ro - ${PWD}/bitcoin:/bitcoin:ro
- ${PWD}/fulcrum:/data - ${PWD}/fulcrumx:/data
command: /usr/bin/Fulcrum /data/fulcrum.conf command: /usr/bin/FulcrumX /data/fulcrumx.conf
restart: on-failure restart: on-failure
stop_grace_period: 5m stop_grace_period: 5m
ports: ports:

View File

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
electrum:
container_name: electrum
image: ghcr.io/runcitadel/fulcrumx:latest@sha256:557a54a5652b475b01c59eb6cffee3568f18b74d875c4cc125e5ac190c4b0706
working_dir: /data
volumes:
- ${PWD}/bitcoin:/bitcoin:ro
- ${PWD}/fulcrumx:/data
command: /usr/bin/FulcrumX /data/fulcrumx.conf
restart: on-failure
stop_grace_period: 5m
ports:
- "$ELECTRUM_PORT:$ELECTRUM_PORT"
networks:
default:
ipv4_address: $ELECTRUM_IP

View File

@ -1,6 +1,6 @@
lightning: lightning:
container_name: lightning container_name: lightning
image: lightninglabs/lnd:v0.15.0-beta@sha256:d227a9db0727ff56020c8d6604c8c369757123d238ab6ce679579c2dd0d0d259 image: lightninglabs/lnd:v0.14.3-beta@sha256:6a2234b0aad4caed3d993736816b198d6228f32c59b27ba2218d5ebf516ae905
user: 1000:1000 user: 1000:1000
depends_on: depends_on:
- tor - tor

View File

@ -31,21 +31,6 @@ args = parser.parse_args()
# Function to install a service # Function to install a service
# To install it, read the service's YAML file (nodeRoot/services/name.yml) and add it to the main compose file (nodeRoot/docker-compose.yml) # To install it, read the service's YAML file (nodeRoot/services/name.yml) and add it to the main compose file (nodeRoot/docker-compose.yml)
def setService(name, implementation): def setService(name, implementation):
# Get all available services
services = next(os.walk(os.path.join(nodeRoot, "services")))[1]
if not name in services:
print("\"{}\" is not a valid service.".format(name))
exit(1)
# Get all available implementations
implementations = next(os.walk(os.path.join(nodeRoot, "services", name)), (None, None, []))[2]
implementations = [x.split('.')[0] for x in implementations]
if not implementation in implementations:
print("\"{}\" is not a valid implementation.".format(implementation))
exit(1)
# Read the YAML file # Read the YAML file
with open(os.path.join(nodeRoot, "services", name, implementation + ".yml"), 'r') as stream: with open(os.path.join(nodeRoot, "services", name, implementation + ".yml"), 'r') as stream:
service = yaml.safe_load(stream) service = yaml.safe_load(stream)