Merge lp:~evilnick/juju-core/docs-amulet into lp:juju-core/docs

Proposed by Nick Veitch
Status: Merged
Merge reported by: Nick Veitch
Merged at revision: not available
Proposed branch: lp:~evilnick/juju-core/docs-amulet
Merge into: lp:juju-core/docs
Diff against target: 526 lines (+499/-1)
2 files modified
htmldocs/css/main.css (+14/-1)
htmldocs/tools-amulet.html (+485/-0)
To merge this branch: bzr merge lp:~evilnick/juju-core/docs-amulet
Reviewer Review Type Date Requested Status
charmers Pending
Review via email: mp+201061@code.launchpad.net

Description of the change

added initial amulet docs

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/css/main.css'
--- htmldocs/css/main.css 2013-12-03 01:36:47 +0000
+++ htmldocs/css/main.css 2014-01-09 17:59:35 +0000
@@ -279,10 +279,22 @@
279}279}
280280
281.doc-content h3{281.doc-content h3{
282 font-size: 18px;282 font-size: 20px;
283 color: #5E2750;283 color: #5E2750;
284 font-weight: 800;284 font-weight: 800;
285}285}
286
287/**
288 * Foldout stuff
289 */
290
291.doc-content code.method {
292 font-size: 14px;
293 color: #5E2750;
294 font-weight: 800;
295 background-color: #ffffff
296}
297
286/**298/**
287 * Walkthrough steps299 * Walkthrough steps
288 */300 */
@@ -482,6 +494,7 @@
482 overflow-x: auto;494 overflow-x: auto;
483}495}
484496
497
485hr {498hr {
486 background: #fff;499 background: #fff;
487 height: 1px;500 height: 1px;
488501
=== added file 'htmldocs/tools-amulet.html'
--- htmldocs/tools-amulet.html 1970-01-01 00:00:00 +0000
+++ htmldocs/tools-amulet.html 2014-01-09 17:59:35 +0000
@@ -0,0 +1,485 @@
1<!DOCTYPE html>
2<html>
3<!--Head-->
4 <head>
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6 <title>Juju Documentation</title>
7 <script src="/wp-content/themes/ubuntu/library/js/all-yui.js"></script>
8 <link href="https://fonts.googleapis.com/css?family=Ubuntu:400,300,300italic,400italic,700,700italic|Ubuntu+Mono" rel="stylesheet" type="text/css">
9 <link rel="stylesheet" type="text/css" media="screen" href="https://juju.ubuntu.com/wp-content/themes/juju-website/css/reset.css">
10 <link rel="shortcut icon" href="//assets.ubuntu.com/sites/ubuntu/latest/u/img/favicon.ico" />
11 <link rel="stylesheet" type="text/css" media="screen" href="//assets.ubuntu.com/sites/guidelines/css/latest/ubuntu-styles.css" />
12 <link rel="stylesheet" type="text/css" media="screen" href="//assets.ubuntu.com/sites/ubuntu/latest/u/css/global.css" />
13 <link rel="stylesheet" type="text/css" media="screen" href="https://juju.ubuntu.com/wp-content/themes/juju-website/css/960.css">
14 <link rel="stylesheet" type="text/css" media="screen" href="https://juju.ubuntu.com/wp-content/themes/juju-website/css/home-new.css">
15 <link rel="stylesheet" id="stacktack-css" href="https://juju.ubuntu.com/wp-content/plugins/stacktack/css/stacktack.min.css?ver=3.4.2" type="text/css" media="all">
16 <link href="./css/main.css" rel="stylesheet" type="text/css">
17
18 <!--[if lt IE 9]>
19 <script type="text/javascript" src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
20 <![endif]-->
21 </head>
22<!--End-Head-->
23
24
25
26
27
28
29
30 <body class="resources">
31
32<!--Header-->
33
34 <header class="banner global" role="banner">
35 <nav role="navigation" class="nav-primary nav-right">
36 <div class="logo">
37 <a class="logo-ubuntu" href="https://juju.ubuntu.com/">
38 <img width="118" height="27" src="//assets.ubuntu.com/sites/ubuntu/latest/u/img/logo.png" alt="Juju logo" />
39 <span>Juju</span>
40 </a>
41 </div>
42 <ul>
43 <li class="accessibility-aid"><a accesskey="s" href="#main-content">Jump to content</a></li>
44 <li class="page_item page-item-8"><a href="https://juju.ubuntu.com/charms/">Charms</a></li>
45 <li class="page_item page-item-10"><a href="https://juju.ubuntu.com/features/">Features</a></li>
46 <li class="page_item page-item-12"><a href="https://juju.ubuntu.com/deployment/">Deploy</a></li>
47 <li class="page_item page-item-14"><a href="https://juju.ubuntu.com/resources/">Resources</a></li>
48 <li class="page_item page-item-16"><a href="https://juju.ubuntu.com/community/">Community</a></li>
49 <li class="page_item page-item-18"><a href="https://juju.ubuntu.com/download/">Install Juju</a></li>
50 </ul>
51 <div id="box-search">
52 <form class="search-form" method="get" id="searchform" action="https://juju.ubuntu.com/">
53 <label class="off-left" for="s">Search:</label>
54 <input class="form-text" type="text" value="" name="s" id="s" />
55 <button class="off-left form-submit" type="submit" id="searchsubmit">Search</button>
56 </form>
57 </div>
58 </nav>
59 </header>
60<!--End-Header-->
61<!--Preamble-->
62<div class="wrapper">
63 <div id="main-content" class="inner-wrapper" role="main">
64 <div class="row no-border">
65 <div class="header-navigation-secondary"></div>
66 <h2 class="pagetitle">Juju documentation</h2>
67 </div>
68 <section id="content" class="container-12">
69 <div class="grid-12 doc-container">
70 <div id="navlinks" class="grid-3 doc-navigation">LINKS</div>
71 <div class="grid-9 doc-content">
72<!--End-Preamble-->
73 <article>
74 <section id="amulet">
75 <h1>Amulet, a testing harness</h1>
76<p>Amulet is a set of tools designed to simplify the testing process for charm authors. Amulet aims to be:</p>
77<ul>
78<li>a testing harness for writing and running tests.</li>
79<li>a way to validate charm relation data (not just what a charm expects/receives).</li>
80<li>a method to exercise and test charm relations outside of a deployment.</li>
81 </ul>
82<p>While these tools are designed to help make test writing easier, much like charm helpers are designed to make hook writing easier, they are not required to write tests for charms. This library is offered as a completely optional set of tools for you to use.</p>
83</section>
84<section id="install">
85
86 <h1 >Installation</h1>
87 <p>Amulet is available as both a package and via pip. For source packages, see <a href="https://github.com/marcoceppi/amulet/releases"> GitHub</a>.</p>
88 <section class="code-example code-overflow">
89 <nav class="control">
90 <a href="." class="selected" data-action="ubuntu">Ubuntu</a>
91 <a href="." class="" data-action="macosx">Mac OSX</a>
92 <a href="." class="" data-action="windows">Windows</a>
93 <a href="." class="" data-action="source">Source</a>
94 </nav>
95 <div class="terminal-wrap">
96 <div data-section="ubuntu">
97 <p>Amulet is available in the Juju Stable PPA for Ubuntu</p>
98
99<pre class="prettyprint">sudo add-apt-repository ppa:juju/stable
100sudo apt-get update
101sudo apt-get install amulet
102</pre>
103 </div>
104 <div data-section="macosx">
105 <p>Amulet is available via Pip:</p>
106
107<pre class="prettyprint lang-bash">sudo pip install amulet</pre>
108
109 </div>
110 <div data-section="windows">
111 <p>Amulet is available via Pip:</p>
112
113<pre class="prettyprint lang-bash">pip install amulet</pre>
114 </div>
115 <div data-section="source">
116 <p>Amulet is built with Python3, make sure it's installed prior to following these steps. While you can run Amulet from source, it's not recommended as it requires several changes to environment variables in order for Amulet to operate as it does in the packaged version.</p>
117
118<p>To install Amulet from source, first get the source:</p>
119
120<pre class="prettyprint lang-bash">git clone https://github.com/marcoceppi/amulet.git</pre>
121<p>Move in to the <code>amulet</code> directory and run
122<pre class="prettyprint lang-bash">sudo python3 setup.py install</pre>
123<p>You can also access the Python libraries; however, your <code>PYTHONPATH</code> will need to be amended in order for it to find the amulet directory.</p>
124
125 </div>
126 </div>
127 </section>
128
129
130
131
132<section>
133<h2 id="usage">Usage</h2>
134
135<p>Amulet comes packaged with several tools. In order to provide the most flexibility, Amulet offers both direct Python library access and generic access via a programmable API for other languages (for example, <code>bash</code>).</p>
136
137<h3>Python</h3>
138
139<p>Amulet is made available to Python via the <code>amulet</code> module which you can import:</p>
140
141<pre class="prettyprint lang-python">import amulet</pre>
142<p>The amulet module seeds each module/command directly, so Deployment is made available in amulet/deployer.py and is accessible directly from amulet using:</p>
143
144<pre class="prettyprint lang-python">from amulet import Deployment</pre>
145<p>Though <code>deployer</code> is also available in the event you wish to execute any of the helper functions:</p>
146<pre class="prettyprint lang-python">
147from amulet import deployer
148d = deployer.Deployment()
149</pre>
150
151<h3 id="api">Programmable API</h3>
152
153<p>A limited number of functions are made available through a generic forking API. The following examples assume you're using a BOURNE Shell, though this syntax could be used from within other languauges with the same expected results.</p>
154
155<p>Unlike the Python modules, only some of the functions of Amulet are available through this API, though efforts are being made to make the majority of the core functionality available.</p>
156
157<p>This API follows the subcommand workflow, much like Git or Bazaar. Amulet makes an amulet command available and each function is tied to a sub-command. To mimic the Python example you can create a a new Deployment by issuing the following command:</p>
158
159<pre class="prettyprint lang-bash">amulet deployment</pre>
160
161<p>Depending on the syntax and worflow for each function you can expect to provide either additional sub-commands, command-line flags, or a combination of the two.</p>
162
163<h2 id="functionality">Core functionality</h2>
164<p>This section is deigned to outline the core functions of Amulet. Again, please refer to the developer documentation for an exhaustive list of functions and methods.
165
166<h3>amulet.deployer</h3>
167
168<p>The Deployer module houses several classes for interacting and setting up an environment. These classes and methods are outlined below
169
170amulet.deployer.Deployment()
171
172<p>Deployment (amulet deployment, from amulet import Deployment) is an abstraction layer to the juju-deployer Juju plugin and a service lifecycle management tool. It's designed to allow an author to describe their deployment in simple terms:</p>
173<pre class="prettyprint lang-python">
174import amulet
175
176d = amulet.Deployment()
177d.add('mysql')
178d.add('mediawiki')
179d.relate('mysql:db', 'mediawiki:db')
180d.expose('mediawiki')
181d.configure('mediawiki', title="My Wiki", skin="Nostolgia")
182d.setup()
183</pre>
184<p>
185That information is then translated to a Juju Deployer deployment file then, finally, juju-deployer executes the described setup. Amulet strives to ensure it implements the correct version and syntax of Juju Deployer, to avoid charm authors having to potentially intervene each time an update to <code>juju-deployer</code> is made.
186</p>
187<p>Once an environment has been set up, deployer can still drive the environment outside of of juju-deployer. So the same commands (add, relate, configure, expose) will instead interact directly with the environment by using either the Juju API or the juju commands.</p>
188
189<h4 id="object">Class:</h4>
190<p><code>Deployment(juju_env=None, series='precise', sentries=True, juju_deployer='juju-deployer', sentry_template=None)</code></p>
191
192<h4>Methods:</h4>
193<details><summary><code class="method">Deployment.add(service, charm=None, units=1)</code>
194
195<p>Add a new service to the deployment schema.</p></summary>
196
197<ul><li><code>service</code> Name of the service to deploy.</li>
198<li><code>charm</code> If provided, will be the charm used. Otherwise service is used as the charm.</li>
199<li><code>units</code> Number of units to deploy.</li></ul>
200<pre class="prettyprint lang-python">import amulet
201
202d = amulet.Deployment()
203d.add('wordpress')
204d.add('second-wp', charm='wordpress')
205d.add('personal-wp', charm='~marcoceppi/wordpress', units=2)
206</pre>
207</details>
208<details><summary><code class="method">Deployment.build_relations()</code>
209
210<p>Private method invoked during deployer_map. Creates relation mapping.</p></summary></details>
211
212<details><summary><code class="method">Deployment.build_sentries()</code>
213
214<p>Private method invoked during deployer_map. Creates sentries for services.</p>
215 </summary></details>
216<details><summary><code class="method">Deployment.configure(service, **options)</code>
217
218<p>Change configuration options for a service.</p></summary>
219
220<ul><li><code>service</code> The service to configure.</li>
221<li><code>**options</code> Seed with key=val.</li></ul>
222<pre class="prettyprint lang-python">
223import amulet
224
225d = amulet.Deployment()
226d.add('postgresql')
227d.configure('postgresql', autovacuum=True, cluster_name='cname')
228</pre>
229</details>
230<details><summary><code class="method">Deployment.deployer_map(services, relations)</code>
231
232<p>Create deployer file from provided services and relations.</p>
233</summary>
234
235<ul><li><code>services</code> Object of service and service data.</li>
236<li><code>relations</code> List of relations to map.</li>
237</ul>
238</details>
239<details><summary><code class="method">Deployment.expose(service)</code>
240
241<p>Indicate if a service should be exposed after deployment.</p></summary>
242
243<ul><li><code>service</code> - Name of service to expose</li></ul>
244<pre class="prettyprint lang-python">
245import amulet
246
247d = amulet.Deployment()
248d.add('varnish')
249d.expose('varnish')
250</pre></details>
251<details><summary><code class="method">Deployment.load(deploy_cfg)</code>
252
253<p>Import an existing deployer object.</p>
254</summary>
255<ul><li><code>deploy_cfg</code> Already parsed deployer yaml/json file.</li></ul>
256</details>
257<details><summary><code class="method">Deployment.relate(*args)</code>
258
259<p>Relate two services together.</p></summary>
260
261<ul><li><code>*args</code> - <code>service:relation</code> to be related.</li></ul>
262<p>If more than two arguments are given, it's assumed they're to be added to the first argument as a relation.</p>
263<pre class="prettyprint lang-python">import amulet
264
265d = amulet.Deployment()
266d.add('postgresql')
267d.add('mysql')
268d.add('wordpress')
269d.add('mediawiki')
270d.add('discourse')
271
272d.relate('postgresql:db-admin', 'discourse:db')
273d.relate('mysql:db', 'wordpress:db', 'mediawiki:database')
274</pre>
275</details>
276
277<details><summary><code class="method">Deployment.setup(timeout=600)</code>
278
279<p>This will create the deployer mapping, create any sentries that are required, and execute juju-deployer with the generated mapping.</p></summary>
280
281<ul><li><code>timeout</code> in seconds, how long to wait for setup</li></ul>
282
283<pre class="prettyprint lang-python">import amulet
284
285d = amulet.Deployment()
286d.add('wordpress')
287d.add('mysql')
288d.configure('wordpress', debug=True)
289d.relate('wordpress:db', 'mysql:db')
290try:
291 d.setup(timeout=900)
292except amulet.helpers.TimeoutError:
293 # Setup didn't complete before timeout
294 pass
295
296</pre></details>
297<h3>amulet.sentry</h3>
298<p>Sentries are an additional service built in to the Deployment tool which allow an author the ability to dig deeper in to a deployment environment. This is done by adding a set of tools to each service/unit deployed via a subordinate charm and a final "relation sentry" charm is deployed which all relations are proxied through. In doing so you can inspect on each service/unit deployed as well as receive detailed information about what data is being sent by which units/service during a relation.</p>
299
300<p>Sentries can be accessed from within your deployment using the sentry object. Using the above example from ## Deployer, each service and unit can be accessed using the following:</p>
301<pre class="prettyprint lang-python">
302import amulet
303
304d = amulet.Deployment()
305d.add('mediawiki')
306d.add('mysql')
307d.setup()
308
309d.sentry.unit['mysql/0']
310d.sentry.unit['mediawiki/0']
311</pre>
312<p>Sentries provide several methods for which you can use to gather information about an environment. The following are a few examples.</p>
313
314<h2 id="examples">Examples</h2>
315<p>Here are a few examples of Amulet tests</p>
316
317<h3>WordPress</h3>
318
319<h4>tests/00-setup</h4>
320<pre class="prettyprint lang-python">
321#!/bin/bash
322
323sudo apt-get install install amulet python-requests
324</pre>
325<h4>tests/01-simple</h4>
326<pre class="prettyprint lang-python">
327import os
328import amulet
329import requests
330
331from .lib import helper
332
333d = amulet.Deployment()
334d.add('mysql')
335d.add('wordpress')
336d.relate('mysql:db', 'wordpress:db')
337d.expose('wordpress')
338
339try:
340 # Create the deployment described above, give us 900 seconds to do it
341 d.setup(timeout=900)
342 # Setup will only make sure the services are deployed, related, and in a
343 # "started" state. We can employ the sentries to actually make sure there
344 # are no more hooks being executed on any of the nodes.
345 d.sentry.wait()
346except amulet.helpers.TimeoutError:
347 amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
348except:
349 # Something else has gone wrong, raise the error so we can see it and this
350 # will automatically "FAIL" the test.
351 raise
352
353# Shorten the names a little to make working with unit data easier
354wp_unit = d.sentry.unit['wordpress/0']
355mysql_unit = d.sentry.unit['mysql/0']
356
357# WordPress requires user input to "finish" a setup. This code is contained in
358# the helper.py file found in the lib directory. If it's not able to complete
359# the WordPress setup we need to quit the test, not as failed per se, but as a
360# SKIPed test since we can't accurately setup the environment
361try:
362 helper.finish_setup(wp_unit.info['public-address'], password='amulet-test')
363except:
364 amulet.raise_status(amulet.SKIP, msg="Unable to finish WordPress setup")
365
366home_page = requests.get('http://%s/' % wp_unit.info['public-address'])
367home_page.raise_for_status() # Make sure it's not 5XX error
368</pre>
369<h4>tests/lib/helper.py</h4>
370<pre class="prettyprint lang-python">
371import requests
372
373def finish_setup(unit, user='admin', password=None):
374 h = {'User-Agent': 'Mozilla/5.0 Gecko/20100101 Firefox/12.0',
375 'Content-Type': 'application/x-www-form-urlencoded',
376 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*',
377 'Accept-Encoding': 'gzip, deflate'}
378
379 r = requests.post('http://%s/wp-admin/install.php?step=2' % unit,
380 headers=h, data={'weblog_title': 'Amulet Test %s' % unit,
381 'user_name': user, 'admin_password': password,
382 'admin_email': 'test@example.tld',
383 'admin_password2': password,
384 'Submit': 'Install WordPress'})
385</pre>
386
387
388
389 </section>
390 </article>
391<!--Postamble-->
392 </div>
393 </div>
394 </section>
395 </div>
396</div>
397<!--End-Postamble-->
398<!--Footer-->
399 <footer class="global clearfix" role="contentinfo">
400 <nav role="navigation">
401 <div class="footer-a">
402 <div class="clearfix">
403 <ul>
404 <li>
405 <h2><a href="/">Juju</a></h2>
406 <ul>
407 <li><a href="/charms">Charms</a></li>
408 <li><a href="/features">Features</a></li>
409 <li><a href="/deployment">Deployment</a></li>
410 </ul>
411 </li>
412 <li>
413 <h2><a href="/resources">Resources</a></h2>
414 <ul>
415 <li><a href="/resources/juju-overview/">Overview</a></li>
416 <li><a href="/docs/">Documentation</a></li>
417 <li><a href="/resources/the-juju-gui/">The Juju web UI</a></li>
418 <li><a href="/docs/authors-charm-store.html">The charm store</a></li>
419 <li><a href="/docs/getting-started.html#test">Tutorial</a></li>
420 <li><a href="/resources/videos/">Videos</a></li>
421 <li><a href="/resources/easy-tasks-for-new-developers/">Easy tasks for new developers</a></li>
422 </ul>
423 </li>
424 <li>
425 <h2><a href="/community">Community</a></h2>
426 <ul>
427 <li><a href="/community/blog/">Juju Blog</a></li>
428 <li><a href="/events/">Events</a></li>
429 <li><a href="/community/weekly-charm-meeting/">Weekly charm meeting</a></li>
430 <li><a href="/community/charmers/">Charmers</a></li>
431 <li><a href="/docs/authors-charm-writing.html">Write a charm</a></li>
432 <li><a href="/docs/contributing.html">Help with documentation</a></li>
433 <li><a href="https://bugs.launchpad.net/juju-core/+filebug">File a bug</a></li> <li><a href="/labs/">Juju Labs</a></li>
434 </ul>
435 </li>
436 <li>
437 <h2><a href="https://jujucharms.com/sidebar/">Try Juju</a></h2>
438 <ul>
439 <li><a href="https://jujucharms.com/">Charm store</a></li>
440 <li><a href="/download/">Download Juju</a></li>
441 </ul>
442 </li>
443 </ul>
444 </div>
445 </div>
446 </nav>
447 <div class="legal clearfix">
448 <p>&copy; 2013 Canonical Ltd. Ubuntu and Canonical are registered trademarks of <a href="http://canonical.com">Canonical Ltd</a>.</p>
449 </div>
450 </footer>
451
452<!--End-Footer-->
453
454
455<!--Scripts-->
456 <script src="//google-code-prettify.googlecode.com/svn/loader/run_prettify.js?skin=sunburst"></script>
457 <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
458 <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script>
459 <script src="//d38yea5fb4e2oh.cloudfront.net/jquery.stacktack.min.js"></script>
460 <script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.js"></script>
461 <script type="text/javascript" src="./js/main.js"></script>
462 <script type="text/javascript" src="./js/jquery.details.js"></script>
463 <script src="//assets.ubuntu.com/sites/ubuntu/latest/u/js/core.js"></script>
464 <script src="//assets.ubuntu.com/sites/ubuntu/latest/u/js/global.js"></script>
465 <!-- google analytics -->
466 <script>
467 var _gaq = _gaq || [];
468 _gaq.push(['_setAccount', 'UA-1018242-41']);
469 _gaq.push(['_trackPageview']);
470
471 (function() {
472 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
473 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
474 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
475 })();
476 </script>
477<!--End-Scripts-->
478
479
480
481
482
483
484</body></html>
485
0\ No newline at end of file486\ No newline at end of file

Subscribers

People subscribed via source and target branches