Merge lp:~evilnick/juju-core/docs-new-walkthrough into lp:juju-core/docs

Proposed by Nick Veitch
Status: Merged
Merged at revision: 36
Proposed branch: lp:~evilnick/juju-core/docs-new-walkthrough
Merge into: lp:juju-core/docs
Diff against target: 665 lines (+267/-340)
3 files modified
htmldocs/authors-charm-writing.html (+264/-338)
htmldocs/charms-deploying.html (+2/-2)
skel/footer.tpl (+1/-0)
To merge this branch: bzr merge lp:~evilnick/juju-core/docs-new-walkthrough
Reviewer Review Type Date Requested Status
Marco Ceppi Pending
Review via email: mp+176833@code.launchpad.net

Commit message

added new charm writing walktrhough

Description of the change

Added a new walkthrough to replace the old charm-writing page. This is completely new material and should actually work, unlike the old one.

Please check that this works and makes sense.

I am aware there are still issues with the syntax colouring of the source files, but I'd really like to get a working example up as soon as possible

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'htmldocs/authors-charm-writing.html'
--- htmldocs/authors-charm-writing.html 2013-07-15 13:01:43 +0000
+++ htmldocs/authors-charm-writing.html 2013-07-25 01:29:31 +0000
@@ -83,8 +83,9 @@
83 </ul>83 </ul>
84 <h1>Charm Authors</h1>84 <h1>Charm Authors</h1>
85 <ul></ul>85 <ul></ul>
86 <li class=""><a href="authors-charm-intro.html">Want to write a charm?</a></li>
86 <li class=""><a href="authors-charm-anatomy.html">Anatomy of a charm</a></li>87 <li class=""><a href="authors-charm-anatomy.html">Anatomy of a charm</a></li>
87 <li class=""><a href="authors-charm-writing.html">Writing a charm</a></li>88 <li class=""><a href="authors-charm-writing.html">Writing a Charm</a></li>
88 <li class=" sub"><a href="authors-subordinate-services.html">Subordinate services</a></li>89 <li class=" sub"><a href="authors-subordinate-services.html">Subordinate services</a></li>
89 <li class=" sub"><a href="authors-implicit-relations.html">Implicit Relations</a></li>90 <li class=" sub"><a href="authors-implicit-relations.html">Implicit Relations</a></li>
90 <li class=" sub"><a href="authors-testing.html">Charm Testing</a></li>91 <li class=" sub"><a href="authors-testing.html">Charm Testing</a></li>
@@ -104,343 +105,267 @@
104 </div>105 </div>
105 <div class="grid-9 doc-content">106 <div class="grid-9 doc-content">
106 <article>107 <article>
107 <section id ="charm-writing">108<h1>Your First charm starts here!</h1>
108 <h1>Writing a charm</h1>109<p>Okay, so you have read up all the background info on what a charm is, how it works, and what parts of the charm do what, now it is time to dust off your favourite code editor (no arguments please) and get charming!</p>
109 <p>This tutorial demonstrates the basic workflow for writing, running and debugging a juju charm. Charms are a way to package and share your service deployment and orchestration knowledge and share them with the world.</p>110<p>For this example, we are imagining that we want to create a charm for <a href="http://vanillaforums.org/" >the Vanilla forum software</a></p>
110 111
111 <h2>Creating the charm</h2>112<span class="number"> 1 </span>
112 <p>In this example we are going to write a charm to deploy the drupal CMS system. For the sake of simplicity, we are going to use the mysql charm that comes bundled with juju. Assuming the current directory is the juju trunk, let's enter the directory</p>113 <div class="step" id="step01">
113 114 <h2>Prepare yourself</h2>
114 <h3>Creating a README File</h3>115 <p>As we are writing a charm, it makes sense to create it in a local charm repository (see how to deploy from a local repository <a href="./charms-deploying.html" target="#local">here</a>) to make it easy to test in your Juju environment.</p>
115 <p>First create a README file with your text editor. This file must be plain text,the Charm Store will parse Markdown format and display it on the Charm Store's web interface, so it will be the public facing page for your charm. We recommend you leave basic usage instructions on how to use your charm in the README.</p>116 <p>Go to your home directory (or wherever is appropriate and make the appropriate file structure:</p>
116 117 <pre>cd ~<br>mkdir -p charms/precise/vanilla </pre>
117 <h3>Making the metadata</h3>118
118 <p>Now let's make the metadata and hooks directory:</p>119 </div>
119 <pre><code>120
120 mkdir -p drupal/hooks121<span class="number" > 2 </span>
121 vim drupal/metadata.yaml122 <div class="step" id="step02">
122 </code></pre>123 <h2>Create the README file</h2>
123 <p>Edit the metadata.yaml file to resemble:</p>124 <p>Fire up your text editor and create a readme file.</p>
124 <pre><code>125 <p>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 seen by you. The README is a good place to make nots about how the charm works, what information it expects to communicate and how.</p>
125 name: drupal126 <p>Although a plain text file is fine, you can also write your README file using Markdown (in which case use the .md suffix). The advantage of this, other than it looks quite neat, is that the Charm Store will render Mardown properly online, so your README will look and read better.</p>
126 summary: "Drupal CMS"127 <p>Here is a quick example README file for our Vanilla charm:</p>
127 maintainer: "Drupal PowerUser &lt;drupaluser@somedomain.foo&gt;"</p>128 <pre class="prettyprint lang-yaml">
128 description: Installs the drupal CMS system, relates to the mysql charm provided in examples directory. Can be scaled to multiple web servers129# Overview
129 &nbsp;requires:130
130 &nbsp;&nbsp;&nbsp;db131Vanilla is a powerful open source web-based forum. This charm will deploy the forum software and connect it to a running MySQL database. This charm will install the Vanilla files in /var/www/vanilla/
131 &nbsp;interface: 132
132 &nbsp;&nbsp;&nbsp;mysql133# Installation
133 </code></pre>134
134 135To deploy this charm you will need at a minimum: a cloud environment, working Juju installation and a successful bootstrap. Once bootstrapped, deploy the MySQL charm and then this Vanilla charm:
135 <p>The metadata.yaml file provides metadata around the charm. The file declares a charm with the name drupal. Since this is the first time to edit this charm, its revision number is one. A short and long description of the charm are provided. The final field is <cite>requires</cite>, this mentions the interface type required by this charm. Since this drupal charm uses the services of a mysql database, we need to require it in the metadata. Since this charm does not provide a service to any other charm, there is no <cite>provides</cite> field. You might be wondering where did the interface name &quot;mysql&quot; come from, you can locate the interface information from the mysql charm's metadata.yaml. Here it is for convenience:</p>136
136 <pre><code>137 juju deploy mysql
137 name: mysql138 juju deploy vanilla
138 summary: "MySQL relational database provider"139
139 maintainer: "Joe Charmer &lt;youremail@whatever.com&gt;"140Add a relation between the two of them:
140 description: |141
141 &nbsp;&nbsp;&nbsp;&nbsp;Installs and configures the MySQL package (mysqldb), then runs it.142 juju add-relation mysql vanilla
142 143
143 &nbsp;&nbsp;&nbsp;Upon a consuming service establishing a relation, creates a new144And finally expose the Vanilla service:
144 &nbsp;&nbsp;&nbsp;database for that service, if the database does not yet145
145 &nbsp;&nbsp;&nbsp;exist. Publishes the following relation settings for consuming146 juju expose vanilla</pre>
146 &nbsp;&nbsp;&nbsp;services:147
147 148 <p>Obviously, you can include any useful info you wish.</p>
148 &nbsp;&nbsp;database: database name149 </div>
149 &nbsp;&nbsp;&nbsp;&nbsp;user: user name to access database150
150 &nbsp;&nbsp;&nbsp;&nbsp;password: password to access the database151<span class="number" > 3 </span>
151 &nbsp;&nbsp;&nbsp;&nbsp;host: local hostname152 <div class="step" id="step03">
152 provides:153 <h2>Make some metadata.yaml</h2>
153 &nbsp;&nbsp;&nbsp;db:154 <p>The <strong>metadata.yaml</strong> file is really important. This is the file that Juju reads to find out what a charm is, what it does and what it needs to do it.</p>
154 &nbsp;&nbsp;&nbsp;&nbsp;interface: mysql</p>155 <p>The yaml syntax is at once simple, but exact, so if you have any future problems with Juju not recognising your charm, this is the first port of call! the information is stored in simple <span class="pre">&LT;key&GT; : &LT;value&GT;</span> associations. The first four are pretty self explanitory:</p>
155 </code></pre>156 <pre class="prettyprint lang-basic">
156 157name: vanilla
157 <p>That very last line mentions that the interface that mysql provides to us is &quot;mysql&quot;. Also the description mentions that four parameters are sent to the connecting charm (database, user, password, host) in order to enable it to connect to the database. We will make use of those variables once we start writing hooks. Such interface information is either provided in a bundled README file, or in the description. You can also read the charm code to discover such information as well.</p>158summary: Vanilla is an open-source, pluggable, multi-lingual forum.
158 <p>In the next steps we will write the necessary hook scripts.</p>159maintainer: Your Name &lt;your@email.tld&gt;
159 160description: |
160 <h2>Have a plan</h2>161 Vanilla is designed to deploy and grow small communities to scale.
161 <p>When attempting to write a charm, it is beneficial to have a mental plan of what it takes to deploy the software. In our case, you should deploy drupal manually, understand where its configuration information is written, how the first node is deployed, and how further nodes are configured. With respect to this charm, this is the plan:</p>162 This charm deploys Vanilla Forums as outlined by the Vanilla Forums
162 163 installation guide.
163 <ul>164</pre>
164 <li>Install hook installs all needed components (apache, php, drush)</li>165 <p>The summary should be a brief description of the service being deployed, whereas the description can go into more detail.</p>
165 <li>Once the database connection information is ready, call drush on first node to perform the initial setup (creates DB tables, completes setup)</li>166 <p>The next value to define is the category. This is primarily for organising the charm in the charm store. the available categories are:</p>
166 <li>For scaling onto other nodes, the DB tables have already been set-up. Thus we only need to append the database connection information into drupal's settings.php file. We will use a template file for that</li>167 <ul>
167 </ul>168 <li><strong>databases -</strong> MySQL, Postgres, couchDB, etc.</li>
168 169 <li><strong>file-servers - </strong>storage apps such as ceph</li>
169 170 <li><strong>applications -</strong>applications like mediawiki, wordpress</li>
170 <p class="note"><strong>Note:</strong>The hooks in a charm are executable files that can be written using any scripting or programming language. In our case, we'll use bash</p>171 <li><strong>cache-proxy - </strong>services such as haproxy and Varnish.</li>
171 172 <li><strong>app-servers - </strong>infrastructure services like Apache and Tomcat</li>
172 <p>For production charms it is always recommended that you install software components from the Ubuntu archive (using apt-get) in order to get security updates. However in this example I am installing drush (Drupal shell) using apt-get, then using that to download and install the latest version of drupal. If you were deploying your own code, you could just as easily install a revision control tool (bzr, git, hg...etc) and use that to checkout a code branch to deploy from. This demonstrates the flexibility offered by juju which doesn't really force you into one way of doing things.</p>173 <li><strong>miscellaneous - </strong>anything which doesn't neatly fit anywhere above.</li>
173 174 </ul>
174 175 <p>Your charm can belong to more than one category, though in almost all cases it should be in just one. Because there could be more than one entry here, the yaml is formatted as a list:</p>
175 <h2>Write hooks</h2>176 <pre class="prettyprint lang-yaml">categories:<br> - applications</pre>
176 <p>Let's change into the hooks directory:</p>177 <p>Next we need to explain which services are actually provided by this service. This is done using an indent for each service provided, followed by a description of the interface. The interface name is important as it can be used elsewhere in the environment to relate back to this charm, e.g. when writing hooks.</p>
177 <pre>178 <p>Our Vanilla charm is a web-based service which exposes a simple http interface:</p>
178 cd drupal/hooks179 <pre class="prettyprint lang-yaml">provides:<br> website:<br> interface: http</pre>
179 vim install180 <p>The name given here is important as it will be used in hooks that we write later, and the interface name will be used by other charms which may want to relate to this one.</p>
180 </pre>181 <p>Similarly we also need to provide a "requires" section. In this case we need a database. Checking out the metadata of the MySQL charm we can see that it provides this via the interface name "mysql", so we can use this name in our metadata.</p>
181 182 <p>The final file should look like this:</p>
182 <p>Since you should have already installed drupal, you have an idea what it takes to get it installed. My install script looks like:</p>183 <pre class="prettyprint lang-yaml">
183 <pre><code>#!/bin/bash184name: vanilla
184 set -eux # -x for verbose logging to juju debug-log185summary: Vanilla is an open-source, pluggable, themeable, multi-lingual forum.
185 juju-log "Installing drush,apache2,php via apt-get"186maintainer: Your Name &lt;your@email.tld&gt;
186 apt-get -y install drush apache2 php5-gd libapache2-mod-php5 php5-cgi mysql-client-core-5.5187description: |
187 a2enmod php5188 Vanilla is designed to deploy and grow small communities to scale.
188 /etc/init.d/apache2 restart189 This charm deploys Vanilla Forums as outlined by the Vanilla Forums installation guide.
189 juju-log "Using drush to download latest Drupal"190categories:
190 # Typo on next line, it should be www not ww191 - applications
191 cd /var/ww &amp;&amp; drush dl drupal --drupal-project-rename=juju192provides:
192 </code></pre>193 website:
193 194 interface: http
194 <p>I have introduced an artificial typo on the last line &quot;ww not www&quot;, this is to simulate any error which you are bound to face sooner or later. Let's create other hooks:</p>195requires:
195 <pre>196 database:
196 vim start197 interface: mysql</pre>
197 </pre>198 <p>
198 199 <p>For some charms you will want a "peers" section also. This follows the same format, and its used for optional connections, such as you might use for interconnecting services in a cluster
199 <p>The start hook is empty, however it needs to be a valid executable, thus we'll add the first bash shebang line, here it is:</p>200 </p>
200 <pre>201
201 <span class="c">#!/bin/bash</span>202
202 </pre>203 </div>
203 204<span class="number" > 4 </span>
204 <p>Here's the &quot;stop&quot; script:</p>205 <div class="step" id="step04">
205 <pre>206 <h2>Writing hooks</h2>
206 #!/bin/bash207 <p>As you will know from your through reading of <a href="./authors-charm-anatomy.html"> the anatomy of a charm</a> The hooks are the important scripts that actually do things. You can write hooks in whatever language you can reasonably expect to execute on an Ubuntu server</p>
207 juju-log "Stopping apache"208 <p>For our charm, the hooks we will need to create are:</p>
208 /etc/init.d/apache2 stop209 <ul>
209 </pre>210 <li>start - for when the service needs to be started.</li>
210 211 <li>stop - for stopping it again.</li></ul>
211 <p>The final script, which does most of the work is &quot;db-relation-changed&quot;. This script gets the database connection information set by the mysql charm then sets up drupal for the first time, and opens port 80 for web access. Let's start with a simple version that only installs drupal on the first node. Here it is:</p>212 <li>install - for actually fetching and installing the Vanilla code.</li>
212 <pre>#!/bin/bash213 <li>database-relation-changed - this will run when we connect (or re-connect, or disconnect) our service to the MySQL database. This hook will need to manage this connection.</li>
213 set -eux # -x for verbose logging to juju debug-log214 <li>website-relation-joined - this will run when/if a service connects to our charm.</li>
214 hooksdir=$PWD215 </ul>
215 user=`relation-get user`216 <p>So first up we should create the hooks directory, and start creating our first hook:</p>
216 password=`relation-get password`217 <pre>mkdir hooks<br>cd hooks<br>vi start</pre>
217 host=`relation-get host`218 <p>(Use your favourite editor, naturally - no flames please)</p>
218 database=`relation-get database`219 <p>We have started with the start hook, because it is pretty simple. Our charm will be served up by apache, so all we need to do to start the service is make sure apache is running:</p>
219 # All values are set together, so checking on a single value is enough220 <pre class="prettyprint lang-bash">#!/bin/bash<br>set -e<br>service apache2 restart</pre>
220 # If $user is not set, DB is still setting itself up, we exit awaiting next run221 <p>A bit of explanation for this one. As we are writing in bash, and we need the files to be executable, we start with a hash-bang line indicating this is a bash file.
221 [ -z "$user" ] &amp;&amp; exit 0222 the <span class="pre">set -e</span> line means that if any subsequent command returns false (non-zero) the script will stop and raise an error - this is important so that Juju can work out if things are running properly.
222 juju-log "Setting up Drupal for the first time"223 </p>
223 cd /var/www/juju &amp;&amp; drush site-install -y standard \224 <p>The final line starts the apache webservice, 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 opration 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 reload any config changes.</p>
224 --db-url=mysql://$user:$password@$host/$database \225 <p>Once you have saved the file, it is important to make sure that you set it to be executable too!</p>
225 --site-name=juju --clean-url=0226 <pre>chmod +x start</pre>
226 cd /var/www/juju &amp;&amp; chown www-data sites/default/settings.php227 <p>With the easy bit out of the way, how about the install hook? This needs to install any dependencies, fetch the actual software and do any other config and service jobs that need to happen. here is an example for our vanilla charm:</p>
227 open-port 80/tcp228
228 </code>229<pre class="prettyprint">
229 </pre>230#!/bin/bash
230 231
231 <p>The script is quite simple, it reads the four variables needed to connect to mysql, ensures they are not null, then passes them to the drupal installer. Make sure all the hook scripts have executable permissions, and change directory above the examples directory:</p>232set -e # If any command fails, stop execution of the hook with that error
232 <pre>chmod +x *233
233 $ cd ../../../..234apt-get install -y apache2 php5-cgi php5-mysql curl php5-gd wget libapache2-mod-php5
234 </pre>235
235 236dl="https://github.com/vanillaforums/Garden/archive/Vanilla_2.0.18.8.tar.gz"
236 <p>Checking on the drupal charm file-structure, this is what we have:</p>237
237 <pre>238# Grab Vanilla from upstream.
238 find examples/precise/drupal239juju-log "Fetching $dl"
239 examples/precise/drupal240wget "$dl" -O /tmp/vanilla.tar.gz
240 examples/precise/drupal/metadata.yaml241
241 examples/precise/drupal/hooks242# IDEMPOTENCY is very important in all charm hooks, even the install hook.
242 examples/precise/drupal/hooks/db-relation-changed243if [ -f /var/www/vanilla/conf/config.php ]; then
243 examples/precise/drupal/hooks/stop244 cp /var/www/vanilla/conf/config.php /tmp/
244 examples/precise/drupal/hooks/install245 rm -rf /var/www/vanilla
245 examples/precise/drupal/hooks/start246fi
246 </pre>247
247 248# Extract to a known location
248 <h2>Test run</h2>249juju-log "Extracting Vanilla"
249 <p>Let us deploy the drupal charm. Remember that the install hook has a problem and will not exit cleanly. Deploying:</p>250tar -xvzf /tmp/vanilla.tar.gz -C /var/www/
250 251mv /var/www/Garden-Vanilla* /var/www/vanilla
251 <pre>juju bootstrap</pre>252
252 253if [ -f /tmp/config.php ]; then
253 <p>Wait a minute for the environment to bootstrap. Keep issuing the status command till you know the environment is ready:</p>254 mv /tmp/config.php /var/www/vanilla/conf/
254 255fi
255 <pre>256
256 juju status257chmod -R 777 /var/www/vanilla/conf /var/www/vanilla/uploads /var/www/vanilla/cache
257 2011-06-07 14:04:06,816 INFO Connecting to environment.258
258 machines:259juju-log "Creating apache2 configuration"
259 &nbsp;0:260cat &lt;&lt;EOF $gt; /etc/apache2/sites-available/vanilla
260 &nbsp;&nbsp;&nbsp;agent-state: running261&lt;VirtualHost *:80&gt;
261 &nbsp;&nbsp;&nbsp;dns-name: ec2-50-16-107-102.compute-1.amazonaws.com262 ServerAdmin webmaster@localhost
262 &nbsp;&nbsp;&nbsp;instance-id: i-130c9168263 DocumentRoot /var/www/vanilla
263 &nbsp;&nbsp;&nbsp;instance-state: running264
264 services:265 &lt;Directory /var/www/vanilla&gt;
265 2011-06-07 14:04:11,125 INFO 'status' command finished successfully266 Options Indexes FollowSymLinks MultiViews
266 </pre>267 AllowOverride All
267 268 Order allow,deny
268 <p>It can be beneficial when debugging a new charm to always have the distributed debug-log running in a separate window:</p>269 allow from all
269 <pre>juju debug-log</pre>270 &lt;/Directory&gt;
270 <p>Let's deploy the mysql and drupal charms:</p>271
271 <pre>juju deploy --repository=examples local:precise/mysql272 ErrorLog \${APACHE_LOG_DIR}/vanilla.log
272 juju deploy --repository=examples local:precise/drupal</pre>273 LogLevel warn
273 274
274 <p>This deploy is telling juju to look in a local repository for our charm, specifically in the examples/precise/mysql and examples/precise/drupal folders. Local repositories specified with the --repository switch must point to a directory which contains sub-directories named after an Ubuntu series (e.g. precise) and the charms. The repository can be named anything you wish.</p>275 CustomLog \${APACHE_LOG_DIR}/access.log combined
275 <p>Thus, when creating charms locally, this syntax should be followed:</p>276&lt;/VirtualHost&gt;
276 <pre><code>277EOF
277 repositoryName/ubuntuReleaseName/charmName</code></pre>278
278 <p>Once the machines are started (hint: check the debug-log), issue a status command:</p>279a2dissite 000-default
279 <pre>juju status280a2ensite vanilla
280 281
281 <p>machines:282service apache2 reload
282 &nbsp;0:283
283 &nbsp;&nbsp;&nbsp;agent-state: running284juju-log "Files extracted, waiting for other events before we do anything else!"
284 &nbsp;&nbsp;&nbsp;dns-name: ec2-50-16-107-102.compute-1.amazonaws.com285</pre>
285 &nbsp;&nbsp;&nbsp;instance-id: i-130c9168286 <p>We aren't going to go for a line-by-line explanation of that, but there are a few things worth noting</p>
286 &nbsp;&nbsp;&nbsp;instance-state: running</p>287 <p>Firstly, note the use of the -y option of the apt-get command. this assumes a 'yes' answer to any questions and removes any manual install options (e.g. services that run config dialogs when they install</p>
287 &nbsp;1:288 <p>In our script, we are fetching the tarball of the Vanilla software. In these cases, it is obviously always better to point to a specific, permanent link to a version of the software. </p>
288 &nbsp;&nbsp;&nbsp;agent-state: running289 <p>Also, you will notice that we have used the <span class="pre">juju-log</span> command. This basically spits messages out into the juju log, which is very useful for testing and debugging. We will cover that in more detail later in this walkthrough. </p>
289 &nbsp;&nbsp;&nbsp;dns-name: ec2-50-19-24-186.compute-1.amazonaws.com290 <p>The next step is to create the relationship hooks... </p>
290 &nbsp;&nbsp;&nbsp;instance-id: i-17079a6c291 <p>We know from our metadata that we have a connection called 'database', so we can have hooks that relate to that. Note that we don't have to create hooks for all possible events if they are not required - if Juju doesn't find a hook file for a paticular action, it just assumes everything is okay and carries on. It is up to you to decide which events require a hook.</p>
291 &nbsp;&nbsp;&nbsp;instance-state: running292 <p>Let's take a stab at the 'database-relation-changed' hook:</p>
292 &nbsp;2:293 <pre class="prettyprint">#!/bin/bash
293 &nbsp;&nbsp;&nbsp;agent-state: running294
294 &nbsp;&nbsp;&nbsp;dns-name: ec2-23-20-194-198.compute-1.amazonaws.com295set -e # If any command fails, stop execution of the hook with that error
295 &nbsp;&nbsp;&nbsp;instance-id: i-d7079aac296
296 &nbsp;&nbsp;&nbsp;instance-state: running297db_user=`relation-get user`
297 services:298db_db=`relation-get database`
298 &nbsp;mysql:299db_pass=`relation-get password`
299 &nbsp;&nbsp;&nbsp;charm: cs:precise/mysql-3300db_host=`relation-get private-address`
300 &nbsp;&nbsp;&nbsp;relations: {}301
301 &nbsp;&nbsp;&nbsp;units:302if [ -z "$db_db" ]; then
302 &nbsp;&nbsp;&nbsp;mysql/0:303 juju-log "No database information sent yet. Silently exiting"
303 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;agent-state: started304 exit 0
304 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;machine: 2305fi
305 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public-address: ec2-23-20-194-198.compute-1.amazonaws.com306
306 &nbsp;drupal:307vanilla_config="/var/www/vanilla/conf/config.php"
307 &nbsp;&nbsp;&nbsp;charm: local:precise/drupal-3308
308 &nbsp;&nbsp;&nbsp;relations: {}309cat <<EOF > $vanilla_config
309 &nbsp;&nbsp;&nbsp;units:310&LT;?php if (!defined('APPLICATION')) exit();
310 &nbsp;&nbsp;&nbsp;drupal/0:311
311 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;agent-state: install-error312\$Configuration['Database']['Host'] = '$db_host';
312 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;machine: 1313\$Configuration['Database']['Name'] = '$db_db';
313 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public-address: ec2-50-19-24-186.compute-1.amazonaws.com</pre>314\$Configuration['Database']['User'] = '$db_user';
314 315\$Configuration['Database']['Password'] = '$db_pass';
315 <p>Note how mysql is listed as started, while drupal's state is install_error. This is because the install hook has an error, and did not exit cleanly (exit code 1).</p>316EOF
316 <h2>Debugging hooks</h2>317
317 <p>Let's debug the install hook, from a new window:</p>318juju-log "Make the application port available, now that we know we have a site to expose"
318 <pre>juju debug-hooks drupal/0</pre>319
319 320open-port 80
320 <p>This will connect you to the drupal machine, and present a shell. The way the debug-hooks functionality works is by starting a new terminal window instead of executing a hook when it is triggered. This way you get a chance of running the hook manually, fixing any errors and re-running it again. In order to trigger re-running the install hook, from another window:</p>321
321 <pre>juju resolved --retry drupal/0</pre>322</pre>
322 323
323 <p>Switching to the debug-hooks window, you will notice a new window named &quot;install&quot; popped up. Note that &quot;install&quot; is the name of the hook that this debug-hooks session is replacing. We change directory into the hooks directory and rerun the hook manually:</p>324<p>You will notice that this script uses the backticked command <span class="pre">relation-get</span>. This is a Juju helper command that fetches the named values from the corresponding hook on the service we are connecting to.
324 <pre>$ cd /var/lib/juju/units/drupal-0/charm/hooks/325Usually there will be some indication of what these values are, but you can always inspect the corresponding hoks to find out. In this case we know that when connected, the MySQL charm will create a database and generate random values for things like a username and password.</p>
325 $ ./install326<p>These values will all be set at one time, so the next little bit of script just checks one value to see if it exists - if not the corresponding charm hasn't set the values yet.</p>
326 # -- snip --327<p>When it has the values we can use these to modify the config file for Vanilla in the relevant place, and finally open the port to make the service active.</p>
327 + cd /var/ww328<p>The final hook we need to write is for other services which may want to consume Vanilla, 'website-relation-joined'.</p>
328 ./install: line 10: cd: /var/ww: No such file or directory329<pre class="prettyprint">#!/bin/sh
329 </pre>330
330 331relation-set hostname=`unit-get private-address` port=80
331 <p>Problem identified. Let's edit the script, changing ww into www. Rerunning it again should work successfully. This is why it is very good practice to write hook scripts in an idempotent manner such that rerunning them over and over always results in the same state. Do not forget to exit the install window by typing &quot;exit&quot;, this signals that the hook has finished executing successfully. If you have finished debugging, you may want to exit the debug-hooks session completely by typing &quot;exit&quot; into the very first window Window0</p>332</pre>
332 333<p>Here we can see the other end of the information sharing - in this case <span class="pre">relation-set</span> exposes the given values to the connecting charm. In this case one of the commands is backticked, as <span class="pre">unit-get</span> is another helper command, in this case one which returns the requested value form the machine the charm is running on, specifically here it's IP address.
333 <p>Note</p>334 So, any connecting charm will be able to ask for the values 'hostname' and 'port'. Remember, once you have finished writing your hooks make sure you 'chmod +x' them.</p>
334 <p>While we have fixed the script, this was done on the remote machine only. You need to update the local copy of the charm with your changes, increment the revision number in the revision file and perform a charm upgrade to push the changes, like:</p>335<p>For our simplistic charm, that is all the hooks we need for the moment, so now we can test it out!</p>
335 <pre>juju upgrade-charm --repository=examples/ drupal</pre>336</div>
336 337<span class="number"> 5 </span>
337 <p>Let's continue after having fixed the install error:</p>338 <div class="step" id="step05">
338 <pre>juju add-relation mysql drupal</pre>339 <h2>Testing</h2>
339 340 <p>Before we congratulate ourselves too much, we should check that the charm actually works. To help with this, we should open a new terminal window and run the following command:</p>
340 <p>Watching the debug-log window, you can see debugging information to verify the hooks are working as they should. If you spot any error, you can launch debug-hooks in another window to start debugging the misbehaving hooks again. Note that since &quot;add-relation&quot; relates two charms together, you cannot really retrigger it by simply issuing &quot;resolved --retry&quot; like we did for the install hook. In order to retrigger the db-relation-changed hook, you need to remove the relation, and create it again like so:</p>341 <pre>juju debug-log</pre>
341 <pre>juju remove-relation mysql drupal342 <p>This starts a process to tail the juju log file and show us just 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.</p>
342 juju add-relation mysql drupal</pre>343 <p>Following our own recipe, in another terminal we should now do the following (assuming you already have a bootstrapped environment):</p>
343 344 <pre>juju deploy mysql
344 <p>The service should now be ready for use. The remaining step is to expose it to public access. While the charm signaled it needs port 80 to be open, for public accessibility, the port is not open until the administrator explicitly uses the expose command:</p>345juju deploy --repository=/home/evilnick/localcharms/ local:precise/vanilla
345 <pre>juju expose drupal</pre>346juju add-relation mysql vanilla
346 347juju expose vanilla
347 <p>Let's see a status with the ports exposed:</p>348</pre>
348 <pre>349<p>We used the local deploy options to deploy our charm - substitute the path for your own environment. Everything should now be working away, and your log window will look something like this:</p>
349 juju status350
350 351 <img src="./media/author-charm-writing-debug.png" width='600' alt="Step five - debug">
351 machines:352<p>If you wait for all the Juju operations to finish and run a <span class="pre">juju status</span> command, you will be able to retrieve the public address for the Vanilla forum we just deployed. Copy it into your browser and you should see the setup page (prepopulated with the database config) waiting for any changes. Congratulations!</p>
352 &nbsp;0:353 <img src="./media/author-charm-writing-vanilla.png" width='600' alt="Step five - vanilla">
353 &nbsp;&nbsp;&nbsp;agent-state: running354
354 &nbsp;&nbsp;&nbsp;dns-name: ec2-50-16-107-102.compute-1.amazonaws.com355
355 &nbsp;&nbsp;&nbsp;instance-id: i-130c9168356 </div>
356 &nbsp;&nbsp;&nbsp;instance-state: running357
357 &nbsp;1:358<span class="number"> 6 </span>
358 &nbsp;&nbsp;&nbsp;agent-state: running359 <div class="step" id="step06">
359 &nbsp;&nbsp;&nbsp;dns-name: ec2-50-19-24-186.compute-1.amazonaws.com360 <h2>Tidying up</h2>
360 &nbsp;&nbsp;&nbsp;instance-id: i-17079a6c361 <p>With the charm working properly, you may consider everything a job well done. If your charm is really great and you want to share it, particularly on the charm store, then there are a couple of things you ought to add.</p>
361 &nbsp;&nbsp;&nbsp;instance-state: running362 <ol>
362 &nbsp;2:363
363 &nbsp;&nbsp;&nbsp;agent-state: running364 <li>Create a file called 'copyright' and place whatever license information you require in there. </li>
364 &nbsp;&nbsp;&nbsp;dns-name: ec2-23-20-194-198.compute-1.amazonaws.com365 <li>Add a beautiful icon (<a href="./authors-charm-icon.html">there is a guide to making one here</a>) so others can recognise it in the charm store!</li>
365 &nbsp;&nbsp;&nbsp;instance-id: i-d7079aac366 </ol>
366 &nbsp;&nbsp;&nbsp;instance-state: running367 </div>
367 &nbsp;services:368
368 &nbsp;mysql:
369 &nbsp;&nbsp;charm: cs:precise/mysql-3
370 &nbsp;&nbsp;relations:
371 &nbsp;&nbsp;&nbsp;db:
372 &nbsp;&nbsp;&nbsp;- drupal
373 &nbsp;&nbsp;&nbsp;units:
374 &nbsp;&nbsp;&nbsp;&nbsp;mysql/0:
375 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;agent-state: started
376 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;machine: 2
377 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public-address: ec2-23-20-194-198.compute-1.amazonaws.com
378 &nbsp;&nbsp;drupal:
379 &nbsp;&nbsp;&nbsp;charm: cs:precise/drupal-3
380 &nbsp;&nbsp;&nbsp;exposed: true
381 &nbsp;&nbsp;&nbsp;relations:
382 &nbsp;&nbsp;&nbsp;&nbsp;db:
383 &nbsp;&nbsp;&nbsp;&nbsp;- mysql
384 &nbsp;&nbsp;&nbsp;units:
385 &nbsp;&nbsp;&nbsp;&nbsp;drupal/0:
386 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;agent-state: started
387 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;machine: 1
388 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;open-ports:
389 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- 80/tcp
390 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public-address: ec2-50-19-24-186.compute-1.amazonaws.com</pre>
391
392 <p>Congratulations, your charm should now be working successfully! The db-relation-changed hook previously shown is not suitable for scaling drupal to more than one node, since it always drops the database and recreates a new one. A more complete hook would need to first check whether or not the DB tables exist and act accordingly. Here is how such a hook might be written:</p>
393 <pre>#!/bin/bash
394 &nbsp;>set -eux # -x for verbose logging to juju debug-log
395 &nbsp;<p>hooksdir=$PWD
396 &nbsp;user=`relation-get user`
397 &nbsp;password=`relation-get password`
398 &nbsp;host=`relation-get host`
399 &nbsp;database=`relation-get database`
400 &nbsp;# All values are set together, so checking on a single value is enough
401 &nbsp;# If $user is not set, DB is still setting itself up, we exit awaiting next run
402 &nbsp;[ -z "$user" ] &amp;&amp; exit 0
403 &nbsp;
404 &nbsp;if $(mysql -u $user --password=$password -h $host -e 'use drupal; show tables;' | grep -q users); then
405 &nbsp;juju-log "Drupal already set-up. Adding DB info to configuration"
406 &nbsp;cd /var/www/juju/sites/default
407 &nbsp;cp default.settings.php settings.php
408 &nbsp;sed -e "s/USER/$user/" \
409 &nbsp;-e "s/PASSWORD/$password/" \
410 &nbsp;-e "s/HOST/$host/" \
411 &nbsp;-e "s/DATABASE/$database/" \
412 &nbsp;$hooksdir/drupal-settings.template &gt;&gt; settings.php
413 &nbsp;else
414 &nbsp;juju-log "Setting up Drupal for the first time"
415 &nbsp;cd /var/www/juju &amp;&amp; drush site-install -y standard \
416 &nbsp;&nbsp;--db-url=mysql://$user:$password@$host/$database \
417 &nbsp;&nbsp;--site-name=juju --clean-url=0
418 &nbsp;fi
419 &nbsp;cd /var/www/juju &amp;&amp; chown www-data sites/default/settings.php
420 &nbsp;open-port 80/tcp
421 </pre>
422
423
424 <p class="first admonition-title">Note</p>
425 <p class="last">Any files that you store in the hooks directory are transported as is to the deployment machine. You can drop in configuration files or templates that you can use from your hook scripts. An example of this technique is the drupal-settings.template file that is used in the previous hook. The template is rendered using sed, however any other more advanced template engine can be used</p>
426
427 <p><p>Here is the template file used:</p>
428 <pre>$databases = array (
429 &nbsp;'default' =&gt;
430 &nbsp;array (
431 &nbsp;&nbsp;&nbsp;&nbsp;'default' =&gt;
432 &nbsp;&nbsp;&nbsp;&nbsp;array (
433 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'database' =&gt; 'DATABASE',
434 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'username' =&gt; 'USER',
435 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'password' =&gt; 'PASSWORD',
436 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'host' =&gt; 'HOST',
437 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'port' =&gt; '',
438 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'driver' =&gt; 'mysql',
439 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'prefix' =&gt; '',
440 &nbsp;&nbsp;&nbsp;&nbsp;),
441 &nbsp;&nbsp;),
442 &nbsp;);
443 </pre>
444 </article>369 </article>
445 </div>370 </div>
446 </div>371 </div>
@@ -498,6 +423,7 @@
498 <p><a href="https://bugs.launchpad.net/juju-website/+filebug">Report a bug on this site</a></p>423 <p><a href="https://bugs.launchpad.net/juju-website/+filebug">Report a bug on this site</a></p>
499 </div>424 </div>
500 </div>425 </div>
426 <script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js?skin=sunburst"></script>
501</footer>427</footer>
502428
503 <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>429 <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
504430
=== modified file 'htmldocs/charms-deploying.html'
--- htmldocs/charms-deploying.html 2013-07-15 13:01:43 +0000
+++ htmldocs/charms-deploying.html 2013-07-25 01:29:31 +0000
@@ -116,7 +116,7 @@
116 <p>which follows the format:</p>116 <p>which follows the format:</p>
117 <pre><code>&LT;repository&GT;:&LT;series&GT;&LT;service&GT;</code></pre>117 <pre><code>&LT;repository&GT;:&LT;series&GT;&LT;service&GT;</code></pre>
118 118
119 <h1>Deploying from a local repository</h1>119 <h1 id="local">Deploying from a local repository</h1>
120 <p>There are many cases when you may wish to deploy charms from a local filesytem source rather than the charm store:</p>120 <p>There are many cases when you may wish to deploy charms from a local filesytem source rather than the charm store:</p>
121 <ul>121 <ul>
122 <li>When testing charms you have written.</li>122 <li>When testing charms you have written.</li>
@@ -126,7 +126,7 @@
126 <p>... and probably a lot more times which you can imagine yourselves.</p>126 <p>... and probably a lot more times which you can imagine yourselves.</p>
127 <p>Juju can be pointed at a local directory to source charms from using the <span class="pre">--repository=&LT;path/to/files&GT;</span> switch like this:</p>127 <p>Juju can be pointed at a local directory to source charms from using the <span class="pre">--repository=&LT;path/to/files&GT;</span> switch like this:</p>
128 128
129 <pre><code>juju deploy --repository=/usr/share/charms/ vsftpd</code></pre>129 <pre><code>juju deploy --repository=/usr/share/charms/ local:precise/vsftpd</code></pre>
130 <p>You can also make use of standard filesystem shortcuts, so the following examples are also valid:</p>130 <p>You can also make use of standard filesystem shortcuts, so the following examples are also valid:</p>
131 <pre><code>juju deploy --repository=. haproxy</code></pre>131 <pre><code>juju deploy --repository=. haproxy</code></pre>
132 <pre><code>juju deploy --repository=~/charms/ wordpress</code></pre> 132 <pre><code>juju deploy --repository=~/charms/ wordpress</code></pre>
133133
=== added file 'htmldocs/media/author-charm-writing-debug.png'
134Binary files htmldocs/media/author-charm-writing-debug.png 1970-01-01 00:00:00 +0000 and htmldocs/media/author-charm-writing-debug.png 2013-07-25 01:29:31 +0000 differ134Binary files htmldocs/media/author-charm-writing-debug.png 1970-01-01 00:00:00 +0000 and htmldocs/media/author-charm-writing-debug.png 2013-07-25 01:29:31 +0000 differ
=== added file 'htmldocs/media/author-charm-writing-log.png'
135Binary files htmldocs/media/author-charm-writing-log.png 1970-01-01 00:00:00 +0000 and htmldocs/media/author-charm-writing-log.png 2013-07-25 01:29:31 +0000 differ135Binary files htmldocs/media/author-charm-writing-log.png 1970-01-01 00:00:00 +0000 and htmldocs/media/author-charm-writing-log.png 2013-07-25 01:29:31 +0000 differ
=== added file 'htmldocs/media/author-charm-writing-vanilla.png'
136Binary files htmldocs/media/author-charm-writing-vanilla.png 1970-01-01 00:00:00 +0000 and htmldocs/media/author-charm-writing-vanilla.png 2013-07-25 01:29:31 +0000 differ136Binary files htmldocs/media/author-charm-writing-vanilla.png 1970-01-01 00:00:00 +0000 and htmldocs/media/author-charm-writing-vanilla.png 2013-07-25 01:29:31 +0000 differ
=== modified file 'skel/footer.tpl'
--- skel/footer.tpl 2013-07-15 13:01:12 +0000
+++ skel/footer.tpl 2013-07-25 01:29:31 +0000
@@ -50,4 +50,5 @@
50 <p><a href="https://bugs.launchpad.net/juju-website/+filebug">Report a bug on this site</a></p>50 <p><a href="https://bugs.launchpad.net/juju-website/+filebug">Report a bug on this site</a></p>
51 </div>51 </div>
52 </div>52 </div>
53 <script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js?skin=sunburst"></script>
53</footer>54</footer>

Subscribers

People subscribed via source and target branches