Supporting adjust volume size & pre-update backups.
This commit is contained in:
@@ -20,6 +20,27 @@
|
|||||||
},
|
},
|
||||||
"execute-command": "/etc/webhook/queue-restic-snapshot.sh"
|
"execute-command": "/etc/webhook/queue-restic-snapshot.sh"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "queue-restic-snapshot-no-restart",
|
||||||
|
"pass-environment-to-command": [
|
||||||
|
{"source": "payload", "name": "version"},
|
||||||
|
{"source": "payload", "name": "path"},
|
||||||
|
{"source": "payload", "name": "tag"},
|
||||||
|
{"source": "payload", "name": "request_id"}
|
||||||
|
],
|
||||||
|
"trigger-rule":
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"type": "payload-hmac-sha256",
|
||||||
|
"secret": "$INSTANCE_CONTROL_WEBHOOKS_SECRET",
|
||||||
|
"parameter": {
|
||||||
|
"source": "header",
|
||||||
|
"name": "X-Nassella-Signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"execute-command": "/etc/webhook/queue-restic-snapshot-no-restart.sh"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "restic-snapshot-status",
|
"id": "restic-snapshot-status",
|
||||||
"include-command-output-in-response": true,
|
"include-command-output-in-response": true,
|
||||||
|
|||||||
15
all-apps/instance-control/hooks/queue-restic-snapshot-no-restart.sh
Executable file
15
all-apps/instance-control/hooks/queue-restic-snapshot-no-restart.sh
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# TODO the systemd unit should actually do this
|
||||||
|
# touch /maintenance/maintenance.on
|
||||||
|
# rm /maintenance/maintenance.on
|
||||||
|
|
||||||
|
# for instance-control docker compose setup:
|
||||||
|
# make a directory in /tmp for these pipes and mount that as a volume
|
||||||
|
# into the container
|
||||||
|
|
||||||
|
# TODO read 'version' arg from request and make sure it
|
||||||
|
# matches the version of this script
|
||||||
|
|
||||||
|
# use a named pipe
|
||||||
|
printf "%s\t%s\t%s\t%s\n" "$HOOK_tag" "$HOOK_request_id" "$HOOK_path" "false" > /tmp/restic/snapshot_trigger_pipe
|
||||||
@@ -12,4 +12,4 @@
|
|||||||
# matches the version of this script
|
# matches the version of this script
|
||||||
|
|
||||||
# use a named pipe
|
# use a named pipe
|
||||||
printf "%s\t%s\t%s\n" "$HOOK_tag" "$HOOK_request_id" "$HOOK_path" > /tmp/restic/snapshot_trigger_pipe
|
printf "%s\t%s\t%s\t%s\n" "$HOOK_tag" "$HOOK_request_id" "$HOOK_path" "true" > /tmp/restic/snapshot_trigger_pipe
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ server_type = "s-2vcpu-2gb" # the digital ocean server type to deploy
|
|||||||
|
|
||||||
do_token = "" # token from "API" settings on DigitalOcean
|
do_token = "" # token from "API" settings on DigitalOcean
|
||||||
|
|
||||||
|
digitalocean_volume_size = # size in GB of the app storage volume
|
||||||
|
|
||||||
cloudflare_api_token = "" # corresponding API token should allow modifying DNS settings for the Nassella configured domain
|
cloudflare_api_token = "" # corresponding API token should allow modifying DNS settings for the Nassella configured domain
|
||||||
cloudflare_zone_id = "" # corresponding zone ID for API token for the Nassella configured domain
|
cloudflare_zone_id = "" # corresponding zone ID for API token for the Nassella configured domain
|
||||||
cloudflare_account_id = "" # corresponding account ID for API token
|
cloudflare_account_id = "" # corresponding account ID for API token
|
||||||
|
|||||||
8
main.tf
8
main.tf
@@ -79,6 +79,11 @@ variable "subdomains" {
|
|||||||
description = "Subdomains to setup"
|
description = "Subdomains to setup"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "digitalocean_volume_size" {
|
||||||
|
type = number
|
||||||
|
description = "Size in GB of the app storage digitalocean volume"
|
||||||
|
}
|
||||||
|
|
||||||
provider "digitalocean" {
|
provider "digitalocean" {
|
||||||
token = var.do_token
|
token = var.do_token
|
||||||
}
|
}
|
||||||
@@ -124,7 +129,7 @@ resource "cloudflare_dns_record" "subdomains" {
|
|||||||
resource "digitalocean_volume" "machine" {
|
resource "digitalocean_volume" "machine" {
|
||||||
region = var.datacenter
|
region = var.datacenter
|
||||||
name = "${var.cluster_name}"
|
name = "${var.cluster_name}"
|
||||||
size = 60
|
size = var.digitalocean_volume_size
|
||||||
initial_filesystem_type = "ext4"
|
initial_filesystem_type = "ext4"
|
||||||
initial_filesystem_label = "appstorage"
|
initial_filesystem_label = "appstorage"
|
||||||
description = "persistent storage for docker apps"
|
description = "persistent storage for docker apps"
|
||||||
@@ -137,7 +142,6 @@ resource "digitalocean_droplet" "machine" {
|
|||||||
size = var.server_type
|
size = var.server_type
|
||||||
ssh_keys = [digitalocean_ssh_key.first.fingerprint]
|
ssh_keys = [digitalocean_ssh_key.first.fingerprint]
|
||||||
user_data = file("ignition.json")
|
user_data = file("ignition.json")
|
||||||
graceful_shutdown = true
|
|
||||||
lifecycle {
|
lifecycle {
|
||||||
create_before_destroy = true
|
create_before_destroy = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ for config_string in ${APP_CONFIGS[@]}; do
|
|||||||
fulldomain="$subdomain.$ROOT_DOMAIN"
|
fulldomain="$subdomain.$ROOT_DOMAIN"
|
||||||
|
|
||||||
echo "$fulldomain {"
|
echo "$fulldomain {"
|
||||||
|
if [ "$app" != "instance-control" ] && [ "$app" != "dozzle" ]; then
|
||||||
# config for maintenance mode
|
# config for maintenance mode
|
||||||
echo "@maintenanceModeActive file /maintenance/maintenance.on {"
|
echo "@maintenanceModeActive file /maintenance/maintenance.on {"
|
||||||
echo " root /"
|
echo " root /"
|
||||||
@@ -73,6 +74,7 @@ for config_string in ${APP_CONFIGS[@]}; do
|
|||||||
echo "handle @maintenanceModeActive {"
|
echo "handle @maintenanceModeActive {"
|
||||||
echo " respond \"We are performing a maintenance, come back later\" 503"
|
echo " respond \"We are performing a maintenance, come back later\" 503"
|
||||||
echo "}"
|
echo "}"
|
||||||
|
fi
|
||||||
|
|
||||||
echo $body
|
echo $body
|
||||||
echo "}"
|
echo "}"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ while read -u 3 msg; do
|
|||||||
tag=$1
|
tag=$1
|
||||||
request_id=$2
|
request_id=$2
|
||||||
path=$3 # TODO not currently used
|
path=$3 # TODO not currently used
|
||||||
|
restart=$4 # if we should restart the databases after the snapshot or stay in maintenance mode
|
||||||
|
|
||||||
# update status for webhooks
|
# update status for webhooks
|
||||||
printf "%s\n" "running" > "/tmp/restic/snapshot_status_$request_id"
|
printf "%s\n" "running" > "/tmp/restic/snapshot_status_$request_id"
|
||||||
@@ -60,6 +61,7 @@ while read -u 3 msg; do
|
|||||||
# perform the snapshot
|
# perform the snapshot
|
||||||
docker run --rm --volume /nassella:/nassella --volume /restic-password:/restic-password -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} -i restic/restic:0.18.0 backup --verbose --repo s3:${BACKBLAZE_BUCKET_URL} --password-file /restic-password --tag "$tag" "/nassella"
|
docker run --rm --volume /nassella:/nassella --volume /restic-password:/restic-password -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} -i restic/restic:0.18.0 backup --verbose --repo s3:${BACKBLAZE_BUCKET_URL} --password-file /restic-password --tag "$tag" "/nassella"
|
||||||
|
|
||||||
|
if [ $restart = "true" ]; then
|
||||||
# restart databases
|
# restart databases
|
||||||
if [ $ghost_db_running = true ]; then
|
if [ $ghost_db_running = true ]; then
|
||||||
docker start app-ghost_db-1
|
docker start app-ghost_db-1
|
||||||
@@ -81,6 +83,7 @@ while read -u 3 msg; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
rm /app/maintenance/maintenance.on
|
rm /app/maintenance/maintenance.on
|
||||||
|
fi
|
||||||
|
|
||||||
# update status for webhooks
|
# update status for webhooks
|
||||||
printf "%s\n" "complete" > "/tmp/restic/snapshot_status_$request_id"
|
printf "%s\n" "complete" > "/tmp/restic/snapshot_status_$request_id"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ create table user_service_configs(
|
|||||||
digitalocean_api_token_enc varchar(255),
|
digitalocean_api_token_enc varchar(255),
|
||||||
digitalocean_region varchar(255),
|
digitalocean_region varchar(255),
|
||||||
digitalocean_size varchar(255),
|
digitalocean_size varchar(255),
|
||||||
|
digitalocean_volume_size integer,
|
||||||
backblaze_application_key_enc varchar(255),
|
backblaze_application_key_enc varchar(255),
|
||||||
backblaze_key_id_enc varchar(255),
|
backblaze_key_id_enc varchar(255),
|
||||||
backblaze_bucket_url_enc varchar(255)
|
backblaze_bucket_url_enc varchar(255)
|
||||||
@@ -75,6 +76,7 @@ create table deployments(
|
|||||||
terraform_machine_destroy deployment_status not null default 'queued',
|
terraform_machine_destroy deployment_status not null default 'queued',
|
||||||
terraform_ip_create deployment_status not null default 'queued',
|
terraform_ip_create deployment_status not null default 'queued',
|
||||||
terraform_ip_destroy deployment_status not null default 'queued',
|
terraform_ip_destroy deployment_status not null default 'queued',
|
||||||
|
instance_backup deployment_status not null default 'queued',
|
||||||
log_enc text
|
log_enc text
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
12
src/db.scm
12
src/db.scm
@@ -230,6 +230,7 @@ returning users.user_id;"
|
|||||||
(digitalocean-api-token . ("digitalocean_api_token_enc" #t))
|
(digitalocean-api-token . ("digitalocean_api_token_enc" #t))
|
||||||
(digitalocean-region . ("digitalocean_region" #f))
|
(digitalocean-region . ("digitalocean_region" #f))
|
||||||
(digitalocean-size . ("digitalocean_size" #f))
|
(digitalocean-size . ("digitalocean_size" #f))
|
||||||
|
(digitalocean-volume-size . ("digitalocean_volume_size" #f))
|
||||||
(backblaze-application-key . ("backblaze_application_key_enc" #t))
|
(backblaze-application-key . ("backblaze_application_key_enc" #t))
|
||||||
(backblaze-key-id . ("backblaze_key_id_enc" #t))
|
(backblaze-key-id . ("backblaze_key_id_enc" #t))
|
||||||
(backblaze-bucket-url . ("backblaze_bucket_url_enc" #t))))
|
(backblaze-bucket-url . ("backblaze_bucket_url_enc" #t))))
|
||||||
@@ -416,6 +417,11 @@ returning users.user_id;"
|
|||||||
(custom-image . "terraform_custom_image")
|
(custom-image . "terraform_custom_image")
|
||||||
(machine-create . "terraform_machine_create")
|
(machine-create . "terraform_machine_create")
|
||||||
(machine-destroy . "terraform_machine_destroy")
|
(machine-destroy . "terraform_machine_destroy")
|
||||||
|
(ip-create . "terraform_ip_create")
|
||||||
|
(ip-destroy . "terraform_ip_destroy")
|
||||||
|
(volume-create . "terraform_volume_create")
|
||||||
|
(volume-destroy . "terraform_volume_destroy")
|
||||||
|
(instance-backup . "instance_backup")
|
||||||
(status . "status")
|
(status . "status")
|
||||||
(id . "id")
|
(id . "id")
|
||||||
(instance-id . "instance_id")))
|
(instance-id . "instance_id")))
|
||||||
@@ -621,13 +627,15 @@ returning users.user_id;"
|
|||||||
;; The "up" file is called to run the migration and the "down" file is called to
|
;; The "up" file is called to run the migration and the "down" file is called to
|
||||||
;; "undo" the migration.
|
;; "undo" the migration.
|
||||||
(define *migrations*
|
(define *migrations*
|
||||||
'((0 . "adding-instance-control-app")))
|
'((0 . "adding-instance-control-app")
|
||||||
|
(1 . "adding-service-config-digitalocean-volume-size")
|
||||||
|
(2 . "adding-deployments-instance-backup")))
|
||||||
|
|
||||||
(define (run-pending-migrations conn)
|
(define (run-pending-migrations conn)
|
||||||
(let* ((migration-ids (sort (map car *migrations*) <))
|
(let* ((migration-ids (sort (map car *migrations*) <))
|
||||||
(migration-rows (query conn "select migration_id from migrations;"))
|
(migration-rows (query conn "select migration_id from migrations;"))
|
||||||
(applied-migration-ids (if (> (row-count migration-rows) 0)
|
(applied-migration-ids (if (> (row-count migration-rows) 0)
|
||||||
(row-values migration-rows)
|
(column-values migration-rows)
|
||||||
'())))
|
'())))
|
||||||
(for-each
|
(for-each
|
||||||
(lambda (id)
|
(lambda (id)
|
||||||
|
|||||||
1
src/migrations/0-adding-instance-control-app-down.sql
Normal file
1
src/migrations/0-adding-instance-control-app-down.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
alter table user_selected_apps drop column instance_control_version;
|
||||||
1
src/migrations/0-adding-instance-control-app-up.sql
Normal file
1
src/migrations/0-adding-instance-control-app-up.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
alter table user_selected_apps add instance_control_version varchar(100);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table user_service_configs drop column digitalocean_volume_size;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table user_service_configs add digitalocean_volume_size integer;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table deployments drop column instance_backup;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table deployments add instance_backup deployment_status not null default 'queued';
|
||||||
102
src/nassella.scm
102
src/nassella.scm
@@ -723,23 +723,31 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
(with-output-to-file (string-append dir "/terraform.tfstate.backup") (lambda () (write-string state-backup))))
|
(with-output-to-file (string-append dir "/terraform.tfstate.backup") (lambda () (write-string state-backup))))
|
||||||
|
|
||||||
(define (parse-deployment-log log)
|
(define (parse-deployment-log log)
|
||||||
(define (search complete in-progress)
|
(define (search complete in-progress failed)
|
||||||
(cond ((irregex-search complete log)
|
(cond ((irregex-search failed log)
|
||||||
|
'failed)
|
||||||
|
((irregex-search complete log)
|
||||||
'complete)
|
'complete)
|
||||||
((irregex-search in-progress log)
|
((irregex-search in-progress log)
|
||||||
'in-progress)
|
'in-progress)
|
||||||
(else 'queued)))
|
(else 'queued)))
|
||||||
`((generate-configs . ,(search "terraform apply" "NASSELLA_CONFIG: start"))
|
`((generate-configs . ,(search "terraform apply" "NASSELLA_CONFIG: start" "Failed to install provider"))
|
||||||
;; TODO this didn't seem to work right when upgrading the flatcar image
|
;; TODO this didn't seem to work right when upgrading the flatcar image
|
||||||
;; log: [0m[1mdigitalocean_custom_image.flatcar: Creating...[0m[0m
|
;; log: [0m[1mdigitalocean_custom_image.flatcar: Creating...[0m[0m
|
||||||
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m10s elapsed][0m[0m
|
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m10s elapsed][0m[0m
|
||||||
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m20s elapsed][0m[0m
|
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m20s elapsed][0m[0m
|
||||||
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m30s elapsed][0m[0m
|
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m30s elapsed][0m[0m
|
||||||
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m40s elapsed][0m[0m
|
;; [0m[1mdigitalocean_custom_image.flatcar: Still creating... [00m40s elapsed][0m[0m
|
||||||
(custom-image . ,(search "custom_image.flatcar: Modifications complete" "custom_image.flatcar: Modifying"))
|
(custom-image . ,(search '(or "custom_image.flatcar: Modifications complete" "custom_image.flatcar: Creation complete")
|
||||||
(machine-create . ,(search "droplet.machine: Creation complete" "droplet.machine: Creating..."))
|
'(or "custom_image.flatcar: Modifying" "custom_image.flatcar: Creating")
|
||||||
|
"XXX - nothing"))
|
||||||
|
(machine-create . ,(search "droplet.machine: Creation complete" "droplet.machine: Creating..." "XXX - nothing"))
|
||||||
(machine-destroy . ,(search "droplet.machine: Destruction complete"
|
(machine-destroy . ,(search "droplet.machine: Destruction complete"
|
||||||
'(: "droplet.machine (deposed object " (* alphanum) "): Destroying...")))))
|
'(: "droplet.machine (deposed object " (* alphanum) "): Destroying...") "XXX - nothing"))
|
||||||
|
(ip-destroy . ,(search "reserved_ip_assignment.machine: Destruction complete" "reserved_ip_assignment.machine: Destroying..." "XXX - nothing"))
|
||||||
|
(ip-create . ,(search "reserved_ip_assignment.machine: Creation complete" "reserved_ip_assignment.machine: Creating..." "Error Assigning reserved IP"))
|
||||||
|
(volume-create . ,(search "volume_attachment.machine: Creation complete" "volume_attachment.machine: Creating..." "XXX - nothing"))
|
||||||
|
(volume-destroy . ,(search "volume_attachment.machine: Destruction complete" "volume_attachment.machine: Destroying..." "XXX - nothing"))))
|
||||||
|
|
||||||
(define (write-config-entry name value)
|
(define (write-config-entry name value)
|
||||||
(display name)
|
(display name)
|
||||||
@@ -1318,7 +1326,9 @@ chmod -R 777 /opt/keys")))
|
|||||||
(method POST))
|
(method POST))
|
||||||
(VStack
|
(VStack
|
||||||
(Fieldset
|
(Fieldset
|
||||||
(@ (title "Size"))
|
(@ (title "Instance Properties"))
|
||||||
|
(Field (@ (name "volume-size") (label ("Volume Size in GB (For persistent application storage)"))
|
||||||
|
(value ,(alist-ref 'digitalocean-volume-size config eq? "60"))))
|
||||||
(Field (@ (element select) (name "size") (input-style ((max-width "100%"))))
|
(Field (@ (element select) (name "size") (input-style ((max-width "100%"))))
|
||||||
,@(map (lambda (s) `(option (@ (value ,(alist-ref 'slug s))
|
,@(map (lambda (s) `(option (@ (value ,(alist-ref 'slug s))
|
||||||
,@(if (equal? (alist-ref 'slug s) "s-2vcpu-2gb") `((selected "selected")) '()))
|
,@(if (equal? (alist-ref 'slug s) "s-2vcpu-2gb") `((selected "selected")) '()))
|
||||||
@@ -1338,7 +1348,8 @@ chmod -R 777 /opt/keys")))
|
|||||||
db
|
db
|
||||||
(session-user-id)
|
(session-user-id)
|
||||||
instance-id
|
instance-id
|
||||||
`((digitalocean-size . ,(alist-ref 'size (current-params)))))))
|
`((digitalocean-size . ,(alist-ref 'size (current-params)))
|
||||||
|
(digitalocean-volume-size . ,(alist-ref 'volume-size (current-params)))))))
|
||||||
(redirect (conc "/config/wizard/review/" instance-id))))
|
(redirect (conc "/config/wizard/review/" instance-id))))
|
||||||
|
|
||||||
(get/widgets
|
(get/widgets
|
||||||
@@ -1375,6 +1386,7 @@ chmod -R 777 /opt/keys")))
|
|||||||
(li "Size: " ,(alist-ref 'digitalocean-size service-config)))
|
(li "Size: " ,(alist-ref 'digitalocean-size service-config)))
|
||||||
(form
|
(form
|
||||||
(@ (action ,(conc "/config/wizard/review-submit/" instance-id)) (method POST))
|
(@ (action ,(conc "/config/wizard/review-submit/" instance-id)) (method POST))
|
||||||
|
(input (@ (type "hidden") (value ,(alist-ref 'force (current-params))) (name "force")))
|
||||||
(VStack
|
(VStack
|
||||||
(Form-Nav (@ (back-to ,(conc "/config/wizard/machine2/" instance-id)) (submit-button "Launch")))))))))
|
(Form-Nav (@ (back-to ,(conc "/config/wizard/machine2/" instance-id)) (submit-button "Launch")))))))))
|
||||||
|
|
||||||
@@ -1386,7 +1398,8 @@ chmod -R 777 /opt/keys")))
|
|||||||
(with-db/transaction
|
(with-db/transaction
|
||||||
(lambda (db)
|
(lambda (db)
|
||||||
(get-most-recent-deployment-status db (session-user-id) instance-id)))))))
|
(get-most-recent-deployment-status db (session-user-id) instance-id)))))))
|
||||||
(when (not (or (eq? status 'queued) (eq? status 'in-progress)))
|
(when (or (not (or (eq? status 'queued) (eq? status 'in-progress)))
|
||||||
|
(equal? (alist-ref 'force (current-params)) "true"))
|
||||||
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
||||||
(restic-snapshot-id (alist-ref 'restic-snapshot-id (current-params)))
|
(restic-snapshot-id (alist-ref 'restic-snapshot-id (current-params)))
|
||||||
(results
|
(results
|
||||||
@@ -1466,21 +1479,66 @@ chmod -R 777 /opt/keys")))
|
|||||||
(write-config-entry (car e) (cdr e)))
|
(write-config-entry (car e) (cdr e)))
|
||||||
`(("server_type" . ,(alist-ref 'digitalocean-size service-config))
|
`(("server_type" . ,(alist-ref 'digitalocean-size service-config))
|
||||||
("do_token" . ,(alist-ref 'digitalocean-api-token service-config))
|
("do_token" . ,(alist-ref 'digitalocean-api-token service-config))
|
||||||
|
("digitalocean_volume_size" . ,(alist-ref 'digitalocean-volume-size service-config))
|
||||||
("cloudflare_api_token" . ,(alist-ref 'cloudflare-api-token service-config))
|
("cloudflare_api_token" . ,(alist-ref 'cloudflare-api-token service-config))
|
||||||
("cloudflare_zone_id" . ,(alist-ref 'cloudflare-zone-id service-config))
|
("cloudflare_zone_id" . ,(alist-ref 'cloudflare-zone-id service-config))
|
||||||
("cloudflare_account_id" . ,(alist-ref 'cloudflare-account-id service-config))
|
("cloudflare_account_id" . ,(alist-ref 'cloudflare-account-id service-config))
|
||||||
("cluster_name" . "nassella")
|
("cluster_name" . "nassella")
|
||||||
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
||||||
;; (source <(curl -sSfL https://stable.release.flatcar-linux.net/amd64-usr/current/version.txt); echo "${FLATCAR_VERSION_ID}")
|
;; (source <(curl -sSfL https://stable.release.flatcar-linux.net/amd64-usr/current/version.txt); echo "${FLATCAR_VERSION_ID}")
|
||||||
("flatcar_stable_version" . "4593.2.1")))
|
("flatcar_stable_version" . "4593.2.2")))
|
||||||
;; remove the newline that generating the ssh key adds
|
;; remove the newline that generating the ssh key adds
|
||||||
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]"))))
|
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]"))))
|
||||||
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
(let* ((instance-id (alist-ref "id" (current-params) equal?))
|
||||||
(user-id (session-user-id))
|
(user-id (session-user-id))
|
||||||
|
(app-config
|
||||||
|
(with-db/transaction
|
||||||
|
(lambda (db)
|
||||||
|
(get-user-app-config db (session-user-id) instance-id))))
|
||||||
(deployment-id (with-db/transaction (lambda (db) (create-deployment db user-id instance-id))))
|
(deployment-id (with-db/transaction (lambda (db) (create-deployment db user-id instance-id))))
|
||||||
(dir (deployment-directory user-id instance-id)))
|
(dir (deployment-directory user-id instance-id))
|
||||||
|
(backup-request-id (conc (truncate (time->seconds (current-time))) "-" (pseudo-random-integer 10000))))
|
||||||
|
(with-db/transaction
|
||||||
|
(lambda (db)
|
||||||
|
(update-deployment-progress db deployment-id '((instance-backup . in-progress)))))
|
||||||
|
(handle-exceptions
|
||||||
|
exn
|
||||||
|
(with-db/transaction
|
||||||
|
(lambda (db)
|
||||||
|
(update-deployment-progress db deployment-id '((instance-backup . failed)))))
|
||||||
|
(send-instance-control-command
|
||||||
|
(alist-ref 'root-domain app-config)
|
||||||
|
(alist-ref 'subdomain (alist-ref 'instance-control (alist-ref 'config app-config)))
|
||||||
|
"queue-restic-snapshot-no-restart"
|
||||||
|
(alist-ref 'webhooks-secret (alist-ref 'instance-control (alist-ref 'config app-config)))
|
||||||
|
`((path . "/")
|
||||||
|
(tag . "automated_pre_instance_update")
|
||||||
|
;; effectively a guid, we just want something unique
|
||||||
|
(request_id . ,backup-request-id)
|
||||||
|
(version . 0))))
|
||||||
(thread-start!
|
(thread-start!
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
;; TODO add a timeout and set status to failed and error if we hit the timeout
|
||||||
|
(let ((start-time (time->seconds (current-time))))
|
||||||
|
(let loop ()
|
||||||
|
(thread-sleep! 1)
|
||||||
|
(let* ((status-result
|
||||||
|
(handle-exceptions
|
||||||
|
exn
|
||||||
|
'((status . "error"))
|
||||||
|
(send-instance-control-command
|
||||||
|
(alist-ref 'root-domain app-config)
|
||||||
|
(alist-ref 'subdomain (alist-ref 'instance-control (alist-ref 'config app-config)))
|
||||||
|
"restic-snapshot-status"
|
||||||
|
(alist-ref 'webhooks-secret (alist-ref 'instance-control (alist-ref 'config app-config)))
|
||||||
|
`((request_id . ,backup-request-id)
|
||||||
|
(version . 0)))))
|
||||||
|
(complete (string=? (alist-ref 'status status-result) "complete")))
|
||||||
|
(if (or complete (> (- (time->seconds (current-time)) start-time) 45))
|
||||||
|
(with-db/transaction
|
||||||
|
(lambda (db)
|
||||||
|
(update-deployment-progress db deployment-id `((instance-backup . ,(or (and complete 'complete) 'failed))))))
|
||||||
|
(loop)))))
|
||||||
(change-directory dir)
|
(change-directory dir)
|
||||||
(let ((pid (process-run "make apply > make-out 2>&1")))
|
(let ((pid (process-run "make apply > make-out 2>&1")))
|
||||||
(with-db/transaction (lambda (db) (update-deployment-in-progress db deployment-id pid)))
|
(with-db/transaction (lambda (db) (update-deployment-in-progress db deployment-id pid)))
|
||||||
@@ -1512,15 +1570,9 @@ chmod -R 777 /opt/keys")))
|
|||||||
(with-db/transaction
|
(with-db/transaction
|
||||||
(lambda (db)
|
(lambda (db)
|
||||||
(update-deployment-progress db deployment-id progress)
|
(update-deployment-progress db deployment-id progress)
|
||||||
;; TODO THIS DOESN'T WORK RIGHT FOR TERRAFORM OP FAILURES
|
|
||||||
;; like the random digital ocean error saying the IP can't be
|
|
||||||
;; updated because another operation is in progress.
|
|
||||||
;; it still registers as "success".
|
|
||||||
;; probably need to also write stderr to a file and read/store/parse that?
|
|
||||||
;; Should we parse make-out for string "Apply complete!" ?
|
|
||||||
(update-deployment-status
|
(update-deployment-status
|
||||||
db user-id deployment-id
|
db user-id deployment-id
|
||||||
(if exit-normal 'complete 'failed)
|
(if (= status 0) 'complete 'failed)
|
||||||
(with-input-from-file (string-append dir "/make-out") read-string))
|
(with-input-from-file (string-append dir "/make-out") read-string))
|
||||||
(update-user-terraform-state db user-id instance-id
|
(update-user-terraform-state db user-id instance-id
|
||||||
(if (eof-object? tf-state) "" tf-state)
|
(if (eof-object? tf-state) "" tf-state)
|
||||||
@@ -1542,7 +1594,7 @@ chmod -R 777 /opt/keys")))
|
|||||||
(lambda (db)
|
(lambda (db)
|
||||||
`((status . ,(get-most-recent-deployment-status db (session-user-id) instance-id))
|
`((status . ,(get-most-recent-deployment-status db (session-user-id) instance-id))
|
||||||
(progress . ,(get-most-recent-deployment-progress db (session-user-id) instance-id))))))
|
(progress . ,(get-most-recent-deployment-progress db (session-user-id) instance-id))))))
|
||||||
(output (with-input-from-file (string-append (deployment-directory (session-user-id) instance-id) "/make-out") read-string))
|
(output (handle-exceptions exn "" (with-input-from-file (string-append (deployment-directory (session-user-id) instance-id) "/make-out") read-string)))
|
||||||
(progress (alist-ref 'progress res))
|
(progress (alist-ref 'progress res))
|
||||||
(status (alist-ref 'status res)))
|
(status (alist-ref 'status res)))
|
||||||
`(App
|
`(App
|
||||||
@@ -1554,9 +1606,14 @@ chmod -R 777 /opt/keys")))
|
|||||||
((in-progress) "Deployment in progress")
|
((in-progress) "Deployment in progress")
|
||||||
((complete) "Deployment complete!")
|
((complete) "Deployment complete!")
|
||||||
((failed) "Deployment failed")))
|
((failed) "Deployment failed")))
|
||||||
(ul (li "generate configs: " ,(progress-status->text (alist-ref 'generate-configs progress)))
|
(ul (li "perform backup: " ,(progress-status->text (alist-ref 'instance-backup progress)))
|
||||||
|
(li "generate configs: " ,(progress-status->text (alist-ref 'generate-configs progress)))
|
||||||
(li "custom flatcar image: " ,(progress-status->text (alist-ref 'custom-image progress)))
|
(li "custom flatcar image: " ,(progress-status->text (alist-ref 'custom-image progress)))
|
||||||
(li "machine create: " ,(progress-status->text (alist-ref 'machine-create progress)))
|
(li "application volume disconnect: " ,(progress-status->text (alist-ref 'volume-destroy progress)))
|
||||||
|
(li "instance create: " ,(progress-status->text (alist-ref 'machine-create progress)))
|
||||||
|
(li "instance mapped ip disconnect: " ,(progress-status->text (alist-ref 'ip-destroy progress)))
|
||||||
|
(li "instance mapped ip connect: " ,(progress-status->text (alist-ref 'ip-create progress)))
|
||||||
|
(li "application volume connect: " ,(progress-status->text (alist-ref 'volume-create progress)))
|
||||||
(li "cleanup previous machine: " ,(progress-status->text (alist-ref 'machine-destroy progress))))
|
(li "cleanup previous machine: " ,(progress-status->text (alist-ref 'machine-destroy progress))))
|
||||||
(div
|
(div
|
||||||
(a (@ (href "/dashboard")) "Dashboard")
|
(a (@ (href "/dashboard")) "Dashboard")
|
||||||
@@ -1733,7 +1790,7 @@ chmod -R 777 /opt/keys")))
|
|||||||
("cluster_name" . "nassella")
|
("cluster_name" . "nassella")
|
||||||
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
("datacenter" . ,(alist-ref 'digitalocean-region service-config))
|
||||||
;; (source <(curl -sSfL https://stable.release.flatcar-linux.net/amd64-usr/current/version.txt); echo "${FLATCAR_VERSION_ID}")
|
;; (source <(curl -sSfL https://stable.release.flatcar-linux.net/amd64-usr/current/version.txt); echo "${FLATCAR_VERSION_ID}")
|
||||||
("flatcar_stable_version" . "4593.2.1")))
|
("Flatcar_stable_version" . "4593.2.2")))
|
||||||
;; remove the newline that generating the ssh key adds
|
;; remove the newline that generating the ssh key adds
|
||||||
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]")))
|
(display "ssh_keys=[\"") (display (string-drop-right ssh-pub-key 1)) (print "\"]")))
|
||||||
;; TODO need a new table to track destroying?
|
;; TODO need a new table to track destroying?
|
||||||
@@ -1825,6 +1882,7 @@ chmod -R 777 /opt/keys")))
|
|||||||
`((ul (li "generate configs: " ,(progress-status->text (alist-ref 'generate-configs progress)))
|
`((ul (li "generate configs: " ,(progress-status->text (alist-ref 'generate-configs progress)))
|
||||||
(li "custom flatcar image: " ,(progress-status->text (alist-ref 'custom-image progress)))
|
(li "custom flatcar image: " ,(progress-status->text (alist-ref 'custom-image progress)))
|
||||||
(li "machine create: " ,(progress-status->text (alist-ref 'machine-create progress)))
|
(li "machine create: " ,(progress-status->text (alist-ref 'machine-create progress)))
|
||||||
|
(li "attaching : " ,(progress-status->text (alist-ref 'machine-create progress)))
|
||||||
(li "cleanup previous machine: " ,(progress-status->text (alist-ref 'machine-destroy progress))))
|
(li "cleanup previous machine: " ,(progress-status->text (alist-ref 'machine-destroy progress))))
|
||||||
(div
|
(div
|
||||||
(a (@ (href "/dashboard")) "Dashboard")
|
(a (@ (href "/dashboard")) "Dashboard")
|
||||||
|
|||||||
Reference in New Issue
Block a user