zk_html/diary/2017-01-05-part-3.html

158 lines
23 KiB
HTML

<!doctype html>
<html>
<head>
<title>Zk | 2017-01-05-part-3</title>
<link rel="stylesheet" type="text/css" href="/style.css" />
<meta name="viewport" content="width=device-width" />
<meta charset="utf-8" />
</head>
<body>
<main>
<header>
<h1>Zk | 2017-01-05-part-3</h1>
</header>
<article class="content">
<hr />
<p>type: post
date: 2017-01-05
slug: part-3
title: How Charming - Part 3</p>
<hr />
<p><em>In the previous entry, we started the process of pulling together our charm, including a lot of configuration values. Some of those were standard charm fare, metadata and configuration values, but some were configuration for the charm layers. We also investigated what layers were and how they're different from hooks. If you haven't read the first two entries, please make sure that you do so, or this one won't make much sense!</em></p>
<p>How was your Christmas? How was your New Years Eve? How was your...uh...<a href="https://en.wikipedia.org/wiki/Tibb's_Eve">Tibb's Eve</a>?!</p>
<p>Mine were good. I got struck by a hypomanic episode and started about eighteen projects. Very productive. Felt like garbage. Unfortunately, none of those projects involved Honeycomb. The end result is that I got a bit of a vacation and am now catching up on Work™.</p>
<p>Now we're back to it, though. I'm starting to scrape together the resources and requirements that will need to go into the charm. My task right now is to deploy Honeycomb to a test server (I picked up a droplet and named it after the next fox in line<sup id="fnref:names"><a class="footnote-ref" href="#fn:names">1</a></sup>) and ensure that everything works when plugged in together.</p>
<p>And writing the charm.</p>
<p>Writing layered charms is weird as heck, for me. This project is as much me learning how to write them as it is explaining how to do so. We relied on writing just hook-based charms for so long that it's difficult to get up to speed with this new style and leave behind so much of what I learned. The docs and tooling are still being written, too, so I find myself stumbling along blind alleys quite often.</p>
<p>Well, come stumble with me as we work through the next part of the charm: fetching our source and setting some configuration values.</p>
<hr />
<p>Lets begin with the process of fetching our project's source, using the Git layer and the charm configuration values. We'll be using the <a href="https://github.com/jamesbeedy/layer-git-deploy"><code>git-deploy</code></a> layer which will help us in grabbing our code from GitHub/GitLab/BitBucket and deploy it to our machine.</p>
<p>Last time, we added the apt layer as one to compose into our final charm, so let's do the same thing with the git layer, in our <code>layers.yaml</code>:</p>
<div class="codehilite"><pre><span></span><code><span class="nt">includes</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">layer:basic</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">layer:apt</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">layer:git-deploy</span>
<span class="nt">repo</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">https://github.com/makyo/wsgi-app.git</span>
<span class="nt">options</span><span class="p">:</span>
<span class="nt">apt</span><span class="p">:</span>
<span class="nt">packages</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">python-pip</span>
</code></pre></div>
<p>Easy peasy, lemon squeezy. Now, when we build our charm, we should see 'git-deploy' show up in the layers being processed:</p>
<div class="codehilite"><pre><span></span><code>makyo@corrin:~/work/charms/layers/wsgi-app$ charm -v build
build: Composing into /home/makyo/work/charms
build: Destination charm directory: /home/makyo/work/charms/builds/wsgi-app
build: Processing layer: layer:basic
build: Processing layer: layer:apt
build: Processing layer: layer:git-deploy
build: Processing layer: wsgi-app
</code></pre></div>
<p>Sweet, looking good.</p>
<p>When a layer is added to a layered charm like this, all of its configuration options get merged in with the configuration options that we specify in our layer. For example, lets go take a look at the <code>config.yaml</code> file in the built charm in <code>../../builds/wsgi-app</code>:</p>
<div class="codehilite"><pre><span></span><code><span class="s">&quot;options&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;extra_packages&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;Space</span><span class="nv"> </span><span class="s">separated</span><span class="nv"> </span><span class="s">list</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">extra</span><span class="nv"> </span><span class="s">deb</span><span class="nv"> </span><span class="s">packages</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">install.\n&quot;</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;string&quot;</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;&quot;</span>
<span class="s">&quot;package_status&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;install&quot;</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;string&quot;</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;The</span><span class="nv"> </span><span class="s">status</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">service-affecting</span><span class="nv"> </span><span class="s">packages</span><span class="nv"> </span><span class="s">will</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">set</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">this</span><span class="nv"> </span><span class="s">value\</span>
<span class="s">\ in</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">dpkg</span><span class="nv"> </span><span class="s">database.</span><span class="nv"> </span><span class="s">Valid</span><span class="nv"> </span><span class="s">values</span><span class="nv"> </span><span class="s">are</span><span class="nv"> </span><span class="s">\&quot;install\&quot;</span><span class="nv"> </span><span class="s">and</span><span class="nv"> </span><span class="s">\&quot;hold\&quot;.\n&quot;</span>
<span class="s">&quot;install_sources&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;List</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">extra</span><span class="nv"> </span><span class="s">apt</span><span class="nv"> </span><span class="s">sources,</span><span class="nv"> </span><span class="s">per</span><span class="nv"> </span><span class="s">charm-helpers</span><span class="nv"> </span><span class="s">standard</span><span class="nv"> </span><span class="s">format</span><span class="nv"> </span><span class="s">(a\</span>
<span class="s">\ yaml</span><span class="nv"> </span><span class="s">list</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">strings</span><span class="nv"> </span><span class="s">encoded</span><span class="nv"> </span><span class="s">as</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">string).</span><span class="nv"> </span><span class="s">Each</span><span class="nv"> </span><span class="s">source</span><span class="nv"> </span><span class="s">may</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">either</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">line\</span>
<span class="s">\ that</span><span class="nv"> </span><span class="s">can</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">added</span><span class="nv"> </span><span class="s">directly</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">sources.list(5),</span><span class="nv"> </span><span class="s">or</span><span class="nv"> </span><span class="s">in</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">form</span><span class="nv"> </span><span class="s">ppa:&lt;user&gt;/&lt;ppa-name&gt;\</span>
<span class="s">\ for</span><span class="nv"> </span><span class="s">adding</span><span class="nv"> </span><span class="s">Personal</span><span class="nv"> </span><span class="s">Package</span><span class="nv"> </span><span class="s">Archives,</span><span class="nv"> </span><span class="s">or</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">distribution</span><span class="nv"> </span><span class="s">component</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">enable.\n&quot;</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;string&quot;</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;&quot;</span>
<span class="s">&quot;install_keys&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;List</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">signing</span><span class="nv"> </span><span class="s">keys</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">install_sources</span><span class="nv"> </span><span class="s">package</span><span class="nv"> </span><span class="s">sources,</span><span class="nv"> </span><span class="s">per\</span>
<span class="s">\ charmhelpers</span><span class="nv"> </span><span class="s">standard</span><span class="nv"> </span><span class="s">format</span><span class="nv"> </span><span class="s">(a</span><span class="nv"> </span><span class="s">yaml</span><span class="nv"> </span><span class="s">list</span><span class="nv"> </span><span class="s">of</span><span class="nv"> </span><span class="s">strings</span><span class="nv"> </span><span class="s">encoded</span><span class="nv"> </span><span class="s">as</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">string).\</span>
<span class="s">\ The</span><span class="nv"> </span><span class="s">keys</span><span class="nv"> </span><span class="s">should</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">full</span><span class="nv"> </span><span class="s">ASCII</span><span class="nv"> </span><span class="s">armoured</span><span class="nv"> </span><span class="s">GPG</span><span class="nv"> </span><span class="s">public</span><span class="nv"> </span><span class="s">keys.</span><span class="nv"> </span><span class="s">While</span><span class="nv"> </span><span class="s">GPG</span><span class="nv"> </span><span class="s">key\</span>
<span class="s">\ ids</span><span class="nv"> </span><span class="s">are</span><span class="nv"> </span><span class="s">also</span><span class="nv"> </span><span class="s">supported</span><span class="nv"> </span><span class="s">and</span><span class="nv"> </span><span class="s">looked</span><span class="nv"> </span><span class="s">up</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">keyserver,</span><span class="nv"> </span><span class="s">operators</span><span class="nv"> </span><span class="s">should</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">aware\</span>
<span class="s">\ that</span><span class="nv"> </span><span class="s">this</span><span class="nv"> </span><span class="s">mechanism</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">insecure.</span><span class="nv"> </span><span class="s">null</span><span class="nv"> </span><span class="s">can</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">used</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">standard</span><span class="nv"> </span><span class="s">package</span><span class="nv"> </span><span class="s">signing\</span>
<span class="s">\ key</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">used</span><span class="nv"> </span><span class="s">that</span><span class="nv"> </span><span class="s">will</span><span class="nv"> </span><span class="s">already</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">installed</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">machine,</span><span class="nv"> </span><span class="s">and</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">PPA</span><span class="nv"> </span><span class="s">sources\</span>
<span class="s">\ where</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">package</span><span class="nv"> </span><span class="s">signing</span><span class="nv"> </span><span class="s">key</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">securely</span><span class="nv"> </span><span class="s">retrieved</span><span class="nv"> </span><span class="s">from</span><span class="nv"> </span><span class="s">Launchpad.\n&quot;</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;string&quot;</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;&quot;</span>
<span class="s">&quot;commit-or-branch&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;string&quot;</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="kt">!!null</span> <span class="s">&quot;&quot;</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;Commit</span><span class="nv"> </span><span class="s">or</span><span class="nv"> </span><span class="s">branch</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">update</span><span class="nv"> </span><span class="s">to&quot;</span>
<span class="s">&quot;repo&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;string&quot;</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="kt">!!null</span> <span class="s">&quot;&quot;</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;The</span><span class="nv"> </span><span class="s">repository</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">clone</span><span class="nv"> </span><span class="s">from,</span><span class="nv"> </span><span class="s">this</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">required&quot;</span>
<span class="s">&quot;deploy-key&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;string&quot;</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;&quot;</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">|</span>
<span class="no">A deploy key is an SSH key that is stored on the server and</span>
<span class="no">grants access to a repository.</span>
<span class="s">&quot;key-required&quot;</span><span class="p p-Indicator">:</span>
<span class="s">&quot;type&quot;</span><span class="p p-Indicator">:</span> <span class="s">&quot;boolean&quot;</span>
<span class="s">&quot;default&quot;</span><span class="p p-Indicator">:</span> <span class="kt">!!bool</span> <span class="s">&quot;false&quot;</span>
<span class="s">&quot;description&quot;</span><span class="p p-Indicator">:</span> <span class="p p-Indicator">|</span>
<span class="no">This should be set to true to ensure that a deploy key is</span>
<span class="no">deployed if necessary</span>
</code></pre></div>
<p>If you pick through all this yaml, what you'll see is that it's broken down into two sections: the configuration from the apt layer and the configuration from the git layer, four of each. From the git layer, which we're focusing on, you can see that we can specify a commit/branch to deploy and a repo from which to deploy, as well as some settings for fetching code from private repositories.</p>
<p>Config files, in charms, are used for specifying default values and any descriptions used on <a href="https://jujucharms.com">the charmstore</a>. When the user deploys our charm, they will rely on defaults or pass in configurations when deploying through their own file. For instance, when deploying Honeycomb using this charm as it stands, my configuration file might be something like this:</p>
<div class="codehilite"><pre><span></span><code><span class="nt">wsgi-app</span><span class="p">:</span>
<span class="nt">extra_packages</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">pandoc</span>
<span class="nt">commit-or-branch</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">master</span> <span class="c1"># Once we tag a release, we can use that branch</span>
<span class="nt">repo</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">https://github.com/OpenFurry/honeycomb.git</span>
</code></pre></div>
<p>When we deploy our charm, we'll deploy it with the config, using <code>juju deploy --config honeycomb-config.yaml wsgi-app</code>. When the charm is fetched, it'll grab pandoc (used to convert uploaded files to markdown), clone the honeycomb repository on the master branch, and then...and then magic. We'll get to how to serve our wsgi app in a future post.</p>
<hr />
<p>For now, let's see what else we can do with our configuration while we're in there.</p>
<p>Hmm, well, Honeycomb requires quite a bit installed, actually. It needs Django, of course, and markdown, and, uh...oh gosh, <a href="github.com/OpenFurry/honeycomb/blob/master/requirements.txt.html">quite a bit</a>. Enough that it needs a pip requirements file. Actually, those are good practice to have, anyway. If they're that common, we should probably make running <code>pip install</code> a part of our charm installation. We can't just call <code>pip install -r requirements.txt</code> from our project's directory, though. What if you named your file differently? Or, say your project is part of a suite, and you're only going to be running one part of the suite, making your requirement in a subdirectory?</p>
<p>The same goes for our WSGI file, too --- in modern Django apps, that's in <code>&lt;project_name&gt;/wsgi.py</code>, but you had to write your own in older versions of Django; in Flask apps. it's <em>usually</em> the main python file, but might not be --- so we should be able to specify that as well.</p>
<p>So let's head back to our <code>wsgi-app</code> layer and dig into <code>config.yaml</code>. We'll need to add two configuration values --- <code>pip-requirements</code> and <code>wsgi-file</code> --- both of which will be strings:</p>
<div class="codehilite"><pre><span></span><code><span class="nt">options</span><span class="p">:</span>
<span class="nt">pip-requirements</span><span class="p">:</span>
<span class="nt">default</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">requirements.txt</span>
<span class="nt">type</span><span class="p">:</span> <span class="s">&#39;string&#39;</span>
<span class="nt">description</span><span class="p">:</span> <span class="p p-Indicator">|</span>
<span class="no">The location within your project&#39;s directory of any requirements file</span>
<span class="no">needed to install Python dependencies. An empty string will mean that</span>
<span class="no">pip will not be run.</span>
<span class="nt">wsgi-file</span><span class="p">:</span>
<span class="nt">default</span><span class="p">:</span> <span class="s">&#39;&#39;</span>
<span class="nt">type</span><span class="p">:</span> <span class="s">&#39;string&#39;</span>
<span class="nt">description</span><span class="p">:</span> <span class="p p-Indicator">|</span>
<span class="no">REQUIRED</span>
<span class="no">The location of the file which exposes `application` to the WSGI server.</span>
</code></pre></div>
<p>While we're at it, I must admit to a goof: in the previous installment, I mentioned that we had to add some config values to our layer's <code>config.yaml</code> file in order to make one of the layers happy. I was wrong, thank goodness. The configuration values fall all the way through to the built charm. So I deleted the ones from the last article, and what you see above is the entire <code>config.yaml</code> file now.</p>
<hr />
<p>We've gone over quite a bit today around configuration values, but we've been tooling around in the edges of charm land. Next time, lets delve into writing some actual charm code! We'll see if we can play around with the states provided by the apt and git layers! Maybe even dive into some interfaces.</p>
<p><a href="https://github.com/makyo/wsgi-app/tree/1b918a491f15800106f7df9dd2688f6b5c18ab8d">Source code for <code>wsgi-app</code> layer</a> as it was at the point reached at the end of the article.</p>
<hr />
<div class="footnote">
<hr />
<ol>
<li id="fn:names">
<p>All of my laptops and desktops are named after planets in Dune (Arrakis is the house server, natch), and all my servers are named after species of fox. Alopex is the crunchy old dev server (because Alopex is no longer used as a genus name anymore), but next in line is <a href="https://en.wikipedia.org/wiki/Bengal_fox">the bengal fox</a>&#160;<a class="footnote-backref" href="#fnref:names" title="Jump back to footnote 1 in the text">&#8617;</a></p>
</li>
</ol>
</div>
</article>
<footer>
<p>Page generated on 2020-04-24</p>
</footer>
</main>
</body>
</html>