diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2021-02-18 11:18:09 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2021-02-18 11:18:09 -0700 |
commit | 28ac2c8b8ba8839a78fc4b6bfea87d5482fb0208 (patch) | |
tree | be7e4a5579f79650b6c282f886c08720619f9312 | |
parent | c8be9aa24f37ff52205e9525756f3338674ccaaf (diff) | |
download | consfigurator-28ac2c8b8ba8839a78fc4b6bfea87d5482fb0208.tar.gz |
tidy up existing docs
Signed-off-by: Sean Whitton <spwhitton@spwhitton.name>
-rw-r--r-- | README.rst | 31 | ||||
-rw-r--r-- | doc/connections.rst | 11 | ||||
-rw-r--r-- | doc/data.rst | 71 | ||||
-rw-r--r-- | doc/guide.rst | 219 | ||||
-rw-r--r-- | doc/introduction.rst | 128 | ||||
-rw-r--r-- | doc/pitfalls.rst | 50 | ||||
-rw-r--r-- | doc/properties.rst | 85 | ||||
-rw-r--r-- | doc/propspecs.rst (renamed from doc/property_application_specs.rst) | 5 |
8 files changed, 324 insertions, 276 deletions
@@ -37,13 +37,15 @@ work! Quick start / introduction ========================== -1. Install Steel Bank Common Lisp and Consfigurator. One way to do the latter - is to clone this git repository into ``~/.local/share/common-lisp/source``. +1. ``apt-get install sbcl cl-ppcre cl-interpol cl-alexandria``. -2. Create a new directory ``consfig`` somewhere where ASDF will pick it up, +2. Install Consfigurator. One way to do that is to clone this git repository + into ``~/.local/share/common-lisp/source``. + +3. Create a new directory ``consfig`` somewhere where ASDF will pick it up, such as ``~/common-lisp/consfig``. -3. Define a Lisp system which represents your configuration. +4. Define a Lisp system which represents your configuration. ~/common-lisp/consfig/com.example.consfig.asd:: @@ -59,9 +61,9 @@ Quick start / introduction (defpackage :com.example.consfig (:use #:cl #:consfigurator) - (:local-nicknames (#:file #:consfigurator.property.file))) + (:local-nicknames (#:etc-default #:consfigurator.property.etc-default))) -4. Define some hosts and connections. +5. Define some hosts and deployments. ~/common-lisp/consfig/consfig.lisp:: @@ -71,14 +73,14 @@ Quick start / introduction (defhost athena.example.com "Web and file server." - (file:contains-lines "/etc/default/locale" '("LANG=en_GB.UTF-8"))) + (etc-default:set "locale" "LANG" "en_GB.UTF-8")) (defhostdeploy :ssh athena.example.com) -5. Get a Lisp REPL started up -- ``M-x slime`` in Emacs or ``sbcl`` at a shell +6. Get a Lisp REPL started up -- ``M-x slime`` in Emacs or ``sbcl`` at a shell prompt. Evaluate ``(asdf:require-system "com.example.consfig")``. -6. Now you should be able to use configure athena by evaulating +7. Now you should be able to use configure athena by evaluating ``(com.example.consfig:athena.example.com)``. You can use the ``CONSFIGURATOR:DEPLOY`` function to try out configuring athena using a different connection type than defined here. @@ -91,18 +93,17 @@ Portability and stability - No attempt is made to support Common Lisp implementations other than SBCL, though portability patches are welcome. -- No attempt is made to support running on Windows -- we often eschew Common - Lisp pathnames in favour of simple strings with forward slashes as directory - separators. +- Little attempt is made by the author to support systems other than Debian + GNU/Linux, but again, portability patches are welcome. Credits ======= Many of the good ideas here come straight from Joey Hess's Propellor_. I'm working on Consfigurator because I think Propellor is great, but wanted to add -Consfigurator's ``:posix`` connections and arbitrary connection nesting -- -Propellor supports something equivalent to a single, unnested ``:lisp`` -connection -- and I wanted to implement that in Lisp. Also, after five years +Consfigurator's POSIX-type connections and arbitrary connection nesting, and I +wanted to implement that in Lisp (Propellor only supports something equivalent +to a single, unnested Lisp-type connection). Additionally, after five years of using and extending Propellor, I've come to disagree with Joey about whether Haskell's type system helps or hinders using and extending Propellor. diff --git a/doc/connections.rst b/doc/connections.rst new file mode 100644 index 0000000..b3e5e1d --- /dev/null +++ b/doc/connections.rst @@ -0,0 +1,11 @@ +Connections +=========== + +Defining connection types +------------------------- + +The code which establishes connections (i.e., implementations of the +``ESTABLISH-CONNECTION`` generic) is like code in ``:posix`` properties -- it +should restrict its I/O to ``RUN``, ``RUNLINES``, ``READFILE`` and +``WRITEFILE``, functions which access the currently active connection. This +is in order to permit the arbitrary nesting of connections. diff --git a/doc/data.rst b/doc/data.rst new file mode 100644 index 0000000..9008bf1 --- /dev/null +++ b/doc/data.rst @@ -0,0 +1,71 @@ +Prerequisite data +================= + +Naming +------ + +A piece of prerequisite data is identified by two strings. Typically the +first of these specifies the context in which the data is relevant. For an +ssh host key, for example, this context would be a hostname. If it's ``nil`` +then the data is valid in any context. The second of these identifies the +data within its context. This is often just the filename in which the +prerequisite data will eventually be stored. It might also be a +human-readable string describing the purpose of the data. + +Reserved names +~~~~~~~~~~~~~~ + +These are exclusive semantics for certain possible pairs of strings +identifying prerequisite data -- to avoid confusion and potential clashes, do +not use prerequisite data identified by strings matching these conditions for +other purposes. + +- ``(HOSTNAME . PATH)`` means the data that should be uploaded to ``PATH`` on + ``HOSTNAME`` (and nowhere else) + +- ``("lisp-system" . SYSTEM)`` means the data is Lisp code which, when loaded, + defines the packages and symbols contained in the ASDF system ``SYSTEM``. + +Mechanics +--------- + +Properties declare that they need certain pieces of prerequisite data by +adding static informational attributes, and a deployment of those properties +will make an attempt to provide the data. Properties then either call the +``GET-DATA`` function, or depend on the ``DATA-UPLOADED`` property, to get +access to the requested data. + +A Lisp connection gathers all needed prerequisite data once at the beginning, +and copies it to an on-disk cache inside the home directory of the remote UID +which will run the Lisp process. A POSIX connection only attempts to obtain +prerequisite data when a property's check function indicates the property is +not already applied. + +Sources of prerequisite data +---------------------------- + +Sources of prerequisite data register two functions. The second returns +either a string of the prerequisite data itself, or a path to a file +containing the data. The first returns the latest version number of the data +that source is able to provide -- i.e., the version number of the data that +the second function would return if called. + +Consfigurator will call the first function to find out if it needs to call the +first rather than just using its caches. The first function should return nil +if it can't obtain the prerequisite data on this host, perhaps because it +can't decrypt the store. If a prerequisite data source wants to effectively +bypass caching and provide fresh data every time Consfigurator deploys the +host, it can use ``GET-UNIVERSAL-TIME`` as its first function. + +Versions are compared using ``dpkg --compare-versions``. + +Security issues +--------------- + +Nothing is done to prevent prerequisite data being swapped out, so ensure your +swap is encrypted. + +Certain connection types require storing unencrypted copies of prerequisite +data under ``~/.cache/consfigurator/data``. Consfigurator only stores data +there when it has to, only the subset of the data that has to be uploaded for +the requested deployment to be successful, and never in the root Lisp. diff --git a/doc/guide.rst b/doc/guide.rst deleted file mode 100644 index 975c891..0000000 --- a/doc/guide.rst +++ /dev/null @@ -1,219 +0,0 @@ -Concepts -======== - -Host ----- - -A machine, container, chroot, or similar. Has a plist of static informational -*host attributes*, usually including at least a hostname, and an ordered list -of properties it should have, or lack, in the order in which they should be -applied or unapplied (thus properties later in the list implicitly depend on -earlier entries). - -Property --------- - -Some configuration which a host can have or lack, and which can be added to -a host by running some code, possibly just by applying a series of other -properties. - -For example: the presence of some lines in a config file; a package being -installed or absent; the availability of a website. - -A property can have a (function returning a) list of host attributes, which -will be added to the attributes plist of any host which has the property in -its list of properties. These attributes can depend on the arguments to the -property, but should not examine the actual state of the host. - -It should also have a check function, which establishes whether the property -is already applied or not. If this is absent, it is assumed that the property -is always unapplied, i.e., an attempt to apply the property will always be made. - -Optionally, it can also have a function which unapplies the property, -permitting creating an inverse of the property. - -Connection ----------- - -A means by which properties can be applied to hosts. There are two types of -connections: those which interact with the remote host by means of a POSIX -shell, and those which apply properties by executing them in a Lisp process -running on the host. The keywords ``:posix`` and ``:lisp`` are used to refer -to these types. - -``:posix`` connections can pass input to and return output from processes, but -cannot start asynchronous processes for interaction with your Lisp functions. -This is so that ``:posix`` connections can be used to administer hosts for -which shell multiplexing is not possible, such as with serial connections. -For asynchronous interaction, use a ``:lisp`` connection. - -The code which establishes a connection is similar to code in ``:posix`` -properties: it only performs I/O using functions which access the currently -active connection. This permits arbitrary nesting of connections. - -Deployment ----------- - -The combination of a connection and a host. Executing a connection deploys -all of a host's properties to that host by means of the given connection. - -A deployment is itself a property. This means that connections can be -nested: one remote host can be used to deploy others, as a controller. - -To deploy single properties, you can use ``host-variant`` to obtain a version -of a host which has all its usual informational attributes, based on its usual -list of properties, but with a different list of properties to be applied. - -Root Lisp ---------- - -The Lisp process you control directly when you execute deployments. Typically -running on your development laptop/workstation. - -Unevaluated property application specification ----------------------------------------------- - -A property application specification, except in atomic property applications -of the form ``(PROPERTY . ARGS)``, ARGS are expressions to be evaluated to -produce the arguments to pass to PROPERTY, rather than those arguments -themselves. An unevaluated property application specification can be -converted into a property application specification by replacing each ARG of -ARGS with the result of ``(eval ARG)``. - -The main place you will find an unevaluated property application specification -is in a call to DEFHOST. That macro converts an unevaluated property -application specification into code which will produce the corresponding -property application specification. - -Prerequisite data ------------------ - -Applying a property may require file contents which should be generated or -extracted, by the root Lisp, at the time of deployment: a tarball containing -the latest version of the web service to be deployed; a secret extracted from -an encrypted store; a git bundle from localhost which the target host cannot -just ``git clone`` to itself. - -A piece of prerequisite data is identified by two strings. Typically the -first of these specifies the context in which the data is relevant. For an -ssh host key, for example, this context would be a hostname. If it's ``nil`` -then the data is valid in any context. The second of these identifies the -data within its context. This is often just the filename in which the -prerequisite data will eventually be stored. It might also be a -human-readable string describing the purpose of the data. - -Prerequisite data is versioned. To replace a secret key, for example, you -change the data and bump the version. If there is no version bump, -Consfigurator will assume connections can re-use old copies of prerequisite -data; this avoids uploading the same data over and over again. - -Properties declare that they need certain pieces of prerequisite data by -adding static informational attributes, and a deployment of those properties -will make an attempt to provide the data. Properties then call either -``get-data`` or the ``data-uploaded`` property to get access to the requested -data. - -A ``:lisp`` connection gathers all the needed prerequisite data once at the -beginning and copies it to an on-disk cache inside the home directory of the -UID which will run the lisp process on the host which will run it. A -``:posix`` connection only attempts to obtain prerequisite data when a -property's check function indicates the property is not already applied. - -In addition to secrets management, prerequisite data is Consfigurator's -mechanism for the common need to upload files to controlled hosts. The same -mechanism is used internally to upload the Lisp code needed to start up remote -Lisp processes for ``:lisp`` connections. - -Reserved names for prerequisite data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -These are exclusive semantics for certain possible pairs of strings -identifying prerequisite data -- to avoid confusion and potential clashes, do -not use prerequisite data identified by strings matching these conditions for -other purposes. - -- ``(HOSTNAME . PATH)`` means the data that should be uploaded to PATH on - HOSTNAME (and nowhere else) - -- ``("lisp-system" . SYSTEM)`` means the data is Lisp code which, when loaded, - defines the packages and symbols contained in the ASDF system SYSTEM. - -Representing prerequisite data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A piece of prerequisite data is represented in your configuration by two -functions. The second will return either a string of the prerequisite data -itself, or a path to a file containing the data. The first returns the -latest version number of the data -- i.e., the version of the data that the -second function would return if executed. - -Consfigurator will call the first function to find out if it needs to call the -first rather than just using its caches. The first function should return nil -if it can't obtain the prerequisite data on this host, perhaps because it -can't decrypt the store. If a prerequisite data source wants to effectively -bypass caching and provide fresh data every time Consfigurator deploys the -host, it can use ``GET-UNIVERSAL-TIME`` as its first function. - -Versions are compared using ``dpkg --compare-versions``. - -Security issues -~~~~~~~~~~~~~~~ - -Nothing is done to prevent prerequisite data being swapped out, so ensure your -swap is encrypted. - -Pitfalls -======== - -Invoking properties from within properties ------------------------------------------- - -Properties can programmatically invoke arbitrary properties to be applied in -the context of their current deployment. But then the informational -attributes of the properties won't be automatically copied to the definition -of the host, so, for example, prerequisite data might be missing. You will -need to manually add the informational attributes of the property you're -invoking to the informational attributes of the invoking property. - -There are other risks in the vicinity: missing informational attributes might -cause other properties to misbehave. So avoid invoking properties in this way -where you can. Use property combinators. - -When you just want to have a property invoke several others, there are -functions which you can use to define a new property from the list of old -ones, which will set all the informational attributes on the host. - -Attempting to work with anonymous properties or connection types ----------------------------------------------------------------- - -Hosts, property application specifications and deployments are mutable values, -which you can build, pass around and change in your own code. For example, -deployments can be built and executed programmatically. However, properties -and connection types should be defined in ``.lisp`` files, loaded into Lisp, -and then *not* created or modified (except by reloading). In particular, do -not try to define properties and connection types programmatically, or try to -dynamically rebind them. - -The reason for this restriction is that some connection types need to invoke -fresh Lisp processes on remote hosts with (equivalents to) the function -objects contained in properties and connections available to be called. Since -function objects are not serialisable, the only way to do this is to send over -the contents of your ``.lisp`` files and load the same properties and -connection types into the remote Lisp. By contrast, hosts, property -application specifications and deployments can be serialised and sent over -that way. - -If you were to dynamically rebind properties or connection types in the root -Lisp, then connections which do not start remote Lisp processes would use your -new definitions, but connections which start remote Lisp processes would use -the static definitions in your ``.lisp`` files (or lack definitions -altogether). This would violate the idea in Consfigurator that properties, -including nested deployments, have the same meaning regardless of the -connection types they are used with. - -Note that you *can* programmatically determine what arguments will get passed -to properties upon deployment, though each of these arguments needs to be -serialisable, so you can't pass anonymous functions or objects containing -those. You can work around the latter restriction by defining a new property -which passes in the desired anonymous function, and then adding the new -property to your property application specification. diff --git a/doc/introduction.rst b/doc/introduction.rst new file mode 100644 index 0000000..ee2038a --- /dev/null +++ b/doc/introduction.rst @@ -0,0 +1,128 @@ +Introduction +============ + +This is the user's guide for Consfigurator. + +Concepts and terminology +------------------------ + +Host +~~~~ + +A machine, container, chroot, or similar. Has a plist of static informational +*host attributes*, usually including at least a hostname, and a property +application specification defining the properties it has. + +Property +~~~~~~~~ + +Some configuration which a host can have or lack, and which can be added to +a host by running some code, possibly just by applying a series of other +properties. + +For example: the presence of some lines in a config file; a package being +installed or absent; the availability of a website. + +Connection +~~~~~~~~~~ + +A means by which properties can be applied to hosts, and multihop connections +to other hosts can be established. There are two types of connections: those +which interact with the remote host by means of a POSIX shell, and those which +apply properties by executing them in a Lisp process running on the host. + +POSIX connections can pass input to and return output from processes, but +cannot start asynchronous processes for interaction with your Lisp functions. +This is so that POSIX connections can be defined to control hosts for which +any kind of shell multiplexing is hard or impossible, such as with serial +connections providing only a single interactive POSIX sh. For asynchronous +interaction, use a Lisp connection. + +Deployment +~~~~~~~~~~ + +The combination of a connection and a host. Executing a connection deploys +all of a host's usual properties to that host by means of the given +connection. To deploy just a few particular properties, you can use +``DEPLOY-THESE``. + +A deployment is itself a property. This is one way in which connections can +be nested: one remote host can be used to deploy others, as a controller. + +Root Lisp +~~~~~~~~~ + +The Lisp process you control directly when you execute deployments. Typically +running on your development laptop/workstation (and not as the ``root`` user). + +Property application specification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An ordered list specifying the properties that a host has and/or lacks. For +example,:: + + '((apt:installed postfix) + (etc-default:set "locale" "LANG" "en_GB.UTF-8") + (unapply (com.example.consfig.services:mail-satellite))) + +Property application specifications are always applied in order, so properties +later in the list implicitly depend on properties earlier in the list. + +Unevaluated property application specification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A property application specification, except in atomic property applications +of the form ``(PROPERTY . ARGS)``, ``ARGS`` are expressions to be evaluated to +produce the arguments to pass to ``PROPERTY``, rather than those arguments +themselves. An unevaluated property application specification can be +converted into a property application specification by evaluating each of +``ARGS``. + +The main place you will find an unevaluated property application specification +is in a call to ``DEFHOST``. That macro converts an unevaluated property +application specification into code which will produce the corresponding +property application specification. + +Prerequisite data +~~~~~~~~~~~~~~~~~ + +File contents required to apply a property which should be generated or +extracted, by the root Lisp, at the time of deployment: a tarball containing +the latest version of the web service to be deployed; a secret extracted from +an encrypted store; a git bundle from localhost which the target host cannot +just ``git clone`` to itself. + +Prerequisite data is versioned. To replace a secret key, for example, you +change the data and bump the version. If there is no version bump, +Consfigurator will assume connections can re-use old copies of prerequisite +data; this avoids uploading the same data over and over again. + +In addition to secrets management, prerequisite data is Consfigurator's +mechanism for the common need to upload files to controlled hosts. The same +mechanism is used internally to upload the Lisp code needed to start up remote +Lisp processes for ``:lisp`` connections. + +Consfig +~~~~~~~ + +An ASDF system in which you define your hosts and initialise sources of +prerequisite data. This system might also define some site-specific +properties, default deployments, and helper functions. Typically the system +is named ``COM.EXAMPLE.CONSFIG`` where ``example.com`` is your primary domain +name. + +The system can contain multiple packages, perhaps to divide up your +definitions of hosts and default deployments from your site-specific +properties (e.g. you might have a package called +``COM.EXAMPLE.CONSFIG.SITES``). + +You can have multiple independent Consfigs loaded into the root Lisp at once, +but if you do, then you should avoid using the ``*CONSFIG*`` global variable. + +Documentation conventions +------------------------- + +All unqualified names of Lisp symbols refer to those exported from the +``CONSFIGURATOR`` package, because it is assumed that this package is imported +unqualified into both user consfigs and Lisp packages providing properties, +connection types and sources of prerequisite data. diff --git a/doc/pitfalls.rst b/doc/pitfalls.rst new file mode 100644 index 0000000..8d0caef --- /dev/null +++ b/doc/pitfalls.rst @@ -0,0 +1,50 @@ +Pitfalls +======== + +Invoking properties from within properties +------------------------------------------ + +Properties can programmatically invoke arbitrary properties to be applied in +the context of their current deployment. However, when this is done the +``:hostattrs`` subroutine of the invoked property will not be called, so, for +example, prerequisite data might be missing. You will need to add a call to +``PROPATTRS`` in the invoking property's own ``:hostattrs`` subroutine. + +There are other risks in the vicinity: missing informational attributes might +cause some other properties to misbehave. To avoid all this, consider using +``DEFPROPLIST`` to combine properties, rather than having them call each +other. + +Attempting to work with anonymous properties or connection types +---------------------------------------------------------------- + +Hosts, property application specifications and deployments are mutable values, +which you can build, pass around and change in your own code. For example, +deployments can be built and executed programmatically. However, properties +and connection types should be defined in ``.lisp`` files, loaded into Lisp, +and then *not* created or modified, except by reloading. In particular, do +not try to define properties and connection types programmatically, or try to +dynamically rebind them. + +The reason for this restriction is that some connection types need to invoke +fresh Lisp processes on remote hosts with (local equivalents to) the function +objects contained in properties and connections available to be called. Since +function objects are not serialisable, the only way to do this is to send over +the contents of your ``.lisp`` files and load the same properties and +connection types into the remote Lisp. By contrast, hosts, property +application specifications and deployments can be send over in serialised form. + +If you were to dynamically rebind properties or connection types in the root +Lisp, then connections which do not start remote Lisp processes would use your +new definitions, but connections which start remote Lisp processes would use +the static definitions in your ``.lisp`` files (or lack definitions +altogether). This would violate the idea in Consfigurator that properties, +including nested deployments, have the same meaning regardless of the +connection types they are used with. + +Note that you *can* programmatically determine the arguments to pass to +properties upon deployment, though each of these arguments needs to be +serialisable, so you can't pass anonymous functions or objects containing +those. You can work around the latter restriction by defining a new property +which passes in the desired anonymous function, and then adding the new +property to your property application specification. diff --git a/doc/properties.rst b/doc/properties.rst index 8da97c1..79808e3 100644 --- a/doc/properties.rst +++ b/doc/properties.rst @@ -1,42 +1,50 @@ +Properties +========== + Property subroutines -~~~~~~~~~~~~~~~~~~~~ +-------------------- + +A property is composed of four subroutines, which all take the same +arguments. At least one of ``:hostattrs`` or ``:apply`` must be present. ``:hostattrs`` subroutines -========================== +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Executed in the root Lisp to (i) add and modify static informational +attributes of hosts to which this property is applied or is to be applied; +and (ii) check that applying this property makes sense -- e.g. that we're not +trying to install a package using apt(1) on a FreeBSD host. + +When this subroutine is called, ``*HOSTATTRS*`` will be bound to the plist of +static informational attributes of the host to which the property is to be +applied, which may be modified. -When this subroutine is executed, consfigurator:*hostattrs* will be bound to -the plist of static information attributes of the host to which the property -has been applied or is to be applied. This subroutine typically pushes new -entries to this list, but it might also modify existing entries (e.g. by -pushing new entries to a sublist). +Should signal the condition ``INCOMPATIBLE-PROPERTY`` if the contents of +``*HOSTATTRS*`` indicates that the property should not be applied to this +host. -Should be a pure function aside from looking at consfigurator:*hostattrs*. -Essentially just a conversion of the arguments to the property to -informational attributes. +Should be a pure function aside from looking at and modifying ``*HOSTATTRS*``. +In particular, should not examine the actual state of the host. Essentially a +conversion of the arguments to the property to appropriate static information +attributes. ``:check`` subroutines -====================== +~~~~~~~~~~~~~~~~~~~~~~ Determine whether or not the property is already applied to the host and return a generalised boolean indicating such. Whether or not the ``:apply`` -and ``:unapply`` subroutines get called depends on this return value. +and ``:unapply`` subroutines get called depends on this return value. If +absent, it is always assumed the property is applied, i.e., an attempt to +apply the property will always be made. ``:apply`` and ``:unapply`` subroutines -======================================= - -The return value or values is up to you, but a few keywords as the first -return value are treated specially. - -- ``:madechange`` -- the property was not (fully) applied before we ran, but - now it is. - -- ``:nochange`` -- the property was already applied +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If neither of these values are returned but one of the ``:apply`` and -``:unapply`` subroutines was executed, then it is assumed that the property -did make a change. If the ``:check`` function indicated that neither of these -subroutines should be run, it is assumed that the property did not make a -change. +Apply or unapply the property. Should return ``:nochange`` if the property +was already applied; any other return value is interpreted as meaning that the +property was not (fully) applied before we ran, but now it is. (If the +``:check`` function indicated that neither ``:apply`` nor ``:unapply`` should +be run, then this is equivalent to those subroutines returning ``:nochange``.) The point of having both these return value semantics and the ``:check`` subroutine is that a property might only be able to check whether it made a @@ -44,24 +52,19 @@ change after trying to apply itself -- it might check whether running a command actually made a change to a particular file, for example. Errors in attempting to apply a property are indicated by signalling a -``failed-change`` condition. +``FAILED-CHANGE`` error condition. ``:posix`` vs. ``:lisp`` properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------------- ``:posix`` properties should not make any assumptions about what localhost is -- they may be running in the root Lisp, but they might be running in a Lisp process running on an intermediary host, or even on the host to be configured. -They should perform I/O only by calling ``run``, ``readfile``, ``writefile``, -requesting prerequisite data, and applying or unapplying other ``:posix`` -properties. Otherwise, they should be pure functions. - -In this respect, the code which establishes connections (i.e., implementations -of the ``establish-connection`` generic function) is like a ``:posix`` -property -- it should restrict its I/O to ``run``, ``readfile`` and -``writefile`` to permit the arbitrary nesting of connections. - -``:lisp`` properties, by contrast, may assume that they are running in a Lisp -process on the host to which they are to be applied, so they can perform -arbitrary I/O in that context. They can also make use of ``run``, -``readfile`` and ``writefile`` if desired. +They should perform I/O only by calling ``RUN``, ``RUNLINES``, ``READFILE``, +``WRITEFILE``, requesting prerequisite data, and applying or unapplying other +``:posix`` properties. Otherwise, they should be pure functions. + +``:lisp`` properties, by contrast, may (and should) assume that they are +running in a Lisp process on the host to which they are to be applied, so they +can perform arbitrary I/O in that context. They can also make use of ``RUN``, +``RUNLINES``, ``READFILE`` and ``WRITEFILE`` if desired. diff --git a/doc/property_application_specs.rst b/doc/propspecs.rst index 307f52a..7143f37 100644 --- a/doc/property_application_specs.rst +++ b/doc/propspecs.rst @@ -1,5 +1,8 @@ +Property application specifications +=================================== + Combinators -=========== +----------- Currently supported: ``(unapply (foo 1 2 3))``, ``((foo 1 2 3) on-change (bar 4 5 6) on-change (baz 7 8 9))`` and combinations thereof. |