Design Changes

Scott James Remnant scott at netsplit.com
Tue Sep 12 03:06:40 BST 2006


We've been having some fairly long discussions on the IRC channel about
Events, more specifically what we want them to look and behave like.

Here's a summary of what we've been thinking about so far, note that
none of this is a decision yet, so if you have any preferences or better
ideas, please weigh in!


Naming
------

We seemed to be pretty happy with the idea that event names remain a
simple string, though this can mean the set of events can get very out
of hand.  Namespacing seemed natural with the '.' or '/' characters.

It's been suggested that aliases should be allowed, so that the system
would know that lsb.network-ready was the same as the local
default-route-up event.


Passing to Job
--------------

Consensus was pretty steep that the name of the event that triggered a
job should be made available to that job in the environment, e.g. with
an UPSTART_EVENT variable.


Scoping
-------

Events are currently global to the init system, and due to jobs issuing
events, share a namespace with those too.  This idea is to give events a
scope, acting both as a kind of forced namespace AND a useful method of
data passing.

"gdm/started" would become a "started" event in the "gdm" scope

	on gdm/started
    becomes
	on gdm started


The tricky "block-device-added" event can now be scoped to the device
itself, and the name shortened:

	on hda1 added

Like jobs, we can now support a whole set of events for the scope of a
named block device.

	on hda1 removed
	on hda1 mounted
	on hda1 unmounted
	on hda1 checked

(We could use the sysfs path here instead, e.g. on /block/hda/hda1 ...;
though I feel that if you care at that level, you should be using udev
itself rather than an event that comes from it -- we're not replacing
udev rules, we're providing a means to run services from them).

The scope would be placed in an UPSTART_SCOPE or UPSTART_SOURCE, etc.
variable along with UPSTART_EVENT.

We'd also set the job name in the environment, so that UPSTART_JOB can
be picked up by initctl and used as the default scope; so the apache
post-start script could just:

	initctl trigger ready
    to start something with
	on apache ready


Arguments
---------

An alternative to scoping is the idea of adding arguments to an event,
which themselves would be simple strings:

	on block-device-added hda
	on block-device-checked hda

We could support one or an infinite number of arguments, and the filter
could either match them exactly (by number) or left-match them so any
extra arguments in the event itself are ignored.

Matching could even use fnmatch?

These arguments could be passed to the job as actual arguments to the
scripts, so one could do things like:

	pre-start script
	    case "$1" in
	    hd*)
		hdparm $1 ...
		;;
	    sd*)
		sdparm $1 ...
		;;
	    esac
	end script

Obviously these can't be passed to the binary or daemon itself, because
that has its own arguments specified -- though there's a cute hack that
would permit that:

	exec /sbin/daemon --foo "$@"

Which forces a shell due to the "special" characters.


Environment
-----------

Another consensus was that events should include environment variables,
which are set for any of the job's processes.  These would be defined by
the event itself, and would be pretty compatible with just about every
other form of event in the system from udev through to ifupdown, etc.

An interesting side-proposal was the possibility to filter these in the
job configuration itself, e.g.

	on network-up IF_ADDR=00:12:13:*

If not, the job could always do it in the pre-start script and fail if
they don't match.


An alternate to arguments was that testing the name of an environment
variable would just test whether it was set.

	on block-device-added hda

would test whether the "hda" variable was set, and the event generator
would set that to some fixed value such as "true".


Job Events
----------

Another solution, instead of scoping, to the problem of shared namespace
between events and jobs would be reversing the current sense of the
events.

Instead of having a "jobname" event, even with a postfix or argument, we
would have "job-started", "job-stopped", etc. events which receive the
job name as an argument or in their environment.

Possible syntax:

	on job-started apache

This makes it very clear that jobs aren't events, they are just
triggered by them and generate different events of their own.


Success or Failure
-------------------

Current events just happen, whether they start or stop anything isn't
directly recorded -- and whether those things started or stopped
actually manage to reach the right state is not recorded.

By recording the event that changed a job's goal (necessary for passing
in environment) we get another bonus for free, the ability to tell when
an event has actually finished making changes to the system *and* the
ability to tell whether it was a success or not.

At the simplest level, this means that "initctl trigger foo" can get an
exit code to say whether it worked or not; the same improvement could
mean that in the meantime it displays the jobs being started and
stopped.

It also means we could generate further events to indicate that the
event was successful or a failure, example syntaxes (depending on the
other choices):

	on shutdown/failed
	on shutdown failed
	on event-failed shutdown
	on event-failed NAME=shutdown

Having ssh and gettys bounce back if shutdown fails would be ... nice.

Obviously these events shouldn't themselves generate success or fails,
or we'd have a storm on our hands.


In order to determine whether an event was a success or not, there would
need to be some way for a pre-start script to indicate whether it's
failing or whether it's just indicating that the job should not be
started.

The obvious answer for the latter is for it to just call "stop" (and
have that pick up UPSTART_JOB), or alternately use special exit codes.


Job Success or Failure
----------------------

This brings us to the thoughts about changes to the job events to
indicate whether the job itself, or its scripts, failed or not.  Success
is indicated currently through the usual events, but it may be useful to
know whether it failed or not.

These would be additional events issued not by the state changes, but by
the child reaper, and may look like:

	on apache start-failed
	on apache failed

The event could include the reason for failure, exit code, signal, etc.
in its environment.

One useful side-effect of this is that it means the "respawning" state
can go entirely, and all in-upstart handling of respawns.  Apache itself
can just give:

	start on apache failed

to get the same effect.  "respawn" could be an alias for this.


Waiting for Events
------------------

This is a particular problem with the event-based system.  It's easy to
wait for either of two events to occur, you'd just put:

	on some-event
	on different-event

in your configuration, and the first to occur starts you job.

It's difficult to say that you need BOTH events to occur.

The easiest way to implement this would be some kind of "waiting for"
state; this has already been partially implemented but only for jobs, it
may be better to reimplement this using events.

The most canon example is:

	on startup
	wait for fhs-filesystem
	wait for network

"wait for" would mean that when the named event was received, the goal
would only be changed if all of the other named events had already been
received.

(Note the current implementation changes the goal immediately, and
defers changing the state; this is not workable for the "STOP"
equivalent as it would prevent process respawning.)

The term is not perfect though, as there's no way to distinguish between
"start" and "stop".

	stop wait for ... doesn't quite work.

Another possible syntax would be "after" ... 

	on startup
	after fhs-filesystem
	after network

and

	stop on shutdown
	stop after apache stopped
	stop after tomcat stopped

Alternatively this can be done with Meta-Events, or by a helper tool
that subscribes to the event feed from init and doesn't return until
those named have happened.


Meta-Events
-----------

In the example above, both fhs-filesystem and network are meta-events.
They're events that are triggered because some accumulation of other
events has occurred, and reached a point determined by some script or
configuration.

Another term might be watershed events?

fhs-filesystem is triggered when the filesystems listed in /proc/mounts
match those in /etc/fstab that are specified by the FHS.

This is not just a simple matter of waiting for a list "mounted" events,
because the list of events we're waiting for can only be determined by
parsing that configuration file.  There would be no "/usr mounted"
unless that's on a separate filesystem.

A question arises about the method of implementation of these watershed
scripts.

One possibility is just to have a simple script or binary that does the
comparison and either emits the event or doesn't.  This could be done as
part of the standard mount procedure, or as a separate job run whenever
a filesystem is mounted.

Another less resource-hungry possibility is to have a job and daemon
that sits and waits until the required point has occurred, emits the
event, and then terminates.


Scott
-- 
Have you ever, ever felt like this?
Had strange things happen?  Are you going round the twist?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 191 bytes
Desc: This is a digitally signed message part
Url : https://lists.ubuntu.com/archives/upstart-devel/attachments/20060912/48b3f0a4/attachment.pgp 


More information about the Upstart-devel mailing list