* [bug#54377] [PATCH 0/3] Add 'guix home container'
@ 2022-03-13 21:52 Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 1/3] linux-container: 'eval/container' honors #:namespaces Ludovic Courtès
2022-03-17 15:01 ` [bug#54377] " Frank Pursel
0 siblings, 2 replies; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-13 21:52 UTC (permalink / raw)
To: 54377; +Cc: Ludovic Courtès, Andrew Tropin
Hello Guix!
This patch series adds a ‘guix home container’ command. You can run:
guix home container config.scm
and you get an interactive shell, started as a login shell, where you
can check the effect of your configuration. Handy!
The code is a bit redundant with that of ‘guix environment’, but
slightly different, so no obvious way to factorize it came to mind.
Feedback welcome!
Ludo’.
Ludovic Courtès (3):
linux-container: 'eval/container' honors #:namespaces.
linux-container: Add #:guest-uid and #:guest-gid to 'eval/container'.
guix home: Add 'container' command.
doc/guix.texi | 58 +++++++
gnu/system/linux-container.scm | 15 +-
guix/scripts/home.scm | 271 ++++++++++++++++++++++++++++++---
tests/guix-home.sh | 58 +++++--
4 files changed, 359 insertions(+), 43 deletions(-)
base-commit: d41c82b481fd0f5c7d45d6e2629fdf9d2085205b
--
2.34.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 1/3] linux-container: 'eval/container' honors #:namespaces.
2022-03-13 21:52 [bug#54377] [PATCH 0/3] Add 'guix home container' Ludovic Courtès
@ 2022-03-13 21:54 ` Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 2/3] linux-container: Add #:guest-uid and #:guest-gid to 'eval/container' Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 3/3] guix home: Add 'container' command Ludovic Courtès
2022-03-17 15:01 ` [bug#54377] " Frank Pursel
1 sibling, 2 replies; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-13 21:54 UTC (permalink / raw)
To: 54377; +Cc: Ludovic Courtès
* gnu/system/linux-container.scm (eval/container): Pass #:namespaces to
'call-with-container'.
---
gnu/system/linux-container.scm | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/gnu/system/linux-container.scm b/gnu/system/linux-container.scm
index e6fd0f1315..415d6b9775 100644
--- a/gnu/system/linux-container.scm
+++ b/gnu/system/linux-container.scm
@@ -291,4 +291,5 @@ (define items
lowered))
(list "-c"
(object->string
- (lowered-gexp-sexp lowered))))))))))))
+ (lowered-gexp-sexp lowered))))))
+ #:namespaces namespaces))))))
--
2.34.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 2/3] linux-container: Add #:guest-uid and #:guest-gid to 'eval/container'.
2022-03-13 21:54 ` [bug#54377] [PATCH 1/3] linux-container: 'eval/container' honors #:namespaces Ludovic Courtès
@ 2022-03-13 21:54 ` Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 3/3] guix home: Add 'container' command Ludovic Courtès
1 sibling, 0 replies; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-13 21:54 UTC (permalink / raw)
To: 54377; +Cc: Ludovic Courtès
* gnu/system/linux-container.scm (eval/container): Add #:guest-uid
and #:guest-gid and honor them.
---
gnu/system/linux-container.scm | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/gnu/system/linux-container.scm b/gnu/system/linux-container.scm
index 415d6b9775..eeb0f68c02 100644
--- a/gnu/system/linux-container.scm
+++ b/gnu/system/linux-container.scm
@@ -1,6 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015 David Thompson <davet@gnu.org>
-;;; Copyright © 2016, 2017, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2016-2017, 2019-2022 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2019 Arun Isaac <arunisaac@systemreboot.net>
;;; Copyright © 2020 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2020 Google LLC
@@ -248,11 +248,13 @@ (define (explain pid)
(define* (eval/container exp
#:key
(mappings '())
- (namespaces %namespaces))
+ (namespaces %namespaces)
+ (guest-uid 0) (guest-gid 0))
"Evaluate EXP, a gexp, in a new process executing in separate namespaces as
listed in NAMESPACES. Add MAPPINGS, a list of <file-system-mapping>, to the
-set of directories visible in the process's mount namespace. Return the
-process' exit status as a monadic value.
+set of directories visible in the process's mount namespace. Inside the
+namespaces, run code as GUEST-UID and GUEST-GID. Return the process' exit
+status as a monadic value.
This is useful to implement processes that, unlike derivations, are not
entirely pure and need to access the outside world or to perform side
@@ -292,4 +294,6 @@ (define items
(list "-c"
(object->string
(lowered-gexp-sexp lowered))))))
- #:namespaces namespaces))))))
+ #:namespaces namespaces
+ #:guest-uid guest-uid
+ #:guest-gid guest-gid))))))
--
2.34.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 3/3] guix home: Add 'container' command.
2022-03-13 21:54 ` [bug#54377] [PATCH 1/3] linux-container: 'eval/container' honors #:namespaces Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 2/3] linux-container: Add #:guest-uid and #:guest-gid to 'eval/container' Ludovic Courtès
@ 2022-03-13 21:54 ` Ludovic Courtès
2022-03-17 6:24 ` Andrew Tropin
1 sibling, 1 reply; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-13 21:54 UTC (permalink / raw)
To: 54377; +Cc: Ludovic Courtès
* guix/scripts/home.scm (show-help, %options): Add '--network',
'--share', and '--expose'.
(not-config?, user-shell, spawn-home-container): New procedures.
(%default-system-profile): New variable.
(perform-action): Add #:file-system-mappings, #:container-command,
and #:network?; honor them.
(process-action): Adjust accordingly.
(guix-home)[parse-sub-command]: Add "container".
[parse-args]: New procedure.
Use it instead of 'parse-command-line'.
* tests/guix-home.sh: Add tests.
* doc/guix.texi (Declaring the Home Environment): Mention 'guix home
container' as a way to test configuration.
(Invoking guix home): Document it.
---
doc/guix.texi | 58 +++++++++
guix/scripts/home.scm | 271 ++++++++++++++++++++++++++++++++++++++----
tests/guix-home.sh | 58 ++++++---
3 files changed, 349 insertions(+), 38 deletions(-)
diff --git a/doc/guix.texi b/doc/guix.texi
index 4b71fb7010..ba9199f336 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -38071,6 +38071,21 @@ be confused with Shepherd services (@pxref{Shepherd Services}). Using this exte
mechanism and some Scheme code that glues things together gives the user
the freedom to declare their own, very custom, home environments.
+@cindex container, for @command{guix home}
+Once the configuration looks good, you can first test it in a throw-away
+``container'':
+
+@example
+guix home container config.scm
+@end example
+
+The command above spawns a shell where your home environment is running.
+The shell runs in a container, meaning it's isolated from the rest of
+the system, so it's a good way to try out your configuration---you can
+see if configuration bits are missing or misbehaving, if daemons get
+started, and so on. Once you exit that shell, you're back to the prompt
+of your original shell ``in the real world''.
+
Once you have a configuration file that suits your needs, you can
reconfigure your home by running:
@@ -38699,6 +38714,49 @@ As for @command{guix search}, the result is written in
@code{recutils} format, which makes it easy to filter the output
(@pxref{Top, GNU recutils databases,, recutils, GNU recutils manual}).
+@cindex container, for @command{guix home}
+@item container
+Spawn a shell in an isolated environment---a
+@dfn{container}---containing your home as specified by @var{file}.
+
+For example, this is how you would start an interactive shell in a
+container with your home:
+
+@example
+guix home container config.scm
+@end example
+
+This is a throw-away container where you can lightheartedly fiddle with
+files; any changes made within the container, any process started---all
+this disappears as soon as you exit that shell.
+
+As with @command{guix shell}, several options control that container:
+
+@table @option
+@item --network
+@itemx -N
+Enable networking within the container (it is disabled by default).
+
+@item --expose=@var{source}[=@var{target}]
+@itemx --share=@var{source}[=@var{target}]
+As with @command{guix shell}, make directory @var{source} of the host
+system available as @var{target} inside the container---read-only if you
+pass @option{--expose}, and writable if you pass @option{--share}
+(@pxref{Invoking guix shell, @option{--expose} and @option{--share}}).
+@end table
+
+Additionally, you can run a command in that container, instead of
+spawning an interactive shell. For instance, here is how you would
+check which Shepherd services are started in a throw-away home
+container:
+
+@example
+guix home container config.scm -- herd status
+@end example
+
+The command to run in the container must come after @code{--} (double
+hyphen).
+
@item reconfigure
Build the home environment described in @var{file}, and switch to it.
Switching means that the activation script will be evaluated and (in
diff --git a/guix/scripts/home.scm b/guix/scripts/home.scm
index 837fd96361..b422cd36e2 100644
--- a/guix/scripts/home.scm
+++ b/guix/scripts/home.scm
@@ -23,8 +23,21 @@ (define-module (guix scripts home)
#:use-module (gnu packages admin)
#:use-module ((gnu services) #:hide (delete))
#:use-module (gnu packages)
+ #:autoload (gnu packages base) (coreutils)
+ #:autoload (gnu packages bash) (bash)
+ #:autoload (gnu packages gnupg) (guile-gcrypt)
+ #:autoload (gnu packages shells) (fish gash zsh)
#:use-module (gnu home)
#:use-module (gnu home services)
+ #:autoload (guix modules) (source-module-closure)
+ #:autoload (gnu build linux-container) (call-with-container %namespaces)
+ #:autoload (gnu system linux-container) (eval/container)
+ #:autoload (gnu system file-systems) (file-system-mapping
+ file-system-mapping-source
+ file-system-mapping->bind-mount
+ specification->file-system-mapping
+ %network-file-mappings)
+ #:autoload (guix self) (make-config.scm)
#:use-module (guix channels)
#:use-module (guix derivations)
#:use-module (guix ui)
@@ -48,6 +61,7 @@ (define-module (guix scripts home)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
+ #:use-module (srfi srfi-71)
#:use-module (ice-9 match)
#:export (guix-home))
@@ -95,6 +109,16 @@ (define (show-help)
(display (G_ "
--allow-downgrades for 'reconfigure', allow downgrades to earlier
channel revisions"))
+ (newline)
+ (display (G_ "
+ -N, --network allow containers to access the network"))
+ (display (G_ "
+ --share=SPEC for containers, share writable host file system
+ according to SPEC"))
+ (display (G_ "
+ --expose=SPEC for containers, expose read-only host file system
+ according to SPEC"))
+ (newline)
(display (G_ "
-v, --verbosity=LEVEL use the given verbosity LEVEL"))
(newline)
@@ -136,6 +160,22 @@ (define %options
(alist-cons 'validate-reconfigure
warn-about-backward-reconfigure
result)))
+
+ ;; Container options.
+ (option '(#\N "network") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'network? #t result)))
+ (option '("share") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'file-system-mapping
+ (specification->file-system-mapping arg #t)
+ result)))
+ (option '("expose") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'file-system-mapping
+ (specification->file-system-mapping arg #f)
+ result)))
+
%standard-build-options))
(define %default-options
@@ -149,6 +189,146 @@ (define %default-options
(debug . 0)
(validate-reconfigure . ,ensure-forward-reconfigure)))
+\f
+;;;
+;;; Container.
+;;;
+
+(define not-config?
+ ;; Select (guix …) and (gnu …) modules, except (guix config).
+ (match-lambda
+ (('guix 'config) #f)
+ (('guix _ ...) #t)
+ (('gnu _ ...) #t)
+ (_ #f)))
+
+(define (user-shell)
+ (match (and=> (or (getenv "SHELL")
+ (passwd:shell (getpwuid (getuid))))
+ basename)
+ ("zsh" (file-append zsh "/bin/zsh"))
+ ("fish" (file-append fish "/bin/fish"))
+ ("gash" (file-append gash "/bin/gash"))
+ (_ (file-append bash "/bin/bash"))))
+
+(define %default-system-profile
+ ;; The "system" profile available when running 'guix home container'. The
+ ;; activation script currently expects to run "env -0" (XXX), so provide
+ ;; Coreutils by default.
+ (delay (profile
+ (name "home-system-profile")
+ (content (packages->manifest (list coreutils))))))
+
+(define* (spawn-home-container home
+ #:key
+ network?
+ (command '())
+ (mappings '())
+ (system-profile
+ (force %default-system-profile)))
+ "Spawn a login shell within a container running HOME, a home environment.
+When COMMAND is a non-empty list, execute it in the container and exit
+immediately. Return the exit status of the process in the container."
+ (define passwd (getpwuid (getuid)))
+ (define home-directory (or (getenv "HOME") (passwd:dir passwd)))
+ (define host (gethostname))
+ (define uid 1000)
+ (define gid 1000)
+ (define user-name (passwd:name passwd))
+ (define user-real-name (passwd:gecos passwd))
+
+ (define (optional-mapping mapping)
+ (and (file-exists? (file-system-mapping-source mapping))
+ mapping))
+
+ (define network-mappings
+ (if network?
+ (filter-map optional-mapping %network-file-mappings)
+ '()))
+
+ (eval/container
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules `(((guix config) => ,(make-config.scm))
+ ,@(source-module-closure
+ '((gnu build accounts)
+ (guix profiles)
+ (guix build utils)
+ (guix build syscalls))
+ #:select? not-config?))
+ #~(begin
+ (use-modules (guix build utils)
+ (gnu build accounts)
+ ((guix build syscalls)
+ #:select (set-network-interface-up)))
+
+ (define shell
+ #$(user-shell))
+
+ (define term
+ #$(getenv "TERM"))
+
+ (define passwd
+ (password-entry
+ (name #$user-name)
+ (real-name #$user-real-name)
+ (uid #$uid) (gid #$gid) (shell shell)
+ (directory #$home-directory)))
+
+ (define groups
+ (list (group-entry (name "users") (gid #$gid))
+ (group-entry (gid 65534) ;the overflow GID
+ (name "overflow"))))
+
+ ;; (guix profiles) loads (guix utils), which calls 'getpw' from the
+ ;; top level. Thus, arrange so that it's loaded after /etc/passwd
+ ;; has been created.
+ (module-autoload! (current-module)
+ '(guix profiles) '(load-profile))
+
+ ;; Create /etc/passwd for applications that need it, such as mcron.
+ (mkdir-p "/etc")
+ (write-passwd (list passwd))
+ (write-group groups)
+
+ (unless #$network?
+ ;; When isolated from the network, provide a minimal /etc/hosts
+ ;; to resolve "localhost".
+ (call-with-output-file "/etc/hosts"
+ (lambda (port)
+ (display "127.0.0.1 localhost\n" port)
+ (chmod port #o444))))
+
+ ;; Set PATH for things that the activation script might expect, such
+ ;; as "env".
+ (load-profile #$system-profile)
+
+ (mkdir-p #$home-directory)
+ (setenv "HOME" #$home-directory)
+ (setenv "GUIX_NEW_HOME" #$home)
+ (primitive-load (string-append #$home "/activate"))
+ (setenv "GUIX_NEW_HOME" #f)
+
+ (when term
+ ;; Preserve TERM for proper interactive use.
+ (setenv "TERM" term))
+
+ (chdir #$home-directory)
+
+ ;; Invoke SHELL with argv[0] starting with "-": that's how shells
+ ;; figure out that they are login shells!
+ (execl shell (string-append "-" (basename shell))
+ #$@(match command
+ (() #~())
+ ((_ ...)
+ #~("-c" #$(string-join command))))))))
+
+ #:namespaces (if network?
+ (delq 'net %namespaces) ; share host network
+ %namespaces)
+ #:mappings (append network-mappings mappings)
+ #:guest-uid uid
+ #:guest-gid gid))
+
\f
;;;
;;; Actions.
@@ -159,7 +339,12 @@ (define* (perform-action action he
dry-run?
derivations-only?
use-substitutes?
- (validate-reconfigure ensure-forward-reconfigure))
+ (validate-reconfigure ensure-forward-reconfigure)
+
+ ;; Container options.
+ (file-system-mappings '())
+ (container-command '())
+ network?)
"Perform ACTION for home environment. "
(define println
@@ -180,24 +365,37 @@ (define println
(he-out-path -> (derivation->output-path he-drv)))
(if (or dry-run? derivations-only?)
(return #f)
- (begin
- (for-each (compose println derivation->output-path) drvs)
+ (case action
+ ((reconfigure)
+ (let* ((number (generation-number %guix-home))
+ (generation (generation-file-name
+ %guix-home (+ 1 number))))
- (case action
- ((reconfigure)
- (let* ((number (generation-number %guix-home))
- (generation (generation-file-name
- %guix-home (+ 1 number))))
-
- (switch-symlinks generation he-out-path)
- (switch-symlinks %guix-home generation)
- (setenv "GUIX_NEW_HOME" he-out-path)
- (primitive-load (string-append he-out-path "/activate"))
- (setenv "GUIX_NEW_HOME" #f)
- (return he-out-path)))
- (else
- (newline)
- (return he-out-path)))))))
+ (switch-symlinks generation he-out-path)
+ (switch-symlinks %guix-home generation)
+ (setenv "GUIX_NEW_HOME" he-out-path)
+ (primitive-load (string-append he-out-path "/activate"))
+ (setenv "GUIX_NEW_HOME" #f)
+ (return he-out-path)))
+ ((container)
+ (mlet %store-monad ((status (spawn-home-container
+ he
+ #:network? network?
+ #:mappings file-system-mappings
+ #:command
+ container-command)))
+ (match (status:exit-val status)
+ (0 (return #t))
+ ((? integer? n) (return (exit n)))
+ (#f
+ (if (status:term-sig status)
+ (leave (G_ "process terminated with signal ~a~%")
+ (status:term-sig status))
+ (leave (G_ "process stopped with signal ~a~%")
+ (status:stop-sig status)))))))
+ (else
+ (for-each (compose println derivation->output-path) drvs)
+ (return he-out-path))))))
(define (process-action action args opts)
"Process ACTION, a sub-command, with the arguments are listed in ARGS.
@@ -236,6 +434,10 @@ (define (ensure-home-environment file-or-exp obj)
(else
(leave (G_ "no configuration specified~%")))))))
+ (mappings (filter-map (match-lambda
+ (('file-system-mapping . mapping) mapping)
+ (_ #f))
+ opts))
(dry? (assoc-ref opts 'dry-run?)))
(with-store store
@@ -256,7 +458,11 @@ (define (ensure-home-environment file-or-exp obj)
#:derivations-only? (assoc-ref opts 'derivations-only?)
#:use-substitutes? (assoc-ref opts 'substitutes?)
#:validate-reconfigure
- (assoc-ref opts 'validate-reconfigure))))))
+ (assoc-ref opts 'validate-reconfigure)
+ #:network? (assoc-ref opts 'network?)
+ #:file-system-mappings mappings
+ #:container-command
+ (or (assoc-ref opts 'container-command) '()))))))
(warn-about-disk-space)))
@@ -345,7 +551,7 @@ (define (parse-sub-command arg result)
list-generations describe
delete-generations roll-back
switch-generation search
- import)
+ import container)
(alist-cons 'action action result))
(else (leave (G_ "~a: unknown action~%") action))))))
@@ -383,11 +589,28 @@ (define (fail)
(fail))))
args))
+ (define (parse-args args)
+ ;; Parse the list of command line arguments ARGS.
+
+ ;; The '--' token is used to separate the command to run from the rest of
+ ;; the operands.
+ (let* ((args rest (break (cut string=? "--" <>) args))
+ (opts (parse-command-line args %options (list %default-options)
+ #:argument-handler
+ parse-sub-command)))
+ (match rest
+ (() opts)
+ (("--") opts)
+ (("--" command ...)
+ (match (assoc-ref opts 'action)
+ ('container
+ (alist-cons 'container-command command opts))
+ (_
+ (leave (G_ "~a: extraneous command~%")
+ (string-join command))))))))
+
(with-error-handling
- (let* ((opts (parse-command-line args %options
- (list %default-options)
- #:argument-handler
- parse-sub-command))
+ (let* ((opts (parse-args args))
(args (option-arguments opts))
(command (assoc-ref opts 'action)))
(parameterize ((%graft? (assoc-ref opts 'graft?)))
diff --git a/tests/guix-home.sh b/tests/guix-home.sh
index f054d15172..13c02d6269 100644
--- a/tests/guix-home.sh
+++ b/tests/guix-home.sh
@@ -26,6 +26,16 @@ set -e
guix home --version
+container_supported ()
+{
+ if guile -c '((@ (guix scripts environment) assert-container-features))'
+ then
+ return 0
+ else
+ return 1
+ fi
+}
+
NIX_STORE_DIR="$(guile -c '(use-modules (guix config))(display %storedir)')"
localstatedir="$(guile -c '(use-modules (guix config))(display %localstatedir)')"
GUIX_DAEMON_SOCKET="$localstatedir/guix/daemon-socket/socket"
@@ -47,20 +57,6 @@ trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
(
cd "$test_directory" || exit 77
- HOME="$test_directory"
- export HOME
-
- #
- # Test 'guix home reconfigure'.
- #
-
- echo "# This file will be overridden and backed up." > "$HOME/.bashrc"
- mkdir "$HOME/.config"
- echo "This file will be overridden too." > "$HOME/.config/test.conf"
- echo "This file will stay around." > "$HOME/.config/random-file"
-
- echo -n "# dot-bashrc test file for guix home" > "dot-bashrc"
-
cat > "home.scm" <<'EOF'
(use-modules (guix gexp)
(gnu home)
@@ -93,6 +89,40 @@ trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
"# the content of bashrc-test-config.sh"))))))))
EOF
+ echo -n "# dot-bashrc test file for guix home" > "dot-bashrc"
+
+
+ if container_supported
+ then
+ # Run the home in a container.
+ guix home container home.scm -- true
+ ! guix home container home.scm -- false
+ test "$(guix home container home.scm -- echo '$HOME')" = "$HOME"
+ guix home container home.scm -- cat '~/.config/test.conf' | \
+ grep "the content of"
+ guix home container home.scm -- test -h '~/.bashrc'
+ test "$(guix home container home.scm -- id -u)" = 1000
+ ! guix home container home.scm -- test -f '$HOME/sample/home.scm'
+ guix home container home.scm --expose="$PWD=$HOME/sample" -- \
+ test -f '$HOME/sample/home.scm'
+ ! guix home container home.scm --expose="$PWD=$HOME/sample" -- \
+ rm -v '$HOME/sample/home.scm'
+ else
+ echo "'guix home container' test SKIPPED" >&2
+ fi
+
+ HOME="$test_directory"
+ export HOME
+
+ #
+ # Test 'guix home reconfigure'.
+ #
+
+ echo "# This file will be overridden and backed up." > "$HOME/.bashrc"
+ mkdir "$HOME/.config"
+ echo "This file will be overridden too." > "$HOME/.config/test.conf"
+ echo "This file will stay around." > "$HOME/.config/random-file"
+
guix home reconfigure "${test_directory}/home.scm"
test -d "${HOME}/.guix-home"
test -h "${HOME}/.bash_profile"
--
2.34.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 3/3] guix home: Add 'container' command.
2022-03-13 21:54 ` [bug#54377] [PATCH 3/3] guix home: Add 'container' command Ludovic Courtès
@ 2022-03-17 6:24 ` Andrew Tropin
2022-03-18 13:25 ` Ludovic Courtès
0 siblings, 1 reply; 13+ messages in thread
From: Andrew Tropin @ 2022-03-17 6:24 UTC (permalink / raw)
To: Ludovic Courtès, 54377; +Cc: Ludovic Courtès
[-- Attachment #1: Type: text/plain, Size: 23658 bytes --]
On 2022-03-13 22:54, Ludovic Courtès wrote:
> * guix/scripts/home.scm (show-help, %options): Add '--network',
> '--share', and '--expose'.
> (not-config?, user-shell, spawn-home-container): New procedures.
> (%default-system-profile): New variable.
> (perform-action): Add #:file-system-mappings, #:container-command,
> and #:network?; honor them.
> (process-action): Adjust accordingly.
> (guix-home)[parse-sub-command]: Add "container".
> [parse-args]: New procedure.
> Use it instead of 'parse-command-line'.
> * tests/guix-home.sh: Add tests.
> * doc/guix.texi (Declaring the Home Environment): Mention 'guix home
> container' as a way to test configuration.
> (Invoking guix home): Document it.
> ---
> doc/guix.texi | 58 +++++++++
> guix/scripts/home.scm | 271 ++++++++++++++++++++++++++++++++++++++----
> tests/guix-home.sh | 58 ++++++---
> 3 files changed, 349 insertions(+), 38 deletions(-)
>
> diff --git a/doc/guix.texi b/doc/guix.texi
> index 4b71fb7010..ba9199f336 100644
> --- a/doc/guix.texi
> +++ b/doc/guix.texi
> @@ -38071,6 +38071,21 @@ be confused with Shepherd services (@pxref{Shepherd Services}). Using this exte
> mechanism and some Scheme code that glues things together gives the user
> the freedom to declare their own, very custom, home environments.
>
> +@cindex container, for @command{guix home}
> +Once the configuration looks good, you can first test it in a throw-away
> +``container'':
> +
> +@example
> +guix home container config.scm
> +@end example
> +
> +The command above spawns a shell where your home environment is running.
> +The shell runs in a container, meaning it's isolated from the rest of
> +the system, so it's a good way to try out your configuration---you can
> +see if configuration bits are missing or misbehaving, if daemons get
> +started, and so on. Once you exit that shell, you're back to the prompt
> +of your original shell ``in the real world''.
> +
> Once you have a configuration file that suits your needs, you can
> reconfigure your home by running:
>
> @@ -38699,6 +38714,49 @@ As for @command{guix search}, the result is written in
> @code{recutils} format, which makes it easy to filter the output
> (@pxref{Top, GNU recutils databases,, recutils, GNU recutils manual}).
>
> +@cindex container, for @command{guix home}
> +@item container
> +Spawn a shell in an isolated environment---a
> +@dfn{container}---containing your home as specified by @var{file}.
> +
> +For example, this is how you would start an interactive shell in a
s/interactive/login ?
or interactive login shell
> +container with your home:
> +
> +@example
> +guix home container config.scm
> +@end example
> +
> +This is a throw-away container where you can lightheartedly fiddle with
> +files; any changes made within the container, any process started---all
> +this disappears as soon as you exit that shell.
> +
> +As with @command{guix shell}, several options control that container:
> +
> +@table @option
> +@item --network
> +@itemx -N
> +Enable networking within the container (it is disabled by default).
> +
> +@item --expose=@var{source}[=@var{target}]
> +@itemx --share=@var{source}[=@var{target}]
> +As with @command{guix shell}, make directory @var{source} of the host
> +system available as @var{target} inside the container---read-only if you
> +pass @option{--expose}, and writable if you pass @option{--share}
> +(@pxref{Invoking guix shell, @option{--expose} and @option{--share}}).
> +@end table
> +
> +Additionally, you can run a command in that container, instead of
> +spawning an interactive shell. For instance, here is how you would
In fact the sentence is correct, but gives a feeling that the shell
won't be executed at all. Don't know if we need to change it somehow.
> +check which Shepherd services are started in a throw-away home
> +container:
> +
> +@example
> +guix home container config.scm -- herd status
> +@end example
> +
> +The command to run in the container must come after @code{--} (double
> +hyphen).
> +
> @item reconfigure
> Build the home environment described in @var{file}, and switch to it.
> Switching means that the activation script will be evaluated and (in
> diff --git a/guix/scripts/home.scm b/guix/scripts/home.scm
> index 837fd96361..b422cd36e2 100644
> --- a/guix/scripts/home.scm
> +++ b/guix/scripts/home.scm
> @@ -23,8 +23,21 @@ (define-module (guix scripts home)
> #:use-module (gnu packages admin)
> #:use-module ((gnu services) #:hide (delete))
> #:use-module (gnu packages)
> + #:autoload (gnu packages base) (coreutils)
> + #:autoload (gnu packages bash) (bash)
> + #:autoload (gnu packages gnupg) (guile-gcrypt)
> + #:autoload (gnu packages shells) (fish gash zsh)
> #:use-module (gnu home)
> #:use-module (gnu home services)
> + #:autoload (guix modules) (source-module-closure)
> + #:autoload (gnu build linux-container) (call-with-container %namespaces)
> + #:autoload (gnu system linux-container) (eval/container)
> + #:autoload (gnu system file-systems) (file-system-mapping
> + file-system-mapping-source
> + file-system-mapping->bind-mount
> + specification->file-system-mapping
> + %network-file-mappings)
> + #:autoload (guix self) (make-config.scm)
> #:use-module (guix channels)
> #:use-module (guix derivations)
> #:use-module (guix ui)
> @@ -48,6 +61,7 @@ (define-module (guix scripts home)
> #:use-module (srfi srfi-26)
> #:use-module (srfi srfi-35)
> #:use-module (srfi srfi-37)
> + #:use-module (srfi srfi-71)
> #:use-module (ice-9 match)
> #:export (guix-home))
>
> @@ -95,6 +109,16 @@ (define (show-help)
> (display (G_ "
> --allow-downgrades for 'reconfigure', allow downgrades to earlier
> channel revisions"))
> + (newline)
> + (display (G_ "
> + -N, --network allow containers to access the network"))
Is plural form intended?
> + (display (G_ "
> + --share=SPEC for containers, share writable host file system
> + according to SPEC"))
> + (display (G_ "
> + --expose=SPEC for containers, expose read-only host file system
> + according to SPEC"))
> + (newline)
> (display (G_ "
> -v, --verbosity=LEVEL use the given verbosity LEVEL"))
> (newline)
> @@ -136,6 +160,22 @@ (define %options
> (alist-cons 'validate-reconfigure
> warn-about-backward-reconfigure
> result)))
> +
> + ;; Container options.
> + (option '(#\N "network") #f #f
> + (lambda (opt name arg result)
> + (alist-cons 'network? #t result)))
> + (option '("share") #t #f
> + (lambda (opt name arg result)
> + (alist-cons 'file-system-mapping
> + (specification->file-system-mapping arg #t)
> + result)))
> + (option '("expose") #t #f
> + (lambda (opt name arg result)
> + (alist-cons 'file-system-mapping
> + (specification->file-system-mapping arg #f)
> + result)))
> +
> %standard-build-options))
>
> (define %default-options
> @@ -149,6 +189,146 @@ (define %default-options
> (debug . 0)
> (validate-reconfigure . ,ensure-forward-reconfigure)))
>
> +\f
> +;;;
> +;;; Container.
> +;;;
> +
> +(define not-config?
> + ;; Select (guix …) and (gnu …) modules, except (guix config).
> + (match-lambda
> + (('guix 'config) #f)
> + (('guix _ ...) #t)
> + (('gnu _ ...) #t)
> + (_ #f)))
> +
> +(define (user-shell)
> + (match (and=> (or (getenv "SHELL")
Be aware that in some cases $SHELL can differ from the value in
/etc/passwd. For example I set SHELL to the full path to zsh and all
interactive non-login shells are zsh for me, but my login shell is bash.
> + (passwd:shell (getpwuid (getuid))))
> + basename)
> + ("zsh" (file-append zsh "/bin/zsh"))
> + ("fish" (file-append fish "/bin/fish"))
> + ("gash" (file-append gash "/bin/gash"))
> + (_ (file-append bash "/bin/bash"))))
Why we use hardcoded shell packages?
1. The will be built in case user use a zsh-patched for example.
2. The version of the shell in the container can differ from one user
expects.
3. $SHELL and the value in /etc/passwd should be absolute paths to the
store already, we can just add a check and throw an exception if $SHELL
is misscofigured and the path isn't absolute.
> +
> +(define %default-system-profile
> + ;; The "system" profile available when running 'guix home container'. The
> + ;; activation script currently expects to run "env -0" (XXX), so provide
> + ;; Coreutils by default.
> + (delay (profile
> + (name "home-system-profile")
> + (content (packages->manifest (list coreutils))))))
> +
> +(define* (spawn-home-container home
> + #:key
> + network?
> + (command '())
> + (mappings '())
> + (system-profile
> + (force %default-system-profile)))
> + "Spawn a login shell within a container running HOME, a home environment.
> +When COMMAND is a non-empty list, execute it in the container and exit
> +immediately. Return the exit status of the process in the container."
> + (define passwd (getpwuid (getuid)))
> + (define home-directory (or (getenv "HOME") (passwd:dir passwd)))
> + (define host (gethostname))
> + (define uid 1000)
> + (define gid 1000)
> + (define user-name (passwd:name passwd))
> + (define user-real-name (passwd:gecos passwd))
> +
> + (define (optional-mapping mapping)
> + (and (file-exists? (file-system-mapping-source mapping))
> + mapping))
> +
> + (define network-mappings
> + (if network?
> + (filter-map optional-mapping %network-file-mappings)
> + '()))
> +
> + (eval/container
> + (with-extensions (list guile-gcrypt)
> + (with-imported-modules `(((guix config) => ,(make-config.scm))
> + ,@(source-module-closure
> + '((gnu build accounts)
> + (guix profiles)
> + (guix build utils)
> + (guix build syscalls))
> + #:select? not-config?))
> + #~(begin
> + (use-modules (guix build utils)
> + (gnu build accounts)
> + ((guix build syscalls)
> + #:select (set-network-interface-up)))
> +
> + (define shell
> + #$(user-shell))
> +
> + (define term
> + #$(getenv "TERM"))
> +
> + (define passwd
> + (password-entry
> + (name #$user-name)
> + (real-name #$user-real-name)
> + (uid #$uid) (gid #$gid) (shell shell)
> + (directory #$home-directory)))
> +
> + (define groups
> + (list (group-entry (name "users") (gid #$gid))
> + (group-entry (gid 65534) ;the overflow GID
> + (name "overflow"))))
> +
> + ;; (guix profiles) loads (guix utils), which calls 'getpw' from the
> + ;; top level. Thus, arrange so that it's loaded after /etc/passwd
> + ;; has been created.
> + (module-autoload! (current-module)
> + '(guix profiles) '(load-profile))
> +
> + ;; Create /etc/passwd for applications that need it, such as mcron.
> + (mkdir-p "/etc")
> + (write-passwd (list passwd))
> + (write-group groups)
> +
> + (unless #$network?
> + ;; When isolated from the network, provide a minimal /etc/hosts
> + ;; to resolve "localhost".
> + (call-with-output-file "/etc/hosts"
> + (lambda (port)
> + (display "127.0.0.1 localhost\n" port)
> + (chmod port #o444))))
> +
> + ;; Set PATH for things that the activation script might expect, such
> + ;; as "env".
> + (load-profile #$system-profile)
> +
> + (mkdir-p #$home-directory)
> + (setenv "HOME" #$home-directory)
> + (setenv "GUIX_NEW_HOME" #$home)
> + (primitive-load (string-append #$home "/activate"))
> + (setenv "GUIX_NEW_HOME" #f)
> +
> + (when term
> + ;; Preserve TERM for proper interactive use.
> + (setenv "TERM" term))
Just a note: the shell can missbehave if terminfo files for current TERM
isn't present in the container (for example terminal package was removed
from home profile). Idk how to properly cover this, but just letting
you know. We can use TERM=dumb, but I'm not sure if it worth it.
> +
> + (chdir #$home-directory)
> +
> + ;; Invoke SHELL with argv[0] starting with "-": that's how shells
> + ;; figure out that they are login shells!
> + (execl shell (string-append "-" (basename shell))
> + #$@(match command
> + (() #~())
> + ((_ ...)
> + #~("-c" #$(string-join command))))))))
> +
> + #:namespaces (if network?
> + (delq 'net %namespaces) ; share host network
> + %namespaces)
> + #:mappings (append network-mappings mappings)
> + #:guest-uid uid
> + #:guest-gid gid))
> +
> \f
> ;;;
> ;;; Actions.
> @@ -159,7 +339,12 @@ (define* (perform-action action he
> dry-run?
> derivations-only?
> use-substitutes?
> - (validate-reconfigure ensure-forward-reconfigure))
> + (validate-reconfigure ensure-forward-reconfigure)
> +
> + ;; Container options.
> + (file-system-mappings '())
> + (container-command '())
> + network?)
> "Perform ACTION for home environment. "
>
> (define println
> @@ -180,24 +365,37 @@ (define println
> (he-out-path -> (derivation->output-path he-drv)))
> (if (or dry-run? derivations-only?)
> (return #f)
> - (begin
> - (for-each (compose println derivation->output-path) drvs)
> + (case action
> + ((reconfigure)
> + (let* ((number (generation-number %guix-home))
> + (generation (generation-file-name
> + %guix-home (+ 1 number))))
>
> - (case action
> - ((reconfigure)
> - (let* ((number (generation-number %guix-home))
> - (generation (generation-file-name
> - %guix-home (+ 1 number))))
> -
> - (switch-symlinks generation he-out-path)
> - (switch-symlinks %guix-home generation)
> - (setenv "GUIX_NEW_HOME" he-out-path)
> - (primitive-load (string-append he-out-path "/activate"))
> - (setenv "GUIX_NEW_HOME" #f)
> - (return he-out-path)))
> - (else
> - (newline)
> - (return he-out-path)))))))
> + (switch-symlinks generation he-out-path)
> + (switch-symlinks %guix-home generation)
> + (setenv "GUIX_NEW_HOME" he-out-path)
> + (primitive-load (string-append he-out-path "/activate"))
> + (setenv "GUIX_NEW_HOME" #f)
> + (return he-out-path)))
> + ((container)
> + (mlet %store-monad ((status (spawn-home-container
> + he
> + #:network? network?
> + #:mappings file-system-mappings
> + #:command
> + container-command)))
> + (match (status:exit-val status)
> + (0 (return #t))
> + ((? integer? n) (return (exit n)))
> + (#f
> + (if (status:term-sig status)
> + (leave (G_ "process terminated with signal ~a~%")
> + (status:term-sig status))
> + (leave (G_ "process stopped with signal ~a~%")
> + (status:stop-sig status)))))))
> + (else
> + (for-each (compose println derivation->output-path) drvs)
> + (return he-out-path))))))
>
> (define (process-action action args opts)
> "Process ACTION, a sub-command, with the arguments are listed in ARGS.
> @@ -236,6 +434,10 @@ (define (ensure-home-environment file-or-exp obj)
> (else
> (leave (G_ "no configuration specified~%")))))))
>
> + (mappings (filter-map (match-lambda
> + (('file-system-mapping . mapping) mapping)
> + (_ #f))
> + opts))
> (dry? (assoc-ref opts 'dry-run?)))
>
> (with-store store
> @@ -256,7 +458,11 @@ (define (ensure-home-environment file-or-exp obj)
> #:derivations-only? (assoc-ref opts 'derivations-only?)
> #:use-substitutes? (assoc-ref opts 'substitutes?)
> #:validate-reconfigure
> - (assoc-ref opts 'validate-reconfigure))))))
> + (assoc-ref opts 'validate-reconfigure)
> + #:network? (assoc-ref opts 'network?)
> + #:file-system-mappings mappings
> + #:container-command
> + (or (assoc-ref opts 'container-command) '()))))))
> (warn-about-disk-space)))
>
>
> @@ -345,7 +551,7 @@ (define (parse-sub-command arg result)
> list-generations describe
> delete-generations roll-back
> switch-generation search
> - import)
> + import container)
> (alist-cons 'action action result))
> (else (leave (G_ "~a: unknown action~%") action))))))
>
> @@ -383,11 +589,28 @@ (define (fail)
> (fail))))
> args))
>
> + (define (parse-args args)
> + ;; Parse the list of command line arguments ARGS.
> +
> + ;; The '--' token is used to separate the command to run from the rest of
> + ;; the operands.
> + (let* ((args rest (break (cut string=? "--" <>) args))
> + (opts (parse-command-line args %options (list %default-options)
> + #:argument-handler
> + parse-sub-command)))
> + (match rest
> + (() opts)
> + (("--") opts)
> + (("--" command ...)
> + (match (assoc-ref opts 'action)
> + ('container
> + (alist-cons 'container-command command opts))
> + (_
> + (leave (G_ "~a: extraneous command~%")
> + (string-join command))))))))
> +
> (with-error-handling
> - (let* ((opts (parse-command-line args %options
> - (list %default-options)
> - #:argument-handler
> - parse-sub-command))
> + (let* ((opts (parse-args args))
> (args (option-arguments opts))
> (command (assoc-ref opts 'action)))
> (parameterize ((%graft? (assoc-ref opts 'graft?)))
> diff --git a/tests/guix-home.sh b/tests/guix-home.sh
> index f054d15172..13c02d6269 100644
> --- a/tests/guix-home.sh
> +++ b/tests/guix-home.sh
> @@ -26,6 +26,16 @@ set -e
>
> guix home --version
>
> +container_supported ()
> +{
> + if guile -c '((@ (guix scripts environment) assert-container-features))'
> + then
> + return 0
> + else
> + return 1
> + fi
> +}
> +
> NIX_STORE_DIR="$(guile -c '(use-modules (guix config))(display %storedir)')"
> localstatedir="$(guile -c '(use-modules (guix config))(display %localstatedir)')"
> GUIX_DAEMON_SOCKET="$localstatedir/guix/daemon-socket/socket"
> @@ -47,20 +57,6 @@ trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
> (
> cd "$test_directory" || exit 77
>
> - HOME="$test_directory"
> - export HOME
> -
> - #
> - # Test 'guix home reconfigure'.
> - #
> -
> - echo "# This file will be overridden and backed up." > "$HOME/.bashrc"
> - mkdir "$HOME/.config"
> - echo "This file will be overridden too." > "$HOME/.config/test.conf"
> - echo "This file will stay around." > "$HOME/.config/random-file"
> -
> - echo -n "# dot-bashrc test file for guix home" > "dot-bashrc"
> -
> cat > "home.scm" <<'EOF'
> (use-modules (guix gexp)
> (gnu home)
> @@ -93,6 +89,40 @@ trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
> "# the content of bashrc-test-config.sh"))))))))
> EOF
>
> + echo -n "# dot-bashrc test file for guix home" > "dot-bashrc"
> +
> +
> + if container_supported
> + then
> + # Run the home in a container.
> + guix home container home.scm -- true
> + ! guix home container home.scm -- false
> + test "$(guix home container home.scm -- echo '$HOME')" = "$HOME"
> + guix home container home.scm -- cat '~/.config/test.conf' | \
> + grep "the content of"
> + guix home container home.scm -- test -h '~/.bashrc'
> + test "$(guix home container home.scm -- id -u)" = 1000
> + ! guix home container home.scm -- test -f '$HOME/sample/home.scm'
> + guix home container home.scm --expose="$PWD=$HOME/sample" -- \
> + test -f '$HOME/sample/home.scm'
> + ! guix home container home.scm --expose="$PWD=$HOME/sample" -- \
> + rm -v '$HOME/sample/home.scm'
> + else
> + echo "'guix home container' test SKIPPED" >&2
> + fi
> +
> + HOME="$test_directory"
> + export HOME
> +
> + #
> + # Test 'guix home reconfigure'.
> + #
> +
> + echo "# This file will be overridden and backed up." > "$HOME/.bashrc"
> + mkdir "$HOME/.config"
> + echo "This file will be overridden too." > "$HOME/.config/test.conf"
> + echo "This file will stay around." > "$HOME/.config/random-file"
> +
> guix home reconfigure "${test_directory}/home.scm"
> test -d "${HOME}/.guix-home"
> test -h "${HOME}/.bash_profile"
Very cool feature! Looking forward to add it to my workflow.
--
Best regards,
Andrew Tropin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 853 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 0/3] Add 'guix home container'
2022-03-13 21:52 [bug#54377] [PATCH 0/3] Add 'guix home container' Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 1/3] linux-container: 'eval/container' honors #:namespaces Ludovic Courtès
@ 2022-03-17 15:01 ` Frank Pursel
2022-03-19 10:32 ` Ludovic Courtès
1 sibling, 1 reply; 13+ messages in thread
From: Frank Pursel @ 2022-03-17 15:01 UTC (permalink / raw)
To: 54377
Ludo,
This is a very exciting idea. Entry into guix home has some wrinkles in
it that are scary. I know it required several iterations over
home-configuration.scm and 'guix home reconfigure' before I was able to
make peace with my guix home. I see in irc that others are also
experiencing challenges getting the environments where they want them to
be. This patch allows more control over the adoption of guix home while
also cleverly leveraging improvements to guix containers. Very cool, I
want it!
I ran this patch against my current, working home configuration to see
what happened and I ran into a problem. My home-configuration.scm sets
the GUIX_PACKAGE_PATH (using simple-service
'local-environment-variables) and then references packages that are in
that path (in my case a package called emacs-ix). When I executed 'guix
home container <my-config.scm>' I received the following errors:
guix home: error: emacs-ix: unknown package
guix home: error: failed to load '/home/fpp/src/guix-home-config/home-configuration.scm':
gnu/packages.scm:544:4: In procedure specification->package+output:
IDK if this is the desired behavior. Can the environment set in
config.scm be set before loading the packages that may modify the
environment?
Regards,
Frank Pursel
acrow#guix
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 3/3] guix home: Add 'container' command.
2022-03-17 6:24 ` Andrew Tropin
@ 2022-03-18 13:25 ` Ludovic Courtès
2022-03-19 5:09 ` Andrew Tropin
0 siblings, 1 reply; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-18 13:25 UTC (permalink / raw)
To: Andrew Tropin; +Cc: 54377
Hi Andrew,
Andrew Tropin <andrew@trop.in> skribis:
>> +For example, this is how you would start an interactive shell in a
>
> s/interactive/login ?
>
> or interactive login shell
Yeah. I thought that as a user, what matters is that it’s interactive;
the fact that it’s a “login shell” is more of an implementation detail,
and too few people understand what that means anyway. :-)
[...]
>> +Additionally, you can run a command in that container, instead of
>> +spawning an interactive shell. For instance, here is how you would
>
> In fact the sentence is correct, but gives a feeling that the shell
> won't be executed at all. Don't know if we need to change it somehow.
I agree that the sentence is an approximation of how it does things, but
hopefully it gives a good idea of what it.
>> + (display (G_ "
>> + -N, --network allow containers to access the network"))
>
> Is plural form intended?
(Copied from environment.scm.) I think it’s grammatically OK.
>> +(define (user-shell)
>> + (match (and=> (or (getenv "SHELL")
>
> Be aware that in some cases $SHELL can differ from the value in
> /etc/passwd. For example I set SHELL to the full path to zsh and all
> interactive non-login shells are zsh for me, but my login shell is bash.
Agreed, that’s why I thought $SHELL should take precedence. (I used
“SHELL=zsh guix home container …” and similar to test other shells.)
>> + (passwd:shell (getpwuid (getuid))))
>> + basename)
>> + ("zsh" (file-append zsh "/bin/zsh"))
>> + ("fish" (file-append fish "/bin/fish"))
>> + ("gash" (file-append gash "/bin/gash"))
>> + (_ (file-append bash "/bin/bash"))))
>
> Why we use hardcoded shell packages?
For reproducibility.
Initially I thought about using the actual $SHELL (as long as it’s in
the store). However, that would make ‘guix home container’ stateful:
it’d provide different results depending on the environment.
I thought we’d rather avoid that.
> 1. The will be built in case user use a zsh-patched for example.
That’s the downside, yes.
I don’t have a good answer to that. I guess I value reproducibility
more than customization in this case.
Perhaps we could eventually add a ‘--shell’ option or similar if that
helps, though. WDYT?
>> + (when term
>> + ;; Preserve TERM for proper interactive use.
>> + (setenv "TERM" term))
>
> Just a note: the shell can missbehave if terminfo files for current TERM
> isn't present in the container (for example terminal package was removed
> from home profile). Idk how to properly cover this, but just letting
> you know. We can use TERM=dumb, but I'm not sure if it worth it.
Good point. In my tests preserving TERM was good enough for
Bash/Readline, Zsh, and less (all from an xterm). I suppose problems
could happen with fancy curses apps and the like.
> Very cool feature! Looking forward to add it to my workflow.
Glad you like it! It makes it easier to test new features or services,
much like using ‘guix system vm’ when testing Guix System changes.
Thanks for taking the time to review!
Ludo’.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 3/3] guix home: Add 'container' command.
2022-03-18 13:25 ` Ludovic Courtès
@ 2022-03-19 5:09 ` Andrew Tropin
2022-03-19 10:39 ` [bug#54377] [PATCH 0/3] Add 'guix home container' Ludovic Courtès
2022-03-19 18:24 ` bug#54377: " Ludovic Courtès
0 siblings, 2 replies; 13+ messages in thread
From: Andrew Tropin @ 2022-03-19 5:09 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: 54377
[-- Attachment #1: Type: text/plain, Size: 4395 bytes --]
On 2022-03-18 14:25, Ludovic Courtès wrote:
> Hi Andrew,
>
> Andrew Tropin <andrew@trop.in> skribis:
>
>>> +For example, this is how you would start an interactive shell in a
>>
>> s/interactive/login ?
>>
>> or interactive login shell
>
> Yeah. I thought that as a user, what matters is that it’s interactive;
> the fact that it’s a “login shell” is more of an implementation detail,
> and too few people understand what that means anyway. :-)
>
> [...]
>
>>> +Additionally, you can run a command in that container, instead of
>>> +spawning an interactive shell. For instance, here is how you would
>>
>> In fact the sentence is correct, but gives a feeling that the shell
>> won't be executed at all. Don't know if we need to change it somehow.
>
> I agree that the sentence is an approximation of how it does things, but
> hopefully it gives a good idea of what it.
>
>>> + (display (G_ "
>>> + -N, --network allow containers to access the network"))
>>
>> Is plural form intended?
>
> (Copied from environment.scm.) I think it’s grammatically OK.
>
>>> +(define (user-shell)
>>> + (match (and=> (or (getenv "SHELL")
>>
>> Be aware that in some cases $SHELL can differ from the value in
>> /etc/passwd. For example I set SHELL to the full path to zsh and all
>> interactive non-login shells are zsh for me, but my login shell is bash.
>
> Agreed, that’s why I thought $SHELL should take precedence. (I used
> “SHELL=zsh guix home container …” and similar to test other shells.)
>
IIRC, $SHELL must contain a full path
https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
If we want to control a shell type inside container it probably should
be a different variable, like GUIX_CONTAINER_SHELL=zsh.
>>> + (passwd:shell (getpwuid (getuid))))
>>> + basename)
>>> + ("zsh" (file-append zsh "/bin/zsh"))
>>> + ("fish" (file-append fish "/bin/fish"))
>>> + ("gash" (file-append gash "/bin/gash"))
>>> + (_ (file-append bash "/bin/bash"))))
>>
>> Why we use hardcoded shell packages?
>
> For reproducibility.
>
> Initially I thought about using the actual $SHELL (as long as it’s in
> the store). However, that would make ‘guix home container’ stateful:
> it’d provide different results depending on the environment.
>
> I thought we’d rather avoid that.
>
What I meant by previous comment: to match a real state of the things
it's better to use a shell from /etc/passwd, because it will be launched
on user login and will read env vars and run all the following
processes, but to make it more reproducible and independent from system
state I think we always have to use hardcoded bash and inside the
container inspect the value of $SHELL set by login shell (hardcoded
bash) and spawn new shell if $SHELL is NOT empty.
>> 1. The will be built in case user use a zsh-patched for example.
>
> That’s the downside, yes.
>
> I don’t have a good answer to that. I guess I value reproducibility
> more than customization in this case.
>
> Perhaps we could eventually add a ‘--shell’ option or similar if that
> helps, though. WDYT?
>
I don't think we need such customization. User can spawn nested shell
himself from home profile by `-- zsh` or if we inspect $SHELL and use it
value inside container it will be spawned automatically.
>>> + (when term
>>> + ;; Preserve TERM for proper interactive use.
>>> + (setenv "TERM" term))
>>
>> Just a note: the shell can missbehave if terminfo files for current TERM
>> isn't present in the container (for example terminal package was removed
>> from home profile). Idk how to properly cover this, but just letting
>> you know. We can use TERM=dumb, but I'm not sure if it worth it.
>
> Good point. In my tests preserving TERM was good enough for
> Bash/Readline, Zsh, and less (all from an xterm). I suppose problems
> could happen with fancy curses apps and the like.
>
>> Very cool feature! Looking forward to add it to my workflow.
>
> Glad you like it! It makes it easier to test new features or services,
> much like using ‘guix system vm’ when testing Guix System changes.
>
> Thanks for taking the time to review!
Sure!)
--
Best regards,
Andrew Tropin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 853 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 0/3] Add 'guix home container'
2022-03-17 15:01 ` [bug#54377] " Frank Pursel
@ 2022-03-19 10:32 ` Ludovic Courtès
2022-03-23 3:06 ` Frank Pursel
0 siblings, 1 reply; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-19 10:32 UTC (permalink / raw)
To: Frank Pursel; +Cc: 54377
Hi Frank,
Frank Pursel <frank.pursel@gmail.com> skribis:
> This is a very exciting idea. Entry into guix home has some wrinkles in
> it that are scary. I know it required several iterations over
> home-configuration.scm and 'guix home reconfigure' before I was able to
> make peace with my guix home. I see in irc that others are also
> experiencing challenges getting the environments where they want them to
> be. This patch allows more control over the adoption of guix home while
> also cleverly leveraging improvements to guix containers. Very cool, I
> want it!
I agree, that very well summarizes my motivation for adding it!
> I ran this patch against my current, working home configuration to see
> what happened and I ran into a problem. My home-configuration.scm sets
> the GUIX_PACKAGE_PATH (using simple-service
> 'local-environment-variables) and then references packages that are in
> that path (in my case a package called emacs-ix). When I executed 'guix
> home container <my-config.scm>' I received the following errors:
>
> guix home: error: emacs-ix: unknown package
> guix home: error: failed to load '/home/fpp/src/guix-home-config/home-configuration.scm':
> gnu/packages.scm:544:4: In procedure specification->package+output:
>
> IDK if this is the desired behavior. Can the environment set in
> config.scm be set before loading the packages that may modify the
> environment?
If you want ‘home-configuration.scm’ to refer to files in that path,
then GUIX_PACKAGE_PATH needs to be set in the environment of where ‘guix
home’ itself is running. Like so:
export GUIX_PACKAGE_PATH=/path/to/your/packages
guix home container …
Alternatively, you can run:
guix home container -L /path/to/your/packages …
HTH!
Ludo’.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 0/3] Add 'guix home container'
2022-03-19 5:09 ` Andrew Tropin
@ 2022-03-19 10:39 ` Ludovic Courtès
2022-03-19 15:03 ` Andrew Tropin
2022-03-19 18:24 ` bug#54377: " Ludovic Courtès
1 sibling, 1 reply; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-19 10:39 UTC (permalink / raw)
To: Andrew Tropin; +Cc: 54377
Hi,
Andrew Tropin <andrew@trop.in> skribis:
> What I meant by previous comment: to match a real state of the things
> it's better to use a shell from /etc/passwd, because it will be launched
> on user login and will read env vars and run all the following
> processes, but to make it more reproducible and independent from system
> state I think we always have to use hardcoded bash and inside the
> container inspect the value of $SHELL set by login shell (hardcoded
> bash) and spawn new shell if $SHELL is NOT empty.
Yeah, there’s a tension between conflicting criteria: independence from
system state, and faithful reproduction of what would happen on the
system. I feel like checking $SHELL is a middle ground.
I don’t know, we can revisit that as we gain more experience with the
tool I guess.
Thanks,
Ludo’.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 0/3] Add 'guix home container'
2022-03-19 10:39 ` [bug#54377] [PATCH 0/3] Add 'guix home container' Ludovic Courtès
@ 2022-03-19 15:03 ` Andrew Tropin
0 siblings, 0 replies; 13+ messages in thread
From: Andrew Tropin @ 2022-03-19 15:03 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: 54377
[-- Attachment #1: Type: text/plain, Size: 1466 bytes --]
On 2022-03-19 11:39, Ludovic Courtès wrote:
> Hi,
>
> Andrew Tropin <andrew@trop.in> skribis:
>
>> What I meant by previous comment: to match a real state of the things
>> it's better to use a shell from /etc/passwd, because it will be launched
>> on user login and will read env vars and run all the following
>> processes, but to make it more reproducible and independent from system
>> state I think we always have to use hardcoded bash and inside the
>> container inspect the value of $SHELL set by login shell (hardcoded
>> bash) and spawn new shell if $SHELL is NOT empty.
>
> Yeah, there’s a tension between conflicting criteria: independence from
> system state, and faithful reproduction of what would happen on the
> system. I feel like checking $SHELL is a middle ground.
>
> I don’t know, we can revisit that as we gain more experience with the
> tool I guess.
I think the good implementation will be a hardcoded bash (or other shell
defined in /etc/passwd) launched as a login shell, which tries to
execute $SHELL if it is present in bash_profile or somewhere else in
shell startup files (not the $SHELL defined in host environment, but the
one which will be set inside container by login shell). It both makes
the container reproducible and adds possibility to use patched zsh, fish
or whatever unusual shell user set in his $SHELL.
The current solution probably ok as well.
--
Best regards,
Andrew Tropin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 853 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* bug#54377: [PATCH 0/3] Add 'guix home container'
2022-03-19 5:09 ` Andrew Tropin
2022-03-19 10:39 ` [bug#54377] [PATCH 0/3] Add 'guix home container' Ludovic Courtès
@ 2022-03-19 18:24 ` Ludovic Courtès
1 sibling, 0 replies; 13+ messages in thread
From: Ludovic Courtès @ 2022-03-19 18:24 UTC (permalink / raw)
To: Andrew Tropin; +Cc: 54377-done
Hi!
I pushed the series together with a news entry:
a58defdae8 news: Add entry about 'guix home container'.
094a2cfbe4 guix home: Add 'container' command.
cff9fee82a linux-container: Add #:guest-uid and #:guest-gid to 'eval/container'.
c77544b387 linux-container: 'eval/container' honors #:namespaces.
Let’s tweak things such as the default shell as we get more hands-on
feedback.
I also expect we might want a ‘--system-manifest’ option for users who’d
like to have more than just Coreutils in the container.
Thanks,
Ludo’.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [bug#54377] [PATCH 0/3] Add 'guix home container'
2022-03-19 10:32 ` Ludovic Courtès
@ 2022-03-23 3:06 ` Frank Pursel
0 siblings, 0 replies; 13+ messages in thread
From: Frank Pursel @ 2022-03-23 3:06 UTC (permalink / raw)
To: Ludovic Courtès; +Cc: 54377
[-- Attachment #1: Type: text/plain, Size: 2408 bytes --]
Yes, of course, export or -L were needed to get my GUIX_PACKAGE_PATH
recognized. and so it was helpful. After catching a configuration error
the GUIX_PACKAGE_PATH I had added to an instance of
home-environment-service-type works; so that, I no longer need to do this.
I'm in the process of finding out how helpful the new guix home container
is in rooting out problems in guix home configurations.
Thank you,
Frank
On Sat, Mar 19, 2022 at 10:32 AM Ludovic Courtès <ludo@gnu.org> wrote:
> Hi Frank,
>
> Frank Pursel <frank.pursel@gmail.com> skribis:
>
> > This is a very exciting idea. Entry into guix home has some wrinkles in
> > it that are scary. I know it required several iterations over
> > home-configuration.scm and 'guix home reconfigure' before I was able to
> > make peace with my guix home. I see in irc that others are also
> > experiencing challenges getting the environments where they want them to
> > be. This patch allows more control over the adoption of guix home while
> > also cleverly leveraging improvements to guix containers. Very cool, I
> > want it!
>
> I agree, that very well summarizes my motivation for adding it!
>
> > I ran this patch against my current, working home configuration to see
> > what happened and I ran into a problem. My home-configuration.scm sets
> > the GUIX_PACKAGE_PATH (using simple-service
> > 'local-environment-variables) and then references packages that are in
> > that path (in my case a package called emacs-ix). When I executed 'guix
> > home container <my-config.scm>' I received the following errors:
> >
> > guix home: error: emacs-ix: unknown package
> > guix home: error: failed to load
> '/home/fpp/src/guix-home-config/home-configuration.scm':
> > gnu/packages.scm:544:4: In procedure specification->package+output:
> >
> > IDK if this is the desired behavior. Can the environment set in
> > config.scm be set before loading the packages that may modify the
> > environment?
>
> If you want ‘home-configuration.scm’ to refer to files in that path,
> then GUIX_PACKAGE_PATH needs to be set in the environment of where ‘guix
> home’ itself is running. Like so:
>
> export GUIX_PACKAGE_PATH=/path/to/your/packages
> guix home container …
>
> Alternatively, you can run:
>
> guix home container -L /path/to/your/packages …
>
> HTH!
>
> Ludo’.
>
[-- Attachment #2: Type: text/html, Size: 3043 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2022-03-23 3:07 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-03-13 21:52 [bug#54377] [PATCH 0/3] Add 'guix home container' Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 1/3] linux-container: 'eval/container' honors #:namespaces Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 2/3] linux-container: Add #:guest-uid and #:guest-gid to 'eval/container' Ludovic Courtès
2022-03-13 21:54 ` [bug#54377] [PATCH 3/3] guix home: Add 'container' command Ludovic Courtès
2022-03-17 6:24 ` Andrew Tropin
2022-03-18 13:25 ` Ludovic Courtès
2022-03-19 5:09 ` Andrew Tropin
2022-03-19 10:39 ` [bug#54377] [PATCH 0/3] Add 'guix home container' Ludovic Courtès
2022-03-19 15:03 ` Andrew Tropin
2022-03-19 18:24 ` bug#54377: " Ludovic Courtès
2022-03-17 15:01 ` [bug#54377] " Frank Pursel
2022-03-19 10:32 ` Ludovic Courtès
2022-03-23 3:06 ` Frank Pursel
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/guix.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.