aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2021-02-18 11:18:09 -0700
committerSean Whitton <spwhitton@spwhitton.name>2021-02-18 11:18:09 -0700
commit28ac2c8b8ba8839a78fc4b6bfea87d5482fb0208 (patch)
treebe7e4a5579f79650b6c282f886c08720619f9312
parentc8be9aa24f37ff52205e9525756f3338674ccaaf (diff)
downloadconsfigurator-28ac2c8b8ba8839a78fc4b6bfea87d5482fb0208.tar.gz
tidy up existing docs
Signed-off-by: Sean Whitton <spwhitton@spwhitton.name>
-rw-r--r--README.rst31
-rw-r--r--doc/connections.rst11
-rw-r--r--doc/data.rst71
-rw-r--r--doc/guide.rst219
-rw-r--r--doc/introduction.rst128
-rw-r--r--doc/pitfalls.rst50
-rw-r--r--doc/properties.rst85
-rw-r--r--doc/propspecs.rst (renamed from doc/property_application_specs.rst)5
8 files changed, 324 insertions, 276 deletions
diff --git a/README.rst b/README.rst
index c42256f..90099b7 100644
--- a/README.rst
+++ b/README.rst
@@ -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.