Subject: Re: [bug#75144] [PATCH] machine: Implement 'hetzner-environment-type'.

From: Sergey Trofimov <[email-masked]>
Date: Thu, 13 Mar 2025 11:07:50 +0100

Hi Roman,

Roman Scherer <[email-masked]> writes:

> * gnu/machine/hetzner.scm: New file.
> * gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
> * guix/ssh.scm (open-ssh-session): Add stricthostkeycheck option.
> * doc/guix.texi (Invoking guix deploy): Add documentation for
> 'hetzner-configuration'.
>

That's a very welcome change and I'm glad it is merged already. I've
given it a try and stumbled upon some errors. See them below.

I started with the minimal configuration similar to the example from docs.
--8<---------------cut here---------------start------------->8---
(hetzner-configuration
    (server-type "cax11")
    (location "hel1")
    (ssh-key ".../id_rsa"))
--8<---------------cut here---------------end--------------->8---

Deploying it worked only half-way - the server got created, but
deploying actual OS failed due to my host (x86_64) not able to build
derivations for aarch64-linux.

This one I fixed by adding `qemu-binfmt-service-type` to my system config.
Second deployment attempt picked up the work (that's nice!) and started
building the derivations. After about 40 minutes I'd aborted it,
although I'm pretty sure it would've completed successfully. I just
didn't want to wait too long.

Next I've addded `(build-locally? #f)` to the VM config and repeated the
deployment. This one progressed much quicker, however it failed while
building `linux-modules`:

--8<---------------cut here---------------start------------->8---
building path(s) `/gnu/store/lsa1716vbccxf9flpnzbfqzbm9rh4ljl-linux-modules'
Backtrace:
          18 (primitive-load "/gnu/store/mfk843zl41s21banhzwkyfdxapa?")
In ice-9/eval.scm:
    619:8 17 (_ #f)
   626:19 16 (_ #<directory (guile-user) fffff771ec80>)
   293:34 15 (_ #(#<directory (guile-user) fffff771ec80> #<procedu?>))
In srfi/srfi-1.scm:
   586:29 14 (map1 _)
   586:29 13 (map1 _)
   586:29 12 (map1 _)
   586:29 11 (map1 _)
   586:29 10 (map1 _)
   586:29  9 (map1 _)
   586:29  8 (map1 _)
   586:29  7 (map1 _)
   586:29  6 (map1 _)
   586:29  5 (map1 _)
   586:29  4 (map1 _)
   586:29  3 (map1 _)
   586:29  2 (map1 _)
   586:17  1 (map1 ("pata_acpi" "pata_atiixp" "isci" "virtio_pci" # ?))
In gnu/build/linux-modules.scm:
    278:5  0 (_)

gnu/build/linux-modules.scm:278:5: kernel module not found "pata_acpi" "/gnu/store/nh5icvr5qvlaq1y54gpkqndy0rv2cq9r-linux-libre-6.13.6/lib/modules"
--8<---------------cut here---------------end--------------->8---

This seem to be caused by `deploy` not supporting `--target` parameter.
Adding these looked simple and I've jotted a small patch:

--8<---------------cut here---------------start------------->8---
>From 0d438d2fadc95fbe2eca73fc3c7f4278d58829d7 Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
From: Sergey Trofimov <[email-masked]>
Subject: [PATCH] Support --target and --system in guix deploy

---
 guix/scripts/deploy.scm | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/guix/scripts/deploy.scm b/guix/scripts/deploy.scm
index e2ef0006e0..5b6c6b8e79 100644
--- a/guix/scripts/deploy.scm
+++ b/guix/scripts/deploy.scm
@@ -26,6 +26,7 @@ (define-module (guix scripts deploy)
   #:use-module (guix scripts)
   #:use-module (guix scripts build)
   #:use-module (guix store)
+  #:use-module (guix utils)
   #:use-module (guix gexp)
   #:use-module (guix ui)
   #:use-module ((guix status) #:select (with-status-verbosity))
@@ -93,21 +94,22 @@ (define %options
          (option '(#\x "execute") #f #f
                  (lambda (opt name arg result)
                    (alist-cons 'execute-command? #t result)))
-         (option '(#\s "system") #t #f
-                 (lambda (opt name arg result)
-                   (alist-cons 'system arg
-                               (alist-delete 'system result eq?))))
          (option '(#\v "verbosity") #t #f
                  (lambda (opt name arg result)
                    (let ((level (string->number* arg)))
                      (alist-cons 'verbosity level
                                  (alist-delete 'verbosity result)))))

-         %standard-build-options))
+         (append
+          %standard-build-options
+          %standard-native-build-options
+          %standard-cross-build-options)))

 (define %default-options
   ;; Alist of default option values.
   `((verbosity . 1)
+    (system . ,(%current-system))
+    (target . #f)
     (debug . 0)
     (graft? . #t)
     (substitutes? . #t)
@@ -186,9 +188,13 @@ (define (deploy-machine* store machine)
             (when (deploy-error-should-roll-back c)
               (info (G_ "rolling back ~a...~%")
                     (machine-display-name machine))
-              (run-with-store store (roll-back-machine machine)))
+              (run-with-store store (roll-back-machine machine)
+                              #:system (%current-system)
+                              #:target (%current-target-system)))
             (apply throw (deploy-error-captured-args c))))
-      (run-with-store store (deploy-machine machine))
+      (run-with-store store (deploy-machine machine)
+           #:system (%current-system)
+           #:target (%current-target-system))

     (info (G_ "successfully deployed ~a~%")
           (machine-display-name machine))))
@@ -266,7 +272,9 @@ (define (invoke-command store machine command)
                (loop (cons line lines))))))))

   (match (run-with-store store
-           (machine-remote-eval machine invocation))
+           (machine-remote-eval machine invocation)
+           #:system (%current-system)
+           #:target (%current-target-system))
     ((code output)
      (match code
        ((? zero?)
@@ -325,7 +333,9 @@ (define-command (guix-deploy . args)
                                               #:verbosity
                                               (assoc-ref opts 'verbosity)
                                               #:dry-run? dry-run?)
-            (parameterize ((%graft? (assq-ref opts 'graft?)))
+            (parameterize ((%graft? (assq-ref opts 'graft?))
+                           (%current-target-system (assoc-ref opts 'target))
+                           (%current-system (assoc-ref opts 'system)))
               (if execute-command?
                   (match command
                     (("--" command ..1)

base-commit: 9449ab3c2025820d2e6fd679fa7e34832b667ea7
--
2.48.1

--8<---------------cut here---------------end--------------->8---

I wasn't able to confirm the patch works as during the deployment it
tries to build the toolchain which I can't afford on my host:

--8<---------------cut here---------------start------------->8---
The following derivations will be built:
  /gnu/store/gxr8v1yisdiyndka0abxrc0xzrra66sv-binutils-cross-aarch64-linux-gnu-2.41.drv
  /gnu/store/lch3711iiczn6smxsr7r3sj991p8avwv-ld-wrapper-aarch64-linux-gnu-0.drv
  /gnu/store/zmsnlbyml0vmphfdxyxw4ps25bgrwz92-gcc-cross-sans-libc-aarch64-linux-gnu-14.2.0.drv
  /gnu/store/57jnlmvqlvk6jkyvqcnrk4psffhmak91-linux-libre-headers-cross-aarch64-linux-gnu-5.15.49.drv
  /gnu/store/b4f1my595ggl7d5qn46vr6qllwx7g49z-glibc-cross-aarch64-linux-gnu-2.39.drv
  /gnu/store/sl5vfnwdarghf9ypbspq1bdlamnz3j2a-gcc-cross-aarch64-linux-gnu-14.2.0.drv
  /gnu/store/3vp8a7mz1576xbk278k9b73nx2zqmzlw-libffi-3.4.4.drv
  /gnu/store/5ij0pv5z7mi25r397y4k62ma7q38qrka-pkg-config-aarch64-linux-gnu-0.29.2.drv
  /gnu/store/y3hqwsbc8rb2g1mac8c9vsdmaacf20xm-libatomic-ops-7.6.12.drv
  /gnu/store/bd09d178ni5sp9db62w869c6m7d3sh6v-libgc-8.2.4.drv
  /gnu/store/cs7mzhrypgdad8v0v29arafc8brl7ynd-bash-minimal-5.1.16.drv
  /gnu/store/np51g0ak713az6shj6sv9j3wkq4cjvjx-libunistring-1.1.drv
  /gnu/store/rbkb4ig158h9gblbrah5nx5annvfpb4q-libxcrypt-4.4.36.drv
  /gnu/store/lfmamfv5vx690l9n6a1ixbbk6kzw3gsr-guile-3.0.9.drv
--8<---------------cut here---------------end--------------->8---

Finally, I want to highlight a couple things that I haven't figured out
for my use-case yet:
1. My private ssh key is stored in GnuPG and I'd like to keep it that
way. Afaik `managed-host-environment-type` can utilise the running
ssh-agent, could it be also implemented for hetzner machines?

2. My use-case is an on-demand wireguard VPN. In my current setup I have
created a static ipv6 address which I attach to the VM created using
`hcloud`. The wireguard config hardcodes the same ipv6 and is installed
on the VM during cloud-init provision (`--user-data-from-file`
parameter). To replicate the same in guix deploy,
`hetzner-configuration` should be more flexible in regards to public ip
addresses. I.e. it should allow to use either v4 or v6 and to accept
existing one provided by the user.