~ruther/guix-local

07ec349229eeae9f733fe92a300c7cfa4cf8e321 — Mike Gerwitz 8 years ago 99654a1
environment: Add --link-profile.

This change is motivated by attempts to run programs (like GNU IceCat) within
containers.  The 'fontconfig' program, for example, is configured explicitly
to check ~/.guix-profile for additional fonts.

There were no existing container tests in 'tests/guix-environment.sh', but I
added one anyway for this change.

* doc/guix.texi (Invoking guix environment): Add '--link-profile'.
* guix/scripts/environment.scm (show-help): Add '--link-profile'.
(%options): Add 'link-profile' as '#\P', assigned to 'link-profile?'.
(link-environment): New procedure.
(launch-environment/container): Use it when 'link-profile?'.
[link-profile?]: New parameter.
(guix-environment): Leave when '--link-prof' but not '--container'.  Add
'#:link-profile?' argument to 'launch-environment/container' application.
* tests/guix-environment-container.sh: New '--link-profile' test.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
3 files changed, 70 insertions(+), 8 deletions(-)

M doc/guix.texi
M guix/scripts/environment.scm
M tests/guix-environment-container.sh
M doc/guix.texi => doc/guix.texi +19 -2
@@ 46,7 46,8 @@ Copyright @copyright{} 2017 Andy Wingo@*
Copyright @copyright{} 2017, 2018 Arun Isaac@*
Copyright @copyright{} 2017 nee@*
Copyright @copyright{} 2018 Rutger Helling@*
Copyright @copyright{} 2018 Oleg Pykhalov
Copyright @copyright{} 2018 Oleg Pykhalov@*
Copyright @copyright{} 2018 Mike Gerwitz

Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or


@@ 1572,7 1573,7 @@ To be able to use such full names for the TrueType fonts installed in
your Guix profile, you need to extend the font path of the X server:

@example
xset +fp ~/.guix-profile/share/fonts/truetype
xset +fp `readlink -f ~/.guix-profile/share/fonts/truetype`
@end example

@cindex @code{xlsfonts}


@@ 7296,6 7297,22 @@ For containers, share the network namespace with the host system.
Containers created without this flag only have access to the loopback
device.

@item --link-profile
@itemx -P
For containers, link the environment profile to
@file{~/.guix-profile} within the container.  This is equivalent to
running the command @command{ln -s $GUIX_ENVIRONMENT ~/.guix-profile}
within the container.  Linking will fail and abort the environment if
the directory already exists, which will certainly be the case if
@command{guix environment} was invoked in the user's home directory.

Certain packages are configured to look in
@code{~/.guix-profile} for configuration files and data;@footnote{For
example, the @code{fontconfig} package inspects
@file{~/.guix-profile/share/fonts} for additional fonts.}
@code{--link-profile} allows these programs to behave as expected within
the environment.

@item --expose=@var{source}[=@var{target}]
For containers, expose the file system @var{source} from the host system
as the read-only file system @var{target} within the container.  If

M guix/scripts/environment.scm => guix/scripts/environment.scm +37 -6
@@ 1,6 1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2014, 2015, 2018 David Thompson <davet@gnu.org>
;;; Copyright © 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2018 Mike Gerwitz <mtg@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;


@@ 160,6 161,9 @@ COMMAND or an interactive shell in that environment.\n"))
  (display (G_ "
  -N, --network          allow containers to access the network"))
  (display (G_ "
  -P, --link-profile     link environment profile to ~/.guix-profile within
                         an isolated container"))
  (display (G_ "
      --share=SPEC       for containers, share writable host file system
                         according to SPEC"))
  (display (G_ "


@@ 243,6 247,9 @@ COMMAND or an interactive shell in that environment.\n"))
         (option '(#\N "network") #f #f
                 (lambda (opt name arg result)
                   (alist-cons 'network? #t result)))
         (option '(#\P "link-profile") #f #f
                 (lambda (opt name arg result)
                   (alist-cons 'link-profile? #t result)))
         (option '("share") #t #f
                 (lambda (opt name arg result)
                   (alist-cons 'file-system-mapping


@@ 404,18 411,20 @@ environment variables are cleared before setting the new ones."
           ((_ . status) status)))))

(define* (launch-environment/container #:key command bash user-mappings
                                       profile paths network?)
                                       profile paths link-profile? network?)
  "Run COMMAND within a container that features the software in PROFILE.
Environment variables are set according to PATHS, a list of native search
paths.  The global shell is BASH, a file name for a GNU Bash binary in the
store.  When NETWORK?, access to the host system network is permitted.
USER-MAPPINGS, a list of file system mappings, contains the user-specified
host file systems to mount inside the container."
host file systems to mount inside the container.  LINK-PROFILE? creates a
symbolic link from ~/.guix-profile to the environment profile."
  (mlet %store-monad ((reqs (inputs->requisites
                             (list (direct-store-path bash) profile))))
    (return
     (let* ((cwd (getcwd))
            (passwd (getpwuid (getuid)))
     (let* ((cwd      (getcwd))
            (passwd   (getpwuid (getuid)))
            (home-dir (passwd:dir passwd))
            ;; Bind-mount all requisite store items, user-specified mappings,
            ;; /bin/sh, the current working directory, and possibly networking
            ;; configuration files within the container.


@@ 460,8 469,13 @@ host file systems to mount inside the container."

            ;; Create a dummy home directory under the same name as on the
            ;; host.
            (mkdir-p (passwd:dir passwd))
            (setenv "HOME" (passwd:dir passwd))
            (mkdir-p home-dir)
            (setenv "HOME" home-dir)

            ;; If requested, link $GUIX_ENVIRONMENT to $HOME/.guix-profile;
            ;; this allows programs expecting that path to continue working as
            ;; expected within a container.
            (when link-profile? (link-environment profile home-dir))

            ;; Create a dummy /etc/passwd to satisfy applications that demand
            ;; to read it, such as 'git clone' over SSH, a valid use-case when


@@ 491,6 505,18 @@ host file systems to mount inside the container."
                           (delq 'net %namespaces) ; share host network
                           %namespaces)))))))

(define (link-environment profile home-dir)
  "Create a symbolic link from HOME-DIR/.guix-profile to PROFILE."
  (let ((profile-dir (string-append home-dir "/.guix-profile")))
    (catch 'system-error
      (lambda ()
        (symlink profile profile-dir))
      (lambda args
        (if (= EEXIST (system-error-errno args))
            (leave (G_ "cannot link profile: '~a' already exists within container~%")
                   profile-dir)
            (apply throw args))))))

(define (environment-bash container? bootstrap? system)
  "Return a monadic value in the store monad for the version of GNU Bash
needed in the environment for SYSTEM, if any.  If CONTAINER? is #f, return #f.


@@ 564,6 590,7 @@ message if any test fails."
    (let* ((opts       (parse-args args))
           (pure?      (assoc-ref opts 'pure))
           (container? (assoc-ref opts 'container?))
           (link-prof? (assoc-ref opts 'link-profile?))
           (network?   (assoc-ref opts 'network?))
           (bootstrap? (assoc-ref opts 'bootstrap?))
           (system     (assoc-ref opts 'system))


@@ 597,6 624,9 @@ message if any test fails."

      (when container? (assert-container-features))

      (when (and (not container?) link-prof?)
        (leave (G_ "'--link-profile' cannot be used without '--container'~%")))

      (with-store store
        (set-build-options-from-command-line store opts)



@@ 646,6 676,7 @@ message if any test fails."
                                                  #:user-mappings mappings
                                                  #:profile profile
                                                  #:paths paths
                                                  #:link-profile? link-prof?
                                                  #:network? network?)))
                 (else
                  (return

M tests/guix-environment-container.sh => tests/guix-environment-container.sh +14 -0
@@ 97,6 97,20 @@ grep -e "$NIX_STORE_DIR/.*-bash" $tmpdir/mounts # bootstrap bash

rm $tmpdir/mounts

# Make sure 'GUIX_ENVIRONMENT' is linked to '~/.guix-profile' when requested
# within a container.
(
  linktest='(exit (string=? (getenv "GUIX_ENVIRONMENT")
(readlink (string-append (getenv "HOME") "/.guix-profile"))))'

  cd "$tmpdir" \
     && guix environment --bootstrap --container --link-profile \
             --ad-hoc guile-bootstrap --pure \
             -- guile -c "$linktest"
)

# Check the exit code.

abnormal_exit_code="
(use-modules (system foreign))
;; Purposely make Guile crash with a segfault. :)