<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Consuming Patience]]></title><description><![CDATA[A purported programmer's personal pensieve]]></description><link>http://blog.conpat.io/</link><generator>Ghost 0.6</generator><lastBuildDate>Sat, 01 Aug 2015 03:19:10 GMT</lastBuildDate><atom:link href="http://blog.conpat.io/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Browsing Homebrew]]></title><description><![CDATA[<p>I was sick as a dog this weekend, and while that was the perfect excuse to watch the start of the NBA/NHL playoffs, there's only so much I can take before I need to do something else than stay miserable in bed and watch able-bodied athletes zip around.</p>

<p>I</p>]]></description><link>http://blog.conpat.io/homebrew-browsing/</link><guid isPermaLink="false">3006df6d-44d3-4c19-9632-5f5d646a75e0</guid><category><![CDATA[bash]]></category><category><![CDATA[os x]]></category><category><![CDATA[homebrew]]></category><category><![CDATA[brewCheck]]></category><dc:creator><![CDATA[Andres Rojas]]></dc:creator><pubDate>Mon, 20 Apr 2015 20:20:44 GMT</pubDate><content:encoded><![CDATA[<p>I was sick as a dog this weekend, and while that was the perfect excuse to watch the start of the NBA/NHL playoffs, there's only so much I can take before I need to do something else than stay miserable in bed and watch able-bodied athletes zip around.</p>

<p>I was too addled to actually get any work done, but I did try to fiddle with my work environment on my laptop. At some point, I found myself browsing through Homebrew to see if there were any tools I hadn't been using that would spark my interest. I quickly got tired of using <code>brew search</code>, <code>brew info</code> and then popping the URL into Chrome to see what the package was all about.</p>

<p>Of course, there is <a href="http://braumeister.org">braumeister.org</a> to browse through Homebrew packages online, but I'm the obsessive sort. Particularly when I have nothing better to do. I wanted to go through as much as I could. That's when I decided to automate.</p>

<div data-gist-id="e99fb2e97e1727d52caa" data-gist-line="1,38-81" data-gist-file="brewCheck.sh" data-gist-hide-line-numbers="true"></div>

<p><a href="https://gist.github.com/andres-rojas/e99fb2e97e1727d52caa">brewCheck</a> runs through every package listed by your local <code>brew search</code> command, and opens up the package's listed URL in Chrome for you to take a look at. It then asks if it looks like something you want to install. Type <code>y</code>/<code>Y</code> and it will add the package to a running list in a <code>.brewInstall</code> file kept in the local user's home directory. Any other input, and the script closes the tab in Chrome, and goes to the next available package to start the process anew.</p>

<p>Since there are over 3000 packages to go through, I figured it would be nice to be able to stop/resume this at any time. That's accomplished by keeping track of what packages were checked in a <code>.brewChecked</code> file, also kept in the local user's home directory. Any packages listed there will not be checked on subsequent runs of the script. This also means that as new packages are added, it will remain a quick and useful way to go through them.</p>

<p>The script does have three dependencies that aren't provided by default on OS X:</p>

<ul>
<li><a href="http://www.google.com/chrome">Google Chrome</a></li>
<li><a href="https://github.com/prasmussen/chrome-cli">chrome-cli</a></li>
<li><a href="http://tasktools.org/projects/vramsteg.html">vramsteg</a></li>
</ul>

<p>I also included the ability to have it do the same thing with <code>brew-cask</code> or any other similar <code>brew</code> subcommand, by calling the script with the subcommand as an argument (i.e: <code>brewCheck cask</code>).</p>

<p>Honestly, this is a total timewaster of a script. The majority of the packages are libraries of one sort or another. That being said, it did lead to me stumbling on <a href="https://www.allegro.cc/">allegro</a>, and tinkering with that was a fun diversion during the Memphis Grizzlies/Portland Trialblazers blowout.</p>]]></content:encoded></item><item><title><![CDATA[Dockerizing Seafile]]></title><description><![CDATA[<div style="background: khaki">  
<h6>Notice:</h6> Looks like the JS I inserted to display line-specific blocks of code from my Gists has broken. I'll be looking into fixing this over the weekend when I have some time to spare.  
</div>  

<p><br></p>

<p>For a while now, I've been wanting to try out one of the self-hosted alternatives to</p>]]></description><link>http://blog.conpat.io/dockerizing-seafile/</link><guid isPermaLink="false">b9cccb11-9736-46a1-b6ff-3b372574a97d</guid><category><![CDATA[docker]]></category><category><![CDATA[seafile]]></category><category><![CDATA[bash]]></category><category><![CDATA[python]]></category><dc:creator><![CDATA[Andres Rojas]]></dc:creator><pubDate>Wed, 15 Apr 2015 05:24:43 GMT</pubDate><content:encoded><![CDATA[<div style="background: khaki">  
<h6>Notice:</h6> Looks like the JS I inserted to display line-specific blocks of code from my Gists has broken. I'll be looking into fixing this over the weekend when I have some time to spare.  
</div>  

<p><br></p>

<p>For a while now, I've been wanting to try out one of the self-hosted alternatives to DropBox. As it stands, I've made do with the 10GB of free storage I've accumulated on DropBox, and when necessary I've dabbled in a few of the other free alternatives like Google Drive, OneDrive and Box. Ultimately though, I'm the sort that likes to roll my own when I can, so I will.</p>

<p>I did some reading, and settled on trying out <a href="http://seafile.com">Seafile</a>. I figure I might be able to run this on my usual VPS within a Docker container. A <a href="https://registry.hub.docker.com/search?q=seafile">quick search on Docker Hub</a> brings up a few existing repos for deploying Seafile, but all of them assume you'll be using MySQL/MariaDB and/or require you to shell into the container to run the interactive install/setup scripts for Seafile.</p>

<p>For my use case, I figure I'll probably be ok with SQLite, and doing so would reduce the memory footprint I need to run it, which will keep my VPS costs down. And as for the interactive install script, I was curious so I quickly dove into the code for those, and I'm thinking we don't need no stinking interactive install script.</p>

<p>The <a href="http://manual.seafile.com/deploy/using_sqlite.html">instructions</a> provided for setting up a personal instance of Seafile up on a Linux box are actually pretty good. They're pretty straight-forward, and hold your hand with all the commands you'll likely need. Most of the configuration is done through an interactive shell script that calls a Python script behind the scenes to do the dirty work.</p>

<p>This is all well and good, but unfortunately Docker doesn't do interactive scripts, so I'm going to have to work out what I need to get things configured up front. Off the top of my head, this might require some user-defined environment variables, and a custom non-interactive script that runs at build time.</p>

<p>Of course, I might end up finding out exactly why no one else has done this yet. All part of the fun.</p>

<h2 id="initialdockerfile">Initial Dockerfile</h2>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="1-3" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>To start off with, we're basing from the official Ubuntu 14.04 repo, and then some boilerplate. If you're wondering what the REFRESHED_AT declaration is about, I'd recommend you read <a href="http://www.dockerbook.com/">James Turnbull's Docker Book</a>.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="5-6" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>First things first, I create and set my working directory.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="8-19" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>I follow the install manual pretty closely here. I just make sure to set up my python/sqlite3 dependencies before anything else.</p>

<p>After building this image, I can jump into an interactive container with the usual <code>docker run -it &lt;image&gt; /bin/bash</code> to poke around and experiment.</p>

<h2 id="letslookatconfiguration">Let's Look At Configuration</h2>

<p>In the documentation, there is a <a href="http://manual.seafile.com/config/index.html">section about the config files</a> and how they're set up, but a quick look over that and a manual run of the installation flow makes it seem like there might be a thing or two I'm missing. That and I'm bored, and I could use an excuse to flex my Bash/Python comprehension muscles.</p>

<p>Our first installation script is <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh">setup-seafile.sh</a>. Quick skim and it looks like some effort was made to keep things tidy, with default settings and utility functions up top, and a decent amount of documentation/commenting. Said comments helpfully point out that the "main workflow of this script" starts on <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L354">line 354</a>. I'm not going to deep dive into any of this, so if you're interested in implementation details, you can look at the code yourself. At a glance, it all looks pretty straightforward.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="354-360" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>The first few function calls do a bit of sanity/dependency checking. They warn if you run as root, make sure the filetree in the install path looks sane, and make sure you have the necessary system dependencies (i.e. python &amp; sqlite). For our purposes, we can skip all this.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="362-367" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>The next bits check to see if there is an existing ccnet config file, and if not, prompts for the information it needs. In this case, it's asking for:</p>

<ul>
<li>A name for the Seafile server</li>
<li>The IP or domain for the server</li>
<li>The port to use for the ccnet server</li>
</ul>

<p>The name &amp; IP/domain should probably be handled as an environment variable in the Dockerfile. The port we'll leave as the default 10001, which we'll expose in the Dockerfile for the end-user.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="369-373" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Here we see a similar pattern, however in this case, the user is always prompted for the desired path for the data directory. It will then check to see if the directory has any data, and prompts accordingly depending on what it finds. Again, we can skip all of this, and move on to where it prompts for the info it needs to fill out the config:</p>

<ul>
<li>The port to use for the seafile server</li>
<li>The port to use for the seafile fileserver</li>
</ul>

<p>And for posterity, let's not forget:</p>

<ul>
<li>The path for the Seafile data directory</li>
</ul>

<p>Again, we'll set an environment variable for the data directory, and expost the default ports (12001 &amp; 8082, respectively) in the Dockerfile.</p>

<p>In fact, this is a good place to pause and get back to our Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="21-25" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Some of those environment variables are obvious placeholders. I'll make sure to note in the eventual README that they really need to be defined at build time. I've also exposed the ports we need so far at their respective default values.</p>

<p>Back to it then.</p>

<h2 id="createccnetconf">Create ccnet conf</h2>

<p>Now, we skip past the script confirming the data provided, and on to the helpfully titled <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L405">"Create ccnet conf" section</a>.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="405-413" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Here's where it gets interesting. It uses a binary to generate our ccnet config file, and passes the information needed (i.e. server name/port/ip/domain) via CLI arguments. However, it prepends the call with a declaration for <code>LD_LIBRARY_PATH</code>. A quick test confirms that without that environment variable set, the binary won't run:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="1-2" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>A quick search for <code>SEAFILE_LD_LIBRARY_PATH</code>, and there it is on <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L11">line 11</a>.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="11" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Let's try prepending that environment variable manually:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="4-12" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Let's see what happens when we run it with some arguments:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="14-19" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>OK, now we can get this in to our Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="27-35" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<h2 id="waittheresnobuildtimeenvoption">Wait, there's no build-time env option?</h2>

<p>Now our Dockerfile build generates a ccnet config for us. Of course, the problem here is that since the configuration is set at build time, the end-user is going to be stuck with the defaults if they try to <code>docker pull</code> this image in the future.</p>

<p>Here we have the inherent dilemma that probably prevented this from being pushed up to Docker Hub by anyone. An alternative option would be to provide a boilerplate ccnet.conf in the repo, provide instructions on customizing the file, and including the file with an <code>ADD</code> command in the Dockerfile.</p>

<p>It would be a lot more elegant however, if we could just provide environment variables at <code>docker build</code> time like we already can at <code>docker run</code> time. I was all ready to storm into the Docker repo to submit a Feature Request when:</p>

<p><a href="https://github.com/docker/docker/pull/9176">Glory be, it's already on the way</a>.</p>

<p>As such, I'm going to go ahead and continue down this path rather than try to refactor this. For now, people will have to grab the Dockerfile and build on their own if they want to crib this. In the hopefully near future, we'll all have better options.</p>

<h2 id="createseafileconf">Create seafile conf</h2>

<p><a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L420">Onward</a>.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="420-430" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>This looks awfully familiar. Let's give that binary a go, with the required environment variable already set in our container:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="21-32" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Oooh, <em>verbose</em>. We can have some fun here.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="34-43" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>I suppose there wasn't much to be verbose about.</p>

<p>Back to the Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="37-49" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Since we're doing multiple things from the seafile-server-* directory, I decided to DRY it up a little.</p>

<p>Skipping ahead to the brief <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L437">"Write seafile.ini"</a> section:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="436" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>This is easy enough to include in our Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="51" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<h2 id="createseafeventsconf">Create seafevents.conf</h2>

<p>The <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L443">next section</a> is a one-line function call, so let's just go <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L325">straight to that function</a></p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="325-340" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Looks like it's just creating a conf directory and generating a static file. Getting that into the Dockerfile is simple enough.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="53" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>We'll just add the static seafdav.conf in as part of the repo, alongside the Dockerfile.</p>

<h2 id="createseahubsettingspy">Create seahub/settings.py</h2>

<p><a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L448">Next section</a>:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="447-454" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Again, simple enough. We just need to call this python script to generate a key and pipe that into a file. Let's get that in the Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="55" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<h2 id="seahubconfiguration">Seahub configuration</h2>

<p>There's some interesting code that's commented out in the <a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L460">"Seahub related config"</a> section. It looks like at some point, they were setting up the initial admin user authentication credentials for the Seahub web UI through some functions in this script. The functions never get called though, as that batch of code is completely commented out.</p>

<p><a href="https://github.com/haiwen/seafile/blob/master/scripts/setup-seafile.sh#L549">Skipping ahead</a> we get to the generation of the Seahub database in sqlite3:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="548-554" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Into the Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="57" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Now for some media folder shenanigans:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="560-568" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Not sure about all that relative traversal for the link, but sure. Into the Dockerfile we go:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="59-60" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Now, a <code>seafile-server-latest</code> symlink:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="577-582" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="62-63" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Some file permissions:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="586-589" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="65-66" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>And finally, some user manuals get moved around:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="setup-seafile.sh" data-gist-line="342-347" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Last dance:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="68-69" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<h2 id="finalseahubconfigs">Final Seahub configs</h2>

<p>Looking at the startup script for Seahub, it seems to set its port interactively at startup. However, it defaults to 8000, so we'll just expose that in the Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="71" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Finally, we still need to setup that initial admin user for Seahub. When actually using the scripts provided, the <code>seahub.sh</code> startup script actually <a href="https://github.com/haiwen/seafile/blob/master/scripts/seahub.sh#L213">checks for an existing admin user</a> and prompts for the information needed to set one up if one doesn't exist:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="seahub.sh" data-gist-line="213-218" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>The function actually just calls a separate Python script to handle this task, <a href="https://github.com/haiwen/seafile/blob/master/scripts/check_init_admin.py#L360">so let's dive into that one</a>:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="check_init_admin.py" data-gist-line="360-362" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>The main program flow is actually contained within an <code>if __name__ == '__main__':</code> block, which is a nice Python idiom for only executing the program if the script is called directly. This means we can import the file and make use of the script's functions in our own script without invoking the original program flow.</p>

<p>A quick glance over the main() function and we can spot a call to <code>create_admin(email, passwd)</code>. <a href="https://github.com/haiwen/seafile/blob/master/scripts/check_init_admin.py#L298">That definitely looks like what we need</a>:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="check_init_admin.py" data-gist-line="298-306" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Let's give this a whirl in a Python REPL:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="repl.py" data-gist-line="1-8" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>We're missing some libraries. A quick skim of the calling <code>seahub.sh</code> reveals a <a href="https://github.com/haiwen/seafile/blob/master/scripts/seahub.sh#L145">before_start</a> function:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="seahub.sh" data-gist-line="145,153-158" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>A quick <code>grep</code> in those lib directories, and I find the culprit:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="45" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Another try:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="repl.py" data-gist-line="10-19" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p><code>CCNET_CONF_DIR</code> was also declared in <code>before_start()</code>.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="47" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>With that, we can finally import the <code>create_admin</code> function:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="repl.py" data-gist-line="21-41" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Only to bump into another error. Looking through the traceback it seems to be trying to connect to another service. Best guess is that it expects the Seafile server to be running.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="history.sh" data-gist-line="49-54" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Back in the REPL:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="repl.py" data-gist-line="43-50" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>And there we go. Now to get all of this into our Dockerfile build.</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="73-82" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>We</p>

<ul>
<li>create some environment variables for setting the admin email &amp; password</li>
<li>import <code>create_admin.py</code></li>
<li>start up the Seafile server</li>
<li>run <code>create_admin.py</code> with the appropriate environment variables set</li>
<li>delete <code>create_admin.py</code></li>
</ul>

<p>create_admin.py:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="create_admin.py" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>Looks good to me. Now to have it start the server and the UI together at container boot.</p>

<p>Dockerfile:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="Dockerfile" data-gist-line="84-87" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>For the <code>start_services.sh</code> script, I had the problem that Seafile's startup scripts aren't continually running processes. So, if I simply called <code>./seafile.sh start &amp;&amp; ./seahub.sh start</code>, the container would boot Seafile &amp; Seahub, but would then stop the container, as the process was successfully finisehed at that point. To fix that, I decided the simplest method would be to provide some sort of sleep infinite while loop that would keep the process going indefinitely.</p>

<p>I also noticed that Seafile's startup scripts don't provide any logging, so to both keep the container running as a daemon and provide a barebones look into if the Seafile processes are actually running, I provided some basic output to the container's stdout:</p>

<div class="gist" data-gist-id="589a84efc4a29b3bb3ed" data-gist-file="start_services.sh" data-gist-hide-footer="true" data-gist-hide-line-numbers="true"></div>

<p>That will be the extent of it unless the script detects a change in the running process list. In that case, it will alert that a change was detected, and will show the “Running Seafile processes” again.</p>

<h2 id="wrapup">Wrap-up</h2>

<p>Thinking it over, I would say that it would be worth looking into separating out each process into its own container and then linking them together. At the very least, seafile-server and seahub could probably be separated and linked. Maybe even a third container to handle a shared /seafile volume for the two. A project for another day perhaps.</p>

<p>While this isn't a production ready Seafile server, and the image probably won't be useful with the defaults set up in the Dockerfile's ENV calls, this may still be of some use to someone. As such, I'll be writing up a quick README, and pushing this up to Github, and I'll probably link the Github repo to Docker Hub in case anyone wants to play with it for some reason.</p>

<p>And hey, if that build-time ENV feature gets merged into the next revision of Docker, we can revisit and maybe make it a viable public image after all.</p>

<ul>
<li><a href="https://github.com/andres-rojas/seafile-docker">https://github.com/andres-rojas/seafile-docker</a></li>
</ul>]]></content:encoded></item></channel></rss>