From f8faebc4e846430e4afb5678ce3e7c049370f5b2 Mon Sep 17 00:00:00 2001 From: Joshua Strobl Date: Tue, 23 Sep 2014 11:41:41 +0300 Subject: [PATCH] URL tweaking to reference exact relation hooks. Dropped some notes regarding bugs that have been resolved for some time. Note in authors-charm-hooks was broken, syntax changed to reflect Juju markdown syntax. Changed authors-charm-hooks references to hooks, such as -joined, to make it more obvious / stand out more against the paragraphs and walls of text. Dropped hook-kinds since it was just an outdated version of charm-hooks. --- src/en/authors-charm-components.md | 2 +- src/en/authors-charm-config.md | 8 -- src/en/authors-charm-hooks.md | 18 +-- src/en/authors-charm-store.md | 2 +- src/en/authors-charm-upgrades.md | 2 +- src/en/authors-charm-writing.md | 19 ++-- src/en/authors-hook-environment.md | 18 ++- src/en/authors-hook-kinds.md | 175 ----------------------------- src/en/authors-testing.md | 3 +- 9 files changed, 31 insertions(+), 216 deletions(-) delete mode 100644 src/en/authors-hook-kinds.md diff --git a/src/en/authors-charm-components.md b/src/en/authors-charm-components.md index 2442b61fa..c6557741d 100644 --- a/src/en/authors-charm-components.md +++ b/src/en/authors-charm-components.md @@ -18,7 +18,7 @@ The following files will be treated specially, if present: - `/hooks` must be a directory holding executables with specific names, that will be invoked by juju at the relevant times. A charm needs to implement at least one hook in order to do anything at all. How to implement hooks is covered more thoroughly in the [Hooks section](./authors-charm-hooks.html) - `config.yaml` defines service configuration options.[ The config.yaml file is descibed more fully here](./authors-charm-config.html). - - `icon.svg` is used to identify your charm in the GUI and in the charm store.[See the walkthrough for creating an icon.](authors-charm-icon.html) + - `icon.svg` is used to identify your charm in the GUI and in the charm store. [See the walkthrough for creating an icon.](authors-charm-icon.html) - `README` is made available in the charm store. It should be comprehensible to a reasonably ignorant user. - `revision` is now deprecated. - files matching `.juju*` should **not** be used. diff --git a/src/en/authors-charm-config.md b/src/en/authors-charm-config.md index 2cc1e07d2..13da0b5de 100644 --- a/src/en/authors-charm-config.md +++ b/src/en/authors-charm-config.md @@ -37,14 +37,6 @@ first time someone uses your charm, they're likely to run `juju deploy yourcharm` and see what happens; if it doesn't work out of the box ont the first go, many potential users won't give it a second try. -## Warning - -**Warning:** There's a [bug](https://bugs.launchpad.net/juju-core/+bug/1194945) -in the service configuration CLI at the moment; if a string-typed option has an -explicit default that is _not_ the empty string, it will become impossible to -set the value to the empty string at runtime. If your option needs to accept an -empty string value, it should make the empty string the explicit default value. - ## Sample config.yaml files The MediaWiki has some simple but useful configuration options: diff --git a/src/en/authors-charm-hooks.md b/src/en/authors-charm-hooks.md index dc0b67959..fdd75f264 100644 --- a/src/en/authors-charm-hooks.md +++ b/src/en/authors-charm-hooks.md @@ -20,7 +20,7 @@ during hook execution. There are two types of hooks, described in more detail in the following sections. -**Note: **None of the unit or relation hooks are required; if you don't +!!! Note: None of the unit or relation hooks are required; if you don't implement a hook, it just doesn't get run. When a hook event occurs, Juju will look for the corresponding hook file to execute, but if it finds none, will continue running without generating an error. @@ -124,7 +124,7 @@ should wait for a -changed hook that presents the right information. ### -relation-changed -`-relation-changed` is always run once, after -joined, and will +`-relation-changed` is always run once, after `-joined`, and will subsequently be run whenever that remote unit changes its settings for the relation. It should be the _only_ hook that _relies_ upon remote relation settings from `relation-get`, and it should _not_ error if the settings are @@ -137,8 +137,8 @@ relation's [interface](./authors-charm-interfaces.html). ### -relation-departed `-relation-departed` is run once only, when the remote unit is known to be -leaving the relation; it will only run once at least one -changed has been run, -and after -departed has run, no further -changeds will be run. This should be +leaving the relation; it will only run once at least one `-changed` has been run, +and after `-departed` has run, no further `-changed` hooks will be run. This should be used to remove all references to the remote unit, because there's no guarantee that it's still part of the system; it's perfectly probable (although not guaranteed) that the system running that unit has already shut down. @@ -148,26 +148,26 @@ agent continues to uphold the ordering guarantees above; but within those constraints, it will run the fewest possible hooks to notify the charm of the departure of each individual remote unit. -Once all necessary -departed hooks have been run for such a relation, the unit +Once all necessary `-departed` hooks have been run for such a relation, the unit agent will run the final relation hook: ### -relation-broken `-relation-broken` indicates that the current relation is no longer valid, and that the charm's software must be configured as though the relation had -never existed. It will only be called after every necessary -departed hook has +never existed. It will only be called after every necessary `-departed` hook has been run; if it's being executed, you can be sure that no remote units are currently known locally. -It is important to note that the -broken hook might run even if no other units +It is important to note that the `-broken` hook might run even if no other units have ever joined the relation. This is not a bug: even if no remote units have ever joined, the fact of the unit's participation can be detected in other hooks -via the `relation-ids` tool, and the -broken hook needs to execute to give the +via the `relation-ids` tool, and the `-broken` hook needs to execute to give the charm an opportunity to clean up any optimistically-generated configuration. And, again, it's important to internalise the fact that there may be multiple runtime relations in play with the same name, and that they're independent: one --broken hook does not mean that _every_ such relation is broken. +`-broken` hook does not mean that _every_ such relation is broken. ## Writing hooks diff --git a/src/en/authors-charm-store.md b/src/en/authors-charm-store.md index 4621d7146..42fca4b5c 100644 --- a/src/en/authors-charm-store.md +++ b/src/en/authors-charm-store.md @@ -67,7 +67,7 @@ you must initialize the repository and push your development branch to LaunchPad For the purpose of this documentation, we will call our charm `ubucharm` - bzr push lp:~username/charms/series/ubucharm/trunk + bzr push lp:~username/charms/series/ubucharm/trunk The /trunk branch identifier is the *only* branch that will be recognized by the charm store ingestion process. This free's the developer to push multiple branches diff --git a/src/en/authors-charm-upgrades.md b/src/en/authors-charm-upgrades.md index 2478e083f..8b31ff879 100644 --- a/src/en/authors-charm-upgrades.md +++ b/src/en/authors-charm-upgrades.md @@ -28,7 +28,7 @@ settings will not be affected by subsequent changes to the service's settings. ## Forced charm upgrades -Juju defines the upgrade-charm [hook](./authors-hook-kinds.html) for resolving +Juju defines the [upgrade-charm hook](authors-charm-hooks.html#upgrade-charm) for resolving differences between versions of the same charm. No notice is given of charm upgrades; a charm upgrade may run at any time the unit is started, and the only opportunity for resolution that exists occurs *after* the change has taken diff --git a/src/en/authors-charm-writing.md b/src/en/authors-charm-writing.md index c01b442e4..577525c32 100644 --- a/src/en/authors-charm-writing.md +++ b/src/en/authors-charm-writing.md @@ -12,7 +12,7 @@ Tools. [ Find out how to get and install charm tools here ](tools-charm-tools.ht hurry back. For this example, we are imagining that we want to create a charm for -[the Vanilla forum software](http://vanillaforums.org/) +[the Vanilla forum software](http://vanillaforums.org/). ## Prepare yourself @@ -22,7 +22,7 @@ charm repository (see how to deploy from a local repository [here](./charms-deploying.html)) to make it easy to test in your Juju environment. -Go to your home directory (or wherever is appropriate and make the +Go to your home directory (or wherever is appropriate) and make the appropriate file structure: cd ~ @@ -46,7 +46,7 @@ this ## Create the README file -Fire up your text editor and load/edit the readme file. +Fire up your text editor and load/edit the `README.ex` file. This step is especially important if you intend making your charm public, but it is very useful even if your charm will only ever be @@ -72,7 +72,10 @@ Here is a quick example README file for our Vanilla charm: And finally expose the Vanilla service: juju expose vanilla -Obviously, you can include any useful info you wish. +Obviously, you can include any useful info you wish. After sufficiently modifying the README.ex file, we recommend changing +the extension from .ex to .md to make it more clear that it is utilizing Markdown formatting. + + mv README.ex README.md ## Make some metadata.yaml @@ -214,8 +217,8 @@ raise an error - this is important so that Juju can work out if things are running properly. The final line starts the Apache web server, thus also starting our -Vanilla service. Why do we call 'restart'? One of the important ideas -behind hooks is that they should be 'idempotent'. That means that the +Vanilla service. Why do we call `restart`? One of the important ideas +behind hooks is that they should be idempotent, meaning that the operation should be capable of being run many times without changing the intended result (basically). In this case, we don't want an error if Apache is actually already running, we just want it to run and @@ -395,7 +398,7 @@ The output classifies messages as: - E - An error; these are blocker which must be fixed for the charm to be used. -some example output might be: +Some example output might be: E: no copyright file E: README.ex Includes boilerplate README.ex line 1 @@ -418,7 +421,7 @@ exactly what is happening. It won't do much to begin with, but you should see messages appearing when we start to deploy our charm. Following our own recipe, in another terminal we should now do the -following (assuming you already have a bootstrapped environment): +following (assuming you already have a [bootstrapped environment](getting-started.html)): juju deploy mysql juju deploy --repository=/home/$USER/charms/ local:precise/vanilla diff --git a/src/en/authors-hook-environment.md b/src/en/authors-hook-environment.md index 202c6a86f..7f859d5dc 100644 --- a/src/en/authors-hook-environment.md +++ b/src/en/authors-hook-environment.md @@ -42,7 +42,7 @@ In addition, every relation hook makes available relation-specific variables. important, because it's the only reasonable way of telling the difference between (say) a database service's many independent clients. -...and, if that relation hook is not a -broken hook: +...and, if that relation hook is not a [-broken](authors-charm-hooks.html#-relation-broken) hook: - The `$JUJU_REMOTE_UNIT` variable holds the name of the unit which is being reported to have -joined, -changed, or -departed. @@ -94,10 +94,7 @@ important, please `juju-log` it. juju-log "some important text" It accepts a `--debug` flag which causes the message to be logged at `DEBUG` -level; in all other cases it's logged at `INFO` level. The `-l`/`--level` -argument is ignored, and is present only to prevent legacy charms from entirely -failing to run; the inability to specify logging levels and targets in more -detail is a known [bug](https://bugs.launchpad.net/juju-core/+bug/1223325). +level; in all other cases it's logged at `INFO` level. ### unit-get @@ -144,7 +141,7 @@ if the setting doesn't exist. In both cases nothing will be returned. config-get [key-with-no-default] config-get [missing-key] -**Note: ** The above two examples are not misprints - asking for a value which +!!! Note: The above two examples are not misprints - asking for a value which doesn't exist or has not been set returns nothing and raises no errors. ### open-port @@ -264,7 +261,7 @@ To get one setting from the default remote unit in the default relation enter: jim To get all settings from a particular remote unit in a particular relation you -specify them together with the command. So +specify them together with the command. relation-get -r database:7 - mongodb/5 username: bob @@ -279,21 +276,20 @@ necessarily _accurate_, in that you will always see settings that: You should never depend upon the presence of any given key in `relation-get` output. Processing that depends on specific values (other than `private-address`) -should be restricted to -changed hooks for the relevant unit, and the absence +should be restricted to [-changed](authors-charm-hooks.html#-relation-changed) hooks for the relevant unit, and the absence of a remote unit's value should never be treated as an [error](./authors-hook-errors.html) in the local unit. In practice, it is common and encouraged for -relation-changed hooks to exit early, without error, after inspecting `relation-get` output and determining it -to be inadequate; and for [all other hooks](./authors-hook-kinds.html) to be +to be inadequate; and for [all other hooks](authors-charm-hooks.html) to be resilient in the face of missing keys, such that -relation-changed hooks will be sufficient to complete all configuration that depends on remote unit settings. Settings for remote units already known to have departed remain accessible for the lifetime of the relation. -`relation-get` currently has a -[bug](https://bugs.launchpad.net/juju-core/+bug/1223339) +!!! Note: `relation-get` currently has a [bug](https://bugs.launchpad.net/juju-core/+bug/1223339) that allows units of the same service to see each other's settings outside of a peer relation. Depending on this behaviour is foolish in the extreme: if you need to share settings between units of the same service, diff --git a/src/en/authors-hook-kinds.md b/src/en/authors-hook-kinds.md deleted file mode 100644 index 36d3ded8b..000000000 --- a/src/en/authors-hook-kinds.md +++ /dev/null @@ -1,175 +0,0 @@ -# Charm hooks - -A service unit's direct action is entirely defined by its charm's hooks. Hooks -are executable files in a charm's `hooks` directory; hooks with particular names (see below) will be invoked by the juju unit agent at particular times, and thereby cause changes to the world. - -Whenever a hook-worthy event takes place, the unit agent first checks whether -that hook is being [debugged](./authors-hook-debug.html), and if so hands over -control to the user. Otherwise, it tries to find a hook with precisely the right name. If the hook doesn't exist, the agent continues without complaint; if it does, it is invoked without arguments in a specific [environment](./authors-hook-environment.html), and its output is written to the unit agent's log. If it returns a non-zero exit code, the agent enters an [error state](./authors-hook-errors.html) and awaits user intervention. - -The agent will also enter an error state if the unit agent process is aborted -during hook execution. - -There are two types of hooks, described in more detail in the following -sections. - -**Note: **None of the unit or relation hooks are required; if you don't implement a hook, it just doesn't get run. When a hook event occurs, Juju will look for the corresponding hook file to execute, but if it finds none, will continue running without generating an error. - -## Unit hooks - -There are 5 "unit hooks" with predefined names that can be implemented by any -charm: - - - install - - config-changed - - start - - upgrade-charm - - stop - -For every relation defined by a charm, an additional 4 "relation hooks" can be -implemented, named after the charm relation: - - - -relation-joined - - -relation-changed - - -relation-departed - - -relation-broken - -None of the unit or relation hooks are required; if you don't implement a hook, -it just doesn't get run, except when debugging. When a hook event occurs, Juju -will look for the corresponding hook file to execute, but if it finds none, will continue running without generating an error. - -The unit hooks are run under the following circumstances. - -### install - -`install` runs just once, before any other hook. It should be used to perform -one-time setup operations only. - -### config-changed - -`config-changed` runs in several different situations. - - - immediately after "install" - - immediately after "upgrade-charm" - - at least once when the unit agent is restarted (but, if the unit is in an [error state](./authors-hook-errors.html), it won't be run until after the error state is cleared). - -It cannot assume that the software has already been started; it should not start stopped software, but should (if appropriate) restart running software to take configuration changes into account. - -### start - -`start` runs immediately after the first `config-changed` hook. It should be -used to ensure the charm's software is running. Note that the charm's software -should be configured so as to persist through reboots without further -intervention on juju's part. - -### upgrade-charm - -`upgrade-charm` runs immediately after any -[upgrade](./authors-charm-upgrades.html) operation that does _not_ itself -interrupt an existing [error state.](./authors-hook-errors.html). It should be -used to reconcile local state written by some other version of the charm into -whatever form it needs to take to be manipulated by the current version. - -While the forced upgrade functionality is intended as a developer tool, and is -not generally suitable for end users, it's somewhat optimistic to depend on the -functionality never being abused. In light of this, if you need to run an -`upgrade-charm` hook before your other hooks will work correctly, it may be wise -to preface all your other hooks with a quick call to your (idempotent) -`upgrade-charm`. - -### stop - -`stop` runs immediately before the end of the unit's destruction sequence. It -should be used to ensure that the charm's software is not running, and will not -start again on reboot. - -## Relation hooks - -Units will only participate in relations after they're been started, and before -they've been stopped. Within that time window, the unit may participate in -several different relations at a time, _including_ multiple relations with the -same name. - -To illustrate, consider a database service that will be used by multiple client -services. Units of a single client service will surely want to connect to, and -use, the same database; but if units of another client service were to use that -same database, the consequences could be catastrophic for all concerned. - -If juju respected the `limit` field in relation [metadata](./authors-charm- -metadata.html), it would be possible to work around this, but it's not a high- -priority [bug](https://bugs.launchpad.net/bugs/1089297): most provider services -_should_ be able to handle multiple requirers anyway; and most requirers will -only be connected to one provider anyway. - -When a unit running a given charm participates in a given relation, it runs at -least three hooks for every remote unit it becomes aware of in that relation. - -### [name]-relation-joined - -`-relation-joined` is run once only, when that remote unit is first -observed by the unit. It should be used to `relation-set` any local unit -settings that can be determined using no more than the name of the joining unit -and the remote `private-address` setting, which is always available when the -relation is created and is by convention not deleted. - -You should not depend upon any other relation settings in the -joined hook -because they're not guaranteed to be present; if you need more information you -should wait for a -changed hook that presents the right information. - -### [name]-relation-changed - -`-relation-changed` is always run once, after -joined, and will -subsequently be run whenever that remote unit changes its settings for the -relation. It should be the _only_ hook that _relies_ upon remote relation -settings from `relation-get`, and it should _not_ error if the settings are -incomplete: you can guarantee that when the remote unit changes its settings, -the hook will be run again. - -The settings that you can get, and that you should set, are determined by the -relation's [interface](./authors-charm-interfaces.html). - -### [name]-relation-departed - -`-relation-departed` is run once only, when the remote unit is known to be leaving the relation; it will only run once at least one -changed has been run, and after -departed has run, no further -changeds will be run. This should be used to remove all references to the remote unit, because there's no guarantee that it's still part of the system; it's perfectly probable (although not guaranteed) that the system running that unit has already shut down. - -When a unit's own participation in a relation is known to be ending, the unit -agent continues to uphold the ordering guarantees above; but within those -constraints, it will run the fewest possible hooks to notify the charm of the -departure of each individual remote unit. - -Once all necessary -departed hooks have been run for such a relation, the unit -agent will run the final relation hook: - -### [name]-relation-broken - -`-relation-broken` indicates that the current relation is no longer valid, -and that the charm's software must be configured as though the relation had -never existed. It will only be called after every necessary -departed hook has -been run; if it's being executed, you can be sure that no remote units are -currently known locally. - -It is important to note that the -broken hook might run even if no other units -have ever joined the relation. This is not a bug: even if no remote units have -ever joined, the fact of the unit's participation can be detected in other hooks via the `relation-ids` tool, and the -broken hook needs to execute to give the charm an opportunity to clean up any optimistically-generated configuration. - -And, again, it's important to internalise the fact that there may be multiple -runtime relations in play with the same name, and that they're independent: one --broken hook does not mean that _every_ such relation is broken. - -## Writing hooks - -If you follow the [tutorial](./authors-charm-writing.html), you'll get a good -sense of the basics. To fill out your knowledge, you'll want to study the hook -[environment and tools](./authors-hook-environment.html), and to experiment with [debug-hooks](./authors-hook-debug.html). - -Independent of the nuts and bolts, though, good hooks display a number of useful high-level properties: - - - They are _idempotent_: that is to say that there should be no observable difference between running a hook once, and running it N times in a row. If this property does not hold, you are likely to be making your own life unnecesarily difficult: apart from anything else, the average user's most likely first response to a failed hook will be to try to run it again (if they don't just skip it). - - They are _easy to read_ and understand. It's tempting to write a single file that does everything, and which just calls different functions internally depending on the value of `argv[0]`, and to symlink that one file for every hook; but such structures quickly become unwieldy. The time taken to write a library, separate from the hooks, is very likely to bewell spent: it lets you write single hooks that are clear and focused, and insulates the maintainer from irrelevant details. - - Where possible, they reuse [common code](https://launchpad.net/charm-tools) already written to ease or solve common use cases. - - They do not return [errors](./authors-hook-errors.html) unless there is a good reason to believe that they cannot be resolved without user intervention. Doing so is an admission of defeat: a user who sees your charm returning an error state is unlikely to have the specific expertise necessary to resolve it. If you have to return an error, please be sure to at least write any context you can to the log before you do so. - - They write only _very_ sparingly to the [charm directory](./authors-charm-components.html). - -We recommend you also familiarize yourself with the [best practices](./authors- -charm-best-practice.html) and, if you plan to distribute your charm, the [charm -store policy](./authors-charm-policy.html). diff --git a/src/en/authors-testing.md b/src/en/authors-testing.md index 87d1486da..fb936048d 100644 --- a/src/en/authors-testing.md +++ b/src/en/authors-testing.md @@ -27,8 +27,7 @@ the state of the service from install to config to started. Because of this, all Other generic tests may be identified, so a collection of generic tests should be the focus of an implementation. -Note that this requirement is already satisfied by Mark Mims' jenkins tester: -[https://github.com/mmm/charmtester/](https://github.com/mmm/charmtester/) +Note that this requirement is already satisfied by [Mark Mims' jenkins tester](https://github.com/mmm/charmtester/). ## Phase 2 - Charm Specific tests