From e372f2157bf9bb008b4a67bb74d7a65ebfc6ee0d Mon Sep 17 00:00:00 2001 From: Thomas Hintz Date: Sun, 30 Nov 2025 11:36:19 -0800 Subject: [PATCH] Adding tests. --- src/db.scm | 17 +- src/mocks.scm | 3096 ++++++++++++++++++++++++++++++++++++++++++++++ src/nassella.scm | 343 ++--- src/test.scm | 428 +++++++ 4 files changed, 3718 insertions(+), 166 deletions(-) create mode 100644 src/mocks.scm create mode 100644 src/test.scm diff --git a/src/db.scm b/src/db.scm index bb40a03..a5a7ea9 100644 --- a/src/db.scm +++ b/src/db.scm @@ -5,7 +5,7 @@ ;;functions with-db with-db/transaction - create-user + create-user delete-user update-user-service-config get-user-service-config update-user-selected-apps get-user-selected-apps update-user-app-config get-user-app-config @@ -126,13 +126,6 @@ (user-iv (blob->hexstring/uppercase (generate-iv)))) (receive (enc-user-key tag) (encrypt user-key *root-key-key* *root-key-iv* (string->blob (number->string auth-user-id))) - ;; (write user-key) - ;; (newline) - ;; (write user-iv) - ;; (newline) - ;; (write (blob->hexstring/uppercase (string->blob enc-user-key))) - ;; (newline) - ;; (write (blob->hexstring/uppercase (string->blob tag))) (let ((user-id (value-at (query conn @@ -145,7 +138,11 @@ returning users.user_id;" (query conn "insert into user_service_configs(user_id) values ($1);" user-id) (query conn "insert into user_selected_apps(user_id) values ($1);" user-id) (query conn "insert into user_app_configs(user_id) values ($1);" user-id) - (query conn "insert into user_terraform_state(user_id) values ($1);" user-id))))) + (query conn "insert into user_terraform_state(user_id) values ($1);" user-id) + user-id)))) + +(define (delete-user conn user-id) + (query conn "delete from users where user_id=$1;" user-id)) (define *user-service-configs-column-map* '((cloudflare-api-token . ("cloudflare_api_token_enc" #t)) @@ -443,7 +440,7 @@ returning users.user_id;" "" (user-decrypt-from-db (alist-ref 'state_backup_enc res) user-key user-iv user-id))))))) -(with-db/transaction (lambda (db) (get-user-deployments db 7))) +;; (with-db/transaction (lambda (db) (get-user-deployments db 7))) ;; (with-db/transaction (lambda (db) (get-most-recent-deployment-progress db 7))) ;; (with-db/transaction (lambda (db) (get-deployment-progress db 14))) ;; (with-db/transaction (lambda (db) (update-deployment-progress db 14 '((generate-configs . complete) (custom-image . in-progress) (machine-create . queued))))) diff --git a/src/mocks.scm b/src/mocks.scm new file mode 100644 index 0000000..b6b3295 --- /dev/null +++ b/src/mocks.scm @@ -0,0 +1,3096 @@ +(define *digital-ocean-regions-response* + '(((name . "New York 1") (slug . "nyc1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "c-48-intel" "m3-24vcpu-192gb-intel" "gd-40vcpu-160gb" "c2-48vcpu-96gb-intel" "g5_5-32vcpu-128gb-intel" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "c-60-intel" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g-48vcpu-192gb-intel" "c2-60vcpu-120gb-intel" "gd-48vcpu-192gb-intel" "c5-48vcpu-96gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "c5-60vcpu-120gb-intel" "g5_5-60vcpu-240gb-intel")) ((name . "New York 2") (slug . "nyc2") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "m3-24vcpu-192gb-intel" "gd-40vcpu-160gb" "g5_5-32vcpu-128gb-intel" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g-48vcpu-192gb-intel" "gd-48vcpu-192gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "gpu-h100x1-80gb" "gpu-h200x1-141gb" "gpu-h200x8-1128gb")) ((name . "Singapore 1") (slug . "sgp1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m3-2vcpu-16gb" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m3-4vcpu-32gb" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "c2-16vcpu-32gb" "m3-8vcpu-64gb" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "c2-32vcpu-64gb" "m3-16vcpu-128gb" "c5-32vcpu-64gb" "c-48" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "c2-48vcpu-96gb" "m3-24vcpu-192gb" "g-40vcpu-160gb" "so1_5-16vcpu-128gb" "m-32vcpu-256gb" "gd-40vcpu-160gb" "g6_5-32vcpu-128gb" "c5-48vcpu-96gb" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m3-32vcpu-256gb" "g6_5-40vcpu-160gb" "so1_5-24vcpu-192gb" "so-32vcpu-256gb" "m6-32vcpu-256gb" "so1_5-32vcpu-256gb")) ((name . "London 1") (slug . "lon1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "c2-32vcpu-64gb" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "c5-32vcpu-64gb" "c-48" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "c2-48vcpu-96gb" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "c-48-intel" "m3-24vcpu-192gb-intel" "m-32vcpu-256gb" "gd-40vcpu-160gb" "g6_5-32vcpu-128gb" "c2-48vcpu-96gb-intel" "g5_5-32vcpu-128gb-intel" "c5-48vcpu-96gb" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "c-60-intel" "m3-32vcpu-256gb" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g6_5-40vcpu-160gb" "g-48vcpu-192gb-intel" "c2-60vcpu-120gb-intel" "gd-48vcpu-192gb-intel" "c5-48vcpu-96gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "so-32vcpu-256gb" "m6-32vcpu-256gb" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "g-60vcpu-240gb-intel" "m-48vcpu-384gb-intel" "gd-60vcpu-240gb-intel" "c5-60vcpu-120gb-intel" "so1_5-32vcpu-256gb" "m3-48vcpu-384gb-intel" "g5_5-60vcpu-240gb-intel" "so-48vcpu-384gb-intel")) ((name . "New York 3") (slug . "nyc3") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "c-48-intel" "m3-24vcpu-192gb-intel" "gd-40vcpu-160gb" "c2-48vcpu-96gb-intel" "g5_5-32vcpu-128gb-intel" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "c-60-intel" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g-48vcpu-192gb-intel" "gd-48vcpu-192gb-intel" "c5-48vcpu-96gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "m-48vcpu-384gb-intel" "c5-60vcpu-120gb-intel" "m3-48vcpu-384gb-intel" "g5_5-60vcpu-240gb-intel")) ((name . "Amsterdam 3") (slug . "ams3") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m3-2vcpu-16gb" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m3-4vcpu-32gb" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "c2-16vcpu-32gb" "m3-8vcpu-64gb" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "c2-32vcpu-64gb" "m3-16vcpu-128gb" "c-48" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "c2-48vcpu-96gb" "m3-24vcpu-192gb" "g-40vcpu-160gb" "so1_5-16vcpu-128gb" "gd-40vcpu-160gb" "g6_5-32vcpu-128gb" "so-24vcpu-192gb" "m6-24vcpu-192gb" "gpu-h100x1-80gb")) ((name . "Frankfurt 1") (slug . "fra1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m3-2vcpu-16gb" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m3-4vcpu-32gb" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "c2-16vcpu-32gb" "m3-8vcpu-64gb" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "c2-32vcpu-64gb" "m3-16vcpu-128gb" "m-24vcpu-192gb" "g-32vcpu-128gb" "gd-32vcpu-128gb" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-40vcpu-160gb")) ((name . "Toronto 1") (slug . "tor1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "gpu-4000adax1-20gb" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "gpu-6000adax1-48gb" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "c-48-intel" "m3-24vcpu-192gb-intel" "m-32vcpu-256gb" "g6_5-32vcpu-128gb" "c2-48vcpu-96gb-intel" "g5_5-32vcpu-128gb-intel" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "c-60-intel" "m3-32vcpu-256gb" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g-48vcpu-192gb-intel" "c2-60vcpu-120gb-intel" "gd-48vcpu-192gb-intel" "c5-48vcpu-96gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "so-32vcpu-256gb" "m6-32vcpu-256gb" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "g-60vcpu-240gb-intel" "m-48vcpu-384gb-intel" "gd-60vcpu-240gb-intel" "c5-60vcpu-120gb-intel" "gpu-h100x1-80gb" "so1_5-32vcpu-256gb" "m3-48vcpu-384gb-intel" "g5_5-60vcpu-240gb-intel" "so-48vcpu-384gb-intel")) ((name . "San Francisco 2") (slug . "sfo2") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-120gb-intel" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "c2-32vcpu-64gb" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "c5-32vcpu-64gb" "c-48" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "c2-48vcpu-96gb" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "c-48-intel" "m3-24vcpu-192gb-intel" "m-32vcpu-256gb" "gd-40vcpu-160gb" "g6_5-32vcpu-128gb" "c2-48vcpu-96gb-intel" "g5_5-32vcpu-128gb-intel" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "c-60-intel" "m3-32vcpu-256gb" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g-48vcpu-192gb-intel" "c2-60vcpu-120gb-intel" "gd-48vcpu-192gb-intel" "c5-48vcpu-96gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "so-32vcpu-256gb" "m6-32vcpu-256gb" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "g-60vcpu-240gb-intel" "m-48vcpu-384gb-intel" "gd-60vcpu-240gb-intel" "c5-60vcpu-120gb-intel" "so1_5-32vcpu-256gb" "m3-48vcpu-384gb-intel" "g5_5-60vcpu-240gb-intel" "so-48vcpu-384gb-intel")) ((name . "Bangalore 1") (slug . "blr1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "c2-32vcpu-64gb" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "c5-32vcpu-64gb" "c-48" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "c2-48vcpu-96gb" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "c-48-intel" "m3-24vcpu-192gb-intel" "m-32vcpu-256gb" "gd-40vcpu-160gb" "g6_5-32vcpu-128gb" "c2-48vcpu-96gb-intel" "g5_5-32vcpu-128gb-intel" "c5-48vcpu-96gb" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "c-60-intel" "m3-32vcpu-256gb" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g6_5-40vcpu-160gb" "g-48vcpu-192gb-intel" "c2-60vcpu-120gb-intel" "gd-48vcpu-192gb-intel" "c5-48vcpu-96gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "so-32vcpu-256gb" "m6-32vcpu-256gb" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "m-48vcpu-384gb-intel" "c5-60vcpu-120gb-intel" "so1_5-32vcpu-256gb" "m3-48vcpu-384gb-intel" "g5_5-60vcpu-240gb-intel")) ((name . "San Francisco 3") (slug . "sfo3") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "g-2vcpu-8gb-intel" "gd-2vcpu-8gb-intel" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "g5_5-2vcpu-8gb-intel" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m-2vcpu-16gb-intel" "m3-2vcpu-16gb" "c-4-intel" "m3-2vcpu-16gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c2-4vcpu-8gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb-intel" "g-4vcpu-16gb-intel" "gd-4vcpu-16gb-intel" "c5-4vcpu-8gb-intel" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "g5_5-4vcpu-16gb-intel" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m-4vcpu-32gb-intel" "m3-4vcpu-32gb" "c-8-intel" "m3-4vcpu-32gb-intel" "c2-8vcpu-16gb-intel" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb-intel" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb-intel" "g-8vcpu-32gb-intel" "gd-8vcpu-32gb-intel" "c5-8vcpu-16gb-intel" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "g5_5-8vcpu-32gb-intel" "c2-16vcpu-32gb" "m-8vcpu-64gb-intel" "m3-8vcpu-64gb" "c-16-intel" "m3-8vcpu-64gb-intel" "c2-16vcpu-32gb-intel" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb-intel" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb-intel" "g-16vcpu-64gb-intel" "gd-16vcpu-64gb-intel" "c5-16vcpu-32gb-intel" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "g5_5-16vcpu-64gb-intel" "c2-32vcpu-64gb" "m-16vcpu-128gb-intel" "m3-16vcpu-128gb" "c-32-intel" "m3-16vcpu-128gb-intel" "c2-32vcpu-64gb-intel" "c5-32vcpu-64gb" "c-48" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb-intel" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "so1_5-16vcpu-128gb-intel" "c2-48vcpu-96gb" "m-24vcpu-192gb-intel" "g-32vcpu-128gb-intel" "m3-24vcpu-192gb" "g-40vcpu-160gb" "gd-32vcpu-128gb-intel" "c5-32vcpu-64gb-intel" "so1_5-16vcpu-128gb" "c-48-intel" "m3-24vcpu-192gb-intel" "m-32vcpu-256gb" "gd-40vcpu-160gb" "g6_5-32vcpu-128gb" "c2-48vcpu-96gb-intel" "g5_5-32vcpu-128gb-intel" "so-24vcpu-192gb-intel" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m-32vcpu-256gb-intel" "c-60-intel" "m3-32vcpu-256gb" "so1_5-24vcpu-192gb-intel" "m3-32vcpu-256gb-intel" "g6_5-40vcpu-160gb" "g-48vcpu-192gb-intel" "c2-60vcpu-120gb-intel" "gd-48vcpu-192gb-intel" "c5-48vcpu-96gb-intel" "so1_5-24vcpu-192gb" "so-32vcpu-256gb-intel" "so-32vcpu-256gb" "m6-32vcpu-256gb" "g5_5-48vcpu-192gb-intel" "so1_5-32vcpu-256gb-intel" "g-60vcpu-240gb-intel" "m-48vcpu-384gb-intel" "gd-60vcpu-240gb-intel" "c5-60vcpu-120gb-intel" "so1_5-32vcpu-256gb" "m3-48vcpu-384gb-intel" "g5_5-60vcpu-240gb-intel" "so-48vcpu-384gb-intel")) ((name . "Sydney 1") (slug . "syd1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-512mb-10gb" "s-1vcpu-1gb" "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "c-2" "c2-2vcpu-4gb" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "c5-2vcpu-4gb" "g-2vcpu-8gb" "s-4vcpu-8gb-240gb-intel" "gd-2vcpu-8gb" "s-4vcpu-16gb-amd" "m-2vcpu-16gb" "c-4" "g6_5-2vcpu-8gb" "c2-4vcpu-8gb" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb" "m3-2vcpu-16gb" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "c5-4vcpu-8gb" "g-4vcpu-16gb" "s-8vcpu-16gb-480gb-intel" "so-2vcpu-16gb" "m6-2vcpu-16gb" "gd-4vcpu-16gb" "so1_5-2vcpu-16gb" "s-8vcpu-32gb-amd" "m-4vcpu-32gb" "c-8" "g6_5-4vcpu-16gb" "c2-8vcpu-16gb" "s-8vcpu-32gb-640gb-intel" "m3-4vcpu-32gb" "c5-8vcpu-16gb" "g-8vcpu-32gb" "so-4vcpu-32gb" "m6-4vcpu-32gb" "gd-8vcpu-32gb" "so1_5-4vcpu-32gb" "m-8vcpu-64gb" "c-16" "g6_5-8vcpu-32gb" "c2-16vcpu-32gb" "m3-8vcpu-64gb" "c5-16vcpu-32gb" "g-16vcpu-64gb" "so-8vcpu-64gb" "m6-8vcpu-64gb" "gd-16vcpu-64gb" "so1_5-8vcpu-64gb" "m-16vcpu-128gb" "c-32" "g6_5-16vcpu-64gb" "c2-32vcpu-64gb" "m3-16vcpu-128gb" "c5-32vcpu-64gb" "c-48" "m-24vcpu-192gb" "g-32vcpu-128gb" "so-16vcpu-128gb" "m6-16vcpu-128gb" "gd-32vcpu-128gb" "c2-48vcpu-96gb" "m3-24vcpu-192gb" "g-40vcpu-160gb" "so1_5-16vcpu-128gb" "m-32vcpu-256gb" "gd-40vcpu-160gb" "g6_5-32vcpu-128gb" "c5-48vcpu-96gb" "so-24vcpu-192gb" "m6-24vcpu-192gb" "m3-32vcpu-256gb" "g6_5-40vcpu-160gb" "so1_5-24vcpu-192gb" "so-32vcpu-256gb" "m6-32vcpu-256gb" "so1_5-32vcpu-256gb")) ((name . "Atlanta 1") (slug . "atl1") (features "backups" "ipv6" "metadata" "install_agent" "storage" "image_transfer") (available . #t) (sizes "s-1vcpu-1gb-amd" "s-1vcpu-1gb-intel" "s-1vcpu-1gb-35gb-intel" "s-1vcpu-2gb-amd" "s-1vcpu-2gb-intel" "s-1vcpu-2gb-70gb-intel" "s-2vcpu-2gb-amd" "s-2vcpu-2gb-intel" "s-2vcpu-2gb-90gb-intel" "s-2vcpu-4gb-amd" "s-2vcpu-4gb-intel" "s-2vcpu-4gb-120gb-intel" "s-2vcpu-8gb-amd" "s-2vcpu-8gb-160gb-intel" "s-4vcpu-8gb-amd" "s-4vcpu-8gb-intel" "s-4vcpu-8gb-240gb-intel" "s-4vcpu-16gb-amd" "s-4vcpu-16gb-320gb-intel" "s-8vcpu-16gb-amd" "s-8vcpu-16gb-intel" "s-8vcpu-16gb-480gb-intel" "s-8vcpu-32gb-amd" "s-8vcpu-32gb-640gb-intel")))) + +(define *digital-ocean-sizes-response* + '((sizes ((slug . "s-1vcpu-512mb-10gb") + (memory . 512) + (vcpus . 1) + (disk . 10) + (transfer . 0.5) + (price_monthly . 4) + (price_hourly . 0.00595) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 10) (unit . "gib"))))) + ((slug . "s-1vcpu-1gb") + (memory . 1024) + (vcpus . 1) + (disk . 25) + (transfer . 1.0) + (price_monthly . 6) + (price_hourly . 0.00893) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 25) (unit . "gib"))))) + ((slug . "s-1vcpu-1gb-amd") + (memory . 1024) + (vcpus . 1) + (disk . 25) + (transfer . 1.0) + (price_monthly . 7) + (price_hourly . 0.01042) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 25) (unit . "gib"))))) + ((slug . "s-1vcpu-1gb-intel") + (memory . 1024) + (vcpus . 1) + (disk . 25) + (transfer . 1.0) + (price_monthly . 7) + (price_hourly . 0.01042) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 25) (unit . "gib"))))) + ((slug . "s-1vcpu-1gb-35gb-intel") + (memory . 1024) + (vcpus . 1) + (disk . 35) + (transfer . 1.0) + (price_monthly . 8) + (price_hourly . 0.0119) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 35) (unit . "gib"))))) + ((slug . "s-1vcpu-2gb") + (memory . 2048) + (vcpus . 1) + (disk . 50) + (transfer . 2.0) + (price_monthly . 12) + (price_hourly . 0.01786) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "s-1vcpu-2gb-amd") + (memory . 2048) + (vcpus . 1) + (disk . 50) + (transfer . 2.0) + (price_monthly . 14) + (price_hourly . 0.02083) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "s-1vcpu-2gb-intel") + (memory . 2048) + (vcpus . 1) + (disk . 50) + (transfer . 2.0) + (price_monthly . 14) + (price_hourly . 0.02083) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "s-1vcpu-2gb-70gb-intel") + (memory . 2048) + (vcpus . 1) + (disk . 70) + (transfer . 2.0) + (price_monthly . 16) + (price_hourly . 0.02381) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 70) (unit . "gib"))))) + ((slug . "s-2vcpu-2gb") + (memory . 2048) + (vcpus . 2) + (disk . 60) + (transfer . 3.0) + (price_monthly . 18) + (price_hourly . 0.02679) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 60) (unit . "gib"))))) + ((slug . "s-2vcpu-2gb-amd") + (memory . 2048) + (vcpus . 2) + (disk . 60) + (transfer . 3.0) + (price_monthly . 21) + (price_hourly . 0.03125) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 60) (unit . "gib"))))) + ((slug . "s-2vcpu-2gb-intel") + (memory . 2048) + (vcpus . 2) + (disk . 60) + (transfer . 3.0) + (price_monthly . 21) + (price_hourly . 0.03125) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 60) (unit . "gib"))))) + ((slug . "s-2vcpu-2gb-90gb-intel") + (memory . 2048) + (vcpus . 2) + (disk . 90) + (transfer . 3.0) + (price_monthly . 24) + (price_hourly . 0.03571) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 90) (unit . "gib"))))) + ((slug . "s-2vcpu-4gb") + (memory . 4096) + (vcpus . 2) + (disk . 80) + (transfer . 4.0) + (price_monthly . 24) + (price_hourly . 0.03571) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 80) (unit . "gib"))))) + ((slug . "s-2vcpu-4gb-amd") + (memory . 4096) + (vcpus . 2) + (disk . 80) + (transfer . 4.0) + (price_monthly . 28) + (price_hourly . 0.04167) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 80) (unit . "gib"))))) + ((slug . "s-2vcpu-4gb-intel") + (memory . 4096) + (vcpus . 2) + (disk . 80) + (transfer . 4.0) + (price_monthly . 28) + (price_hourly . 0.04167) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 80) (unit . "gib"))))) + ((slug . "s-2vcpu-4gb-120gb-intel") + (memory . 4096) + (vcpus . 2) + (disk . 120) + (transfer . 4.0) + (price_monthly . 32) + (price_hourly . 0.04762) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 120) (unit . "gib"))))) + ((slug . "s-2vcpu-8gb-amd") + (memory . 8192) + (vcpus . 2) + (disk . 100) + (transfer . 5.0) + (price_monthly . 42) + (price_hourly . 0.0625) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "c-2") + (memory . 4096) + (vcpus . 2) + (disk . 25) + (transfer . 4.0) + (price_monthly . 42) + (price_hourly . 0.0625) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 25) (unit . "gib"))))) + ((slug . "c2-2vcpu-4gb") + (memory . 4096) + (vcpus . 2) + (disk . 50) + (transfer . 4.0) + (price_monthly . 47) + (price_hourly . 0.06994) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "s-2vcpu-8gb-160gb-intel") + (memory . 8192) + (vcpus . 2) + (disk . 160) + (transfer . 5.0) + (price_monthly . 48) + (price_hourly . 0.07143) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 160) (unit . "gib"))))) + ((slug . "s-4vcpu-8gb") + (memory . 8192) + (vcpus . 4) + (disk . 160) + (transfer . 5.0) + (price_monthly . 48) + (price_hourly . 0.07143) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 160) (unit . "gib"))))) + ((slug . "s-4vcpu-8gb-amd") + (memory . 8192) + (vcpus . 4) + (disk . 160) + (transfer . 5.0) + (price_monthly . 56) + (price_hourly . 0.08333) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 160) (unit . "gib"))))) + ((slug . "s-4vcpu-8gb-intel") + (memory . 8192) + (vcpus . 4) + (disk . 160) + (transfer . 5.0) + (price_monthly . 56) + (price_hourly . 0.08333) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 160) (unit . "gib"))))) + ((slug . "c5-2vcpu-4gb") + (memory . 4096) + (vcpus . 2) + (disk . 125) + (transfer . 4.0) + (price_monthly . 62) + (price_hourly . 0.09226) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU Intensive 5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 125) (unit . "gib"))))) + ((slug . "g-2vcpu-8gb") + (memory . 8192) + (vcpus . 2) + (disk . 25) + (transfer . 4.0) + (price_monthly . 63) + (price_hourly . 0.09375) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 25) (unit . "gib"))))) + ((slug . "s-4vcpu-8gb-240gb-intel") + (memory . 8192) + (vcpus . 4) + (disk . 240) + (transfer . 6.0) + (price_monthly . 64) + (price_hourly . 0.09524) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 240) (unit . "gib"))))) + ((slug . "gd-2vcpu-8gb") + (memory . 8192) + (vcpus . 2) + (disk . 50) + (transfer . 4.0) + (price_monthly . 68) + (price_hourly . 0.10119) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "g-2vcpu-8gb-intel") + (memory . 8192) + (vcpus . 2) + (disk . 30) + (transfer . 4.0) + (price_monthly . 76) + (price_hourly . 0.1131) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 30) (unit . "gib"))))) + ((slug . "gd-2vcpu-8gb-intel") + (memory . 8192) + (vcpus . 2) + (disk . 60) + (transfer . 4.0) + (price_monthly . 79) + (price_hourly . 0.11756) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel 2x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 60) (unit . "gib"))))) + ((slug . "s-4vcpu-16gb-amd") + (memory . 16384) + (vcpus . 4) + (disk . 200) + (transfer . 8.0) + (price_monthly . 84) + (price_hourly . 0.125) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "m-2vcpu-16gb") + (memory . 16384) + (vcpus . 2) + (disk . 50) + (transfer . 4.0) + (price_monthly . 84) + (price_hourly . 0.125) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "c-4") + (memory . 8192) + (vcpus . 4) + (disk . 50) + (transfer . 5.0) + (price_monthly . 84) + (price_hourly . 0.125) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "g6_5-2vcpu-8gb") + (memory . 8192) + (vcpus . 2) + (disk . 163) + (transfer . 4.0) + (price_monthly . 90.6) + (price_hourly . 0.13482) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 6.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 163) (unit . "gib"))))) + ((slug . "g5_5-2vcpu-8gb-intel") + (memory . 8192) + (vcpus . 2) + (disk . 165) + (transfer . 4.0) + (price_monthly . 92.2) + (price_hourly . 0.1372) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose - Premium Intel 5.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 165) (unit . "gib"))))) + ((slug . "c2-4vcpu-8gb") + (memory . 8192) + (vcpus . 4) + (disk . 100) + (transfer . 5.0) + (price_monthly . 94) + (price_hourly . 0.13988) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "s-4vcpu-16gb-320gb-intel") + (memory . 16384) + (vcpus . 4) + (disk . 320) + (transfer . 8.0) + (price_monthly . 96) + (price_hourly . 0.14286) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 320) (unit . "gib"))))) + ((slug . "s-8vcpu-16gb") + (memory . 16384) + (vcpus . 8) + (disk . 320) + (transfer . 6.0) + (price_monthly . 96) + (price_hourly . 0.14286) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 320) (unit . "gib"))))) + ((slug . "m-2vcpu-16gb-intel") + (memory . 16384) + (vcpus . 2) + (disk . 50) + (transfer . 4.0) + (price_monthly . 99) + (price_hourly . 0.14732) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "m3-2vcpu-16gb") + (memory . 16384) + (vcpus . 2) + (disk . 150) + (transfer . 4.0) + (price_monthly . 104) + (price_hourly . 0.15476) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 3x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 150) (unit . "gib"))))) + ((slug . "c-4-intel") + (memory . 8192) + (vcpus . 4) + (disk . 50) + (transfer . 5.0) + (price_monthly . 109) + (price_hourly . 0.1622) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "m3-2vcpu-16gb-intel") + (memory . 16384) + (vcpus . 2) + (disk . 150) + (transfer . 4.0) + (price_monthly . 110) + (price_hourly . 0.16369) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized 3x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 150) (unit . "gib"))))) + ((slug . "s-8vcpu-16gb-amd") + (memory . 16384) + (vcpus . 8) + (disk . 320) + (transfer . 6.0) + (price_monthly . 112) + (price_hourly . 0.16667) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 320) (unit . "gib"))))) + ((slug . "s-8vcpu-16gb-intel") + (memory . 16384) + (vcpus . 8) + (disk . 320) + (transfer . 6.0) + (price_monthly . 112) + (price_hourly . 0.16667) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 320) (unit . "gib"))))) + ((slug . "c2-4vcpu-8gb-intel") + (memory . 8192) + (vcpus . 4) + (disk . 100) + (transfer . 5.0) + (price_monthly . 122) + (price_hourly . 0.18155) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "c5-4vcpu-8gb") + (memory . 8192) + (vcpus . 4) + (disk . 250) + (transfer . 5.0) + (price_monthly . 124) + (price_hourly . 0.18452) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU Intensive 5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 250) (unit . "gib"))))) + ((slug . "g-4vcpu-16gb") + (memory . 16384) + (vcpus . 4) + (disk . 50) + (transfer . 5.0) + (price_monthly . 126) + (price_hourly . 0.1875) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 50) (unit . "gib"))))) + ((slug . "s-8vcpu-16gb-480gb-intel") + (memory . 16384) + (vcpus . 8) + (disk . 480) + (transfer . 9.0) + (price_monthly . 128) + (price_hourly . 0.19048) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 480) (unit . "gib"))))) + ((slug . "so-2vcpu-16gb-intel") + (memory . 16384) + (vcpus . 2) + (disk . 300) + (transfer . 4.0) + (price_monthly . 131) + (price_hourly . 0.19494) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 300) (unit . "gib"))))) + ((slug . "so-2vcpu-16gb") + (memory . 16384) + (vcpus . 2) + (disk . 300) + (transfer . 4.0) + (price_monthly . 131) + (price_hourly . 0.19494) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 300) (unit . "gib"))))) + ((slug . "m6-2vcpu-16gb") + (memory . 16384) + (vcpus . 2) + (disk . 300) + (transfer . 4.0) + (price_monthly . 131) + (price_hourly . 0.19494) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 6x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 300) (unit . "gib"))))) + ((slug . "gd-4vcpu-16gb") + (memory . 16384) + (vcpus . 4) + (disk . 100) + (transfer . 5.0) + (price_monthly . 136) + (price_hourly . 0.20238) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "so1_5-2vcpu-16gb-intel") + (memory . 16384) + (vcpus . 2) + (disk . 450) + (transfer . 4.0) + (price_monthly . 139) + (price_hourly . 0.20685) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized 1.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 450) (unit . "gib"))))) + ((slug . "g-4vcpu-16gb-intel") + (memory . 16384) + (vcpus . 4) + (disk . 60) + (transfer . 5.0) + (price_monthly . 151) + (price_hourly . 0.2247) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 60) (unit . "gib"))))) + ((slug . "gd-4vcpu-16gb-intel") + (memory . 16384) + (vcpus . 4) + (disk . 120) + (transfer . 5.0) + (price_monthly . 158) + (price_hourly . 0.23512) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel 2x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 120) (unit . "gib"))))) + ((slug . "c5-4vcpu-8gb-intel") + (memory . 8192) + (vcpus . 4) + (disk . 250) + (transfer . 5.0) + (price_monthly . 161) + (price_hourly . 0.23958) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "CPU Optimized - Premium Intel 5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 250) (unit . "gib"))))) + ((slug . "so1_5-2vcpu-16gb") + (memory . 16384) + (vcpus . 2) + (disk . 450) + (transfer . 4.0) + (price_monthly . 163) + (price_hourly . 0.24256) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized 1.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 450) (unit . "gib"))))) + ((slug . "s-8vcpu-32gb-amd") + (memory . 32768) + (vcpus . 8) + (disk . 400) + (transfer . 10.0) + (price_monthly . 168) + (price_hourly . 0.25) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic AMD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "m-4vcpu-32gb") + (memory . 32768) + (vcpus . 4) + (disk . 100) + (transfer . 6.0) + (price_monthly . 168) + (price_hourly . 0.25) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "c-8") + (memory . 16384) + (vcpus . 8) + (disk . 100) + (transfer . 6.0) + (price_monthly . 168) + (price_hourly . 0.25) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "g6_5-4vcpu-16gb") + (memory . 16384) + (vcpus . 4) + (disk . 325) + (transfer . 5.0) + (price_monthly . 181) + (price_hourly . 0.26935) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 6.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 325) (unit . "gib"))))) + ((slug . "g5_5-4vcpu-16gb-intel") + (memory . 16384) + (vcpus . 4) + (disk . 330) + (transfer . 5.0) + (price_monthly . 183.4) + (price_hourly . 0.27292) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose - Premium Intel 5.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 330) (unit . "gib"))))) + ((slug . "c2-8vcpu-16gb") + (memory . 16384) + (vcpus . 8) + (disk . 200) + (transfer . 6.0) + (price_monthly . 188) + (price_hourly . 0.27976) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "s-8vcpu-32gb-640gb-intel") + (memory . 32768) + (vcpus . 8) + (disk . 640) + (transfer . 10.0) + (price_monthly . 192) + (price_hourly . 0.28571) + (regions + "ams3" + "atl1" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "nyc3" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Basic Intel") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 640) (unit . "gib"))))) + ((slug . "m-4vcpu-32gb-intel") + (memory . 32768) + (vcpus . 4) + (disk . 100) + (transfer . 6.0) + (price_monthly . 198) + (price_hourly . 0.29464) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "m3-4vcpu-32gb") + (memory . 32768) + (vcpus . 4) + (disk . 300) + (transfer . 6.0) + (price_monthly . 208) + (price_hourly . 0.30952) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 3x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 300) (unit . "gib"))))) + ((slug . "c-8-intel") + (memory . 16384) + (vcpus . 8) + (disk . 100) + (transfer . 6.0) + (price_monthly . 218) + (price_hourly . 0.3244) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "m3-4vcpu-32gb-intel") + (memory . 32768) + (vcpus . 4) + (disk . 300) + (transfer . 6.0) + (price_monthly . 220) + (price_hourly . 0.32738) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized 3x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 300) (unit . "gib"))))) + ((slug . "c2-8vcpu-16gb-intel") + (memory . 16384) + (vcpus . 8) + (disk . 200) + (transfer . 6.0) + (price_monthly . 244) + (price_hourly . 0.3631) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "c5-8vcpu-16gb") + (memory . 16384) + (vcpus . 8) + (disk . 500) + (transfer . 6.0) + (price_monthly . 248) + (price_hourly . 0.36905) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU Intensive 5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 500) (unit . "gib"))))) + ((slug . "g-8vcpu-32gb") + (memory . 32768) + (vcpus . 8) + (disk . 100) + (transfer . 6.0) + (price_monthly . 252) + (price_hourly . 0.375) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 100) (unit . "gib"))))) + ((slug . "so-4vcpu-32gb-intel") + (memory . 32768) + (vcpus . 4) + (disk . 600) + (transfer . 6.0) + (price_monthly . 262) + (price_hourly . 0.38988) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "so-4vcpu-32gb") + (memory . 32768) + (vcpus . 4) + (disk . 600) + (transfer . 6.0) + (price_monthly . 262) + (price_hourly . 0.38988) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "m6-4vcpu-32gb") + (memory . 32768) + (vcpus . 4) + (disk . 600) + (transfer . 6.0) + (price_monthly . 262) + (price_hourly . 0.38988) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 6x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "gd-8vcpu-32gb") + (memory . 32768) + (vcpus . 8) + (disk . 200) + (transfer . 6.0) + (price_monthly . 272) + (price_hourly . 0.40476) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "so1_5-4vcpu-32gb-intel") + (memory . 32768) + (vcpus . 4) + (disk . 900) + (transfer . 6.0) + (price_monthly . 278) + (price_hourly . 0.41369) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized 1.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 900) (unit . "gib"))))) + ((slug . "g-8vcpu-32gb-intel") + (memory . 32768) + (vcpus . 8) + (disk . 120) + (transfer . 6.0) + (price_monthly . 302) + (price_hourly . 0.4494) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 120) (unit . "gib"))))) + ((slug . "gd-8vcpu-32gb-intel") + (memory . 32768) + (vcpus . 8) + (disk . 240) + (transfer . 6.0) + (price_monthly . 317) + (price_hourly . 0.47173) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel 2x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 240) (unit . "gib"))))) + ((slug . "c5-8vcpu-16gb-intel") + (memory . 16384) + (vcpus . 8) + (disk . 500) + (transfer . 6.0) + (price_monthly . 322) + (price_hourly . 0.47917) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "CPU Optimized - Premium Intel 5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 500) (unit . "gib"))))) + ((slug . "so1_5-4vcpu-32gb") + (memory . 32768) + (vcpus . 4) + (disk . 900) + (transfer . 6.0) + (price_monthly . 326) + (price_hourly . 0.48512) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized 1.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 900) (unit . "gib"))))) + ((slug . "m-8vcpu-64gb") + (memory . 65536) + (vcpus . 8) + (disk . 200) + (transfer . 7.0) + (price_monthly . 336) + (price_hourly . 0.5) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "c-16") + (memory . 32768) + (vcpus . 16) + (disk . 200) + (transfer . 7.0) + (price_monthly . 336) + (price_hourly . 0.5) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "g6_5-8vcpu-32gb") + (memory . 32768) + (vcpus . 8) + (disk . 650) + (transfer . 6.0) + (price_monthly . 362) + (price_hourly . 0.53869) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 6.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 650) (unit . "gib"))))) + ((slug . "g5_5-8vcpu-32gb-intel") + (memory . 32768) + (vcpus . 8) + (disk . 660) + (transfer . 6.0) + (price_monthly . 366.8) + (price_hourly . 0.54583) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose - Premium Intel 5.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 660) (unit . "gib"))))) + ((slug . "c2-16vcpu-32gb") + (memory . 32768) + (vcpus . 16) + (disk . 400) + (transfer . 7.0) + (price_monthly . 376) + (price_hourly . 0.55952) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "m-8vcpu-64gb-intel") + (memory . 65536) + (vcpus . 8) + (disk . 200) + (transfer . 7.0) + (price_monthly . 396) + (price_hourly . 0.58929) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "m3-8vcpu-64gb") + (memory . 65536) + (vcpus . 8) + (disk . 600) + (transfer . 7.0) + (price_monthly . 416) + (price_hourly . 0.61905) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 3x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "c-16-intel") + (memory . 32768) + (vcpus . 16) + (disk . 200) + (transfer . 7.0) + (price_monthly . 437) + (price_hourly . 0.6503) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "m3-8vcpu-64gb-intel") + (memory . 65536) + (vcpus . 8) + (disk . 600) + (transfer . 7.0) + (price_monthly . 440) + (price_hourly . 0.65476) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized 3x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "c2-16vcpu-32gb-intel") + (memory . 32768) + (vcpus . 16) + (disk . 400) + (transfer . 7.0) + (price_monthly . 489) + (price_hourly . 0.72768) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "c5-16vcpu-32gb") + (memory . 32768) + (vcpus . 16) + (disk . 1000) + (transfer . 7.0) + (price_monthly . 496) + (price_hourly . 0.7381) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU Intensive 5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1000) (unit . "gib"))))) + ((slug . "g-16vcpu-64gb") + (memory . 65536) + (vcpus . 16) + (disk . 200) + (transfer . 7.0) + (price_monthly . 504) + (price_hourly . 0.75) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 200) (unit . "gib"))))) + ((slug . "so-8vcpu-64gb-intel") + (memory . 65536) + (vcpus . 8) + (disk . 1200) + (transfer . 7.0) + (price_monthly . 524) + (price_hourly . 0.77976) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "so-8vcpu-64gb") + (memory . 65536) + (vcpus . 8) + (disk . 1200) + (transfer . 7.0) + (price_monthly . 524) + (price_hourly . 0.77976) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "m6-8vcpu-64gb") + (memory . 65536) + (vcpus . 8) + (disk . 1200) + (transfer . 7.0) + (price_monthly . 524) + (price_hourly . 0.77976) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 6x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "gd-16vcpu-64gb") + (memory . 65536) + (vcpus . 16) + (disk . 400) + (transfer . 7.0) + (price_monthly . 544) + (price_hourly . 0.80952) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "so1_5-8vcpu-64gb-intel") + (memory . 65536) + (vcpus . 8) + (disk . 1800) + (transfer . 7.0) + (price_monthly . 556) + (price_hourly . 0.82738) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized 1.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1800) (unit . "gib"))))) + ((slug . "gpu-4000adax1-20gb") + (memory . 32768) + (vcpus . 8) + (disk . 500) + (transfer . 10.0) + (price_monthly . 565.44) + (price_hourly . 0.76) + (regions "tor1") + (available . #t) + (description . "RTX 4000 Ada GPU Droplet - 1X") + (networking_throughput . 10000) + (gpu_info + (count . 1) + (vram (amount . 20) (unit . "gib")) + (model . "nvidia_rtx4000_ada")) + (disk_info ((type . "local") (size (amount . 500) (unit . "gib"))))) + ((slug . "g-16vcpu-64gb-intel") + (memory . 65536) + (vcpus . 16) + (disk . 240) + (transfer . 7.0) + (price_monthly . 605) + (price_hourly . 0.9003) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 240) (unit . "gib"))))) + ((slug . "gd-16vcpu-64gb-intel") + (memory . 65536) + (vcpus . 16) + (disk . 480) + (transfer . 7.0) + (price_monthly . 634) + (price_hourly . 0.94345) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel 2x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 480) (unit . "gib"))))) + ((slug . "c5-16vcpu-32gb-intel") + (memory . 32768) + (vcpus . 16) + (disk . 1000) + (transfer . 7.0) + (price_monthly . 645) + (price_hourly . 0.95982) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "CPU Optimized - Premium Intel 5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1000) (unit . "gib"))))) + ((slug . "so1_5-8vcpu-64gb") + (memory . 65536) + (vcpus . 8) + (disk . 1800) + (transfer . 7.0) + (price_monthly . 652) + (price_hourly . 0.97024) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized 1.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1800) (unit . "gib"))))) + ((slug . "m-16vcpu-128gb") + (memory . 131072) + (vcpus . 16) + (disk . 400) + (transfer . 8.0) + (price_monthly . 672) + (price_hourly . 1) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "c-32") + (memory . 65536) + (vcpus . 32) + (disk . 400) + (transfer . 9.0) + (price_monthly . 672) + (price_hourly . 1) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "g6_5-16vcpu-64gb") + (memory . 65536) + (vcpus . 16) + (disk . 1300) + (transfer . 7.0) + (price_monthly . 724) + (price_hourly . 1.07738) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 6.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1300) (unit . "gib"))))) + ((slug . "g5_5-16vcpu-64gb-intel") + (memory . 65536) + (vcpus . 16) + (disk . 1320) + (transfer . 7.0) + (price_monthly . 734.6) + (price_hourly . 1.09315) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose - Premium Intel 5.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1320) (unit . "gib"))))) + ((slug . "c2-32vcpu-64gb") + (memory . 65536) + (vcpus . 32) + (disk . 800) + (transfer . 9.0) + (price_monthly . 752) + (price_hourly . 1.11905) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "CPU-Optimized 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 800) (unit . "gib"))))) + ((slug . "m-16vcpu-128gb-intel") + (memory . 131072) + (vcpus . 16) + (disk . 400) + (transfer . 8.0) + (price_monthly . 792) + (price_hourly . 1.17857) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "m3-16vcpu-128gb") + (memory . 131072) + (vcpus . 16) + (disk . 1200) + (transfer . 8.0) + (price_monthly . 832) + (price_hourly . 1.2381) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 3x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "c-32-intel") + (memory . 65536) + (vcpus . 32) + (disk . 400) + (transfer . 9.0) + (price_monthly . 874) + (price_hourly . 1.3006) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "m3-16vcpu-128gb-intel") + (memory . 131072) + (vcpus . 16) + (disk . 1200) + (transfer . 8.0) + (price_monthly . 880) + (price_hourly . 1.30952) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized 3x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "c2-32vcpu-64gb-intel") + (memory . 65536) + (vcpus . 32) + (disk . 800) + (transfer . 9.0) + (price_monthly . 978) + (price_hourly . 1.45536) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 800) (unit . "gib"))))) + ((slug . "c5-32vcpu-64gb") + (memory . 65536) + (vcpus . 32) + (disk . 2000) + (transfer . 9.0) + (price_monthly . 992) + (price_hourly . 1.47619) + (regions "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1") + (available . #t) + (description . "CPU Intensive 5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 2000) (unit . "gib"))))) + ((slug . "c-48") + (memory . 98304) + (vcpus . 48) + (disk . 600) + (transfer . 11.0) + (price_monthly . 1008) + (price_hourly . 1.5) + (regions "ams3" "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1") + (available . #t) + (description . "CPU-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "m-24vcpu-192gb") + (memory . 196608) + (vcpus . 24) + (disk . 600) + (transfer . 9.0) + (price_monthly . 1008) + (price_hourly . 1.5) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "g-32vcpu-128gb") + (memory . 131072) + (vcpus . 32) + (disk . 400) + (transfer . 8.0) + (price_monthly . 1008) + (price_hourly . 1.5) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 400) (unit . "gib"))))) + ((slug . "so-16vcpu-128gb-intel") + (memory . 131072) + (vcpus . 16) + (disk . 2400) + (transfer . 8.0) + (price_monthly . 1048) + (price_hourly . 1.55952) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 2400) (unit . "gib"))))) + ((slug . "so-16vcpu-128gb") + (memory . 131072) + (vcpus . 16) + (disk . 2400) + (transfer . 8.0) + (price_monthly . 1048) + (price_hourly . 1.55952) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 2400) (unit . "gib"))))) + ((slug . "m6-16vcpu-128gb") + (memory . 131072) + (vcpus . 16) + (disk . 2400) + (transfer . 8.0) + (price_monthly . 1048) + (price_hourly . 1.55952) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 6x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 2400) (unit . "gib"))))) + ((slug . "gd-32vcpu-128gb") + (memory . 131072) + (vcpus . 32) + (disk . 800) + (transfer . 8.0) + (price_monthly . 1088) + (price_hourly . 1.61905) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "General Purpose 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 800) (unit . "gib"))))) + ((slug . "so1_5-16vcpu-128gb-intel") + (memory . 131072) + (vcpus . 16) + (disk . 3600) + (transfer . 8.0) + (price_monthly . 1112) + (price_hourly . 1.65476) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized 1.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 3600) (unit . "gib"))))) + ((slug . "c2-48vcpu-96gb") + (memory . 98304) + (vcpus . 48) + (disk . 1200) + (transfer . 11.0) + (price_monthly . 1128) + (price_hourly . 1.67857) + (regions "ams3" "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1") + (available . #t) + (description . "CPU-Optimized 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "gpu-l40sx1-48gb") + (memory . 65536) + (vcpus . 8) + (disk . 500) + (transfer . 10.0) + (price_monthly . 1168.08) + (price_hourly . 1.57) + (regions "tor1") + (available . #t) + (description . "L40S GPU Droplet - 1X") + (networking_throughput . 10000) + (gpu_info + (count . 1) + (vram (amount . 48) (unit . "gib")) + (model . "nvidia_l40s")) + (disk_info ((type . "local") (size (amount . 500) (unit . "gib"))))) + ((slug . "gpu-6000adax1-48gb") + (memory . 65536) + (vcpus . 8) + (disk . 500) + (transfer . 10.0) + (price_monthly . 1168.08) + (price_hourly . 1.57) + (regions "tor1") + (available . #t) + (description . "RTX 6000 Ada GPU Droplet - 1X") + (networking_throughput . 10000) + (gpu_info + (count . 1) + (vram (amount . 48) (unit . "gib")) + (model . "nvidia_rtx6000_ada")) + (disk_info ((type . "local") (size (amount . 500) (unit . "gib"))))) + ((slug . "m-24vcpu-192gb-intel") + (memory . 196608) + (vcpus . 24) + (disk . 600) + (transfer . 9.0) + (price_monthly . 1188) + (price_hourly . 1.76786) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "g-32vcpu-128gb-intel") + (memory . 131072) + (vcpus . 32) + (disk . 480) + (transfer . 8.0) + (price_monthly . 1210) + (price_hourly . 1.8006) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 480) (unit . "gib"))))) + ((slug . "m3-24vcpu-192gb") + (memory . 196608) + (vcpus . 24) + (disk . 1800) + (transfer . 9.0) + (price_monthly . 1248) + (price_hourly . 1.85714) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 3x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1800) (unit . "gib"))))) + ((slug . "g-40vcpu-160gb") + (memory . 163840) + (vcpus . 40) + (disk . 500) + (transfer . 9.0) + (price_monthly . 1260) + (price_hourly . 1.875) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1") + (available . #t) + (description . "General Purpose") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 500) (unit . "gib"))))) + ((slug . "gd-32vcpu-128gb-intel") + (memory . 131072) + (vcpus . 32) + (disk . 960) + (transfer . 8.0) + (price_monthly . 1268) + (price_hourly . 1.8869) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel 2x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 960) (unit . "gib"))))) + ((slug . "c5-32vcpu-64gb-intel") + (memory . 65536) + (vcpus . 32) + (disk . 2000) + (transfer . 9.0) + (price_monthly . 1290) + (price_hourly . 1.91964) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "CPU Optimized - Premium Intel 5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 2000) (unit . "gib"))))) + ((slug . "so1_5-16vcpu-128gb") + (memory . 131072) + (vcpus . 16) + (disk . 3600) + (transfer . 8.0) + (price_monthly . 1304) + (price_hourly . 1.94048) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized 1.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 3600) (unit . "gib"))))) + ((slug . "c-48-intel") + (memory . 98304) + (vcpus . 48) + (disk . 600) + (transfer . 11.0) + (price_monthly . 1310) + (price_hourly . 1.9494) + (regions "blr1" "lon1" "nyc1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 600) (unit . "gib"))))) + ((slug . "m3-24vcpu-192gb-intel") + (memory . 196608) + (vcpus . 24) + (disk . 1800) + (transfer . 9.0) + (price_monthly . 1320) + (price_hourly . 1.96429) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized 3x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1800) (unit . "gib"))))) + ((slug . "m-32vcpu-256gb") + (memory . 262144) + (vcpus . 32) + (disk . 800) + (transfer . 10.0) + (price_monthly . 1344) + (price_hourly . 2) + (regions "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1" "tor1") + (available . #t) + (description . "Memory-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 800) (unit . "gib"))))) + ((slug . "gd-40vcpu-160gb") + (memory . 163840) + (vcpus . 40) + (disk . 1000) + (transfer . 9.0) + (price_monthly . 1360) + (price_hourly . 2.02381) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1") + (available . #t) + (description . "General Purpose 2x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 1000) (unit . "gib"))))) + ((slug . "g6_5-32vcpu-128gb") + (memory . 131072) + (vcpus . 32) + (disk . 2600) + (transfer . 8.0) + (price_monthly . 1448) + (price_hourly . 2.15476) + (regions "ams3" "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1" "tor1") + (available . #t) + (description . "General Purpose 6.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 2600) (unit . "gib"))))) + ((slug . "c2-48vcpu-96gb-intel") + (memory . 98304) + (vcpus . 48) + (disk . 1200) + (transfer . 11.0) + (price_monthly . 1466) + (price_hourly . 2.18155) + (regions "blr1" "lon1" "nyc1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "g5_5-32vcpu-128gb-intel") + (memory . 131072) + (vcpus . 32) + (disk . 2640) + (transfer . 8.0) + (price_monthly . 1469.2) + (price_hourly . 2.18631) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose - Premium Intel 5.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 2640) (unit . "gib"))))) + ((slug . "gpu-mi300x1-192gb") + (memory . 245760) + (vcpus . 20) + (disk . 720) + (transfer . 15.0) + (price_monthly . 1480.56) + (price_hourly . 1.99) + (regions) + (available . #t) + (description . "AMD MI300X - 1X") + (networking_throughput . 10000) + (gpu_info + (count . 1) + (vram (amount . 192) (unit . "gib")) + (model . "amd_mi300x")) + (disk_info + ((type . "local") (size (amount . 720) (unit . "gib"))) + ((type . "scratch") (size (amount . 5120) (unit . "gib"))))) + ((slug . "c5-48vcpu-96gb") + (memory . 98304) + (vcpus . 48) + (disk . 3000) + (transfer . 11.0) + (price_monthly . 1488) + (price_hourly . 2.21429) + (regions "blr1" "lon1" "sgp1" "syd1") + (available . #t) + (description . "CPU Intensive 5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 3000) (unit . "gib"))))) + ((slug . "so-24vcpu-192gb-intel") + (memory . 196608) + (vcpus . 24) + (disk . 3600) + (transfer . 9.0) + (price_monthly . 1572) + (price_hourly . 2.33929) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 3600) (unit . "gib"))))) + ((slug . "so-24vcpu-192gb") + (memory . 196608) + (vcpus . 24) + (disk . 3600) + (transfer . 9.0) + (price_monthly . 1572) + (price_hourly . 2.33929) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 3600) (unit . "gib"))))) + ((slug . "m6-24vcpu-192gb") + (memory . 196608) + (vcpus . 24) + (disk . 3600) + (transfer . 9.0) + (price_monthly . 1572) + (price_hourly . 2.33929) + (regions + "ams3" + "blr1" + "fra1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Memory-Optimized 6x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 3600) (unit . "gib"))))) + ((slug . "m-32vcpu-256gb-intel") + (memory . 262144) + (vcpus . 32) + (disk . 800) + (transfer . 10.0) + (price_monthly . 1584) + (price_hourly . 2.35714) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 800) (unit . "gib"))))) + ((slug . "c-60-intel") + (memory . 122880) + (vcpus . 60) + (disk . 750) + (transfer . 12.0) + (price_monthly . 1639) + (price_hourly . 2.43899) + (regions "blr1" "lon1" "nyc1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 750) (unit . "gib"))))) + ((slug . "m3-32vcpu-256gb") + (memory . 262144) + (vcpus . 32) + (disk . 2400) + (transfer . 10.0) + (price_monthly . 1664) + (price_hourly . 2.47619) + (regions "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1" "tor1") + (available . #t) + (description . "Memory-Optimized 3x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 2400) (unit . "gib"))))) + ((slug . "so1_5-24vcpu-192gb-intel") + (memory . 196608) + (vcpus . 24) + (disk . 5400) + (transfer . 9.0) + (price_monthly . 1668) + (price_hourly . 2.48214) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized 1.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 5400) (unit . "gib"))))) + ((slug . "m3-32vcpu-256gb-intel") + (memory . 262144) + (vcpus . 32) + (disk . 2400) + (transfer . 10.0) + (price_monthly . 1760) + (price_hourly . 2.61905) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized 3x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 2400) (unit . "gib"))))) + ((slug . "g6_5-40vcpu-160gb") + (memory . 163840) + (vcpus . 40) + (disk . 3250) + (transfer . 9.0) + (price_monthly . 1810) + (price_hourly . 2.69345) + (regions "blr1" "lon1" "sfo3" "sgp1" "syd1") + (available . #t) + (description . "General Purpose 6.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 3250) (unit . "gib"))))) + ((slug . "g-48vcpu-192gb-intel") + (memory . 196608) + (vcpus . 48) + (disk . 720) + (transfer . 9.0) + (price_monthly . 1814) + (price_hourly . 2.6994) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 720) (unit . "gib"))))) + ((slug . "c2-60vcpu-120gb-intel") + (memory . 122880) + (vcpus . 60) + (disk . 1500) + (transfer . 12.0) + (price_monthly . 1834) + (price_hourly . 2.72917) + (regions "blr1" "lon1" "nyc1" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1500) (unit . "gib"))))) + ((slug . "gd-48vcpu-192gb-intel") + (memory . 196608) + (vcpus . 48) + (disk . 1440) + (transfer . 11.0) + (price_monthly . 1901) + (price_hourly . 2.82887) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel 2x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1440) (unit . "gib"))))) + ((slug . "c5-48vcpu-96gb-intel") + (memory . 98304) + (vcpus . 48) + (disk . 3000) + (transfer . 11.0) + (price_monthly . 1934) + (price_hourly . 2.87798) + (regions "blr1" "lon1" "nyc1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "CPU Optimized - Premium Intel 5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 3000) (unit . "gib"))))) + ((slug . "so1_5-24vcpu-192gb") + (memory . 196608) + (vcpus . 24) + (disk . 5400) + (transfer . 9.0) + (price_monthly . 1956) + (price_hourly . 2.91071) + (regions + "blr1" + "lon1" + "nyc1" + "nyc2" + "sfo2" + "sfo3" + "sgp1" + "syd1" + "tor1") + (available . #t) + (description . "Storage-Optimized 1.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 5400) (unit . "gib"))))) + ((slug . "so-32vcpu-256gb-intel") + (memory . 262144) + (vcpus . 32) + (disk . 4800) + (transfer . 10.0) + (price_monthly . 2096) + (price_hourly . 3.11905) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 4800) (unit . "gib"))))) + ((slug . "so-32vcpu-256gb") + (memory . 262144) + (vcpus . 32) + (disk . 4800) + (transfer . 10.0) + (price_monthly . 2096) + (price_hourly . 3.11905) + (regions "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1" "tor1") + (available . #t) + (description . "Storage-Optimized") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 4800) (unit . "gib"))))) + ((slug . "m6-32vcpu-256gb") + (memory . 262144) + (vcpus . 32) + (disk . 4800) + (transfer . 10.0) + (price_monthly . 2096) + (price_hourly . 3.11905) + (regions "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1" "tor1") + (available . #t) + (description . "Memory-Optimized 6x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 4800) (unit . "gib"))))) + ((slug . "g5_5-48vcpu-192gb-intel") + (memory . 196608) + (vcpus . 48) + (disk . 3960) + (transfer . 9.0) + (price_monthly . 2202.8) + (price_hourly . 3.27798) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose - Premium Intel 5.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 3960) (unit . "gib"))))) + ((slug . "so1_5-32vcpu-256gb-intel") + (memory . 262144) + (vcpus . 32) + (disk . 7200) + (transfer . 10.0) + (price_monthly . 2224) + (price_hourly . 3.30952) + (regions "blr1" "lon1" "nyc1" "nyc2" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized 1.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 7200) (unit . "gib"))))) + ((slug . "g-60vcpu-240gb-intel") + (memory . 245760) + (vcpus . 60) + (disk . 900) + (transfer . 10.0) + (price_monthly . 2269) + (price_hourly . 3.37649) + (regions "lon1" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 900) (unit . "gib"))))) + ((slug . "m-48vcpu-384gb-intel") + (memory . 393216) + (vcpus . 48) + (disk . 1200) + (transfer . 11.0) + (price_monthly . 2376) + (price_hourly . 3.53571) + (regions "blr1" "lon1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1200) (unit . "gib"))))) + ((slug . "gd-60vcpu-240gb-intel") + (memory . 245760) + (vcpus . 60) + (disk . 1800) + (transfer . 10.0) + (price_monthly . 2378) + (price_hourly . 3.53869) + (regions "lon1" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose — Premium Intel 2x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 1800) (unit . "gib"))))) + ((slug . "c5-60vcpu-120gb-intel") + (memory . 122880) + (vcpus . 60) + (disk . 3750) + (transfer . 12.0) + (price_monthly . 2419) + (price_hourly . 3.5997) + (regions "blr1" "lon1" "nyc1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "CPU Optimized - Premium Intel 5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 3750) (unit . "gib"))))) + ((slug . "gpu-h100x1-80gb") + (memory . 245760) + (vcpus . 20) + (disk . 720) + (transfer . 15.0) + (price_monthly . 2522.16) + (price_hourly . 3.39) + (regions "ams3" "nyc2" "tor1") + (available . #t) + (description . "H100 GPU - 1X") + (networking_throughput . 10000) + (gpu_info + (count . 1) + (vram (amount . 80) (unit . "gib")) + (model . "nvidia_h100")) + (disk_info + ((type . "local") (size (amount . 720) (unit . "gib"))) + ((type . "scratch") (size (amount . 5120) (unit . "gib"))))) + ((slug . "gpu-h200x1-141gb") + (memory . 245760) + (vcpus . 24) + (disk . 720) + (transfer . 15.0) + (price_monthly . 2559.36) + (price_hourly . 3.44) + (regions "nyc2") + (available . #t) + (description . "Nvidia H200 - 1X") + (networking_throughput . 10000) + (gpu_info + (count . 1) + (vram (amount . 141) (unit . "gib")) + (model . "nvidia_h200")) + (disk_info + ((type . "local") (size (amount . 720) (unit . "gib"))) + ((type . "scratch") (size (amount . 5120) (unit . "gib"))))) + ((slug . "so1_5-32vcpu-256gb") + (memory . 262144) + (vcpus . 32) + (disk . 7200) + (transfer . 10.0) + (price_monthly . 2608) + (price_hourly . 3.88095) + (regions "blr1" "lon1" "sfo2" "sfo3" "sgp1" "syd1" "tor1") + (available . #t) + (description . "Storage-Optimized 1.5x SSD") + (networking_throughput . 2000) + (disk_info ((type . "local") (size (amount . 7200) (unit . "gib"))))) + ((slug . "m3-48vcpu-384gb-intel") + (memory . 393216) + (vcpus . 48) + (disk . 3600) + (transfer . 11.0) + (price_monthly . 2640) + (price_hourly . 3.92857) + (regions "blr1" "lon1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Memory-Optimized 3x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 3600) (unit . "gib"))))) + ((slug . "g5_5-60vcpu-240gb-intel") + (memory . 245760) + (vcpus . 60) + (disk . 4950) + (transfer . 10.0) + (price_monthly . 2755) + (price_hourly . 4.0997) + (regions "blr1" "lon1" "nyc1" "nyc3" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "General Purpose - Premium Intel 5.5x SSD") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 4950) (unit . "gib"))))) + ((slug . "so-48vcpu-384gb-intel") + (memory . 393216) + (vcpus . 48) + (disk . 7000) + (transfer . 11.0) + (price_monthly . 3144) + (price_hourly . 4.67857) + (regions "lon1" "sfo2" "sfo3" "tor1") + (available . #t) + (description . "Premium Storage-Optimized") + (networking_throughput . 10000) + (disk_info ((type . "local") (size (amount . 7000) (unit . "gib"))))) + ((slug . "gpu-mi300x8-1536gb") + (memory . 1966080) + (vcpus . 160) + (disk . 2046) + (transfer . 60.0) + (price_monthly . 11844.48) + (price_hourly . 15.92) + (regions) + (available . #t) + (description . "AMD MI300X - 8X") + (networking_throughput . 10000) + (gpu_info + (count . 8) + (vram (amount . 1536) (unit . "gib")) + (model . "amd_mi300x")) + (disk_info + ((type . "local") (size (amount . 2046) (unit . "gib"))) + ((type . "scratch") (size (amount . 40960) (unit . "gib"))))) + ((slug . "gpu-h100x8-640gb") + (memory . 1966080) + (vcpus . 160) + (disk . 2046) + (transfer . 60.0) + (price_monthly . 17796.48) + (price_hourly . 23.92) + (regions) + (available . #t) + (description . "H100 GPU - 8X") + (networking_throughput . 10000) + (gpu_info + (count . 8) + (vram (amount . 640) (unit . "gib")) + (model . "nvidia_h100")) + (disk_info + ((type . "local") (size (amount . 2046) (unit . "gib"))) + ((type . "scratch") (size (amount . 40960) (unit . "gib"))))) + ((slug . "gpu-h200x8-1128gb") + (memory . 1966080) + (vcpus . 192) + (disk . 2046) + (transfer . 60.0) + (price_monthly . 20474.88) + (price_hourly . 27.52) + (regions "nyc2") + (available . #t) + (description . "Nvidia H200 - 8X") + (networking_throughput . 10000) + (gpu_info + (count . 8) + (vram (amount . 1128) (unit . "gib")) + (model . "nvidia_h200")) + (disk_info + ((type . "local") (size (amount . 2046) (unit . "gib"))) + ((type . "scratch") (size (amount . 40960) (unit . "gib")))))) + (links) + (meta (total . 172)))) diff --git a/src/nassella.scm b/src/nassella.scm index b5a90a2..de2a6dd 100644 --- a/src/nassella.scm +++ b/src/nassella.scm @@ -1,4 +1,5 @@ (load "db.scm") +(load "mocks.scm") (import (chicken string) (chicken port) @@ -14,7 +15,7 @@ html-widgets sxml-transforms - (prefix schematra schematra:) + schematra schematra-body-parser schematra-session uri-common @@ -24,7 +25,11 @@ nassella-db sql-null) -(schematra:use-middleware! (body-parser-middleware)) +(define app (schematra/make-app)) + +(with-schematra-app app + (lambda () + (use-middleware! (body-parser-middleware)))) (define *global-css-reset* "/* @@ -261,31 +266,45 @@ h1, h2, h3, h4, h5, h6 { val)) (resolved-style-path-value *style-tokens* path-or-symbol))) + +(define test-mode (make-parameter #f)) +(define last-request-body-sxml (make-parameter '())) +(define last-request-body-widget-sxml (make-parameter '())) + (define (widget-sxml->html sxml-head sxml-body) (let ((sxml-head-out (widget->sxml-and-css sxml-head))) (receive (sxml-body-out css-list) (widget->sxml-and-css sxml-body) + (when test-mode + (last-request-body-widget-sxml sxml-body) + (last-request-body-sxml (widget->sxml sxml-body))) (print "") (SXML->HTML `(html (head (style ,(apply string-append (cons *global-css-reset* css-list))) ,@sxml-head-out) ,sxml-body-out))))) -(schematra:use-middleware! (session-middleware "your-secret-key-here")) +(with-schematra-app app + (lambda () + (use-middleware! (session-middleware "your-secret-key-here")))) ;; TODO generate better one + +(define test-user-id (make-parameter 7)) +(define (session-user-id) + (or (session-get "user-id") (test-user-id))) -(define-syntax get +(define-syntax get/widgets (syntax-rules () ((_ (path) body ...) - (schematra:get (path) - (with-output-to-string - (lambda () - (widget-sxml->html - '((meta (@ (name "viewport") (content "width=device-width")))) - (begin - ;; TODO remove once sessions are integrated - (session-set! "user-id" 7) - (session-set! "username" "me") - body ...)))))))) + (get path + (with-output-to-string + (lambda () + (widget-sxml->html + '((meta (@ (name "viewport") (content "width=device-width")))) + (begin + ;; TODO remove once sessions are integrated + (session-set! "user-id" (test-user-id)) + (session-set! "username" "me") + body ...)))))))) (define-widget (Container ((max-width ($ 'width.main.max)) (style '())) contents) `(div (@ (data-name "Container") @@ -479,9 +498,87 @@ h1, h2, h3, h4, h5, h6 { "Back") (Button ,submit-button))) -(get +;; Parsing JSON arrays as lists instead of vectors +(define array-as-list-parser + (cons 'array (lambda (x) x))) + +(json-parsers (cons array-as-list-parser (json-parsers))) + +(define (get-digital-ocean-regions api-token) + (filter + (lambda (r) + (alist-ref 'available r)) + (if (test-mode) + *digital-ocean-regions-response* + (alist-ref + 'regions + (let* ((uri (uri-reference "https://api.digitalocean.com/v2/regions")) + (req (make-request method: 'GET + uri: uri + headers: (headers `((content-type application/json) + (Authorization ,(conc "Bearer " api-token))))))) + (with-input-from-request req #f read-json)))))) + +(define (get-digital-ocean-sizes api-token) + (filter + (lambda (r) + (alist-ref 'available r)) + (alist-ref + 'sizes + (if (test-mode) + *digital-ocean-sizes-response* + (let* ((uri (uri-reference "https://api.digitalocean.com/v2/sizes?per_page=200")) + (req (make-request method: 'GET + uri: uri + headers: (headers `((content-type application/json) + (Authorization ,(conc "Bearer " api-token))))))) + (with-input-from-request req #f read-json)))))) + +(define (deployment-directory user-id) + (string-append "deploy-" (number->string user-id))) + +(define (setup-deploy-files dir state state-backup) + (when (directory-exists? dir) + (delete-directory dir #t)) + (create-directory dir) + (process-wait (process-run (string-append "tar -xf nassella-latest.tar -C " dir))) + (create-directory (string-append dir "/config")) + (copy-file "../config/ssh-keys" (string-append dir "/config/ssh-keys")) ;; TODO remove + (with-output-to-file (string-append dir "/terraform.tfstate") (lambda () (write-string state))) + (with-output-to-file (string-append dir "/terraform.tfstate.backup") (lambda () (write-string state-backup)))) + +(define (parse-deployment-log log) + (define (search complete in-progress) + (cond ((irregex-search complete log) + 'complete) + ((irregex-search in-progress log) + 'in-progress) + (else 'queued))) + `((generate-configs . ,(search "terraform apply" "NASSELLA_CONFIG: start")) + (custom-image . ,(search "custom_image.flatcar: Modifications complete" "custom_image.flatcar: Modifying")) + (machine-create . ,(search "droplet.machine: Creation complete" "droplet.machine: Creating...")) + (machine-destroy . ,(search "droplet.machine: Destruction complete" + '(: "droplet.machine (deposed object " (* alphanum) "): Destroying..."))))) + +(define (write-config-entry name value) + (display name) + (display "=\"") + (display value) + (print "\"")) + +(define (progress-status->text status) + (case status + ((queued) "queued") + ((in-progress) "in progress") + ((complete) "complete") + ((failed) "failed"))) + +(with-schematra-app app + (lambda () + +(get/widgets ("/config/wizard/services") - (let ((config (with-db/transaction (lambda (db) (get-user-service-config db (session-get "user-id")))))) + (let ((config (with-db/transaction (lambda (db) (get-user-service-config db (session-user-id)))))) `(App (Configuration-Wizard (@ (step "Services")) @@ -504,22 +601,22 @@ h1, h2, h3, h4, h5, h6 { (Field (@ (name "backblaze-bucket-url") (label ("Bucket URL")) (value ,(alist-ref 'backblaze-bucket-url config))))) (Form-Nav))))))) -(schematra:post ("/config/wizard/services-submit") +(post "/config/wizard/services-submit" (with-db/transaction (lambda (db) (update-user-service-config db - (session-get "user-id") - `((cloudflare-api-token . ,(alist-ref 'cloudflare-api-token (schematra:current-params))) - (cloudflare-account-id . ,(alist-ref 'cloudflare-account-id (schematra:current-params))) - (cloudflare-zone-id . ,(alist-ref 'cloudflare-zone-id (schematra:current-params))) - (digitalocean-api-token . ,(alist-ref 'digitalocean-api-token (schematra:current-params))) - (backblaze-application-key . ,(alist-ref 'backblaze-application-key (schematra:current-params))) - (backblaze-key-id . ,(alist-ref 'backblaze-key-id (schematra:current-params))) - (backblaze-bucket-url . ,(alist-ref 'backblaze-bucket-url (schematra:current-params))))))) - (schematra:redirect "/config/wizard/services-success")) - -(get + (session-user-id) + `((cloudflare-api-token . ,(alist-ref 'cloudflare-api-token (current-params))) + (cloudflare-account-id . ,(alist-ref 'cloudflare-account-id (current-params))) + (cloudflare-zone-id . ,(alist-ref 'cloudflare-zone-id (current-params))) + (digitalocean-api-token . ,(alist-ref 'digitalocean-api-token (current-params))) + (backblaze-application-key . ,(alist-ref 'backblaze-application-key (current-params))) + (backblaze-key-id . ,(alist-ref 'backblaze-key-id (current-params))) + (backblaze-bucket-url . ,(alist-ref 'backblaze-bucket-url (current-params))))))) + (redirect "/config/wizard/services-success")) + +(get/widgets ("/config/wizard/services-success") `(App (Configuration-Wizard @@ -541,7 +638,7 @@ h1, h2, h3, h4, h5, h6 { (p "Your Backblaze account was successfully connected!")) (Form-Nav (@ (back-to "/config/wizard/services")))))))) -(get +(get/widgets ("/config/wizard/apps") (let ((results (with-db/transaction @@ -549,8 +646,8 @@ h1, h2, h3, h4, h5, h6 { `((selected-apps . ,(map car (filter cdr - (get-user-selected-apps db (session-get "user-id"))))) - (app-config . ,(get-user-app-config db (session-get "user-id")))))))) + (get-user-selected-apps db (session-user-id))))) + (app-config . ,(get-user-app-config db (session-user-id)))))))) `(App (Configuration-Wizard (@ (step "Apps")) @@ -568,18 +665,22 @@ h1, h2, h3, h4, h5, h6 { (Field (@ (name "log-viewer") (type "checkbox") (label ("Log Viewer")) (checked #t) (disabled "disabled")))) (Form-Nav (@ (back-to "/config/wizard/services-success"))))))))) -(schematra:post ("/config/wizard/apps-submit") +(post "/config/wizard/apps-submit" (with-db/transaction (lambda (db) (update-user-selected-apps db - (session-get "user-id") - `((wg-easy . ,(or (and (alist-ref 'wg-easy (schematra:current-params)) "0.0") (sql-null))) - (nextcloud . ,(or (and (alist-ref 'nextcloud (schematra:current-params)) "0.0") (sql-null))))) - (update-root-domain db (session-get "user-id") (alist-ref 'root-domain (schematra:current-params))))) - (schematra:redirect "/config/wizard/apps2")) - -(get + (session-user-id) + `((wg-easy . ,(or (and (alist-ref 'wg-easy (current-params)) "0.0") (sql-null))) + (nextcloud . ,(or (and (alist-ref 'nextcloud (current-params)) "0.0") (sql-null))))) + (update-root-domain db + (session-user-id) + (alist-ref 'root-domain (current-params))))) + (redirect "/config/wizard/apps2")) + +;; TODO should this even allow changing existing username/passwords like for db? +;; wouldn't that break the db connection and you would lose data? +(get/widgets ("/config/wizard/apps2") (let* ((results (with-db/transaction @@ -587,10 +688,10 @@ h1, h2, h3, h4, h5, h6 { `((selected-apps . ,(map car (filter cdr - (get-user-selected-apps db (session-get "user-id"))))) - (app-config . ,(get-user-app-config db (session-get "user-id"))))))) + (get-user-selected-apps db (session-user-id))))) + (app-config . ,(get-user-app-config db (session-user-id))))))) (selected-apps (alist-ref 'selected-apps results)) - (app-config (alist-ref 'app-config results))) + (app-config (alist-ref 'config (alist-ref 'app-config results)))) `(App (Configuration-Wizard (@ (step "Apps")) @@ -622,58 +723,26 @@ h1, h2, h3, h4, h5, h6 { (value ,(alist-ref 'password (alist-ref 'log-viewer app-config eq? '()) eq? ""))))) (Form-Nav (@ (back-to "/config/wizard/apps"))))))))) -(schematra:post ("/config/wizard/apps2-submit") +(post "/config/wizard/apps2-submit" (with-db/transaction (lambda (db) (update-user-app-config db - (session-get "user-id") - `((wg-easy . ((subdomain . ,(alist-ref 'wg-easy-subdomain (schematra:current-params))))) - (nextcloud . ((subdomain . ,(alist-ref 'nextcloud-subdomain (schematra:current-params))) - (admin-user . ,(alist-ref 'nextcloud-admin-user (schematra:current-params))) - (admin-password . ,(alist-ref 'nextcloud-admin-password (schematra:current-params))))) - (log-viewer . ((subdomain . ,(alist-ref 'log-viewer-subdomain (schematra:current-params))) - (user . ,(alist-ref 'log-viewer-user (schematra:current-params))) - (password . ,(alist-ref 'log-viewer-password (schematra:current-params))))))))) - (schematra:redirect "/config/wizard/machine")) - -;; Parsing JSON arrays as lists instead of vectors -(define array-as-list-parser - (cons 'array (lambda (x) x))) - -(json-parsers (cons array-as-list-parser (json-parsers))) - -(define (get-digital-ocean-regions api-token) - (filter - (lambda (r) - (alist-ref 'available r)) - (alist-ref - 'regions - (let* ((uri (uri-reference "https://api.digitalocean.com/v2/regions")) - (req (make-request method: 'GET - uri: uri - headers: (headers `((content-type application/json) - (Authorization ,(conc "Bearer " api-token))))))) - (with-input-from-request req #f read-json))))) - -(define (get-digital-ocean-sizes api-token) - (filter - (lambda (r) - (alist-ref 'available r)) - (alist-ref - 'sizes - (let* ((uri (uri-reference "https://api.digitalocean.com/v2/sizes?per_page=200")) - (req (make-request method: 'GET - uri: uri - headers: (headers `((content-type application/json) - (Authorization ,(conc "Bearer " api-token))))))) - (with-input-from-request req #f read-json))))) - -(get + (session-user-id) + `((wg-easy . ((subdomain . ,(alist-ref 'wg-easy-subdomain (current-params))))) + (nextcloud . ((subdomain . ,(alist-ref 'nextcloud-subdomain (current-params))) + (admin-user . ,(alist-ref 'nextcloud-admin-user (current-params))) + (admin-password . ,(alist-ref 'nextcloud-admin-password (current-params))))) + (log-viewer . ((subdomain . ,(alist-ref 'log-viewer-subdomain (current-params))) + (user . ,(alist-ref 'log-viewer-user (current-params))) + (password . ,(alist-ref 'log-viewer-password (current-params))))))))) + (redirect "/config/wizard/machine")) + +(get/widgets ("/config/wizard/machine") (let ((config (with-db/transaction (lambda (db) - (get-user-service-config db (session-get "user-id")))))) + (get-user-service-config db (session-user-id)))))) `(App (Configuration-Wizard (@ (step "Machine")) @@ -690,20 +759,20 @@ h1, h2, h3, h4, h5, h6 { (get-digital-ocean-regions (alist-ref 'digitalocean-api-token config))))) (Form-Nav (@ (back-to ,(conc "/config/wizard/apps2")))))))))) -(schematra:post ("/config/wizard/machine-submit") +(post "/config/wizard/machine-submit" (with-db/transaction (lambda (db) (update-user-service-config db - (session-get "user-id") - `((digitalocean-region . ,(alist-ref 'region (schematra:current-params))))))) - (schematra:redirect "/config/wizard/machine2")) + (session-user-id) + `((digitalocean-region . ,(alist-ref 'region (current-params))))))) + (redirect "/config/wizard/machine2")) -(get +(get/widgets ("/config/wizard/machine2") (let* ((config (with-db/transaction (lambda (db) - (get-user-service-config db (session-get "user-id"))))) + (get-user-service-config db (session-user-id))))) (region (alist-ref 'digitalocean-region config)) (all-sizes (get-digital-ocean-sizes (alist-ref 'digitalocean-api-token config))) (sizes (filter (lambda (s) (member region (alist-ref 'regions s))) all-sizes))) @@ -727,16 +796,16 @@ h1, h2, h3, h4, h5, h6 { sizes))) (Form-Nav (@ (back-to ,(conc "/config/wizard/machine")))))))))) -(schematra:post ("/config/wizard/machine2-submit") +(post "/config/wizard/machine2-submit" (with-db/transaction (lambda (db) (update-user-service-config db - (session-get "user-id") - `((digitalocean-size . ,(alist-ref 'size (schematra:current-params))))))) - (schematra:redirect "/config/wizard/review")) + (session-user-id) + `((digitalocean-size . ,(alist-ref 'size (current-params))))))) + (redirect "/config/wizard/review")) -(get +(get/widgets ("/config/wizard/review") (let* ((results (with-db/transaction @@ -744,9 +813,9 @@ h1, h2, h3, h4, h5, h6 { `((selected-apps . ,(map car (filter cdr - (get-user-selected-apps db (session-get "user-id"))))) - (app-config . ,(get-user-app-config db (session-get "user-id"))) - (service-config . ,(get-user-service-config db (session-get "user-id"))))))) + (get-user-selected-apps db (session-user-id))))) + (app-config . ,(get-user-app-config db (session-user-id))) + (service-config . ,(get-user-service-config db (session-user-id))))))) (selected-apps (cons 'log-viewer (alist-ref 'selected-apps results))) (app-config (alist-ref 'app-config results)) (config (alist-ref 'config app-config)) @@ -771,56 +840,24 @@ h1, h2, h3, h4, h5, h6 { (VStack (Form-Nav (@ (back-to ,(conc "/config/wizard/machine2")) (submit-button "Launch"))))))))) -(define (deployment-directory user-id) - (string-append "deploy-" (number->string user-id))) - -(define (setup-deploy-files dir state state-backup) - (when (directory-exists? dir) - (delete-directory dir #t)) - (create-directory dir) - (process-wait (process-run (string-append "tar -xf nassella-latest.tar -C " dir))) - (create-directory (string-append dir "/config")) - (copy-file "../config/ssh-keys" (string-append dir "/config/ssh-keys")) ;; TODO remove - (with-output-to-file (string-append dir "/terraform.tfstate") (lambda () (write-string state))) - (with-output-to-file (string-append dir "/terraform.tfstate.backup") (lambda () (write-string state-backup)))) - -(define (parse-deployment-log log) - (define (search complete in-progress) - (cond ((irregex-search complete log) - 'complete) - ((irregex-search in-progress log) - 'in-progress) - (else 'queued))) - `((generate-configs . ,(search "terraform apply" "NASSELLA_CONFIG: start")) - (custom-image . ,(search "custom_image.flatcar: Modifications complete" "custom_image.flatcar: Modifying")) - (machine-create . ,(search "droplet.machine: Creation complete" "droplet.machine: Creating...")) - (machine-destroy . ,(search "droplet.machine: Destruction complete" - '(: "droplet.machine (deposed object " (* alphanum) "): Destroying..."))))) - -(define (write-config-entry name value) - (display name) - (display "=\"") - (display value) - (print "\"")) - -(schematra:post ("/config/wizard/review-submit") +(post "/config/wizard/review-submit" (let* ((results (with-db/transaction (lambda (db) `((selected-apps . ,(map car (filter cdr - (get-user-selected-apps db (session-get "user-id"))))) - (app-config . ,(get-user-app-config db (session-get "user-id"))) - (service-config . ,(get-user-service-config db (session-get "user-id"))) - (terraform-state . ,(get-user-terraform-state db (session-get "user-id"))))))) + (get-user-selected-apps db (session-user-id))))) + (app-config . ,(get-user-app-config db (session-user-id))) + (service-config . ,(get-user-service-config db (session-user-id))) + (terraform-state . ,(get-user-terraform-state db (session-user-id))))))) (selected-apps (cons 'log-viewer (alist-ref 'selected-apps results))) (app-config (alist-ref 'app-config results)) (config (alist-ref 'config app-config)) (root-domain (alist-ref 'root-domain app-config)) (service-config (alist-ref 'service-config results)) (terraform-state (alist-ref 'terraform-state results)) - (dir (deployment-directory (session-get "user-id")))) + (dir (deployment-directory (session-user-id)))) (setup-deploy-files dir (alist-ref 'state terraform-state) (alist-ref 'backup terraform-state)) (with-output-to-file (string-append dir "/config/apps.config") (lambda () @@ -859,7 +896,7 @@ h1, h2, h3, h4, h5, h6 { ("datacenter" . ,(alist-ref 'digitalocean-region service-config)) ("flatcar_stable_version" . "4230.2.3"))) (display "ssh_keys=[\"") (display (with-input-from-file (string-append dir "/config/ssh-keys") read-string)) (print "\"]")))) - (let* ((user-id (session-get "user-id")) + (let* ((user-id (session-user-id)) (deployment-id (with-db/transaction (lambda (db) (create-deployment db user-id)))) (dir (deployment-directory user-id))) (thread-start! @@ -900,22 +937,15 @@ h1, h2, h3, h4, h5, h6 { (update-user-terraform-state db user-id (with-input-from-file (string-append dir "/terraform.tfstate") read-string) (with-input-from-file (string-append dir "/terraform.tfstate.backup") read-string)))))))))))) - (schematra:redirect "/config/wizard/success")) + (redirect "/config/wizard/success")) -(define (progress-status->text status) - (case status - ((queued) "queued") - ((in-progress) "in progress") - ((complete) "complete") - ((failed) "failed"))) - -(get +(get/widgets ("/config/wizard/success") (let* ((res (with-db/transaction (lambda (db) - `((status . ,(get-most-recent-deployment-status db (session-get "user-id"))) - (progress . ,(get-most-recent-deployment-progress db (session-get "user-id"))))))) - (output (with-input-from-file (string-append (deployment-directory (session-get "user-id")) "/make-out") read-string)) + `((status . ,(get-most-recent-deployment-status db (session-user-id))) + (progress . ,(get-most-recent-deployment-progress db (session-user-id))))))) + (output (with-input-from-file (string-append (deployment-directory (session-user-id)) "/make-out") read-string)) (progress (alist-ref 'progress res)) (status (alist-ref 'status res))) `(VStack @@ -932,7 +962,7 @@ h1, h2, h3, h4, h5, h6 { (pre ,output) ))) -(get +(get/widgets ("/dashboard") `(App (Main-Container @@ -945,7 +975,8 @@ h1, h2, h3, h4, h5, h6 { " - ",(alist-ref 'status deployment))) (with-db/transaction (lambda (db) - (get-user-deployments db (session-get "user-id")))))))))) + (get-user-deployments db (session-user-id)))))))))) + +(schematra-install) -(schematra:schematra-install) -(schematra:schematra-start) +)) diff --git a/src/test.scm b/src/test.scm new file mode 100644 index 0000000..6564794 --- /dev/null +++ b/src/test.scm @@ -0,0 +1,428 @@ +(load "nassella.scm") + +(import test spiffy (schematra test) postgresql uri-common + (chicken sort) + srfi-13) + +(define (setup-test-data) + (with-db/transaction + (lambda (db) + (let ((new-auth-id (+ (value-at + (query db + "select auth_user_id from users order by auth_user_id desc limit 1;")) + 1))) + (test-user-id + (create-user + db + new-auth-id + (conc "test" new-auth-id "@example.com") (conc "test-" new-auth-id))))))) + +(define (cleanup-test-data) + (with-db/transaction + (lambda (db) + (delete-user db (test-user-id))))) + +(define (write-test-markup) + (with-output-to-file "markup" (lambda () (pp (last-request-body-widget-sxml))))) + +(define (test-widget-get path) + (begin (test-route-body app 'GET path) + (last-request-body-widget-sxml))) + +(setup-test-data) + +(test-mode #t) + +(test "starting empty, GET /config/wizard/services" + '(App (Configuration-Wizard + (@ (step "Services")) + (form (@ (action "/config/wizard/services-submit") (method POST)) + (VStack + (Fieldset + (@ (title "Cloudflare")) + (Field (@ (name "cloudflare-api-token") + (label ("API Token")) + (value ""))) + (Field (@ (name "cloudflare-zone-id") + (label ("Zone ID")) + (value ""))) + (Field (@ (name "cloudflare-account-id") + (label ("Account ID")) + (value "")))) + (Fieldset + (@ (title "DigitalOcean")) + (Field (@ (name "digitalocean-api-token") + (label ("API Token")) + (value "")))) + (Fieldset + (@ (title "Backblaze")) + (Field (@ (name "backblaze-application-key") + (label ("Application Key")) + (value ""))) + (Field (@ (name "backblaze-key-id") + (label ("Key ID")) + (value ""))) + (Field (@ (name "backblaze-bucket-url") + (label ("Bucket URL")) + (value "")))) + (Form-Nav))))) + (test-widget-get "/config/wizard/services")) + +(define *vars* + `((cloudflare-api-token . "cloudflare-api-token") + (cloudflare-account-id . "cloudflare-account-id") + (cloudflare-zone-id . "cloudflare-zone-id") + (digitalocean-api-token . "do-api-token") + (backblaze-application-key . "bb-app-key") + (backblaze-key-id . "bb-key-id") + (backblaze-bucket-url . "bb-bucket-url") + + (apps . (nextcloud wg-easy)) + (root-domain . "root-domain.com") + + (wg-easy-subdomain . "wg-easy-sub") + (nextcloud-subdomain . "nextcloud-sub") + (nextcloud-admin-user . "nextcloud-admin") + (nextcloud-admin-password . "nextcloud-pass") + (log-viewer-subdomain . "log-viewer-sub") + (log-viewer-user . "log-viewer-user") + (log-viewer-password . "log-viewer-password"))) + +(test "POST /config/wizard/services-submit" + `(found + ,(alist-ref 'cloudflare-api-token *vars*) + ,(alist-ref 'cloudflare-account-id *vars*) + ,(alist-ref 'cloudflare-zone-id *vars*) + ,(alist-ref 'digitalocean-api-token *vars*) + ,(alist-ref 'backblaze-application-key *vars*) + ,(alist-ref 'backblaze-key-id *vars*) + ,(alist-ref 'backblaze-bucket-url *vars*)) + (let* ((resp + (test-route + app 'POST "/config/wizard/services-submit" + headers: '((content-type . (#(application/x-www-form-urlencoded ())))) + body: (form-urlencode + `((cloudflare-api-token . ,(alist-ref 'cloudflare-api-token *vars*)) + (cloudflare-account-id . ,(alist-ref 'cloudflare-account-id *vars*)) + (cloudflare-zone-id . ,(alist-ref 'cloudflare-zone-id *vars*)) + (digitalocean-api-token . ,(alist-ref 'digitalocean-api-token *vars*)) + (backblaze-application-key . ,(alist-ref 'backblaze-application-key *vars*)) + (backblaze-key-id . ,(alist-ref 'backblaze-key-id *vars*)) + (backblaze-bucket-url . ,(alist-ref 'backblaze-bucket-url *vars*)))))) + (c (with-db/transaction (lambda (db) (get-user-service-config db (test-user-id)))))) + `(,(car resp) + ,(alist-ref 'cloudflare-api-token c) + ,(alist-ref 'cloudflare-account-id c) + ,(alist-ref 'cloudflare-zone-id c) + ,(alist-ref 'digitalocean-api-token c) + ,(alist-ref 'backblaze-application-key c) + ,(alist-ref 'backblaze-key-id c) + ,(alist-ref 'backblaze-bucket-url c)))) + +(test "after services-submit, GET /config/wizard/services" + '(App (Configuration-Wizard + (@ (step "Services")) + (form (@ (action "/config/wizard/services-submit") (method POST)) + (VStack + (Fieldset + (@ (title "Cloudflare")) + (Field (@ (name "cloudflare-api-token") + (label ("API Token")) + (value "cloudflare-api-token"))) + (Field (@ (name "cloudflare-zone-id") + (label ("Zone ID")) + (value "cloudflare-zone-id"))) + (Field (@ (name "cloudflare-account-id") + (label ("Account ID")) + (value "cloudflare-account-id")))) + (Fieldset + (@ (title "DigitalOcean")) + (Field (@ (name "digitalocean-api-token") + (label ("API Token")) + (value "do-api-token")))) + (Fieldset + (@ (title "Backblaze")) + (Field (@ (name "backblaze-application-key") + (label ("Application Key")) + (value "bb-app-key"))) + (Field (@ (name "backblaze-key-id") + (label ("Key ID")) + (value "bb-key-id"))) + (Field (@ (name "backblaze-bucket-url") + (label ("Bucket URL")) + (value "bb-bucket-url")))) + (Form-Nav))))) + (test-widget-get "/config/wizard/services")) + +(test "starting empty, GET /config/wizard/services-success" + '(App (Configuration-Wizard + (@ (step "Services")) + (form (@ (action "/config/wizard/apps")) + (VStack + (Fieldset + (@ (title "Cloudflare")) + (h3 "Connected") + (p "Your Cloudflare account was successfully connected!")) + (Fieldset + (@ (title "DigitalOcean")) + (h3 "Connected") + (p "Your DigitalOcean account was successfully connected!")) + (Fieldset + (@ (title "Backblaze")) + (h3 "Connected") + (p "Your Backblaze account was successfully connected!")) + (Form-Nav (@ (back-to "/config/wizard/services"))))))) + (test-widget-get "/config/wizard/services-success")) + +(test "starting empty, GET /config/wizard/apps" + '(App (Configuration-Wizard + (@ (step "Apps")) + (form (@ (action "/config/wizard/apps-submit") (method POST)) + (VStack + (Fieldset + (@ (title "Root Domain")) + (Field (@ (element select) (name "root-domain")) + (option (@ (value #f)) "nassella.cc"))) + (Fieldset + (@ (title "Selected Apps")) + (Field (@ (name "wg-easy") + (type "checkbox") + (label ("WG Easy")) + (checked #f))) + (Field (@ (name "nextcloud") + (type "checkbox") + (label ("NextCloud")) + (checked #f))) + (Field (@ (name "log-viewer") + (type "checkbox") + (label ("Log Viewer")) + (checked #t) + (disabled "disabled")))) + (Form-Nav (@ (back-to "/config/wizard/services-success"))))))) + (test-widget-get "/config/wizard/apps")) + +(test "POST /config/wizard/apps-submit" + `(found + "log-viewer" + ,@(sort (map symbol->string (alist-ref 'apps *vars*)) string<) + ,(alist-ref 'root-domain *vars*)) + (let* ((resp + (test-route + app 'POST "/config/wizard/apps-submit" + headers: '((content-type . (#(application/x-www-form-urlencoded ())))) + body: (form-urlencode + `(,@(map (lambda (x) `(,x . "true")) (alist-ref 'apps *vars*)) + (root-domain . ,(alist-ref 'root-domain *vars*)))))) + (apps (with-db/transaction (lambda (db) (get-user-selected-apps db (test-user-id))))) + (config (with-db/transaction (lambda (db) (get-user-app-config db (test-user-id)))))) + `(,(car resp) + ,@(sort (map (lambda (app) (symbol->string (car app))) apps) string<) + ,(alist-ref 'root-domain config)))) + +(test "after apps-submit, GET /config/wizard/apps" + '(App (Configuration-Wizard + (@ (step "Apps")) + (form (@ (action "/config/wizard/apps-submit") (method POST)) + (VStack + (Fieldset + (@ (title "Root Domain")) + (Field (@ (element select) (name "root-domain")) + (option (@ (value "root-domain.com")) "nassella.cc"))) + (Fieldset + (@ (title "Selected Apps")) + (Field (@ (name "wg-easy") + (type "checkbox") + (label ("WG Easy")) + (checked (wg-easy nextcloud)))) + (Field (@ (name "nextcloud") + (type "checkbox") + (label ("NextCloud")) + (checked (nextcloud)))) + (Field (@ (name "log-viewer") + (type "checkbox") + (label ("Log Viewer")) + (checked #t) + (disabled "disabled")))) + (Form-Nav (@ (back-to "/config/wizard/services-success"))))))) + (test-widget-get "/config/wizard/apps")) + +(test "before submit, GET /config/wizard/apps2" + '(App (Configuration-Wizard + (@ (step "Apps")) + (form (@ (action "/config/wizard/apps2-submit") (method POST)) + (VStack + (Fieldset + (@ (title "WG-Easy")) + (Field (@ (name "wg-easy-subdomain") + (label ("Subdomain")) + (value "wg-easy")))) + (Fieldset + (@ (title "NextCloud")) + (Field (@ (name "nextcloud-subdomain") + (label ("Subdomain")) + (value "nextcloud"))) + (Field (@ (name "nextcloud-admin-user") + (label ("Admin Username")) + (value "admin"))) + (Field (@ (name "nextcloud-admin-password") + (label ("Admin Password")) + (type "password") + (value "")))) + (Fieldset + (@ (title "Log Viewer")) + (Field (@ (name "log-viewer-subdomain") + (label ("Subdomain")) + (value "logs"))) + (Field (@ (name "log-viewer-user") + (label ("Username")) + (value ""))) + (Field (@ (name "log-viewer-password") + (label ("Password")) + (type "password") + (value "")))) + (Form-Nav (@ (back-to "/config/wizard/apps"))))))) + (test-widget-get "/config/wizard/apps2")) + +(test "POST /config/wizard/apps2-submit" + `(found + ,(alist-ref 'wg-easy-subdomain *vars*) + ,(alist-ref 'nextcloud-subdomain *vars*) + ,(alist-ref 'nextcloud-admin-user *vars*) + ,(alist-ref 'nextcloud-admin-password *vars*) + ,(alist-ref 'log-viewer-subdomain *vars*) + ,(alist-ref 'log-viewer-user *vars*) + ,(alist-ref 'log-viewer-password *vars*)) + (let* ((resp + (test-route + app 'POST "/config/wizard/apps2-submit" + headers: '((content-type . (#(application/x-www-form-urlencoded ())))) + body: (form-urlencode + `((wg-easy-subdomain . ,(alist-ref 'wg-easy-subdomain *vars*)) + (nextcloud-subdomain . ,(alist-ref 'nextcloud-subdomain *vars*)) + (nextcloud-admin-user . ,(alist-ref 'nextcloud-admin-user *vars*)) + (nextcloud-admin-password . ,(alist-ref 'nextcloud-admin-password *vars*)) + (log-viewer-subdomain . ,(alist-ref 'log-viewer-subdomain *vars*)) + (log-viewer-user . ,(alist-ref 'log-viewer-user *vars*)) + (log-viewer-password . ,(alist-ref 'log-viewer-password *vars*)))))) + (c (alist-ref 'config + (with-db/transaction (lambda (db) (get-user-app-config db (test-user-id))))))) + `(,(car resp) + ,(alist-ref 'subdomain (alist-ref 'wg-easy c)) + ,(alist-ref 'subdomain (alist-ref 'nextcloud c)) + ,(alist-ref 'admin-user (alist-ref 'nextcloud c)) + ,(alist-ref 'admin-password (alist-ref 'nextcloud c)) + ,(alist-ref 'subdomain (alist-ref 'log-viewer c)) + ,(alist-ref 'user (alist-ref 'log-viewer c)) + ,(alist-ref 'password (alist-ref 'log-viewer c))))) + +(test "after submit, GET /config/wizard/apps2" + '(App (Configuration-Wizard + (@ (step "Apps")) + (form (@ (action "/config/wizard/apps2-submit") (method POST)) + (VStack + (Fieldset + (@ (title "WG-Easy")) + (Field (@ (name "wg-easy-subdomain") + (label ("Subdomain")) + (value "wg-easy-sub")))) + (Fieldset + (@ (title "NextCloud")) + (Field (@ (name "nextcloud-subdomain") + (label ("Subdomain")) + (value "nextcloud-sub"))) + (Field (@ (name "nextcloud-admin-user") + (label ("Admin Username")) + (value "nextcloud-admin"))) + (Field (@ (name "nextcloud-admin-password") + (label ("Admin Password")) + (type "password") + (value "nextcloud-pass")))) + (Fieldset + (@ (title "Log Viewer")) + (Field (@ (name "log-viewer-subdomain") + (label ("Subdomain")) + (value "log-viewer-sub"))) + (Field (@ (name "log-viewer-user") + (label ("Username")) + (value "log-viewer-user"))) + (Field (@ (name "log-viewer-password") + (label ("Password")) + (type "password") + (value "log-viewer-password")))) + (Form-Nav (@ (back-to "/config/wizard/apps"))))))) + (test-widget-get "/config/wizard/apps2")) + +(test "before submit, GET /config/wizard/machine" + '(App (Configuration-Wizard + (@ (step "Machine")) + (form (@ (action "/config/wizard/machine-submit") (method POST)) + (VStack + (Fieldset + (@ (title "Region")) + (Field (@ (element select) (name "region")) + (option (@ (value "")) "") + (option (@ (value "nyc1")) "New York 1") + (option (@ (value "nyc2")) "New York 2") + (option (@ (value "sgp1")) "Singapore 1") + (option (@ (value "lon1")) "London 1") + (option (@ (value "nyc3")) "New York 3") + (option (@ (value "ams3")) "Amsterdam 3") + (option (@ (value "fra1")) "Frankfurt 1") + (option (@ (value "tor1")) "Toronto 1") + (option (@ (value "sfo2")) "San Francisco 2") + (option (@ (value "blr1")) "Bangalore 1") + (option (@ (value "sfo3")) "San Francisco 3") + (option (@ (value "syd1")) "Sydney 1") + (option (@ (value "atl1")) "Atlanta 1"))) + (Form-Nav (@ (back-to "/config/wizard/apps2"))))))) + (test-widget-get "/config/wizard/machine")) + +(test "POST /config/wizard/machine-submit" + `(found + "sfo3") + (let* ((resp + (test-route + app 'POST "/config/wizard/machine-submit" + headers: '((content-type . (#(application/x-www-form-urlencoded ())))) + body: (form-urlencode + `((region . "sfo3"))))) + (c (with-db/transaction (lambda (db) (get-user-service-config db (test-user-id)))))) + `(,(car resp) + ,(alist-ref 'digitalocean-region c)))) + +(test "before submit, GET /config/wizard/machine2" + '(App (Configuration-Wizard (@ (step "Machine")) (form (@ (action "/config/wizard/machine2-submit") (method POST)) (VStack (Fieldset (@ (title "Size")) (Field (@ (element select) (name "size") (input-style ((max-width "100%")))) (option (@ (value "s-1vcpu-512mb-10gb")) "$" 4 " (CPU: " 1 " Mem: " 1/2 " Disk: " 10 ") " "Basic") (option (@ (value "s-1vcpu-1gb")) "$" 6 " (CPU: " 1 " Mem: " 1 " Disk: " 25 ") " "Basic") (option (@ (value "s-1vcpu-1gb-amd")) "$" 7 " (CPU: " 1 " Mem: " 1 " Disk: " 25 ") " "Basic AMD") (option (@ (value "s-1vcpu-1gb-intel")) "$" 7 " (CPU: " 1 " Mem: " 1 " Disk: " 25 ") " "Basic Intel") (option (@ (value "s-1vcpu-1gb-35gb-intel")) "$" 8 " (CPU: " 1 " Mem: " 1 " Disk: " 35 ") " "Basic Intel") (option (@ (value "s-1vcpu-2gb")) "$" 12 " (CPU: " 1 " Mem: " 2 " Disk: " 50 ") " "Basic") (option (@ (value "s-1vcpu-2gb-amd")) "$" 14 " (CPU: " 1 " Mem: " 2 " Disk: " 50 ") " "Basic AMD") (option (@ (value "s-1vcpu-2gb-intel")) "$" 14 " (CPU: " 1 " Mem: " 2 " Disk: " 50 ") " "Basic Intel") (option (@ (value "s-1vcpu-2gb-70gb-intel")) "$" 16 " (CPU: " 1 " Mem: " 2 " Disk: " 70 ") " "Basic Intel") (option (@ (value "s-2vcpu-2gb") (selected "selected")) "$" 18 " (CPU: " 2 " Mem: " 2 " Disk: " 60 ") " "Basic") (option (@ (value "s-2vcpu-2gb-amd")) "$" 21 " (CPU: " 2 " Mem: " 2 " Disk: " 60 ") " "Basic AMD") (option (@ (value "s-2vcpu-2gb-intel")) "$" 21 " (CPU: " 2 " Mem: " 2 " Disk: " 60 ") " "Basic Intel") (option (@ (value "s-2vcpu-2gb-90gb-intel")) "$" 24 " (CPU: " 2 " Mem: " 2 " Disk: " 90 ") " "Basic Intel") (option (@ (value "s-2vcpu-4gb")) "$" 24 " (CPU: " 2 " Mem: " 4 " Disk: " 80 ") " "Basic") (option (@ (value "s-2vcpu-4gb-amd")) "$" 28 " (CPU: " 2 " Mem: " 4 " Disk: " 80 ") " "Basic AMD") (option (@ (value "s-2vcpu-4gb-intel")) "$" 28 " (CPU: " 2 " Mem: " 4 " Disk: " 80 ") " "Basic Intel") (option (@ (value "s-2vcpu-4gb-120gb-intel")) "$" 32 " (CPU: " 2 " Mem: " 4 " Disk: " 120 ") " "Basic Intel") (option (@ (value "s-2vcpu-8gb-amd")) "$" 42 " (CPU: " 2 " Mem: " 8 " Disk: " 100 ") " "Basic AMD") (option (@ (value "c-2")) "$" 42 " (CPU: " 2 " Mem: " 4 " Disk: " 25 ") " "CPU-Optimized") (option (@ (value "c2-2vcpu-4gb")) "$" 47 " (CPU: " 2 " Mem: " 4 " Disk: " 50 ") " "CPU-Optimized 2x SSD") (option (@ (value "s-2vcpu-8gb-160gb-intel")) "$" 48 " (CPU: " 2 " Mem: " 8 " Disk: " 160 ") " "Basic Intel") (option (@ (value "s-4vcpu-8gb")) "$" 48 " (CPU: " 4 " Mem: " 8 " Disk: " 160 ") " "Basic") (option (@ (value "s-4vcpu-8gb-amd")) "$" 56 " (CPU: " 4 " Mem: " 8 " Disk: " 160 ") " "Basic AMD") (option (@ (value "s-4vcpu-8gb-intel")) "$" 56 " (CPU: " 4 " Mem: " 8 " Disk: " 160 ") " "Basic Intel") (option (@ (value "c5-2vcpu-4gb")) "$" 62 " (CPU: " 2 " Mem: " 4 " Disk: " 125 ") " "CPU Intensive 5x SSD") (option (@ (value "g-2vcpu-8gb")) "$" 63 " (CPU: " 2 " Mem: " 8 " Disk: " 25 ") " "General Purpose") (option (@ (value "s-4vcpu-8gb-240gb-intel")) "$" 64 " (CPU: " 4 " Mem: " 8 " Disk: " 240 ") " "Basic Intel") (option (@ (value "gd-2vcpu-8gb")) "$" 68 " (CPU: " 2 " Mem: " 8 " Disk: " 50 ") " "General Purpose 2x SSD") (option (@ (value "g-2vcpu-8gb-intel")) "$" 76 " (CPU: " 2 " Mem: " 8 " Disk: " 30 ") " "General Purpose — Premium Intel") (option (@ (value "gd-2vcpu-8gb-intel")) "$" 79 " (CPU: " 2 " Mem: " 8 " Disk: " 60 ") " "General Purpose — Premium Intel 2x SSD") (option (@ (value "s-4vcpu-16gb-amd")) "$" 84 " (CPU: " 4 " Mem: " 16 " Disk: " 200 ") " "Basic AMD") (option (@ (value "m-2vcpu-16gb")) "$" 84 " (CPU: " 2 " Mem: " 16 " Disk: " 50 ") " "Memory-Optimized") (option (@ (value "c-4")) "$" 84 " (CPU: " 4 " Mem: " 8 " Disk: " 50 ") " "CPU-Optimized") (option (@ (value "g6_5-2vcpu-8gb")) "$" 90.6 " (CPU: " 2 " Mem: " 8 " Disk: " 163 ") " "General Purpose 6.5x SSD") (option (@ (value "g5_5-2vcpu-8gb-intel")) "$" 92.2 " (CPU: " 2 " Mem: " 8 " Disk: " 165 ") " "General Purpose - Premium Intel 5.5x SSD") (option (@ (value "c2-4vcpu-8gb")) "$" 94 " (CPU: " 4 " Mem: " 8 " Disk: " 100 ") " "CPU-Optimized 2x SSD") (option (@ (value "s-4vcpu-16gb-320gb-intel")) "$" 96 " (CPU: " 4 " Mem: " 16 " Disk: " 320 ") " "Basic Intel") (option (@ (value "s-8vcpu-16gb")) "$" 96 " (CPU: " 8 " Mem: " 16 " Disk: " 320 ") " "Basic") (option (@ (value "m-2vcpu-16gb-intel")) "$" 99 " (CPU: " 2 " Mem: " 16 " Disk: " 50 ") " "Premium Memory-Optimized") (option (@ (value "m3-2vcpu-16gb")) "$" 104 " (CPU: " 2 " Mem: " 16 " Disk: " 150 ") " "Memory-Optimized 3x SSD") (option (@ (value "c-4-intel")) "$" 109 " (CPU: " 4 " Mem: " 8 " Disk: " 50 ") " "Premium Intel") (option (@ (value "m3-2vcpu-16gb-intel")) "$" 110 " (CPU: " 2 " Mem: " 16 " Disk: " 150 ") " "Premium Memory-Optimized 3x SSD") (option (@ (value "s-8vcpu-16gb-amd")) "$" 112 " (CPU: " 8 " Mem: " 16 " Disk: " 320 ") " "Basic AMD") (option (@ (value "s-8vcpu-16gb-intel")) "$" 112 " (CPU: " 8 " Mem: " 16 " Disk: " 320 ") " "Basic Intel") (option (@ (value "c2-4vcpu-8gb-intel")) "$" 122 " (CPU: " 4 " Mem: " 8 " Disk: " 100 ") " "Premium Intel") (option (@ (value "c5-4vcpu-8gb")) "$" 124 " (CPU: " 4 " Mem: " 8 " Disk: " 250 ") " "CPU Intensive 5x SSD") (option (@ (value "g-4vcpu-16gb")) "$" 126 " (CPU: " 4 " Mem: " 16 " Disk: " 50 ") " "General Purpose") (option (@ (value "s-8vcpu-16gb-480gb-intel")) "$" 128 " (CPU: " 8 " Mem: " 16 " Disk: " 480 ") " "Basic Intel") (option (@ (value "so-2vcpu-16gb-intel")) "$" 131 " (CPU: " 2 " Mem: " 16 " Disk: " 300 ") " "Premium Storage-Optimized") (option (@ (value "so-2vcpu-16gb")) "$" 131 " (CPU: " 2 " Mem: " 16 " Disk: " 300 ") " "Storage-Optimized") (option (@ (value "m6-2vcpu-16gb")) "$" 131 " (CPU: " 2 " Mem: " 16 " Disk: " 300 ") " "Memory-Optimized 6x SSD") (option (@ (value "gd-4vcpu-16gb")) "$" 136 " (CPU: " 4 " Mem: " 16 " Disk: " 100 ") " "General Purpose 2x SSD") (option (@ (value "so1_5-2vcpu-16gb-intel")) "$" 139 " (CPU: " 2 " Mem: " 16 " Disk: " 450 ") " "Premium Storage-Optimized 1.5x SSD") (option (@ (value "g-4vcpu-16gb-intel")) "$" 151 " (CPU: " 4 " Mem: " 16 " Disk: " 60 ") " "General Purpose — Premium Intel") (option (@ (value "gd-4vcpu-16gb-intel")) "$" 158 " (CPU: " 4 " Mem: " 16 " Disk: " 120 ") " "General Purpose — Premium Intel 2x SSD") (option (@ (value "c5-4vcpu-8gb-intel")) "$" 161 " (CPU: " 4 " Mem: " 8 " Disk: " 250 ") " "CPU Optimized - Premium Intel 5x SSD") (option (@ (value "so1_5-2vcpu-16gb")) "$" 163 " (CPU: " 2 " Mem: " 16 " Disk: " 450 ") " "Storage-Optimized 1.5x SSD") (option (@ (value "s-8vcpu-32gb-amd")) "$" 168 " (CPU: " 8 " Mem: " 32 " Disk: " 400 ") " "Basic AMD") (option (@ (value "m-4vcpu-32gb")) "$" 168 " (CPU: " 4 " Mem: " 32 " Disk: " 100 ") " "Memory-Optimized") (option (@ (value "c-8")) "$" 168 " (CPU: " 8 " Mem: " 16 " Disk: " 100 ") " "CPU-Optimized") (option (@ (value "g6_5-4vcpu-16gb")) "$" 181 " (CPU: " 4 " Mem: " 16 " Disk: " 325 ") " "General Purpose 6.5x SSD") (option (@ (value "g5_5-4vcpu-16gb-intel")) "$" 183.4 " (CPU: " 4 " Mem: " 16 " Disk: " 330 ") " "General Purpose - Premium Intel 5.5x SSD") (option (@ (value "c2-8vcpu-16gb")) "$" 188 " (CPU: " 8 " Mem: " 16 " Disk: " 200 ") " "CPU-Optimized 2x SSD") (option (@ (value "s-8vcpu-32gb-640gb-intel")) "$" 192 " (CPU: " 8 " Mem: " 32 " Disk: " 640 ") " "Basic Intel") (option (@ (value "m-4vcpu-32gb-intel")) "$" 198 " (CPU: " 4 " Mem: " 32 " Disk: " 100 ") " "Premium Memory-Optimized") (option (@ (value "m3-4vcpu-32gb")) "$" 208 " (CPU: " 4 " Mem: " 32 " Disk: " 300 ") " "Memory-Optimized 3x SSD") (option (@ (value "c-8-intel")) "$" 218 " (CPU: " 8 " Mem: " 16 " Disk: " 100 ") " "Premium Intel") (option (@ (value "m3-4vcpu-32gb-intel")) "$" 220 " (CPU: " 4 " Mem: " 32 " Disk: " 300 ") " "Premium Memory-Optimized 3x SSD") (option (@ (value "c2-8vcpu-16gb-intel")) "$" 244 " (CPU: " 8 " Mem: " 16 " Disk: " 200 ") " "Premium Intel") (option (@ (value "c5-8vcpu-16gb")) "$" 248 " (CPU: " 8 " Mem: " 16 " Disk: " 500 ") " "CPU Intensive 5x SSD") (option (@ (value "g-8vcpu-32gb")) "$" 252 " (CPU: " 8 " Mem: " 32 " Disk: " 100 ") " "General Purpose") (option (@ (value "so-4vcpu-32gb-intel")) "$" 262 " (CPU: " 4 " Mem: " 32 " Disk: " 600 ") " "Premium Storage-Optimized") (option (@ (value "so-4vcpu-32gb")) "$" 262 " (CPU: " 4 " Mem: " 32 " Disk: " 600 ") " "Storage-Optimized") (option (@ (value "m6-4vcpu-32gb")) "$" 262 " (CPU: " 4 " Mem: " 32 " Disk: " 600 ") " "Memory-Optimized 6x SSD") (option (@ (value "gd-8vcpu-32gb")) "$" 272 " (CPU: " 8 " Mem: " 32 " Disk: " 200 ") " "General Purpose 2x SSD") (option (@ (value "so1_5-4vcpu-32gb-intel")) "$" 278 " (CPU: " 4 " Mem: " 32 " Disk: " 900 ") " "Premium Storage-Optimized 1.5x SSD") (option (@ (value "g-8vcpu-32gb-intel")) "$" 302 " (CPU: " 8 " Mem: " 32 " Disk: " 120 ") " "General Purpose — Premium Intel") (option (@ (value "gd-8vcpu-32gb-intel")) "$" 317 " (CPU: " 8 " Mem: " 32 " Disk: " 240 ") " "General Purpose — Premium Intel 2x SSD") (option (@ (value "c5-8vcpu-16gb-intel")) "$" 322 " (CPU: " 8 " Mem: " 16 " Disk: " 500 ") " "CPU Optimized - Premium Intel 5x SSD") (option (@ (value "so1_5-4vcpu-32gb")) "$" 326 " (CPU: " 4 " Mem: " 32 " Disk: " 900 ") " "Storage-Optimized 1.5x SSD") (option (@ (value "m-8vcpu-64gb")) "$" 336 " (CPU: " 8 " Mem: " 64 " Disk: " 200 ") " "Memory-Optimized") (option (@ (value "c-16")) "$" 336 " (CPU: " 16 " Mem: " 32 " Disk: " 200 ") " "CPU-Optimized") (option (@ (value "g6_5-8vcpu-32gb")) "$" 362 " (CPU: " 8 " Mem: " 32 " Disk: " 650 ") " "General Purpose 6.5x SSD") (option (@ (value "g5_5-8vcpu-32gb-intel")) "$" 366.8 " (CPU: " 8 " Mem: " 32 " Disk: " 660 ") " "General Purpose - Premium Intel 5.5x SSD") (option (@ (value "c2-16vcpu-32gb")) "$" 376 " (CPU: " 16 " Mem: " 32 " Disk: " 400 ") " "CPU-Optimized 2x SSD") (option (@ (value "m-8vcpu-64gb-intel")) "$" 396 " (CPU: " 8 " Mem: " 64 " Disk: " 200 ") " "Premium Memory-Optimized") (option (@ (value "m3-8vcpu-64gb")) "$" 416 " (CPU: " 8 " Mem: " 64 " Disk: " 600 ") " "Memory-Optimized 3x SSD") (option (@ (value "c-16-intel")) "$" 437 " (CPU: " 16 " Mem: " 32 " Disk: " 200 ") " "Premium Intel") (option (@ (value "m3-8vcpu-64gb-intel")) "$" 440 " (CPU: " 8 " Mem: " 64 " Disk: " 600 ") " "Premium Memory-Optimized 3x SSD") (option (@ (value "c2-16vcpu-32gb-intel")) "$" 489 " (CPU: " 16 " Mem: " 32 " Disk: " 400 ") " "Premium Intel") (option (@ (value "c5-16vcpu-32gb")) "$" 496 " (CPU: " 16 " Mem: " 32 " Disk: " 1000 ") " "CPU Intensive 5x SSD") (option (@ (value "g-16vcpu-64gb")) "$" 504 " (CPU: " 16 " Mem: " 64 " Disk: " 200 ") " "General Purpose") (option (@ (value "so-8vcpu-64gb-intel")) "$" 524 " (CPU: " 8 " Mem: " 64 " Disk: " 1200 ") " "Premium Storage-Optimized") (option (@ (value "so-8vcpu-64gb")) "$" 524 " (CPU: " 8 " Mem: " 64 " Disk: " 1200 ") " "Storage-Optimized") (option (@ (value "m6-8vcpu-64gb")) "$" 524 " (CPU: " 8 " Mem: " 64 " Disk: " 1200 ") " "Memory-Optimized 6x SSD") (option (@ (value "gd-16vcpu-64gb")) "$" 544 " (CPU: " 16 " Mem: " 64 " Disk: " 400 ") " "General Purpose 2x SSD") (option (@ (value "so1_5-8vcpu-64gb-intel")) "$" 556 " (CPU: " 8 " Mem: " 64 " Disk: " 1800 ") " "Premium Storage-Optimized 1.5x SSD") (option (@ (value "g-16vcpu-64gb-intel")) "$" 605 " (CPU: " 16 " Mem: " 64 " Disk: " 240 ") " "General Purpose — Premium Intel") (option (@ (value "gd-16vcpu-64gb-intel")) "$" 634 " (CPU: " 16 " Mem: " 64 " Disk: " 480 ") " "General Purpose — Premium Intel 2x SSD") (option (@ (value "c5-16vcpu-32gb-intel")) "$" 645 " (CPU: " 16 " Mem: " 32 " Disk: " 1000 ") " "CPU Optimized - Premium Intel 5x SSD") (option (@ (value "so1_5-8vcpu-64gb")) "$" 652 " (CPU: " 8 " Mem: " 64 " Disk: " 1800 ") " "Storage-Optimized 1.5x SSD") (option (@ (value "m-16vcpu-128gb")) "$" 672 " (CPU: " 16 " Mem: " 128 " Disk: " 400 ") " "Memory-Optimized") (option (@ (value "c-32")) "$" 672 " (CPU: " 32 " Mem: " 64 " Disk: " 400 ") " "CPU-Optimized") (option (@ (value "g6_5-16vcpu-64gb")) "$" 724 " (CPU: " 16 " Mem: " 64 " Disk: " 1300 ") " "General Purpose 6.5x SSD") (option (@ (value "g5_5-16vcpu-64gb-intel")) "$" 734.6 " (CPU: " 16 " Mem: " 64 " Disk: " 1320 ") " "General Purpose - Premium Intel 5.5x SSD") (option (@ (value "c2-32vcpu-64gb")) "$" 752 " (CPU: " 32 " Mem: " 64 " Disk: " 800 ") " "CPU-Optimized 2x SSD") (option (@ (value "m-16vcpu-128gb-intel")) "$" 792 " (CPU: " 16 " Mem: " 128 " Disk: " 400 ") " "Premium Memory-Optimized") (option (@ (value "m3-16vcpu-128gb")) "$" 832 " (CPU: " 16 " Mem: " 128 " Disk: " 1200 ") " "Memory-Optimized 3x SSD") (option (@ (value "c-32-intel")) "$" 874 " (CPU: " 32 " Mem: " 64 " Disk: " 400 ") " "Premium Intel") (option (@ (value "m3-16vcpu-128gb-intel")) "$" 880 " (CPU: " 16 " Mem: " 128 " Disk: " 1200 ") " "Premium Memory-Optimized 3x SSD") (option (@ (value "c2-32vcpu-64gb-intel")) "$" 978 " (CPU: " 32 " Mem: " 64 " Disk: " 800 ") " "Premium Intel") (option (@ (value "c5-32vcpu-64gb")) "$" 992 " (CPU: " 32 " Mem: " 64 " Disk: " 2000 ") " "CPU Intensive 5x SSD") (option (@ (value "c-48")) "$" 1008 " (CPU: " 48 " Mem: " 96 " Disk: " 600 ") " "CPU-Optimized") (option (@ (value "m-24vcpu-192gb")) "$" 1008 " (CPU: " 24 " Mem: " 192 " Disk: " 600 ") " "Memory-Optimized") (option (@ (value "g-32vcpu-128gb")) "$" 1008 " (CPU: " 32 " Mem: " 128 " Disk: " 400 ") " "General Purpose") (option (@ (value "so-16vcpu-128gb-intel")) "$" 1048 " (CPU: " 16 " Mem: " 128 " Disk: " 2400 ") " "Premium Storage-Optimized") (option (@ (value "so-16vcpu-128gb")) "$" 1048 " (CPU: " 16 " Mem: " 128 " Disk: " 2400 ") " "Storage-Optimized") (option (@ (value "m6-16vcpu-128gb")) "$" 1048 " (CPU: " 16 " Mem: " 128 " Disk: " 2400 ") " "Memory-Optimized 6x SSD") (option (@ (value "gd-32vcpu-128gb")) "$" 1088 " (CPU: " 32 " Mem: " 128 " Disk: " 800 ") " "General Purpose 2x SSD") (option (@ (value "so1_5-16vcpu-128gb-intel")) "$" 1112 " (CPU: " 16 " Mem: " 128 " Disk: " 3600 ") " "Premium Storage-Optimized 1.5x SSD") (option (@ (value "c2-48vcpu-96gb")) "$" 1128 " (CPU: " 48 " Mem: " 96 " Disk: " 1200 ") " "CPU-Optimized 2x SSD") (option (@ (value "m-24vcpu-192gb-intel")) "$" 1188 " (CPU: " 24 " Mem: " 192 " Disk: " 600 ") " "Premium Memory-Optimized") (option (@ (value "g-32vcpu-128gb-intel")) "$" 1210 " (CPU: " 32 " Mem: " 128 " Disk: " 480 ") " "General Purpose — Premium Intel") (option (@ (value "m3-24vcpu-192gb")) "$" 1248 " (CPU: " 24 " Mem: " 192 " Disk: " 1800 ") " "Memory-Optimized 3x SSD") (option (@ (value "g-40vcpu-160gb")) "$" 1260 " (CPU: " 40 " Mem: " 160 " Disk: " 500 ") " "General Purpose") (option (@ (value "gd-32vcpu-128gb-intel")) "$" 1268 " (CPU: " 32 " Mem: " 128 " Disk: " 960 ") " "General Purpose — Premium Intel 2x SSD") (option (@ (value "c5-32vcpu-64gb-intel")) "$" 1290 " (CPU: " 32 " Mem: " 64 " Disk: " 2000 ") " "CPU Optimized - Premium Intel 5x SSD") (option (@ (value "so1_5-16vcpu-128gb")) "$" 1304 " (CPU: " 16 " Mem: " 128 " Disk: " 3600 ") " "Storage-Optimized 1.5x SSD") (option (@ (value "c-48-intel")) "$" 1310 " (CPU: " 48 " Mem: " 96 " Disk: " 600 ") " "Premium Intel") (option (@ (value "m3-24vcpu-192gb-intel")) "$" 1320 " (CPU: " 24 " Mem: " 192 " Disk: " 1800 ") " "Premium Memory-Optimized 3x SSD") (option (@ (value "m-32vcpu-256gb")) "$" 1344 " (CPU: " 32 " Mem: " 256 " Disk: " 800 ") " "Memory-Optimized") (option (@ (value "gd-40vcpu-160gb")) "$" 1360 " (CPU: " 40 " Mem: " 160 " Disk: " 1000 ") " "General Purpose 2x SSD") (option (@ (value "g6_5-32vcpu-128gb")) "$" 1448 " (CPU: " 32 " Mem: " 128 " Disk: " 2600 ") " "General Purpose 6.5x SSD") (option (@ (value "c2-48vcpu-96gb-intel")) "$" 1466 " (CPU: " 48 " Mem: " 96 " Disk: " 1200 ") " "Premium Intel") (option (@ (value "g5_5-32vcpu-128gb-intel")) "$" 1469.2 " (CPU: " 32 " Mem: " 128 " Disk: " 2640 ") " "General Purpose - Premium Intel 5.5x SSD") (option (@ (value "so-24vcpu-192gb-intel")) "$" 1572 " (CPU: " 24 " Mem: " 192 " Disk: " 3600 ") " "Premium Storage-Optimized") (option (@ (value "so-24vcpu-192gb")) "$" 1572 " (CPU: " 24 " Mem: " 192 " Disk: " 3600 ") " "Storage-Optimized") (option (@ (value "m6-24vcpu-192gb")) "$" 1572 " (CPU: " 24 " Mem: " 192 " Disk: " 3600 ") " "Memory-Optimized 6x SSD") (option (@ (value "m-32vcpu-256gb-intel")) "$" 1584 " (CPU: " 32 " Mem: " 256 " Disk: " 800 ") " "Premium Memory-Optimized") (option (@ (value "c-60-intel")) "$" 1639 " (CPU: " 60 " Mem: " 120 " Disk: " 750 ") " "Premium Intel") (option (@ (value "m3-32vcpu-256gb")) "$" 1664 " (CPU: " 32 " Mem: " 256 " Disk: " 2400 ") " "Memory-Optimized 3x SSD") (option (@ (value "so1_5-24vcpu-192gb-intel")) "$" 1668 " (CPU: " 24 " Mem: " 192 " Disk: " 5400 ") " "Premium Storage-Optimized 1.5x SSD") (option (@ (value "m3-32vcpu-256gb-intel")) "$" 1760 " (CPU: " 32 " Mem: " 256 " Disk: " 2400 ") " "Premium Memory-Optimized 3x SSD") (option (@ (value "g6_5-40vcpu-160gb")) "$" 1810 " (CPU: " 40 " Mem: " 160 " Disk: " 3250 ") " "General Purpose 6.5x SSD") (option (@ (value "g-48vcpu-192gb-intel")) "$" 1814 " (CPU: " 48 " Mem: " 192 " Disk: " 720 ") " "General Purpose — Premium Intel") (option (@ (value "c2-60vcpu-120gb-intel")) "$" 1834 " (CPU: " 60 " Mem: " 120 " Disk: " 1500 ") " "Premium Intel") (option (@ (value "gd-48vcpu-192gb-intel")) "$" 1901 " (CPU: " 48 " Mem: " 192 " Disk: " 1440 ") " "General Purpose — Premium Intel 2x SSD") (option (@ (value "c5-48vcpu-96gb-intel")) "$" 1934 " (CPU: " 48 " Mem: " 96 " Disk: " 3000 ") " "CPU Optimized - Premium Intel 5x SSD") (option (@ (value "so1_5-24vcpu-192gb")) "$" 1956 " (CPU: " 24 " Mem: " 192 " Disk: " 5400 ") " "Storage-Optimized 1.5x SSD") (option (@ (value "so-32vcpu-256gb-intel")) "$" 2096 " (CPU: " 32 " Mem: " 256 " Disk: " 4800 ") " "Premium Storage-Optimized") (option (@ (value "so-32vcpu-256gb")) "$" 2096 " (CPU: " 32 " Mem: " 256 " Disk: " 4800 ") " "Storage-Optimized") (option (@ (value "m6-32vcpu-256gb")) "$" 2096 " (CPU: " 32 " Mem: " 256 " Disk: " 4800 ") " "Memory-Optimized 6x SSD") (option (@ (value "g5_5-48vcpu-192gb-intel")) "$" 2202.8 " (CPU: " 48 " Mem: " 192 " Disk: " 3960 ") " "General Purpose - Premium Intel 5.5x SSD") (option (@ (value "so1_5-32vcpu-256gb-intel")) "$" 2224 " (CPU: " 32 " Mem: " 256 " Disk: " 7200 ") " "Premium Storage-Optimized 1.5x SSD") (option (@ (value "g-60vcpu-240gb-intel")) "$" 2269 " (CPU: " 60 " Mem: " 240 " Disk: " 900 ") " "General Purpose — Premium Intel") (option (@ (value "m-48vcpu-384gb-intel")) "$" 2376 " (CPU: " 48 " Mem: " 384 " Disk: " 1200 ") " "Premium Memory-Optimized") (option (@ (value "gd-60vcpu-240gb-intel")) "$" 2378 " (CPU: " 60 " Mem: " 240 " Disk: " 1800 ") " "General Purpose — Premium Intel 2x SSD") (option (@ (value "c5-60vcpu-120gb-intel")) "$" 2419 " (CPU: " 60 " Mem: " 120 " Disk: " 3750 ") " "CPU Optimized - Premium Intel 5x SSD") (option (@ (value "so1_5-32vcpu-256gb")) "$" 2608 " (CPU: " 32 " Mem: " 256 " Disk: " 7200 ") " "Storage-Optimized 1.5x SSD") (option (@ (value "m3-48vcpu-384gb-intel")) "$" 2640 " (CPU: " 48 " Mem: " 384 " Disk: " 3600 ") " "Premium Memory-Optimized 3x SSD") (option (@ (value "g5_5-60vcpu-240gb-intel")) "$" 2755 " (CPU: " 60 " Mem: " 240 " Disk: " 4950 ") " "General Purpose - Premium Intel 5.5x SSD") (option (@ (value "so-48vcpu-384gb-intel")) "$" 3144 " (CPU: " 48 " Mem: " 384 " Disk: " 7000 ") " "Premium Storage-Optimized"))) (Form-Nav (@ (back-to "/config/wizard/machine"))))))) + (test-widget-get "/config/wizard/machine2")) + +(test "POST /config/wizard/machine2-submit" + `(found + "s-1vcpu-1gb") + (let* ((resp + (test-route + app 'POST "/config/wizard/machine2-submit" + headers: '((content-type . (#(application/x-www-form-urlencoded ())))) + body: (form-urlencode + `((size . "s-1vcpu-1gb"))))) + (c (with-db/transaction (lambda (db) (get-user-service-config db (test-user-id)))))) + `(,(car resp) + ,(alist-ref 'digitalocean-size c)))) + +(test "before submit, GET /config/wizard/review" + '(App (Configuration-Wizard + (@ (step "Review")) + (h2 "Root Domain") + "root-domain.com" + (h2 "Apps") + (ul (li log-viewer " @ " "log-viewer-sub" "." "root-domain.com") + (li wg-easy " @ " "wg-easy-sub" "." "root-domain.com") + (li nextcloud " @ " "nextcloud-sub" "." "root-domain.com")) + (h2 "Machine") + (ul (li "Region: " "sfo3") (li "Size: " "s-1vcpu-1gb")) + (form (@ (action "/config/wizard/review-submit") (method POST)) + (VStack + (Form-Nav + (@ (back-to "/config/wizard/machine2") + (submit-button "Launch"))))))) + (test-widget-get "/config/wizard/review")) + +(cleanup-test-data)