unofficial mirror of help-guix@gnu.org 
 help / color / mirror / Atom feed
* Dependencies between service extensions
@ 2020-06-07 15:43 conjaroy
  2020-06-23 12:53 ` conjaroy
  2020-06-23 21:51 ` Marius Bakke
  0 siblings, 2 replies; 4+ messages in thread
From: conjaroy @ 2020-06-07 15:43 UTC (permalink / raw)
  To: help-guix

Greetings help-guix,

I've been a casual user of Nix for a couple of years and have decided to
test the waters with Guix. While I'm looking forward to spending time with
Lisp after many years away, my biggest impression is that Guix seems to
have well-documented interfaces in cases where Nix relies more on loose
conventions.

After reviewing the manual and some of the service definitions, I'd like a
better understanding of how to implement a common pattern. Let's say that I
have some application Foo that uses an external system for persistence,
like a SQL database. Before starting up service Foo I need to ensure both
that the database service is running and that the database instance for Foo
has been initialized, because Foo doesn't know how to initialize the
database on its own.

The first issue (how to ensure that the database service is up) seems to be
solved by adding a shepherd-root-service-type service extension that
declares a set of "requirements". And the second issue (performing
pre-startup initialization) seems to be handled by the
activation-service-type extension. So far so good.

But I couldn't find documentation on whether service activation scripts can
safely rely on other services that happen to be declared as requirements in
the shepherd-root-service-type extension. And while I found many activation
scripts that do simple things like modifying the filesystem, I couldn't see
any that interact directly with other services. However, I did see some
evidence of service extensions relying on the side effects of other service
extensions: a number of activation scripts call "getpwnam" for info on
system accounts that could exist only if the corresponding
account-service-type extension has already been executed.

So my questions are: could someone clarify best practices for initializing
state in Service A before Service B starts up? And is there anything about
the ordering/dependencies of a service's extensions that could be better
documented in the manual?

Thanks for all of your work on this project.

Jason

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dependencies between service extensions
  2020-06-07 15:43 Dependencies between service extensions conjaroy
@ 2020-06-23 12:53 ` conjaroy
  2020-06-23 21:51 ` Marius Bakke
  1 sibling, 0 replies; 4+ messages in thread
From: conjaroy @ 2020-06-23 12:53 UTC (permalink / raw)
  To: help-guix

Does anyone have feedback on how to implement this type of dependency
correctly in a Guix service?

On Sun, Jun 7, 2020 at 11:43 AM conjaroy <conjaroy@gmail.com> wrote:

> Greetings help-guix,
>
> I've been a casual user of Nix for a couple of years and have decided to
> test the waters with Guix. While I'm looking forward to spending time with
> Lisp after many years away, my biggest impression is that Guix seems to
> have well-documented interfaces in cases where Nix relies more on loose
> conventions.
>
> After reviewing the manual and some of the service definitions, I'd like a
> better understanding of how to implement a common pattern. Let's say that I
> have some application Foo that uses an external system for persistence,
> like a SQL database. Before starting up service Foo I need to ensure both
> that the database service is running and that the database instance for Foo
> has been initialized, because Foo doesn't know how to initialize the
> database on its own.
>
> The first issue (how to ensure that the database service is up) seems to
> be solved by adding a shepherd-root-service-type service extension that
> declares a set of "requirements". And the second issue (performing
> pre-startup initialization) seems to be handled by the
> activation-service-type extension. So far so good.
>
> But I couldn't find documentation on whether service activation scripts
> can safely rely on other services that happen to be declared as
> requirements in the shepherd-root-service-type extension. And while I found
> many activation scripts that do simple things like modifying the
> filesystem, I couldn't see any that interact directly with other services.
> However, I did see some evidence of service extensions relying on the side
> effects of other service extensions: a number of activation scripts call
> "getpwnam" for info on system accounts that could exist only if the
> corresponding account-service-type extension has already been executed.
>
> So my questions are: could someone clarify best practices for initializing
> state in Service A before Service B starts up? And is there anything about
> the ordering/dependencies of a service's extensions that could be better
> documented in the manual?
>
> Thanks for all of your work on this project.
>
> Jason
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dependencies between service extensions
  2020-06-07 15:43 Dependencies between service extensions conjaroy
  2020-06-23 12:53 ` conjaroy
@ 2020-06-23 21:51 ` Marius Bakke
  2020-06-30 23:11   ` conjaroy
  1 sibling, 1 reply; 4+ messages in thread
From: Marius Bakke @ 2020-06-23 21:51 UTC (permalink / raw)
  To: conjaroy, help-guix

[-- Attachment #1: Type: text/plain, Size: 5184 bytes --]

conjaroy <conjaroy@gmail.com> writes:

> Greetings help-guix,
>
> I've been a casual user of Nix for a couple of years and have decided to
> test the waters with Guix. While I'm looking forward to spending time with
> Lisp after many years away, my biggest impression is that Guix seems to
> have well-documented interfaces in cases where Nix relies more on loose
> conventions.
>
> After reviewing the manual and some of the service definitions, I'd like a
> better understanding of how to implement a common pattern. Let's say that I
> have some application Foo that uses an external system for persistence,
> like a SQL database. Before starting up service Foo I need to ensure both
> that the database service is running and that the database instance for Foo
> has been initialized, because Foo doesn't know how to initialize the
> database on its own.
>
> The first issue (how to ensure that the database service is up) seems to be
> solved by adding a shepherd-root-service-type service extension that
> declares a set of "requirements". And the second issue (performing
> pre-startup initialization) seems to be handled by the
> activation-service-type extension. So far so good.
>
> But I couldn't find documentation on whether service activation scripts can
> safely rely on other services that happen to be declared as requirements in
> the shepherd-root-service-type extension. And while I found many activation
> scripts that do simple things like modifying the filesystem, I couldn't see
> any that interact directly with other services. However, I did see some
> evidence of service extensions relying on the side effects of other service
> extensions: a number of activation scripts call "getpwnam" for info on
> system accounts that could exist only if the corresponding
> account-service-type extension has already been executed.
>
> So my questions are: could someone clarify best practices for initializing
> state in Service A before Service B starts up? And is there anything about
> the ordering/dependencies of a service's extensions that could be better
> documented in the manual?

To encode requirements for an activation script, I think you need to
declare a service type for it with appropriate requirements, and make
the start and stop actions "noop".  Then you can have other services
depend on the "activation service".

I did something similar in a service I'm working on that consists of
many different daemons.  To avoid having to run essentially the same
activation script on each, I created a "common" service that all daemons
depend upon.  It's fairly verbose (you don't need a record type), but
looks like this:

--8<---------------cut here---------------start------------->8---
;; This is a dummy service that all Ganeti daemons depend upon, mainly to
;; avoid having the same activation snippet on each.
(define-record-type* <ganeti-common-configuration>
  ganeti-common-configuration make-ganeti-common-configuration
  ganeti-common-configuration?
  (ganeti ganeti-common-configuration-ganeti            ;<package>
          (default ganeti))
  (directories ganeti-common-configuration-directories  ;list of strings
               (default '("/var/log/ganeti"
                          "/var/log/ganeti/kvm"
                          "/var/log/ganeti/os"
                          "/var/lib/ganeti/rapi"
                          "/var/lib/ganeti/queue"
                          "/var/run/ganeti/bdev-cache"
                          "/var/run/ganeti/socket"
                          "/var/run/ganeti/instance-disks"
                          "/var/run/ganeti/instance-reason"
                          "/var/run/ganeti/livelocks"))))

(define (ganeti-common-activation config)
  (let ((directories (ganeti-common-configuration-directories config)))
  #~(begin
      (use-modules (guix build utils))
      (for-each mkdir-p '#$directories))))

(define ganeti-common-service
  (lambda _
    (list (shepherd-service
           (documentation "Create the directories required by Ganeti.")
           (provision '(ganeti-common))
           (requirement '(file-systems))
           ;; Do nothing but the activation snippet, at least for now.
           (start #~(const #t))))))

(define ganeti-common-service-type
  (service-type (name 'ganeti-common)
                (extensions
                 (list (service-extension activation-service-type
                                          ganeti-common-activation)
                       ;; This service also installs Ganeti to the profile
                       ;; to make gnt-cluster, etc readily available.
                       (service-extension profile-service-type
                                          (compose list
                                                   ganeti-common-configuration-ganeti))
                       (service-extension shepherd-root-service-type
                                          ganeti-common-service)))
                (default-value (ganeti-common-configuration))
                (description
                 "This service creates directories used by other Ganeti
daemons.")))
--8<---------------cut here---------------end--------------->8---

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 487 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Dependencies between service extensions
  2020-06-23 21:51 ` Marius Bakke
@ 2020-06-30 23:11   ` conjaroy
  0 siblings, 0 replies; 4+ messages in thread
From: conjaroy @ 2020-06-30 23:11 UTC (permalink / raw)
  To: Marius Bakke; +Cc: help-guix

Hello Marius,

Thanks a lot for your reply - I agree that the concept of a meta-service to
factor out common startup tasks seems logical.

Based on your example, it sounds like you're suggesting that if I have a
service that extends both activation-service-type and
shepherd-root-service-type, and if the Shepherd extension service defines
its own service dependencies (via shepherd-service-requirement), then the
activation-service-type extension script will run in a context where those
required services are active?

I've spent a few days investigating how the service graph works and here's
what I think is happening:

- Individually, activation-service-type and shepherd-root-service-type
concatenate their respective service extension scripts into a single script.

- These two service types in turn extend boot-service-type, so when
extensions of boot-service-type are processed, the combined
activation-service-type boot script is concatenated with the combined
shepherd-root-service-type boot script (in that order, apparently).

- Therefore, at startup, Guix executes scripts for all
activation-service-type extensions before starting any Shepherd services.

So while the idea of performing initialization as a separate service still
makes sense, I believe that any logic depending on other Shepherd services
needs to run as a shepherd-root-service-type extension instead of an
activation-service-type extension.

If any of the above is incorrect, I'd love to hear more!

Cheers,

Jason

On Tue, Jun 23, 2020 at 5:51 PM Marius Bakke <marius@gnu.org> wrote:

> conjaroy <conjaroy@gmail.com> writes:
>
> > Greetings help-guix,
> >
> > I've been a casual user of Nix for a couple of years and have decided to
> > test the waters with Guix. While I'm looking forward to spending time
> with
> > Lisp after many years away, my biggest impression is that Guix seems to
> > have well-documented interfaces in cases where Nix relies more on loose
> > conventions.
> >
> > After reviewing the manual and some of the service definitions, I'd like
> a
> > better understanding of how to implement a common pattern. Let's say
> that I
> > have some application Foo that uses an external system for persistence,
> > like a SQL database. Before starting up service Foo I need to ensure both
> > that the database service is running and that the database instance for
> Foo
> > has been initialized, because Foo doesn't know how to initialize the
> > database on its own.
> >
> > The first issue (how to ensure that the database service is up) seems to
> be
> > solved by adding a shepherd-root-service-type service extension that
> > declares a set of "requirements". And the second issue (performing
> > pre-startup initialization) seems to be handled by the
> > activation-service-type extension. So far so good.
> >
> > But I couldn't find documentation on whether service activation scripts
> can
> > safely rely on other services that happen to be declared as requirements
> in
> > the shepherd-root-service-type extension. And while I found many
> activation
> > scripts that do simple things like modifying the filesystem, I couldn't
> see
> > any that interact directly with other services. However, I did see some
> > evidence of service extensions relying on the side effects of other
> service
> > extensions: a number of activation scripts call "getpwnam" for info on
> > system accounts that could exist only if the corresponding
> > account-service-type extension has already been executed.
> >
> > So my questions are: could someone clarify best practices for
> initializing
> > state in Service A before Service B starts up? And is there anything
> about
> > the ordering/dependencies of a service's extensions that could be better
> > documented in the manual?
>
> To encode requirements for an activation script, I think you need to
> declare a service type for it with appropriate requirements, and make
> the start and stop actions "noop".  Then you can have other services
> depend on the "activation service".
>
> I did something similar in a service I'm working on that consists of
> many different daemons.  To avoid having to run essentially the same
> activation script on each, I created a "common" service that all daemons
> depend upon.  It's fairly verbose (you don't need a record type), but
> looks like this:
>
> --8<---------------cut here---------------start------------->8---
> ;; This is a dummy service that all Ganeti daemons depend upon, mainly to
> ;; avoid having the same activation snippet on each.
> (define-record-type* <ganeti-common-configuration>
>   ganeti-common-configuration make-ganeti-common-configuration
>   ganeti-common-configuration?
>   (ganeti ganeti-common-configuration-ganeti            ;<package>
>           (default ganeti))
>   (directories ganeti-common-configuration-directories  ;list of strings
>                (default '("/var/log/ganeti"
>                           "/var/log/ganeti/kvm"
>                           "/var/log/ganeti/os"
>                           "/var/lib/ganeti/rapi"
>                           "/var/lib/ganeti/queue"
>                           "/var/run/ganeti/bdev-cache"
>                           "/var/run/ganeti/socket"
>                           "/var/run/ganeti/instance-disks"
>                           "/var/run/ganeti/instance-reason"
>                           "/var/run/ganeti/livelocks"))))
>
> (define (ganeti-common-activation config)
>   (let ((directories (ganeti-common-configuration-directories config)))
>   #~(begin
>       (use-modules (guix build utils))
>       (for-each mkdir-p '#$directories))))
>
> (define ganeti-common-service
>   (lambda _
>     (list (shepherd-service
>            (documentation "Create the directories required by Ganeti.")
>            (provision '(ganeti-common))
>            (requirement '(file-systems))
>            ;; Do nothing but the activation snippet, at least for now.
>            (start #~(const #t))))))
>
> (define ganeti-common-service-type
>   (service-type (name 'ganeti-common)
>                 (extensions
>                  (list (service-extension activation-service-type
>                                           ganeti-common-activation)
>                        ;; This service also installs Ganeti to the profile
>                        ;; to make gnt-cluster, etc readily available.
>                        (service-extension profile-service-type
>                                           (compose list
>
>  ganeti-common-configuration-ganeti))
>                        (service-extension shepherd-root-service-type
>                                           ganeti-common-service)))
>                 (default-value (ganeti-common-configuration))
>                 (description
>                  "This service creates directories used by other Ganeti
> daemons.")))
> --8<---------------cut here---------------end--------------->8---
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-06-30 23:12 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-07 15:43 Dependencies between service extensions conjaroy
2020-06-23 12:53 ` conjaroy
2020-06-23 21:51 ` Marius Bakke
2020-06-30 23:11   ` conjaroy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).