In my previous two posts, we examined two packaging system
options—installer-specific knowledge and
integrated build system—that I believe present costs
that exceed their benefits. Here, we will again examine a design choice
from a negative perspective: package-associated scripting.
System V packaging is rich with scripting hooks; scripts named
the class action scripts. Each of these scripts can do anything they
like. Scripting, even in a relatively primitive shell, is an open-ended
program—opaque to the invoking framework. It's difficult to catch
an incorrect script prior to package publication time, which blocks our
intent to prevent propagation of bad package versions. With a more
limited set of actions—potentially with that limit enforced or
marked—a class of incompletely known resource handling mechanisms
can be kept off the most conservative systems.
One goal we
have is to preserve or improve the hands-off behaviour
associated with package operations. Legacy packaging allows hands-off
by imposing a series of tasks on the deploying administrator.
pkgask(1M) tool can enable the deployer to develop a response to
request script; coming up with an appropriate
restrict the framework's built-in interactive queries. (Interaction
with signed packages also requires the deployer to modify their
invocation.) Removing the scripting degree of freedom means that
obstacles to hands-off behaviour come solely from an interactive
installer or from interactive services acting during system startup.
There's some amusingly egregious violations of the hands-off principle
across the space of known packages. Less fun is that these set a bad
example for later package developers.
A particularly error-prone aspect of the scripting interface in
packaging comes from the variety of contexts the package developer must
understand (and test within). It is legitimate to install packages on
live systems, in alternate filesystem hierarchies of the same or
different architecture, and in whole-root and filesystem inheriting
zones; in fact, you have multiple choices about how your package should
install in a zone.
We can expect the proliferation of virtualized systems, via the various
mechanisms like LDOMs and xVM, to keep all of these contexts relevant as
degrees of sharing make virtualization even more appealing. Making sure
that the package system operates safely in these shared contexts is
critical—another of our goals.
Returning to the zones case, the example pseudo-script in
pkginfo(4)—a series of nested shell
if ; then blocks to
navigate some of these contexts—is helpful, but misleading. There
is much more variable state a package developer needs to consider to
reach correctness. In fact, if you aren't required to rediscover or
reinvent a set of resource-handling cases for each components your
package delivers, it becomes substantially simpler to make the package
and return to improving the software it contains. Reducing the set of
steps reduces developer burdens associated with packaging.
Two particular resources stand out: device drivers and
services. Although some limited amount of awareness—or at least
easily duplicated code—makes these resources somewhat well-behaved
during package operations, there are still problems that scripting
presents: the addition of new contexts, the provision of multiple
genealogies of copied code, and the failure to discover an associated
best practice for any particular kind of resource.
There are other resources, of course; as a start, you could duplicate
our survey of the ON
postinstall and class action scripts.
I believe the key counterargument supporting scripting is that the
set of configuration patterns on Unix-like systems is large, and that
the easiest means of upgrading each of these potential patterns is to allow
a complete programming environment to the package developer. Probably
true, but if we look at service and application configuration with
respect to when a correct configuration state is required, the update
step appears to separate into three classes:
Correct at system startup, no runtime context needed. These are the
configuration settings that the various low-level boot components, the
kernel, and the drivers need to bring the system to its running state.
This class of configuration is generally limited to a specific set of
resources, potentially established by a packaging system via
corresponding resource-handling actions—or by an installer.
Correct at system startup, requiring runtime context. These are
settings where the manipulating agent might be influenced by policy or
require some form of interprocess communication to effect configuration
smf(5) is an example of the latter, and handles its
configuration evolution via the
manifest-import service. Manipulation
of the various local name service tables, like
passwd or the RBAC
configuration is another example, since data about potential principals
must be correct for a group of affected services. Since such
configuration can be required on the system as a result of package
operations, these resources must also be handled via packaging, or
require the use of an appropriate installer.
Correct prior to service startup. Most service and application
configuration falls into this class. It's not necessary, for instance,
to bring a web server's configuration up to date if the service has no
enabled instances. There seem to be a number of avenues for handling
this kind of configuration: leaving it to the service or application,
providing assistance via a configuration mechanism, or giving a hook
where such updates can be made as needed. But the packaging system
needn't provide this hook—there are a number of possible
facilities, of varying suitability.
I should point out that David is making the
update scenarios much more capable and precise with the Enhanced
Profiles project. So, at least, a "configuration mechanism
with assistance" is likely to be present soon.
Since the first and second classes and how their configuration
manipulations vary in the various operating contexts are generally
known, elimination of the third class makes precise, no-scripting
packages a viable design choice.
That's a long series of arguments in favour of a scripting-free package
system. It would be reasonable to ask: "can you actually do it?" So,
as a check on our prototype, we used the branded zone capability to let
us create a
pkg(5)-based whole root zone. Here's a transcript
# zonecfg -z pkg_test
pkg_test: No such zone configured
Use 'create' to begin configuring a new zone.
zonecfg:pkg_test> create -t SUNWipkg
zonecfg:pkg_test> set zonepath=/export/pkg_test
# zoneadm -z pkg_test install
Installing SUNWcs SUNWesu SUNWadmr SUNWts SUNWipkg
Setting up SMF profile links
Copying SMF seed repository
There's dependency following, but no constraint handling; there's no
filtering or snapshotting, but also none of the obvious performance
optimizations has been implemented (for our 211MB resultant image). But
the main point is: it works—installs, boots, upgrades, and still
boots—with no scripting. Time for a project proposal.