ssh.scm 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2019 Jakob L. Kreuze <zerodaysfordays@sdf.org>
  3. ;;; Copyright © 2020-2022 Ludovic Courtès <ludo@gnu.org>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (gnu machine ssh)
  20. #:use-module (gnu bootloader)
  21. #:use-module (gnu machine)
  22. #:autoload (gnu packages gnupg) (guile-gcrypt)
  23. #:use-module (gnu system)
  24. #:use-module (gnu system file-systems)
  25. #:use-module (gnu system uuid)
  26. #:use-module ((gnu services) #:select (sexp->system-provenance))
  27. #:use-module (guix diagnostics)
  28. #:use-module (guix memoization)
  29. #:use-module (guix gexp)
  30. #:use-module (guix i18n)
  31. #:use-module (guix modules)
  32. #:use-module (guix monads)
  33. #:use-module (guix pki)
  34. #:use-module (guix records)
  35. #:use-module (guix remote)
  36. #:use-module (guix scripts system reconfigure)
  37. #:use-module (guix ssh)
  38. #:use-module (guix store)
  39. #:use-module (guix utils)
  40. #:use-module ((guix self) #:select (make-config.scm))
  41. #:use-module ((guix inferior)
  42. #:select (inferior-exception?
  43. inferior-exception-arguments))
  44. #:use-module ((guix platform) #:select (systems))
  45. #:use-module (gcrypt pk-crypto)
  46. #:use-module (ice-9 format)
  47. #:use-module (ice-9 match)
  48. #:use-module (ice-9 textual-ports)
  49. #:use-module (srfi srfi-1)
  50. #:use-module (srfi srfi-9)
  51. #:use-module (srfi srfi-19)
  52. #:use-module (srfi srfi-26)
  53. #:use-module (srfi srfi-34)
  54. #:use-module (srfi srfi-35)
  55. #:export (managed-host-environment-type
  56. machine-ssh-configuration
  57. machine-ssh-configuration?
  58. machine-ssh-configuration
  59. machine-ssh-configuration-host-name
  60. machine-ssh-configuration-build-locally?
  61. machine-ssh-configuration-authorize?
  62. machine-ssh-configuration-allow-downgrades?
  63. machine-ssh-configuration-port
  64. machine-ssh-configuration-user
  65. machine-ssh-configuration-host-key
  66. machine-ssh-configuration-session))
  67. ;;; Commentary:
  68. ;;;
  69. ;;; This module implements remote evaluation and system deployment for
  70. ;;; machines that are accessible over SSH and have a known host-name. In the
  71. ;;; sense of the broader "machine" interface, we describe the environment for
  72. ;;; such machines as 'managed-host.
  73. ;;;
  74. ;;; Code:
  75. ;;;
  76. ;;; Parameters for the SSH client.
  77. ;;;
  78. (define-record-type* <machine-ssh-configuration> machine-ssh-configuration
  79. make-machine-ssh-configuration
  80. machine-ssh-configuration?
  81. this-machine-ssh-configuration
  82. (host-name machine-ssh-configuration-host-name) ; string
  83. (system machine-ssh-configuration-system ; string
  84. (sanitize validate-system-type))
  85. (build-locally? machine-ssh-configuration-build-locally? ; boolean
  86. (default #t))
  87. (authorize? machine-ssh-configuration-authorize? ; boolean
  88. (default #t))
  89. (allow-downgrades? machine-ssh-configuration-allow-downgrades? ; boolean
  90. (default #f))
  91. (safety-checks? machine-ssh-configuration-safety-checks? ;boolean
  92. (default #t))
  93. (port machine-ssh-configuration-port ; integer
  94. (default 22))
  95. (user machine-ssh-configuration-user ; string
  96. (default "root"))
  97. (identity machine-ssh-configuration-identity ; path to a private key
  98. (default #f))
  99. (session machine-ssh-configuration-session ; session
  100. (thunked)
  101. (default
  102. ;; By default, open the session once and cache it.
  103. (open-machine-ssh-session* this-machine-ssh-configuration)))
  104. (host-key machine-ssh-configuration-host-key ; #f | string
  105. (default #f)))
  106. (define-with-syntax-properties (validate-system-type (value properties))
  107. ;; Raise an error if VALUE is not a valid system type.
  108. (unless (string? value)
  109. (raise (make-compound-condition
  110. (condition
  111. (&error-location
  112. (location (source-properties->location properties))))
  113. (formatted-message
  114. (G_ "~a: invalid system type; must be a string")
  115. value))))
  116. (unless (member value (systems))
  117. (raise (apply make-compound-condition
  118. (condition
  119. (&error-location
  120. (location (source-properties->location properties))))
  121. (formatted-message (G_ "~a: unknown system type") value)
  122. (let ((closest (string-closest value (systems)
  123. #:threshold 5)))
  124. (if closest
  125. (list (condition
  126. (&fix-hint
  127. (hint (format #f (G_ "Did you mean @code{~a}?")
  128. closest)))))
  129. '())))))
  130. value)
  131. (define (open-machine-ssh-session config)
  132. "Open an SSH session for CONFIG, a <machine-ssh-configuration> record."
  133. (let ((host-name (machine-ssh-configuration-host-name config))
  134. (user (machine-ssh-configuration-user config))
  135. (port (machine-ssh-configuration-port config))
  136. (identity (machine-ssh-configuration-identity config))
  137. (host-key (machine-ssh-configuration-host-key config)))
  138. (unless host-key
  139. (warning (G_ "<machine-ssh-configuration> without a 'host-key' \
  140. is deprecated~%")))
  141. (open-ssh-session host-name
  142. #:user user
  143. #:port port
  144. #:identity identity
  145. #:host-key host-key)))
  146. (define open-machine-ssh-session*
  147. (mlambdaq (config)
  148. "Memoizing variant of 'open-machine-ssh-session'."
  149. (open-machine-ssh-session config)))
  150. (define (machine-ssh-session machine)
  151. "Return the SSH session that was given in MACHINE's configuration, or create
  152. one from the configuration's parameters if one was not provided."
  153. (maybe-raise-unsupported-configuration-error machine)
  154. (let ((config (machine-configuration machine)))
  155. (or (machine-ssh-configuration-session config)
  156. (open-machine-ssh-session config))))
  157. ;;;
  158. ;;; Remote evaluation.
  159. ;;;
  160. (define (machine-become-command machine)
  161. "Return as a list of strings the program and arguments necessary to run a
  162. shell command with escalated privileges for MACHINE's configuration."
  163. (if (string= "root" (machine-ssh-configuration-user
  164. (machine-configuration machine)))
  165. '()
  166. '("/run/setuid-programs/sudo" "-n" "--")))
  167. (define (managed-host-remote-eval machine exp)
  168. "Internal implementation of 'machine-remote-eval' for MACHINE instances with
  169. an environment type of 'managed-host."
  170. (maybe-raise-unsupported-configuration-error machine)
  171. (let ((config (machine-configuration machine)))
  172. (remote-eval exp (machine-ssh-session machine)
  173. #:build-locally?
  174. (machine-ssh-configuration-build-locally? config)
  175. #:system
  176. (machine-ssh-configuration-system config)
  177. #:become-command
  178. (machine-become-command machine))))
  179. ;;;
  180. ;;; Safety checks.
  181. ;;;
  182. ;; Assertion to be executed remotely. This abstraction exists to allow us to
  183. ;; gather a list of expressions to be evaluated and eventually evaluate them
  184. ;; all at once instead of one by one. (This is pretty much a monad.)
  185. (define-record-type <remote-assertion>
  186. (remote-assertion exp proc)
  187. remote-assertion?
  188. (exp remote-assertion-expression)
  189. (proc remote-assertion-procedure))
  190. (define-syntax-rule (remote-let ((var exp)) body ...)
  191. "Return a <remote-assertion> that binds VAR to the result of evaluating EXP,
  192. a gexp, remotely, and evaluate BODY in that context."
  193. (remote-assertion exp (lambda (var) body ...)))
  194. (define (machine-check-file-system-availability machine)
  195. "Return a list of <remote-assertion> that raise a '&message' error condition
  196. if any of the file-systems specified in MACHINE's 'system' declaration do not
  197. exist on the machine."
  198. (define file-systems
  199. (filter (lambda (fs)
  200. (and (file-system-mount? fs)
  201. (not (member (file-system-type fs)
  202. %pseudo-file-system-types))
  203. ;; Don't try to validate network file systems.
  204. (not (string-prefix? "nfs" (file-system-type fs)))
  205. (not (memq 'bind-mount (file-system-flags fs)))))
  206. (operating-system-file-systems (machine-operating-system machine))))
  207. (define (check-literal-file-system fs)
  208. (remote-let ((errno #~(catch 'system-error
  209. (lambda ()
  210. (stat #$(file-system-device fs))
  211. #t)
  212. (lambda args
  213. (system-error-errno args)))))
  214. (when (number? errno)
  215. (raise (formatted-message (G_ "device '~a' not found: ~a")
  216. (file-system-device fs)
  217. (strerror errno))))))
  218. (define (check-labeled-file-system fs)
  219. (define remote-exp
  220. (with-imported-modules (source-module-closure
  221. '((gnu build file-systems)))
  222. #~(begin
  223. (use-modules (gnu build file-systems))
  224. (find-partition-by-label #$(file-system-label->string
  225. (file-system-device fs))))))
  226. (remote-let ((result remote-exp))
  227. (unless result
  228. (raise (formatted-message (G_ "no file system with label '~a'")
  229. (file-system-label->string
  230. (file-system-device fs)))))))
  231. (define (check-uuid-file-system fs)
  232. (define remote-exp
  233. (with-imported-modules (source-module-closure
  234. '((gnu build file-systems)
  235. (gnu system uuid)))
  236. #~(begin
  237. (use-modules (gnu build file-systems)
  238. (gnu system uuid))
  239. (let ((uuid (uuid #$(uuid->string (file-system-device fs))
  240. '#$(uuid-type (file-system-device fs)))))
  241. (find-partition-by-uuid uuid)))))
  242. (remote-let ((result remote-exp))
  243. (unless result
  244. (raise (formatted-message (G_ "no file system with UUID '~a'")
  245. (uuid->string (file-system-device fs)))))))
  246. (if (machine-ssh-configuration-safety-checks?
  247. (machine-configuration machine))
  248. (append (map check-literal-file-system
  249. (filter (lambda (fs)
  250. (string? (file-system-device fs)))
  251. file-systems))
  252. (map check-labeled-file-system
  253. (filter (lambda (fs)
  254. (file-system-label? (file-system-device fs)))
  255. file-systems))
  256. (map check-uuid-file-system
  257. (filter (lambda (fs)
  258. (uuid? (file-system-device fs)))
  259. file-systems)))
  260. '()))
  261. (define (machine-check-initrd-modules machine)
  262. "Return a list of <remote-assertion> that raise a '&message' error condition
  263. if any of the modules needed by 'needed-for-boot' file systems in MACHINE are
  264. not available in the initrd."
  265. (define file-systems
  266. (filter file-system-needed-for-boot?
  267. (operating-system-file-systems (machine-operating-system machine))))
  268. (define (missing-modules fs)
  269. (define remote-exp
  270. (let ((device (file-system-device fs)))
  271. (with-imported-modules (source-module-closure
  272. '((gnu build file-systems)
  273. (gnu build linux-modules)
  274. (gnu system uuid)))
  275. #~(begin
  276. (use-modules (gnu build file-systems)
  277. (gnu build linux-modules)
  278. (gnu system uuid))
  279. (define dev
  280. #$(cond ((string? device) device)
  281. ((uuid? device) #~(find-partition-by-uuid
  282. (string->uuid
  283. #$(uuid->string device))))
  284. ((file-system-label? device)
  285. #~(find-partition-by-label
  286. #$(file-system-label->string device)))))
  287. (missing-modules dev '#$(operating-system-initrd-modules
  288. (machine-operating-system machine)))))))
  289. (remote-let ((missing remote-exp))
  290. (unless (null? missing)
  291. (raise (formatted-message (G_ "missing modules for ~a:~{ ~a~}~%")
  292. (file-system-device fs)
  293. missing)))))
  294. (if (machine-ssh-configuration-safety-checks?
  295. (machine-configuration machine))
  296. (map missing-modules file-systems)
  297. '()))
  298. (define* (machine-check-forward-update machine)
  299. "Check whether we are making a forward update for MACHINE. Depending on its
  300. 'allow-upgrades?' field, raise an error or display a warning if we are
  301. potentially downgrading it."
  302. (define config
  303. (machine-configuration machine))
  304. (define validate-reconfigure
  305. (if (machine-ssh-configuration-allow-downgrades? config)
  306. warn-about-backward-reconfigure
  307. ensure-forward-reconfigure))
  308. (remote-let ((provenance #~(call-with-input-file
  309. "/run/current-system/provenance"
  310. read)))
  311. (define channels
  312. (sexp->system-provenance provenance))
  313. (check-forward-update validate-reconfigure
  314. #:current-channels channels)))
  315. (define (machine-check-building-for-appropriate-system machine)
  316. "Raise a '&message' error condition if MACHINE is configured to be built
  317. locally and the 'system' field does not match the '%current-system' reported
  318. by MACHINE."
  319. (let ((config (machine-configuration machine))
  320. (system (remote-system (machine-ssh-session machine))))
  321. (when (and (machine-ssh-configuration-build-locally? config)
  322. (not (string= system (machine-ssh-configuration-system config))))
  323. (raise (formatted-message (G_ "incorrect target system\
  324. ('~a' was given, while the system reports that it is '~a')~%")
  325. (machine-ssh-configuration-system config)
  326. system)))))
  327. (define (check-deployment-sanity machine)
  328. "Raise a '&message' error condition if it is clear that deploying MACHINE's
  329. 'system' declaration would fail."
  330. (define assertions
  331. (parameterize ((%current-system
  332. (machine-ssh-configuration-system
  333. (machine-configuration machine)))
  334. (%current-target-system #f))
  335. (append (machine-check-file-system-availability machine)
  336. (machine-check-initrd-modules machine)
  337. (list (machine-check-forward-update machine)))))
  338. (define aggregate-exp
  339. ;; Gather all the expressions so that a single round-trip is enough to
  340. ;; evaluate all the ASSERTIONS remotely.
  341. #~(map (lambda (file)
  342. (false-if-exception (primitive-load file)))
  343. '#$(map (lambda (assertion)
  344. (scheme-file "remote-assertion.scm"
  345. (remote-assertion-expression assertion)))
  346. assertions)))
  347. ;; First check MACHINE's system type--an incorrect value for 'system' would
  348. ;; cause subsequent invocations of 'remote-eval' to fail.
  349. (machine-check-building-for-appropriate-system machine)
  350. (mlet %store-monad ((values (machine-remote-eval machine aggregate-exp)))
  351. (for-each (lambda (proc value)
  352. (proc value))
  353. (map remote-assertion-procedure assertions)
  354. values)
  355. (return #t)))
  356. ;;;
  357. ;;; System deployment.
  358. ;;;
  359. (define not-config?
  360. ;; Select (guix …) and (gnu …) modules, except (guix config).
  361. (match-lambda
  362. (('guix 'config) #f)
  363. (('guix _ ...) #t)
  364. (('gnu _ ...) #t)
  365. (_ #f)))
  366. (define (machine-boot-parameters machine)
  367. "Monadic procedure returning a list of 'boot-parameters' for the generations
  368. of MACHINE's system profile, ordered from most recent to oldest."
  369. (define bootable-kernel-arguments
  370. (@@ (gnu system) bootable-kernel-arguments))
  371. (define remote-exp
  372. (with-extensions (list guile-gcrypt)
  373. (with-imported-modules `(((guix config) => ,(make-config.scm))
  374. ,@(source-module-closure
  375. '((guix profiles))
  376. #:select? not-config?))
  377. #~(begin
  378. (use-modules (guix config)
  379. (guix profiles)
  380. (ice-9 textual-ports))
  381. (define %system-profile
  382. (string-append %state-directory "/profiles/system"))
  383. (define (read-file path)
  384. (call-with-input-file path
  385. (lambda (port)
  386. (get-string-all port))))
  387. (map (lambda (generation)
  388. (let* ((system-path (generation-file-name %system-profile
  389. generation))
  390. (boot-parameters-path (string-append system-path
  391. "/parameters"))
  392. (time (stat:mtime (lstat system-path))))
  393. (list generation
  394. system-path
  395. time
  396. (read-file boot-parameters-path))))
  397. (reverse (generation-numbers %system-profile)))))))
  398. (mlet* %store-monad ((generations (machine-remote-eval machine remote-exp)))
  399. (return
  400. (map (lambda (generation)
  401. (match generation
  402. ((generation system-path time serialized-params)
  403. (let* ((params (call-with-input-string serialized-params
  404. read-boot-parameters))
  405. (root (boot-parameters-root-device params))
  406. (label (boot-parameters-label params))
  407. (version (boot-parameters-version params)))
  408. (boot-parameters
  409. (inherit params)
  410. (label
  411. (string-append label " (#"
  412. (number->string generation) ", "
  413. (let ((time (make-time time-utc 0 time)))
  414. (date->string (time-utc->date time)
  415. "~Y-~m-~d ~H:~M"))
  416. ")"))
  417. (kernel-arguments
  418. (append (bootable-kernel-arguments system-path root version)
  419. (boot-parameters-kernel-arguments params))))))))
  420. generations))))
  421. (define-syntax-rule (with-roll-back should-roll-back? mbody ...)
  422. "Catch exceptions that arise when binding MBODY, a monadic expression in
  423. %STORE-MONAD, and collect their arguments in a &deploy-error condition, with
  424. the 'should-roll-back' field set to SHOULD-ROLL-BACK?"
  425. (catch #t
  426. (lambda ()
  427. mbody ...)
  428. (lambda args
  429. (raise (condition (&deploy-error
  430. (should-roll-back should-roll-back?)
  431. (captured-args args)))))))
  432. (define (deploy-managed-host machine)
  433. "Internal implementation of 'deploy-machine' for MACHINE instances with an
  434. environment type of 'managed-host."
  435. (define config (machine-configuration machine))
  436. (define host (machine-ssh-configuration-host-name config))
  437. (define system (machine-ssh-configuration-system config))
  438. (maybe-raise-unsupported-configuration-error machine)
  439. (when (machine-ssh-configuration-authorize?
  440. (machine-configuration machine))
  441. (unless (file-exists? %public-key-file)
  442. (raise (formatted-message (G_ "no signing key '~a'. \
  443. Have you run 'guix archive --generate-key'?")
  444. %public-key-file)))
  445. (remote-authorize-signing-key (call-with-input-file %public-key-file
  446. (lambda (port)
  447. (string->canonical-sexp
  448. (get-string-all port))))
  449. (machine-ssh-session machine)
  450. (machine-become-command machine)))
  451. (mlet %store-monad ((_ (check-deployment-sanity machine))
  452. (boot-parameters (machine-boot-parameters machine)))
  453. ;; Make sure code that check %CURRENT-SYSTEM, such as
  454. ;; %BASE-INITRD-MODULES, gets to see the right value.
  455. (parameterize ((%current-system system)
  456. (%current-target-system #f))
  457. (let* ((os (machine-operating-system machine))
  458. (eval (cut machine-remote-eval machine <>))
  459. (menu-entries (map boot-parameters->menu-entry boot-parameters))
  460. (bootloader-configuration (operating-system-bootloader os))
  461. (bootcfg (operating-system-bootcfg os menu-entries)))
  462. (define-syntax-rule (eval/error-handling condition handler ...)
  463. ;; Return a wrapper around EVAL such that HANDLER is evaluated if an
  464. ;; exception is raised.
  465. (lambda (exp)
  466. (lambda (store)
  467. (guard (condition ((inferior-exception? condition)
  468. (values (begin handler ...) store)))
  469. (values (run-with-store store (eval exp)
  470. #:system system)
  471. store)))))
  472. (mbegin %store-monad
  473. (with-roll-back #f
  474. (switch-to-system (eval/error-handling c
  475. (raise (formatted-message
  476. (G_ "\
  477. failed to switch systems while deploying '~a':~%~{~s ~}")
  478. host
  479. (inferior-exception-arguments c))))
  480. os))
  481. (with-roll-back #t
  482. (mbegin %store-monad
  483. (upgrade-shepherd-services (eval/error-handling c
  484. (warning (G_ "\
  485. an error occurred while upgrading services on '~a':~%~{~s ~}~%")
  486. host
  487. (inferior-exception-arguments
  488. c)))
  489. os)
  490. (install-bootloader (eval/error-handling c
  491. (raise (formatted-message
  492. (G_ "\
  493. failed to install bootloader on '~a':~%~{~s ~}~%")
  494. host
  495. (inferior-exception-arguments c))))
  496. bootloader-configuration bootcfg))))))))
  497. ;;;
  498. ;;; Roll-back.
  499. ;;;
  500. (define (roll-back-managed-host machine)
  501. "Internal implementation of 'roll-back-machine' for MACHINE instances with
  502. an environment type of 'managed-host."
  503. (define remote-exp
  504. (with-extensions (list guile-gcrypt)
  505. (with-imported-modules (source-module-closure '((guix config)
  506. (guix profiles)))
  507. #~(begin
  508. (use-modules (guix config)
  509. (guix profiles))
  510. (define %system-profile
  511. (string-append %state-directory "/profiles/system"))
  512. (define target-generation
  513. (relative-generation %system-profile -1))
  514. (if target-generation
  515. (switch-to-generation %system-profile target-generation)
  516. 'error)))))
  517. (define roll-back-failure
  518. (condition (&message (message (G_ "could not roll-back machine")))))
  519. (mlet* %store-monad ((boot-parameters (machine-boot-parameters machine))
  520. (_ -> (if (< (length boot-parameters) 2)
  521. (raise roll-back-failure)))
  522. (entries -> (map boot-parameters->menu-entry
  523. (list (second boot-parameters))))
  524. (locale -> (boot-parameters-locale
  525. (second boot-parameters)))
  526. (crypto-dev -> (boot-parameters-store-crypto-devices
  527. (second boot-parameters)))
  528. (store-dir -> (boot-parameters-store-directory-prefix
  529. (second boot-parameters)))
  530. (old-entries -> (map boot-parameters->menu-entry
  531. (drop boot-parameters 2)))
  532. (bootloader -> (operating-system-bootloader
  533. (machine-operating-system machine)))
  534. (bootcfg (lower-object
  535. ((bootloader-configuration-file-generator
  536. (bootloader-configuration-bootloader
  537. bootloader))
  538. bootloader entries
  539. #:locale locale
  540. #:store-crypto-devices crypto-dev
  541. #:store-directory-prefix store-dir
  542. #:old-entries old-entries)))
  543. (remote-result (machine-remote-eval machine remote-exp)))
  544. (when (eqv? 'error remote-result)
  545. (raise roll-back-failure))))
  546. ;;;
  547. ;;; Environment type.
  548. ;;;
  549. (define managed-host-environment-type
  550. (environment-type
  551. (machine-remote-eval managed-host-remote-eval)
  552. (deploy-machine deploy-managed-host)
  553. (roll-back-machine roll-back-managed-host)
  554. (name 'managed-host-environment-type)
  555. (description "Provisioning for machines that are accessible over SSH
  556. and have a known host-name. This entails little more than maintaining an SSH
  557. connection to the host.")))
  558. (define (maybe-raise-unsupported-configuration-error machine)
  559. "Raise an error if MACHINE's configuration is not an instance of
  560. <machine-ssh-configuration>."
  561. (let ((config (machine-configuration machine))
  562. (environment (environment-type-name (machine-environment machine))))
  563. (unless (and config (machine-ssh-configuration? config))
  564. (raise (formatted-message (G_ "unsupported machine configuration '~a'
  565. for environment of type '~a'")
  566. config
  567. environment)))))
  568. ;; Local Variables:
  569. ;; eval: (put 'remote-let 'scheme-indent-function 1)
  570. ;; eval: (put 'with-roll-back 'scheme-indent-function 1)
  571. ;; eval: (put 'eval/error-handling 'scheme-indent-function 1)
  572. ;; End: