diff options
| author | Roman Scherer <roman@burningswell.com> | 2025-02-04 20:01:14 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2025-02-09 18:20:42 +0100 |
| commit | 0753a17ddf6f4fab98b93c25f1a93b97ff9e46bb (patch) | |
| tree | e56f2bcb4c52186364ee63a065bc6a20a2e252be /tests/machine | |
| parent | 96f05f003a862c198e803901abf6f50b23969697 (diff) | |
machine: Implement 'hetzner-environment-type'.
* Makefile.am (SCM_TESTS): Add test modules.
* doc/guix.texi: Add documentation.
* gnu/local.mk (GNU_SYSTEM_MODULES): Add modules.
* gnu/machine/hetzner.scm: Add hetzner-environment-type.
* gnu/machine/hetzner/http.scm: Add HTTP API.
* po/guix/POTFILES.in: Add Hetzner modules.
* tests/machine/hetzner.scm: Add machine tests.
* tests/machine/hetzner/http.scm Add HTTP API tests.
Change-Id: I276ed5afed676bbccc6c852c56ee4db57ce3c1ea
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Diffstat (limited to 'tests/machine')
| -rw-r--r-- | tests/machine/hetzner.scm | 267 | ||||
| -rw-r--r-- | tests/machine/hetzner/http.scm | 631 |
2 files changed, 898 insertions, 0 deletions
diff --git a/tests/machine/hetzner.scm b/tests/machine/hetzner.scm new file mode 100644 index 00000000000..39eac4a4d5f --- /dev/null +++ b/tests/machine/hetzner.scm | |||
| @@ -0,0 +1,267 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | ||
| 2 | ;;; Copyright © 2024 Roman Scherer <roman@burningswell.com> | ||
| 3 | ;;; | ||
| 4 | ;;; This file is part of GNU Guix. | ||
| 5 | ;;; | ||
| 6 | ;;; GNU Guix is free software; you can redistribute it and/or modify it | ||
| 7 | ;;; under the terms of the GNU General Public License as published by | ||
| 8 | ;;; the Free Software Foundation; either version 3 of the License, or (at | ||
| 9 | ;;; your option) any later version. | ||
| 10 | ;;; | ||
| 11 | ;;; GNU Guix is distributed in the hope that it will be useful, but | ||
| 12 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | ;;; GNU General Public License for more details. | ||
| 15 | ;;; | ||
| 16 | ;;; You should have received a copy of the GNU General Public License | ||
| 17 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | |||
| 19 | (define-module (tests machine hetzner) | ||
| 20 | #:use-module (gnu machine hetzner http) | ||
| 21 | #:use-module (gnu machine hetzner) | ||
| 22 | #:use-module (gnu machine ssh) | ||
| 23 | #:use-module (gnu machine) | ||
| 24 | #:use-module (gnu system) | ||
| 25 | #:use-module (guix build utils) | ||
| 26 | #:use-module (guix records) | ||
| 27 | #:use-module (guix ssh) | ||
| 28 | #:use-module (guix tests) | ||
| 29 | #:use-module (srfi srfi-1) | ||
| 30 | #:use-module (srfi srfi-34) | ||
| 31 | #:use-module (srfi srfi-64) | ||
| 32 | #:use-module (ssh key) | ||
| 33 | #:use-module (ssh session)) | ||
| 34 | |||
| 35 | ;;; Unit and integration tests for the (gnu machine hetzner) module. | ||
| 36 | |||
| 37 | ;; Integration tests require the GUIX_HETZNER_API_TOKEN environment variable. | ||
| 38 | ;; https://docs.hetzner.com/cloud/api/getting-started/generating-api-token | ||
| 39 | |||
| 40 | ;; The integration tests sometimes fail due to the Hetzner API not being able | ||
| 41 | ;; to allocate a resource. Switching to a different location might help. | ||
| 42 | |||
| 43 | (define %labels | ||
| 44 | '(("guix.gnu.org/test" . "true"))) | ||
| 45 | |||
| 46 | (define %ssh-key-name | ||
| 47 | "guix-hetzner-machine-test-key") | ||
| 48 | |||
| 49 | (define %ssh-key-file | ||
| 50 | (string-append "/tmp/" %ssh-key-name)) | ||
| 51 | |||
| 52 | (unless (file-exists? %ssh-key-file) | ||
| 53 | (private-key-to-file (make-keypair 'rsa 2048) %ssh-key-file)) | ||
| 54 | |||
| 55 | (define %when-no-token | ||
| 56 | (if (hetzner-api-token (hetzner-api)) 0 1)) | ||
| 57 | |||
| 58 | (define %arm-machine | ||
| 59 | (machine | ||
| 60 | (operating-system | ||
| 61 | (operating-system | ||
| 62 | (inherit %hetzner-os-arm) | ||
| 63 | (host-name "guix-deploy-hetzner-test-arm"))) | ||
| 64 | (environment hetzner-environment-type) | ||
| 65 | (configuration (hetzner-configuration | ||
| 66 | (labels %labels) | ||
| 67 | (server-type "cax41") | ||
| 68 | (ssh-key %ssh-key-file))))) | ||
| 69 | |||
| 70 | (define %x86-machine | ||
| 71 | (machine | ||
| 72 | (operating-system | ||
| 73 | (operating-system | ||
| 74 | (inherit %hetzner-os-x86) | ||
| 75 | (host-name "guix-deploy-hetzner-test-x86"))) | ||
| 76 | (environment hetzner-environment-type) | ||
| 77 | (configuration (hetzner-configuration | ||
| 78 | (labels %labels) | ||
| 79 | (server-type "cpx51") | ||
| 80 | (ssh-key %ssh-key-file))))) | ||
| 81 | |||
| 82 | (define (cleanup machine) | ||
| 83 | (let* ((config (machine-configuration machine)) | ||
| 84 | (api (hetzner-configuration-api config))) | ||
| 85 | (for-each (lambda (server) | ||
| 86 | (hetzner-api-server-delete api server)) | ||
| 87 | (hetzner-api-servers | ||
| 88 | api #:params `(("label_selector" . "guix.gnu.org/test=true")))) | ||
| 89 | (for-each (lambda (ssh-key) | ||
| 90 | (hetzner-api-ssh-key-delete api ssh-key)) | ||
| 91 | (hetzner-api-ssh-keys | ||
| 92 | api #:params `(("label_selector" . "guix.gnu.org/test=true")))) | ||
| 93 | machine)) | ||
| 94 | |||
| 95 | (define-syntax-rule (with-cleanup (machine-sym machine-init) body ...) | ||
| 96 | (let ((machine-sym (cleanup machine-init))) | ||
| 97 | (dynamic-wind | ||
| 98 | (const #t) | ||
| 99 | (lambda () | ||
| 100 | body ...) | ||
| 101 | (lambda () | ||
| 102 | (cleanup machine-sym))))) | ||
| 103 | |||
| 104 | (define (mock-action command) | ||
| 105 | (make-hetzner-action | ||
| 106 | command #f | ||
| 107 | (localtime (current-time)) | ||
| 108 | 1 | ||
| 109 | 100 | ||
| 110 | '() | ||
| 111 | (localtime (current-time)) | ||
| 112 | "success")) | ||
| 113 | |||
| 114 | (define (mock-location machine) | ||
| 115 | (let* ((config (machine-configuration machine)) | ||
| 116 | (name (hetzner-configuration-location config))) | ||
| 117 | (make-hetzner-location | ||
| 118 | "Falkenstein" "DE" "Falkenstein DC Park 1" | ||
| 119 | 1 50.47612 12.370071 name "eu-central"))) | ||
| 120 | |||
| 121 | (define (mock-server-type machine) | ||
| 122 | (let* ((config (machine-configuration machine)) | ||
| 123 | (name (hetzner-configuration-server-type config))) | ||
| 124 | (make-hetzner-server-type | ||
| 125 | "x86" 8 "shared" #f #f (string-upcase name) | ||
| 126 | 160 106 16 name "local"))) | ||
| 127 | |||
| 128 | (define (mock-server machine) | ||
| 129 | (let* ((config (machine-configuration machine)) | ||
| 130 | (name (hetzner-configuration-location config))) | ||
| 131 | (make-hetzner-server | ||
| 132 | 1 | ||
| 133 | (localtime (current-time)) | ||
| 134 | '() | ||
| 135 | (operating-system-host-name (machine-operating-system machine)) | ||
| 136 | (make-hetzner-public-net | ||
| 137 | (make-hetzner-ipv4 #f "server.example.com" 1 "1.2.3.4") | ||
| 138 | (make-hetzner-ipv6 #f "server.example.com" 1 "2001:db8::1")) | ||
| 139 | #f | ||
| 140 | (mock-server-type machine)))) | ||
| 141 | |||
| 142 | (define (mock-ssh-key machine) | ||
| 143 | (let ((config (machine-configuration machine))) | ||
| 144 | (hetzner-ssh-key-read-file (hetzner-configuration-ssh-key config)))) | ||
| 145 | |||
| 146 | (define (expected-ssh-machine? machine ssh-machine) | ||
| 147 | (let ((config (machine-configuration machine)) | ||
| 148 | (ssh-config (machine-configuration ssh-machine))) | ||
| 149 | (and (equal? (hetzner-configuration-authorize? config) | ||
| 150 | (machine-ssh-configuration-authorize? ssh-config)) | ||
| 151 | (equal? (hetzner-configuration-allow-downgrades? config) | ||
| 152 | (machine-ssh-configuration-allow-downgrades? ssh-config)) | ||
| 153 | (equal? (hetzner-configuration-build-locally? config) | ||
| 154 | (machine-ssh-configuration-build-locally? ssh-config)) | ||
| 155 | (equal? (hetzner-server-public-ipv4 (mock-server machine)) | ||
| 156 | (machine-ssh-configuration-host-name ssh-config))))) | ||
| 157 | |||
| 158 | (define-syntax mock* | ||
| 159 | (syntax-rules () | ||
| 160 | ((mock* () body1 body2 ...) | ||
| 161 | (let () body1 body2 ...)) | ||
| 162 | ((mock* ((mod1 sym1 fn1) (mod2 sym2 fn2) ...) | ||
| 163 | body1 body2 ...) | ||
| 164 | (mock (mod1 sym1 fn1) | ||
| 165 | (mock* ((mod2 sym2 fn2) ...) | ||
| 166 | body1) body2 ...)))) | ||
| 167 | |||
| 168 | (test-begin "machine-hetzner") | ||
| 169 | |||
| 170 | ;; The following tests deploy real machines using the Hetzner API and shut | ||
| 171 | ;; them down afterwards. | ||
| 172 | |||
| 173 | (test-skip %when-no-token) | ||
| 174 | (test-assert "deploy-arm-machine" | ||
| 175 | (with-cleanup (machine %arm-machine) | ||
| 176 | (deploy-hetzner machine))) | ||
| 177 | |||
| 178 | (test-skip %when-no-token) | ||
| 179 | (test-assert "deploy-x86-machine" | ||
| 180 | (with-cleanup (machine %x86-machine) | ||
| 181 | (deploy-hetzner machine))) | ||
| 182 | |||
| 183 | ;; The following tests simulate a deployment, they mock out the actual calls | ||
| 184 | ;; to the Hetzner API. | ||
| 185 | |||
| 186 | ;; Note: In order for mocking to work, the Guile compiler should not inline | ||
| 187 | ;; the mocked functions. To prevent this it was necessary to set! | ||
| 188 | ;; hetzner-machine-ssh-run-script in (gnu machine hetzner) like this: | ||
| 189 | |||
| 190 | ;; (set! hetzner-machine-ssh-run-script hetzner-machine-ssh-run-script) | ||
| 191 | |||
| 192 | (test-assert "deploy-machine-mock-with-provisioned-server" | ||
| 193 | (let ((machine (machine | ||
| 194 | (operating-system %hetzner-os-x86) | ||
| 195 | (environment hetzner-environment-type) | ||
| 196 | (configuration (hetzner-configuration | ||
| 197 | (api (hetzner-api (token "mock"))) | ||
| 198 | (ssh-key %ssh-key-file)))))) | ||
| 199 | (mock* (((gnu machine hetzner http) hetzner-api-locations | ||
| 200 | (lambda* (api . options) | ||
| 201 | (list (mock-location machine)))) | ||
| 202 | ((gnu machine hetzner http) hetzner-api-server-types | ||
| 203 | (lambda* (api . options) | ||
| 204 | (list (mock-server-type machine)))) | ||
| 205 | ((gnu machine hetzner http) hetzner-api-ssh-keys | ||
| 206 | (lambda* (api . options) | ||
| 207 | (list (mock-ssh-key machine)))) | ||
| 208 | ((gnu machine hetzner http) hetzner-api-servers | ||
| 209 | (lambda* (api . options) | ||
| 210 | (list (mock-server machine)))) | ||
| 211 | ((gnu machine) deploy-machine | ||
| 212 | (lambda* (ssh-machine) | ||
| 213 | (expected-ssh-machine? machine ssh-machine)))) | ||
| 214 | (deploy-hetzner machine)))) | ||
| 215 | |||
| 216 | (test-assert "deploy-machine-mock-with-unprovisioned-server" | ||
| 217 | (let ((machine (machine | ||
| 218 | (operating-system %hetzner-os-x86) | ||
| 219 | (environment hetzner-environment-type) | ||
| 220 | (configuration (hetzner-configuration | ||
| 221 | (api (hetzner-api (token "mock"))) | ||
| 222 | (ssh-key %ssh-key-file))))) | ||
| 223 | (servers '())) | ||
| 224 | (mock* (((gnu machine hetzner http) hetzner-api-locations | ||
| 225 | (lambda* (api . options) | ||
| 226 | (list (mock-location machine)))) | ||
| 227 | ((gnu machine hetzner http) hetzner-api-server-types | ||
| 228 | (lambda* (api . options) | ||
| 229 | (list (mock-server-type machine)))) | ||
| 230 | ((gnu machine hetzner http) hetzner-api-ssh-keys | ||
| 231 | (lambda* (api . options) | ||
| 232 | (list (mock-ssh-key machine)))) | ||
| 233 | ((gnu machine hetzner http) hetzner-api-servers | ||
| 234 | (lambda* (api . options) | ||
| 235 | servers)) | ||
| 236 | ((gnu machine hetzner http) hetzner-api-server-create | ||
| 237 | (lambda* (api name ssh-keys . options) | ||
| 238 | (set! servers (list (mock-server machine))) | ||
| 239 | (car servers))) | ||
| 240 | ((gnu machine hetzner http) hetzner-api-server-enable-rescue-system | ||
| 241 | (lambda (api server ssh-keys) | ||
| 242 | (mock-action "enable_rescue"))) | ||
| 243 | ((gnu machine hetzner http) hetzner-api-server-power-on | ||
| 244 | (lambda (api server) | ||
| 245 | (mock-action "start_server"))) | ||
| 246 | ((gnu machine hetzner) hetzner-machine-ssh-run-script | ||
| 247 | (lambda (ssh-session name content) | ||
| 248 | #t)) | ||
| 249 | ((guix ssh) open-ssh-session | ||
| 250 | (lambda* (host . options) | ||
| 251 | (make-session #:host host))) | ||
| 252 | ((gnu machine hetzner http) hetzner-api-server-reboot | ||
| 253 | (lambda (api server) | ||
| 254 | (mock-action "reboot_server"))) | ||
| 255 | ((ssh session) write-known-host! | ||
| 256 | (lambda (session) | ||
| 257 | #t)) | ||
| 258 | ((gnu machine) deploy-machine | ||
| 259 | (lambda* (ssh-machine) | ||
| 260 | (expected-ssh-machine? machine ssh-machine)))) | ||
| 261 | (deploy-hetzner machine)))) | ||
| 262 | |||
| 263 | (test-end "machine-hetzner") | ||
| 264 | |||
| 265 | ;; Local Variables: | ||
| 266 | ;; eval: (put 'with-cleanup 'scheme-indent-function 1) | ||
| 267 | ;; End: | ||
diff --git a/tests/machine/hetzner/http.scm b/tests/machine/hetzner/http.scm new file mode 100644 index 00000000000..618d9a4c94e --- /dev/null +++ b/tests/machine/hetzner/http.scm | |||
| @@ -0,0 +1,631 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | ||
| 2 | ;;; Copyright © 2024 Roman Scherer <roman@burningswell.com> | ||
| 3 | ;;; | ||
| 4 | ;;; This file is part of GNU Guix. | ||
| 5 | ;;; | ||
| 6 | ;;; GNU Guix is free software; you can redistribute it and/or modify it | ||
| 7 | ;;; under the terms of the GNU General Public License as published by | ||
| 8 | ;;; the Free Software Foundation; either version 3 of the License, or (at | ||
| 9 | ;;; your option) any later version. | ||
| 10 | ;;; | ||
| 11 | ;;; GNU Guix is distributed in the hope that it will be useful, but | ||
| 12 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | ;;; GNU General Public License for more details. | ||
| 15 | ;;; | ||
| 16 | ;;; You should have received a copy of the GNU General Public License | ||
| 17 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | |||
| 19 | (define-module (tests machine hetzner http) | ||
| 20 | #:use-module (debugging assert) | ||
| 21 | #:use-module (gnu machine hetzner http) | ||
| 22 | #:use-module (guix build utils) | ||
| 23 | #:use-module (guix tests) | ||
| 24 | #:use-module (srfi srfi-1) | ||
| 25 | #:use-module (srfi srfi-34) | ||
| 26 | #:use-module (srfi srfi-64) | ||
| 27 | #:use-module (ssh key)) | ||
| 28 | |||
| 29 | ;; Unit and integration tests the (gnu machine hetzner http) module. | ||
| 30 | |||
| 31 | ;; Integration tests require the GUIX_HETZNER_API_TOKEN environment variable. | ||
| 32 | ;; https://docs.hetzner.com/cloud/api/getting-started/generating-api-token | ||
| 33 | |||
| 34 | ;; The integration tests sometimes fail due to the Hetzner API not being able | ||
| 35 | ;; to allocate a resource. Switching to a different location might help. | ||
| 36 | |||
| 37 | (define %labels | ||
| 38 | '(("guix.gnu.org/test" . "true"))) | ||
| 39 | |||
| 40 | (define %server-name | ||
| 41 | "guix-hetzner-api-test-server") | ||
| 42 | |||
| 43 | (define %ssh-key-name | ||
| 44 | "guix-hetzner-api-test-key") | ||
| 45 | |||
| 46 | (define %ssh-key-file | ||
| 47 | (string-append "/tmp/" %ssh-key-name)) | ||
| 48 | |||
| 49 | (unless (file-exists? %ssh-key-file) | ||
| 50 | (private-key-to-file (make-keypair 'rsa 2048) %ssh-key-file)) | ||
| 51 | |||
| 52 | (define %ssh-key | ||
| 53 | (hetzner-ssh-key-read-file %ssh-key-file)) | ||
| 54 | |||
| 55 | (define %when-no-token | ||
| 56 | (if (hetzner-api-token (hetzner-api)) 0 1)) | ||
| 57 | |||
| 58 | (define action-create-server | ||
| 59 | (make-hetzner-action | ||
| 60 | "create_server" #f *unspecified* 1896091819 0 | ||
| 61 | (list (make-hetzner-resource 59570198 "server")) | ||
| 62 | #(0 17 11 2 1 125 0 32 -1 0 #f) "running")) | ||
| 63 | |||
| 64 | (define action-create-server-alist | ||
| 65 | '(("command" . "create_server") | ||
| 66 | ("error" . null) | ||
| 67 | ("finished" . null) | ||
| 68 | ("id" . 1896091819) | ||
| 69 | ("progress" . 0) | ||
| 70 | ("resources" . #((("type" . "server") ("id" . 59570198)))) | ||
| 71 | ("started" . "2025-02-02T11:17:00+00:00") | ||
| 72 | ("status" . "running"))) | ||
| 73 | |||
| 74 | (define action-delete-server | ||
| 75 | (make-hetzner-action | ||
| 76 | "delete_server" #f *unspecified* 1896091928 0 | ||
| 77 | (list (make-hetzner-resource 59570198 "server")) | ||
| 78 | #(10 17 11 2 1 125 0 32 -1 0 #f) "running")) | ||
| 79 | |||
| 80 | (define action-delete-server-alist | ||
| 81 | '(("command" . "delete_server") | ||
| 82 | ("error" . null) | ||
| 83 | ("finished" . null) | ||
| 84 | ("id" . 1896091928) | ||
| 85 | ("progress" . 0) | ||
| 86 | ("resources" . #((("type" . "server") ("id" . 59570198)))) | ||
| 87 | ("started" . "2025-02-02T11:17:10+00:00") | ||
| 88 | ("status" . "running"))) | ||
| 89 | |||
| 90 | (define action-enable-rescue | ||
| 91 | (make-hetzner-action | ||
| 92 | "enable_rescue" #f *unspecified* 1896091721 0 | ||
| 93 | (list (make-hetzner-resource 59570198 "server")) | ||
| 94 | #(10 17 11 2 1 125 0 32 -1 0 #f) "success")) | ||
| 95 | |||
| 96 | (define action-enable-rescue-alist | ||
| 97 | '(("command" . "enable_rescue") | ||
| 98 | ("error" . null) | ||
| 99 | ("finished" . null) | ||
| 100 | ("id" . 1896091721) | ||
| 101 | ("progress" . 0) | ||
| 102 | ("resources" . #((("type" . "server") ("id" . 59570198)))) | ||
| 103 | ("started" . "2025-02-02T11:17:10+00:00") | ||
| 104 | ("status" . "running"))) | ||
| 105 | |||
| 106 | (define action-power-off | ||
| 107 | (make-hetzner-action | ||
| 108 | "stop_server" #f *unspecified* 1896091721 0 | ||
| 109 | (list (make-hetzner-resource 59570198 "server")) | ||
| 110 | #(10 17 11 2 1 125 0 32 -1 0 #f) "success")) | ||
| 111 | |||
| 112 | (define action-power-off-alist | ||
| 113 | '(("command" . "stop_server") | ||
| 114 | ("error" . null) | ||
| 115 | ("finished" . null) | ||
| 116 | ("id" . 1896091721) | ||
| 117 | ("progress" . 0) | ||
| 118 | ("resources" . #((("type" . "server") ("id" . 59570198)))) | ||
| 119 | ("started" . "2025-02-02T11:17:10+00:00") | ||
| 120 | ("status" . "running"))) | ||
| 121 | |||
| 122 | (define action-power-on | ||
| 123 | (make-hetzner-action | ||
| 124 | "start_server" #f *unspecified* 1896091721 0 | ||
| 125 | (list (make-hetzner-resource 59570198 "server")) | ||
| 126 | #(10 17 11 2 1 125 0 32 -1 0 #f) "success")) | ||
| 127 | |||
| 128 | (define action-power-on-alist | ||
| 129 | '(("command" . "start_server") | ||
| 130 | ("error" . null) | ||
| 131 | ("finished" . null) | ||
| 132 | ("id" . 1896091721) | ||
| 133 | ("progress" . 0) | ||
| 134 | ("resources" . #((("type" . "server") ("id" . 59570198)))) | ||
| 135 | ("started" . "2025-02-02T11:17:10+00:00") | ||
| 136 | ("status" . "running"))) | ||
| 137 | |||
| 138 | (define action-reboot | ||
| 139 | (make-hetzner-action | ||
| 140 | "reboot_server" #f *unspecified* 1896091721 0 | ||
| 141 | (list (make-hetzner-resource 59570198 "server")) | ||
| 142 | #(10 17 11 2 1 125 0 32 -1 0 #f) "success")) | ||
| 143 | |||
| 144 | (define action-reboot-alist | ||
| 145 | '(("command" . "reboot_server") | ||
| 146 | ("error" . null) | ||
| 147 | ("finished" . null) | ||
| 148 | ("id" . 1896091721) | ||
| 149 | ("progress" . 0) | ||
| 150 | ("resources" . #((("type" . "server") ("id" . 59570198)))) | ||
| 151 | ("started" . "2025-02-02T11:17:10+00:00") | ||
| 152 | ("status" . "running"))) | ||
| 153 | |||
| 154 | (define meta-page-alist | ||
| 155 | '("pagination" | ||
| 156 | ("last_page" . 1) | ||
| 157 | ("next_page" . null) | ||
| 158 | ("page" . 1) | ||
| 159 | ("per_page" . 25) | ||
| 160 | ("previous_page" . null) | ||
| 161 | ("total_entries" . 1))) | ||
| 162 | |||
| 163 | (define location-falkenstein | ||
| 164 | (make-hetzner-location | ||
| 165 | "Falkenstein" "DE" "Falkenstein DC Park 1" | ||
| 166 | 1 50.47612 12.370071 "fsn1" "eu-central")) | ||
| 167 | |||
| 168 | (define location-falkenstein-alist | ||
| 169 | `(("city" . "Falkenstein") | ||
| 170 | ("country" . "DE") | ||
| 171 | ("description" . "Falkenstein DC Park 1") | ||
| 172 | ("id" . 1) | ||
| 173 | ("latitude" . 50.47612) | ||
| 174 | ("longitude" . 12.370071) | ||
| 175 | ("name" . "fsn1") | ||
| 176 | ("network_zone" . "eu-central"))) | ||
| 177 | |||
| 178 | (define server-type-cpx-11 | ||
| 179 | (make-hetzner-server-type | ||
| 180 | "x86" 2 "shared" #f *unspecified* | ||
| 181 | "CPX 11" 40 22 2 "cpx11" "local")) | ||
| 182 | |||
| 183 | (define server-type-cpx-11-alist | ||
| 184 | `(("architecture" . "x86") | ||
| 185 | ("cores" . 2) | ||
| 186 | ("cpu_type" . "shared") | ||
| 187 | ("deprecated" . #f) | ||
| 188 | ("deprecation" . null) | ||
| 189 | ("description" . "CPX 11") | ||
| 190 | ("disk" . 40) | ||
| 191 | ("id" . 22) | ||
| 192 | ("memory" . 2) | ||
| 193 | ("name" . "cpx11") | ||
| 194 | ("storage_type" . "local"))) | ||
| 195 | |||
| 196 | (define server-x86 | ||
| 197 | (make-hetzner-server | ||
| 198 | "2024-12-30T16:38:11+00:00" | ||
| 199 | 59570198 | ||
| 200 | '() | ||
| 201 | "guix-x86" | ||
| 202 | (make-hetzner-public-net | ||
| 203 | (make-hetzner-ipv4 #f "static.218.128.13.49.clients.your-server.de" 78014457 "49.13.128.218") | ||
| 204 | (make-hetzner-ipv6 #f '() 78014458 "2a01:4f8:c17:293e::/64")) | ||
| 205 | #f | ||
| 206 | server-type-cpx-11)) | ||
| 207 | |||
| 208 | (define server-x86-alist | ||
| 209 | `(("backup_window" . null) | ||
| 210 | ("created" . "2024-12-30T16:38:11+00:00") | ||
| 211 | ("id" . 59570198) | ||
| 212 | ("included_traffic" . 21990232555520) | ||
| 213 | ("ingoing_traffic" . 124530000) | ||
| 214 | ("iso" . null) | ||
| 215 | ("labels") | ||
| 216 | ("load_balancers" . #()) | ||
| 217 | ("locked" . #f) | ||
| 218 | ("name" . "guix-x86") | ||
| 219 | ("outgoing_traffic" . 1391250000) | ||
| 220 | ("placement_group" . null) | ||
| 221 | ("primary_disk_size" . 320) | ||
| 222 | ("private_net" . #()) | ||
| 223 | ("protection" ("rebuild" . #f) ("delete" . #f)) | ||
| 224 | ("public_net" | ||
| 225 | ("firewalls" . #()) | ||
| 226 | ("floating_ips" . #()) | ||
| 227 | ("ipv6" | ||
| 228 | ("id" . 78014458) | ||
| 229 | ("dns_ptr" . #()) | ||
| 230 | ("blocked" . #f) | ||
| 231 | ("ip" . "2a01:4f8:c17:293e::/64")) | ||
| 232 | ("ipv4" | ||
| 233 | ("id" . 78014457) | ||
| 234 | ("dns_ptr" . "static.218.128.13.49.clients.your-server.de") | ||
| 235 | ("blocked" . #f) | ||
| 236 | ("ip" . "49.13.128.218"))) | ||
| 237 | ("rescue_enabled" . #f) | ||
| 238 | ("server_type" ,@server-type-cpx-11-alist) | ||
| 239 | ("status" . "running") | ||
| 240 | ("volumes" . #()))) | ||
| 241 | |||
| 242 | (define ssh-key-root | ||
| 243 | (make-hetzner-ssh-key | ||
| 244 | #(55 2 19 28 9 123 6 300 -1 0 #f) | ||
| 245 | "8c:25:09:8f:37:0f:d8:f0:99:4e:ab:c7:5c:1b:c6:53" | ||
| 246 | 16510983 '() "root@example.com" | ||
| 247 | "ssh-ed25519 ABCAC3NzaC1lZDI1NTE5AAAAIBT3lLYPfOZV9NNrNk0jGCufWmXbFSz+ORxowJdHoSIM")) | ||
| 248 | |||
| 249 | (define ssh-key-root-alist | ||
| 250 | `(("created" . "2023-10-28T19:02:55+00:00") | ||
| 251 | ("fingerprint" . "8c:25:09:8f:37:0f:d8:f0:99:4e:ab:c7:5c:1b:c6:53") | ||
| 252 | ("id" . 16510983) | ||
| 253 | ("labels") | ||
| 254 | ("name" . "root@example.com") | ||
| 255 | ("public_key" . "ssh-ed25519 ABCAC3NzaC1lZDI1NTE5AAAAIBT3lLYPfOZV9NNrNk0jGCufWmXbFSz+ORxowJdHoSIM"))) | ||
| 256 | |||
| 257 | (define* (create-ssh-key api ssh-key #:key (labels %labels)) | ||
| 258 | (hetzner-api-ssh-key-create | ||
| 259 | api | ||
| 260 | (hetzner-ssh-key-name ssh-key) | ||
| 261 | (hetzner-ssh-key-public-key ssh-key) | ||
| 262 | #:labels labels)) | ||
| 263 | |||
| 264 | (define* (create-server api ssh-key #:key (labels %labels)) | ||
| 265 | (hetzner-api-server-create api %server-name (list ssh-key) | ||
| 266 | #:labels labels | ||
| 267 | #:server-type "cpx31")) | ||
| 268 | |||
| 269 | (define (cleanup api) | ||
| 270 | (for-each (lambda (server) | ||
| 271 | (hetzner-api-server-delete api server)) | ||
| 272 | (hetzner-api-servers | ||
| 273 | api #:params `(("label_selector" . "guix.gnu.org/test=true")))) | ||
| 274 | (for-each (lambda (ssh-key) | ||
| 275 | (hetzner-api-ssh-key-delete api ssh-key)) | ||
| 276 | (hetzner-api-ssh-keys | ||
| 277 | api #:params `(("label_selector" . "guix.gnu.org/test=true")))) | ||
| 278 | api) | ||
| 279 | |||
| 280 | (define-syntax-rule (with-cleanup-api (api-sym api-init) body ...) | ||
| 281 | (let ((api-sym (cleanup api-init))) | ||
| 282 | (dynamic-wind | ||
| 283 | (const #t) | ||
| 284 | (lambda () | ||
| 285 | body ...) | ||
| 286 | (lambda () | ||
| 287 | (cleanup api-sym))))) | ||
| 288 | |||
| 289 | (test-begin "machine-hetzner-api") | ||
| 290 | |||
| 291 | ;; Unit Tests | ||
| 292 | |||
| 293 | (test-equal "hetzner-api-actions-unit" | ||
| 294 | (list action-create-server action-delete-server) | ||
| 295 | (let ((actions (list action-create-server-alist action-delete-server-alist))) | ||
| 296 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 297 | (lambda* (request #:key expected) | ||
| 298 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 299 | (assert (equal? "https://api.hetzner.cloud/v1/actions" | ||
| 300 | (hetzner-api-request-url request))) | ||
| 301 | (assert (unspecified? (hetzner-api-request-body request))) | ||
| 302 | (assert (equal? `(("page" . 1) | ||
| 303 | ("id" . ,(string-join | ||
| 304 | (map (lambda (action) | ||
| 305 | (number->string (assoc-ref action "id"))) | ||
| 306 | actions) | ||
| 307 | ","))) | ||
| 308 | (hetzner-api-request-params request))) | ||
| 309 | (hetzner-api-response | ||
| 310 | (body `(("meta" . ,meta-page-alist) | ||
| 311 | ("actions" . #(,action-create-server-alist ,action-delete-server-alist))))))) | ||
| 312 | (hetzner-api-actions (hetzner-api) | ||
| 313 | (map (lambda (action) | ||
| 314 | (assoc-ref action "id")) | ||
| 315 | actions))))) | ||
| 316 | |||
| 317 | (test-equal "hetzner-api-locations-unit" | ||
| 318 | (list location-falkenstein) | ||
| 319 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 320 | (lambda* (request #:key expected) | ||
| 321 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 322 | (assert (equal? "https://api.hetzner.cloud/v1/locations" | ||
| 323 | (hetzner-api-request-url request))) | ||
| 324 | (assert (unspecified? (hetzner-api-request-body request))) | ||
| 325 | (assert (equal? '(("page" . 1)) (hetzner-api-request-params request))) | ||
| 326 | (hetzner-api-response | ||
| 327 | (body `(("meta" . ,meta-page-alist) | ||
| 328 | ("locations" . #(,location-falkenstein-alist))))))) | ||
| 329 | (hetzner-api-locations (hetzner-api)))) | ||
| 330 | |||
| 331 | (test-equal "hetzner-api-server-types-unit" | ||
| 332 | (list server-type-cpx-11) | ||
| 333 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 334 | (lambda* (request #:key expected) | ||
| 335 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 336 | (assert (equal? "https://api.hetzner.cloud/v1/server_types" | ||
| 337 | (hetzner-api-request-url request))) | ||
| 338 | (assert (unspecified? (hetzner-api-request-body request))) | ||
| 339 | (assert (equal? '(("page" . 1)) (hetzner-api-request-params request))) | ||
| 340 | (hetzner-api-response | ||
| 341 | (body `(("meta" . ,meta-page-alist) | ||
| 342 | ("server_types" . #(,server-type-cpx-11-alist))))))) | ||
| 343 | (hetzner-api-server-types (hetzner-api)))) | ||
| 344 | |||
| 345 | (test-equal "hetzner-api-server-create-unit" | ||
| 346 | server-x86 | ||
| 347 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 348 | (lambda* (request #:key expected) | ||
| 349 | (cond | ||
| 350 | ((equal? "https://api.hetzner.cloud/v1/servers" | ||
| 351 | (hetzner-api-request-url request)) | ||
| 352 | (assert (equal? 'POST (hetzner-api-request-method request))) | ||
| 353 | (hetzner-api-response | ||
| 354 | (body `(("action" . ,action-create-server-alist) | ||
| 355 | ("server" . ,server-x86-alist))))) | ||
| 356 | ((equal? "https://api.hetzner.cloud/v1/actions" | ||
| 357 | (hetzner-api-request-url request)) | ||
| 358 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 359 | (hetzner-api-response | ||
| 360 | (body `(("actions" . ,(vector (cons `("status" . "success") | ||
| 361 | action-create-server-alist))) | ||
| 362 | ("meta" . ,meta-page-alist)))))))) | ||
| 363 | (hetzner-api-server-create (hetzner-api) %server-name (list ssh-key-root)))) | ||
| 364 | |||
| 365 | (test-equal "hetzner-api-server-delete-unit" | ||
| 366 | (make-hetzner-action | ||
| 367 | "delete_server" #f *unspecified* 1896091928 0 | ||
| 368 | (list (make-hetzner-resource 59570198 "server")) | ||
| 369 | #(10 17 11 2 1 125 0 32 -1 0 #f) "success") | ||
| 370 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 371 | (lambda* (request #:key expected) | ||
| 372 | (cond | ||
| 373 | ((equal? "https://api.hetzner.cloud/v1/servers/59570198" | ||
| 374 | (hetzner-api-request-url request)) | ||
| 375 | (assert (equal? 'DELETE (hetzner-api-request-method request))) | ||
| 376 | (hetzner-api-response | ||
| 377 | (body `(("action" . ,action-delete-server-alist))))) | ||
| 378 | ((equal? "https://api.hetzner.cloud/v1/actions" | ||
| 379 | (hetzner-api-request-url request)) | ||
| 380 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 381 | (hetzner-api-response | ||
| 382 | (body `(("actions" . ,(vector (cons `("status" . "success") | ||
| 383 | action-delete-server-alist))) | ||
| 384 | ("meta" . ,meta-page-alist)))))))) | ||
| 385 | (hetzner-api-server-delete (hetzner-api) server-x86))) | ||
| 386 | |||
| 387 | (test-equal "hetzner-api-server-enable-rescue-system-unit" | ||
| 388 | action-enable-rescue | ||
| 389 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 390 | (lambda* (request #:key expected) | ||
| 391 | (cond | ||
| 392 | ((equal? "https://api.hetzner.cloud/v1/servers/59570198/actions/enable_rescue" | ||
| 393 | (hetzner-api-request-url request)) | ||
| 394 | (assert (equal? 'POST (hetzner-api-request-method request))) | ||
| 395 | (hetzner-api-response | ||
| 396 | (body `(("action" . ,action-enable-rescue-alist))))) | ||
| 397 | ((equal? "https://api.hetzner.cloud/v1/actions" | ||
| 398 | (hetzner-api-request-url request)) | ||
| 399 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 400 | (hetzner-api-response | ||
| 401 | (body `(("actions" . ,(vector (cons `("status" . "success") | ||
| 402 | action-enable-rescue-alist))) | ||
| 403 | ("meta" . ,meta-page-alist)))))))) | ||
| 404 | (hetzner-api-server-enable-rescue-system (hetzner-api) server-x86 (list ssh-key-root)))) | ||
| 405 | |||
| 406 | (test-equal "hetzner-api-server-power-on-unit" | ||
| 407 | action-power-on | ||
| 408 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 409 | (lambda* (request #:key expected) | ||
| 410 | (cond | ||
| 411 | ((equal? "https://api.hetzner.cloud/v1/servers/59570198/actions/poweron" | ||
| 412 | (hetzner-api-request-url request)) | ||
| 413 | (assert (equal? 'POST (hetzner-api-request-method request))) | ||
| 414 | (hetzner-api-response | ||
| 415 | (body `(("action" . ,action-power-on-alist))))) | ||
| 416 | ((equal? "https://api.hetzner.cloud/v1/actions" | ||
| 417 | (hetzner-api-request-url request)) | ||
| 418 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 419 | (hetzner-api-response | ||
| 420 | (body `(("actions" . ,(vector (cons `("status" . "success") | ||
| 421 | action-power-on-alist))) | ||
| 422 | ("meta" . ,meta-page-alist)))))))) | ||
| 423 | (hetzner-api-server-power-on (hetzner-api) server-x86))) | ||
| 424 | |||
| 425 | (test-equal "hetzner-api-server-power-off-unit" | ||
| 426 | action-power-off | ||
| 427 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 428 | (lambda* (request #:key expected) | ||
| 429 | (cond | ||
| 430 | ((equal? "https://api.hetzner.cloud/v1/servers/59570198/actions/poweroff" | ||
| 431 | (hetzner-api-request-url request)) | ||
| 432 | (assert (equal? 'POST (hetzner-api-request-method request))) | ||
| 433 | (hetzner-api-response | ||
| 434 | (body `(("action" . ,action-power-off-alist))))) | ||
| 435 | ((equal? "https://api.hetzner.cloud/v1/actions" | ||
| 436 | (hetzner-api-request-url request)) | ||
| 437 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 438 | (hetzner-api-response | ||
| 439 | (body `(("actions" . ,(vector (cons `("status" . "success") | ||
| 440 | action-power-off-alist))) | ||
| 441 | ("meta" . ,meta-page-alist)))))))) | ||
| 442 | (hetzner-api-server-power-off (hetzner-api) server-x86))) | ||
| 443 | |||
| 444 | (test-equal "hetzner-api-server-reboot-unit" | ||
| 445 | action-reboot | ||
| 446 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 447 | (lambda* (request #:key expected) | ||
| 448 | (cond | ||
| 449 | ((equal? "https://api.hetzner.cloud/v1/servers/59570198/actions/reboot" | ||
| 450 | (hetzner-api-request-url request)) | ||
| 451 | (assert (equal? 'POST (hetzner-api-request-method request))) | ||
| 452 | (hetzner-api-response | ||
| 453 | (body `(("action" . ,action-reboot-alist))))) | ||
| 454 | ((equal? "https://api.hetzner.cloud/v1/actions" | ||
| 455 | (hetzner-api-request-url request)) | ||
| 456 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 457 | (hetzner-api-response | ||
| 458 | (body `(("actions" . ,(vector (cons `("status" . "success") | ||
| 459 | action-reboot-alist))) | ||
| 460 | ("meta" . ,meta-page-alist)))))))) | ||
| 461 | (hetzner-api-server-reboot (hetzner-api) server-x86))) | ||
| 462 | |||
| 463 | (test-equal "hetzner-api-servers-unit" | ||
| 464 | (list server-x86) | ||
| 465 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 466 | (lambda* (request #:key expected) | ||
| 467 | (hetzner-api-response | ||
| 468 | (body `(("meta" . ,meta-page-alist) | ||
| 469 | ("servers" . #(,server-x86-alist))))))) | ||
| 470 | (hetzner-api-servers (hetzner-api)))) | ||
| 471 | |||
| 472 | (test-equal "hetzner-api-ssh-key-create-unit" | ||
| 473 | ssh-key-root | ||
| 474 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 475 | (lambda* (request #:key expected) | ||
| 476 | (assert (equal? 'POST (hetzner-api-request-method request))) | ||
| 477 | (assert (equal? "https://api.hetzner.cloud/v1/ssh_keys" | ||
| 478 | (hetzner-api-request-url request))) | ||
| 479 | (assert (equal? `(("name" . "guix-hetzner-api-test-key") | ||
| 480 | ("public_key" . "ssh-ed25519 ABCAC3NzaC1lZDI1NTE5AAAAIBT3lLYPfOZV9NNrNk0jGCufWmXbFSz+ORxowJdHoSIM") | ||
| 481 | ("labels" . (("a" . "1")))) | ||
| 482 | (hetzner-api-request-body request))) | ||
| 483 | (assert (equal? `() (hetzner-api-request-params request))) | ||
| 484 | (hetzner-api-response | ||
| 485 | (body `(("ssh_key" . ,ssh-key-root-alist)))))) | ||
| 486 | (hetzner-api-ssh-key-create | ||
| 487 | (hetzner-api) | ||
| 488 | "guix-hetzner-api-test-key" | ||
| 489 | "ssh-ed25519 ABCAC3NzaC1lZDI1NTE5AAAAIBT3lLYPfOZV9NNrNk0jGCufWmXbFSz+ORxowJdHoSIM" | ||
| 490 | #:labels '(("a" . "1"))))) | ||
| 491 | |||
| 492 | (test-assert "hetzner-api-ssh-key-delete-unit" | ||
| 493 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 494 | (lambda* (request #:key expected) | ||
| 495 | (assert (equal? "https://api.hetzner.cloud/v1/ssh_keys/16510983" | ||
| 496 | (hetzner-api-request-url request))) | ||
| 497 | (assert (equal? 'DELETE (hetzner-api-request-method request))) | ||
| 498 | (hetzner-api-response))) | ||
| 499 | (hetzner-api-ssh-key-delete (hetzner-api) ssh-key-root))) | ||
| 500 | |||
| 501 | (test-equal "hetzner-api-ssh-keys-unit" | ||
| 502 | (list ssh-key-root) | ||
| 503 | (mock ((gnu machine hetzner http) hetzner-api-request-send | ||
| 504 | (lambda* (request #:key expected) | ||
| 505 | (assert (equal? 'GET (hetzner-api-request-method request))) | ||
| 506 | (assert (equal? "https://api.hetzner.cloud/v1/ssh_keys" | ||
| 507 | (hetzner-api-request-url request))) | ||
| 508 | (assert (unspecified? (hetzner-api-request-body request))) | ||
| 509 | (assert (equal? '(("page" . 1)) (hetzner-api-request-params request))) | ||
| 510 | (hetzner-api-response | ||
| 511 | (body `(("meta" . ,meta-page-alist) | ||
| 512 | ("ssh_keys" . #(,ssh-key-root-alist))))))) | ||
| 513 | (hetzner-api-ssh-keys (hetzner-api)))) | ||
| 514 | |||
| 515 | ;; Integration tests | ||
| 516 | |||
| 517 | (test-skip %when-no-token) | ||
| 518 | (test-assert "hetzner-api-actions-integration" | ||
| 519 | (with-cleanup-api (api (hetzner-api)) | ||
| 520 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 521 | (server (create-server api ssh-key)) | ||
| 522 | (action (hetzner-api-server-enable-rescue-system api server (list ssh-key)))) | ||
| 523 | (member action (hetzner-api-actions api (list (hetzner-action-id action))))))) | ||
| 524 | |||
| 525 | (test-skip %when-no-token) | ||
| 526 | (test-assert "hetzner-api-locations-integration" | ||
| 527 | (let ((locations (hetzner-api-locations (hetzner-api)))) | ||
| 528 | (and (> (length locations) 0) | ||
| 529 | (every hetzner-location? locations)))) | ||
| 530 | |||
| 531 | (test-skip %when-no-token) | ||
| 532 | (test-assert "hetzner-api-server-types-integration" | ||
| 533 | (let ((server-types (hetzner-api-server-types (hetzner-api)))) | ||
| 534 | (and (> (length server-types) 0) | ||
| 535 | (every hetzner-server-type? server-types)))) | ||
| 536 | |||
| 537 | (test-skip %when-no-token) | ||
| 538 | (test-assert "hetzner-api-server-create-integration" | ||
| 539 | (with-cleanup-api (api (hetzner-api)) | ||
| 540 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 541 | (server (create-server api ssh-key))) | ||
| 542 | (and (hetzner-server? server) | ||
| 543 | (equal? %server-name (hetzner-server-name server)))))) | ||
| 544 | |||
| 545 | (test-skip %when-no-token) | ||
| 546 | (test-assert "hetzner-api-server-delete-integration" | ||
| 547 | (with-cleanup-api (api (hetzner-api)) | ||
| 548 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 549 | (server (create-server api ssh-key)) | ||
| 550 | (action (hetzner-api-server-delete api server))) | ||
| 551 | (and (hetzner-action? action) | ||
| 552 | (equal? "delete_server" | ||
| 553 | (hetzner-action-command action)))))) | ||
| 554 | |||
| 555 | (test-skip %when-no-token) | ||
| 556 | (test-assert "hetzner-api-server-enable-rescue-system-integration" | ||
| 557 | (with-cleanup-api (api (hetzner-api)) | ||
| 558 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 559 | (server (create-server api ssh-key)) | ||
| 560 | (action (hetzner-api-server-enable-rescue-system api server (list ssh-key)))) | ||
| 561 | (and (hetzner-action? action) | ||
| 562 | (equal? "enable_rescue" | ||
| 563 | (hetzner-action-command action)))))) | ||
| 564 | |||
| 565 | (test-skip %when-no-token) | ||
| 566 | (test-assert "hetzner-api-server-power-on-integration" | ||
| 567 | (with-cleanup-api (api (hetzner-api)) | ||
| 568 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 569 | (server (create-server api ssh-key)) | ||
| 570 | (action (hetzner-api-server-power-on api server))) | ||
| 571 | (and (hetzner-action? action) | ||
| 572 | (equal? "start_server" | ||
| 573 | (hetzner-action-command action)))))) | ||
| 574 | |||
| 575 | (test-skip %when-no-token) | ||
| 576 | (test-assert "hetzner-api-server-power-off-integration" | ||
| 577 | (with-cleanup-api (api (hetzner-api)) | ||
| 578 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 579 | (server (create-server api ssh-key)) | ||
| 580 | (action (hetzner-api-server-power-off api server))) | ||
| 581 | (and (hetzner-action? action) | ||
| 582 | (equal? "stop_server" | ||
| 583 | (hetzner-action-command action)))))) | ||
| 584 | |||
| 585 | (test-skip %when-no-token) | ||
| 586 | (test-assert "hetzner-api-server-reboot-integration" | ||
| 587 | (with-cleanup-api (api (hetzner-api)) | ||
| 588 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 589 | (server (create-server api ssh-key)) | ||
| 590 | (action (hetzner-api-server-reboot api server))) | ||
| 591 | (and (hetzner-action? action) | ||
| 592 | (equal? "reboot_server" | ||
| 593 | (hetzner-action-command action)))))) | ||
| 594 | |||
| 595 | (test-skip %when-no-token) | ||
| 596 | (test-assert "hetzner-api-servers-integration" | ||
| 597 | (with-cleanup-api (api (hetzner-api)) | ||
| 598 | (let* ((ssh-key (create-ssh-key api %ssh-key)) | ||
| 599 | (server (create-server api ssh-key))) | ||
| 600 | (member server (hetzner-api-servers api))))) | ||
| 601 | |||
| 602 | (test-skip %when-no-token) | ||
| 603 | (test-assert "hetzner-api-ssh-key-create-integration" | ||
| 604 | (with-cleanup-api (api (hetzner-api)) | ||
| 605 | (let ((ssh-key (create-ssh-key api %ssh-key))) | ||
| 606 | (and (hetzner-ssh-key? ssh-key) | ||
| 607 | (equal? (hetzner-ssh-key-fingerprint %ssh-key) | ||
| 608 | (hetzner-ssh-key-fingerprint ssh-key)) | ||
| 609 | (equal? (hetzner-ssh-key-name %ssh-key) | ||
| 610 | (hetzner-ssh-key-name ssh-key)) | ||
| 611 | (equal? (hetzner-ssh-key-public-key %ssh-key) | ||
| 612 | (hetzner-ssh-key-public-key ssh-key)))))) | ||
| 613 | |||
| 614 | (test-skip %when-no-token) | ||
| 615 | (test-assert "hetzner-api-ssh-key-delete-integration" | ||
| 616 | (with-cleanup-api (api (hetzner-api)) | ||
| 617 | (let ((ssh-key (create-ssh-key api %ssh-key))) | ||
| 618 | (and (equal? #t (hetzner-api-ssh-key-delete api ssh-key)) | ||
| 619 | (not (member ssh-key (hetzner-api-ssh-keys api))))))) | ||
| 620 | |||
| 621 | (test-skip %when-no-token) | ||
| 622 | (test-assert "hetzner-api-ssh-keys-integration" | ||
| 623 | (with-cleanup-api (api (hetzner-api)) | ||
| 624 | (let ((ssh-key (create-ssh-key api %ssh-key))) | ||
| 625 | (member ssh-key (hetzner-api-ssh-keys api))))) | ||
| 626 | |||
| 627 | (test-end "machine-hetzner-api") | ||
| 628 | |||
| 629 | ;; Local Variables: | ||
| 630 | ;; eval: (put 'with-cleanup-api 'scheme-indent-function 1) | ||
| 631 | ;; End: | ||
