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

Subscribers

People subscribed via source and target branches