<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Mike Sisk</title><link href="../." rel="alternate"></link><link href=".././feeds/all.atom.xml" rel="self"></link><id>../.</id><updated>2011-08-09T00:00:00Z</updated><entry><title>s3cmd with Multiple Accounts</title><link href=".././s3cmd-with-multiple-accounts.html" rel="alternate"></link><updated>2011-08-09T00:00:00Z</updated><author><name>Mike Sisk</name></author><id>.././s3cmd-with-multiple-accounts.html</id><summary type="html">&lt;p&gt;Recently I've been doing a lot of work involving Amazon Simple Storage Service
(aka Amazon S3).&lt;/p&gt;
&lt;p&gt;And while tools like Panic's &lt;a href="http://www.panic.com/transmit/"&gt;Transmit&lt;/a&gt;, the
Firefox &lt;a href="http://www.s3fox.net/"&gt;S3Fox&lt;/a&gt; extension, or even Amazon's own S3
&lt;a href="http://aws.amazon.com/s3/"&gt;Management Console&lt;/a&gt; make it easy to use, sometimes
you really need a command-line tool.&lt;/p&gt;
&lt;p&gt;There's a lot of good tools out there, but the one I've been using is
&lt;strong&gt;&lt;a href="http://s3tools.org/s3cmd"&gt;s3cmd&lt;/a&gt;&lt;/strong&gt;. This tool is done in Python, has been
around awhile and is well documented. Installation on Linux or OS X is simple as
is its configuration. And as a longtime Unix command-line user it's syntax is
simple and Unixy. Some examples:&lt;/p&gt;
&lt;p&gt;To list your buckets:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~ $ s3cmd ls
2010-04-28 23:50  s3://g5-images
2011-01-21 06:42  s3://g5-mongodb-backup
2011-03-21 21:23  s3://g5-mysql-backup
2010-06-03 17:45  s3://g5-west-images
2010-09-02 15:57  s3://g5engineering
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;List the size of a bucket with "human readable" units:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~ $ s3cmd du -H s3://g5-mongodb-backup
1132G    s3://g5-mongodb-backup/
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;List the contents of a bucket:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~ $ s3cmd ls s3://g5-mongodb-backup
2011-08-08 14:43 3273232889   s3://g5-mongodb-backup/mongodb.2011-08-08-06.tar.gz
2011-08-08 21:12 3290592536   s3://g5-mongodb-backup/mongodb.2011-08-08-12.tar.gz
2011-08-09 03:16 3302734859   s3://g5-mongodb-backup/mongodb.2011-08-08-18.tar.gz
2011-08-09 09:09 3308369423   s3://g5-mongodb-backup/mongodb.2011-08-09-00.tar.gz
2011-08-09 14:51 3285753739   s3://g5-mongodb-backup/mongodb.2011-08-09-06.tar.gz
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Show the MD5 hash of an asset:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~ $ s3cmd ls --list-md5 s3://g5-mongodb-backup/mongodb.2011-08-09-06.tar.gz
2011-08-09 14:51 3285753739   07747e3de16138799d9fe1846436a3ce  \
    s3://g5-mongodb-backup/mongodb.2011-08-09-06.tar.gz
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Transferring a file to a bucket uses the &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;put&lt;/code&gt; commands. And if you
forget an option or need a reminder of usage the very complete &lt;code&gt;s3cmd --help&lt;/code&gt;
output will likely be all the help you need.&lt;/p&gt;
&lt;p&gt;One problem I have with most tools for AWS is managing multiple accounts. Most
of these tools assume you have just one account, but I work with multiple
accounts and switching between them can be cumbersome.&lt;/p&gt;
&lt;p&gt;Here's how I work with multiple AWS accounts using s3cmd.&lt;/p&gt;
&lt;p&gt;By default &lt;strong&gt;s3cmd&lt;/strong&gt; puts its configuration file in &lt;code&gt;~/.s3cfg&lt;/code&gt;, but you can
override this and specify a configuration file with the &lt;code&gt;-c&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;What I do is create a separate config file with the appropriate credentials for
each account I work with and give them unique names:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~ $ ls -1 .s3cfg*
.s3cfg-g5
.s3cfg-tcp
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Another option is to keep the credentials for the account you use most often in
the standard &lt;code&gt;~/.s3cfg&lt;/code&gt; file and use the &lt;code&gt;-c&lt;/code&gt; option when/if you need another
account. I don't like this option because it's too easy to mistakenly use the
wrong account. For example, without a &lt;code&gt;~/.s3cfg&lt;/code&gt; this is what happens when I use
&lt;strong&gt;s3cmd&lt;/strong&gt; without specifying a configuration:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~  $ s3cmd ls
ERROR: /Users/mike/.s3cfg: No such file or directory
ERROR: Configuration file not available.
ERROR: Consider using --configure parameter to create one.
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So, what to do? Using the &lt;code&gt;-c&lt;/code&gt; all the time is a PITA. Answer: use Bash aliases!
Here's a subset of the &lt;strong&gt;s3cmd&lt;/strong&gt; aliases I have in my &lt;code&gt;~/.bashrc&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;# s3cmd aliases for different s3 accounts
alias s3g5=&amp;#39;s3cmd -c ~/.s3cfg-g5&amp;#39;
alias s3tcp=&amp;#39;s3cmd -c ~/.s3cfg-tcp&amp;#39;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, to list the buckets in my personal account I just do:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~ $ s3tcp ls
2011-07-01 06:10  s3://mikesisk-img
2011-07-05 23:16  s3://www.tcpipranch.com
2011-07-01 22:55  s3://www.watch4rocks.com
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And I can still pass arguments:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;~ $ s3tcp -H --list-md5 ls s3://mikesisk-img/me.jpg
2011-07-01 06:09         5k  13d7c86bccd8915dd93b085985305394  \
    s3://mikesisk-img/me.jpg
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Just keep in mind that calls to bash aliases from scripts and cronjobs might not
work. Plus it's bad form and &lt;em&gt;will&lt;/em&gt; come back to bite you one of these days.
Just use the long form with &lt;code&gt;-c&lt;/code&gt; in these places and keep the aliases for your
own interactive command-line usage.
&lt;img alt="atom" src="http://c2888087.r87.cf2.rackcdn.com/eol.png"&gt;&lt;/p&gt;</summary></entry><entry><title>Joplin Tornado</title><link href=".././joplin-tornado.html" rel="alternate"></link><updated>2011-07-01T00:00:00Z</updated><author><name>Mike Sisk</name></author><id>.././joplin-tornado.html</id><summary type="html">&lt;p&gt;Some pictures from my recent trip back to my hometown of Joplin, Missouri.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://c2888087.r87.cf2.rackcdn.com/tornado-1.jpg"&gt;&lt;img src="http://c2888087.r87.cf2.rackcdn.com/tornado-1.jpg" width=640 /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://c2888087.r87.cf2.rackcdn.com/tornado-2.jpg"&gt;&lt;img src="http://c2888087.r87.cf2.rackcdn.com/tornado-2.jpg" width=640 /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://c2888087.r87.cf2.rackcdn.com/tornado-3.jpg"&gt;&lt;img src="http://c2888087.r87.cf2.rackcdn.com/tornado-3.jpg" width=640 /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://c2888087.r87.cf2.rackcdn.com/tornado-4.jpg"&gt;&lt;img src="http://c2888087.r87.cf2.rackcdn.com/tornado-4.jpg" width=640 /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="atom" src="http://c2888087.r87.cf2.rackcdn.com/eol.png"&gt;&lt;/p&gt;</summary></entry><entry><title>Cron and Sewing Needles</title><link href=".././cron-and-sewing-needles.html" rel="alternate"></link><updated>2011-02-16T00:00:00Z</updated><author><name>Mike Sisk</name></author><id>.././cron-and-sewing-needles.html</id><summary type="html">&lt;p&gt;&lt;img alt="The actual needle." src="http://c2888087.r87.cf2.rackcdn.com/needle.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Sometimes, even after decades of experience, you still screw up.&lt;/p&gt;
&lt;p&gt;Consider this cron entry I put in last night:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;# Backup MongoDB every 6 hours, zip it up, and rsync it.
* */6 * * * ~/bin/backup_mongo.sh
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I wanted this to run the backup script for MongoDB every six hours. Instead, I
got it running every minute for an hour every six hours. You'd think I'd know
better considering I put the next cron in correctly:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;# Remove MongoDB backups that are more than 24-hours old.
00 02 * * * find /db/backup -mtime +1 -exec rm -f {} \;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;What I meant to do is this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;# Backup MongoDB every 6 hours, zip it up, and rsync it.
00 */6 * * * ~/bin/backup_mongo.sh
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Luckily we host our infrastructure at Engine Yard and their staff noticed the
CPU spike on this server at midnight and fixed the cron.&lt;/p&gt;
&lt;p&gt;Which brings up another point: name your scripts appropriately. In this case a
quick scan of cron revealed this script was running a backup and doing that
every six hours makes sense. If the script was just named mongo, it's
conceivable it could have been a metric collection script that runs every minute
for an hour every six hours.&lt;/p&gt;
&lt;p&gt;So what do sewing needles have to do with cron? I'm working from home this week
and had just finished that MongoDB backup script and was putting it in cron when
my daughter came running (Ok, make that limping) into my office with a large
sewing needle in-bedded in the arch of her foot. I quickly saved the cron entry
to take care of that problem and didn't go back to check my work.&lt;/p&gt;
&lt;p&gt;Moral of the story: whenever you set up a new cron job it's a good idea to watch
it run and see if it's doing what you think it is. Especially if you &lt;em&gt;think&lt;/em&gt; you
know what you're doing.
&lt;img alt="atom" src="http://c2888087.r87.cf2.rackcdn.com/eol.png"&gt;&lt;/p&gt;</summary></entry><entry><title>Driving</title><link href=".././driving.html" rel="alternate"></link><updated>2011-02-13T00:00:00Z</updated><author><name>Mike Sisk</name></author><id>.././driving.html</id><summary type="html">&lt;p&gt;&lt;img alt="1" src="http://c2888087.r87.cf2.rackcdn.com/driving-1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I drive a lot.&lt;/p&gt;
&lt;p&gt;I live in the Willamette Valley but drive to work in Bend, Oregon on the high
desert – that's 130 miles each way over the Cascades at Santiam Pass twice a
week. Generally I work from home on Monday and Friday and I'm in the office
Tuesday though Thursday.&lt;/p&gt;
&lt;p&gt;Until recently I kept an apartment over in Bend, but I gave it up and either
stay at a hotel or just crash in the office. I tend to work long hours and with
a recent rent, electric and cable-modem rate increase I just wasn't getting
much value out of the apartment.&lt;/p&gt;
&lt;p&gt;And so I drive. A lot of folks think I'm crazy, but I love to drive. And
frankly, I've had to commute to work in some horrific traffic (I've commuted in
LA, Boston, and San Francisco), so the scenic lightly traveled roads over the
Cascades is a very pleasant drive in comparison.&lt;/p&gt;
&lt;p&gt;Of course, weather can be a problem in the winter. Santiam Pass can get pretty
nasty at times, but ODOT does a good job keeping it clear.&lt;/p&gt;
&lt;p&gt;It helps that I drive a Jeep with snow tires and chains in reserve. I don't
worry about getting up and down the pass as much as I do about someone in a
hurry running into &lt;em&gt;me&lt;/em&gt;. I've not seen many cars in the ditch but I have seen a
lot of SUVs.&lt;/p&gt;
&lt;p&gt;In the summer time with the Jeep I can take some &lt;em&gt;interesting&lt;/em&gt; shortcuts. The
picture above is one I took heading home this past summer.&lt;/p&gt;
&lt;p&gt;What do I do to pass the time during six hours of driving every week? Listen to
podcasts. There's so much to listen to that I would never have time unless I had
this block of time.
&lt;img alt="atom" src="http://c2888087.r87.cf2.rackcdn.com/eol.png"&gt;&lt;/p&gt;</summary></entry><entry><title>Some vi tips</title><link href=".././some-vi-tips.html" rel="alternate"></link><updated>2011-02-10T00:00:00Z</updated><author><name>Mike Sisk</name></author><id>.././some-vi-tips.html</id><summary type="html">&lt;p&gt;Today I got hit with a last-minute request to update our Nginx redirect map
with data provided in a Google docs spreadsheet. Normally I only have to do
one or two redirect rules at a time. But today I got hit with 120 rules -
each of which needed data from two cells in the spreadsheet. Doing it manually
would require 240 cut-n-paste operations - not fun and error prone. Oh, and I
only had 30-minutes to get this done and up in production.&lt;/p&gt;
&lt;p&gt;We do these sorts of redirects when a customer changes their domain name. In
this particular case the customer had a number of locations under one domain and
they're now splitting each location out into its own domain. But we don't want
visitors to the sites getting 404s due to the URL changing so we put in redirect
rules to rewrite the request and forward the visitor to the new domain.&lt;/p&gt;
&lt;p&gt;There's a bunch of ways to do this, but this is how I did it.&lt;/p&gt;
&lt;p&gt;First, a quick explanation of the Nginx redirect map format. There's not much to
it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;old.example.com    new.example.com;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's the old URL, a space or spaces (or tab), the new URL and a semicolon
terminating the line.&lt;/p&gt;
&lt;p&gt;In this case, each row of the spreadsheet with the redirect data had 7 columns:
the name of the property being redirected, and three URLs that needed to be
redirected with the destination (i.e. Each location in this case only has three
pages so there's only three redirects each). Luckily the order of the data is
just what I needed for the map.&lt;/p&gt;
&lt;p&gt;The first thing I did was a CSV export of the data and opened it up in vi.&lt;/p&gt;
&lt;p&gt;&lt;img alt="1" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-1.png"&gt;&lt;/p&gt;
&lt;p&gt;The CSV export contained title information for the columns I don't need so let's
just delete those right off.&lt;/p&gt;
&lt;p&gt;&lt;img alt="2" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-2.png"&gt;&lt;/p&gt;
&lt;p&gt;Ok, now the data is ready to be processed into something I can use. In another
stroke of luck we can see each redirect pair is separated by two commas since
the spreadsheet contained an empty column between the three pairs for each
location. This will make things much easier.&lt;/p&gt;
&lt;p&gt;&lt;img alt="3" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-3.png"&gt;&lt;/p&gt;
&lt;p&gt;First, let's get each redirect on its own line in the file. We can search and
replace for the domain name to be redirected since they're all the same. We'll
search for the domain and replace it as the first thing on its own line with:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;:%s:www.myfavoriteapartment.com:\rwww.myfavoriteapartment.com:g
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;%&lt;/code&gt; does all lines in the file; the &lt;code&gt;s&lt;/code&gt; is for search; the &lt;code&gt;\r&lt;/code&gt; is vi-speak
for newline; and the &lt;code&gt;g&lt;/code&gt; at the end is for global so it'll process the whole
line rather than the first match it finds on that line. By habit I use &lt;code&gt;:&lt;/code&gt; for
the separator in the search and replace command; you can also (and most people
do) use &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="4" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-4.png"&gt;&lt;/p&gt;
&lt;p&gt;Ok, we're getting there. Next let's get rid of the double commas and – since
that's always at the end of the redirect destination – put a semicolon at the
end as Nginx requires:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;:%s:,,:;:g
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;img alt="5" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-5.png"&gt;&lt;/p&gt;
&lt;p&gt;Now we need to deal with the first column of data from the spreadsheet – the
name of the location. We don't need this information for the map file so let's
just delete it. Each location name is on its own line at this point and ends
with a comma. So let's search for lines ending with a comma and delete 'em:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;:g:,$:d
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In this case, the &lt;code&gt;g&lt;/code&gt; is for a global operation on all lines; the &lt;code&gt;,$&lt;/code&gt; matches
all lines with a &lt;code&gt;,&lt;/code&gt; at the end of a line (the &lt;code&gt;$&lt;/code&gt;); and the &lt;code&gt;d&lt;/code&gt; is for delete.&lt;/p&gt;
&lt;p&gt;&lt;img alt="6" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-6.png"&gt;&lt;/p&gt;
&lt;p&gt;All we have left is to replace the single remaining comma on each line that
separates the source and destination URLs. Nginx requires that the separation
here be one or more spaces. I typically use a tab (although I probably shouldn't
– it makes the map file look messy) so let's do this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;:%s:,:^I:g
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;^I&lt;/code&gt; is the tab character. Nowadays you can usually just press the tab key
and vi will put in the &lt;code&gt;^I&lt;/code&gt;, but in the old days you had to do &lt;strong&gt;control-v&lt;/strong&gt; and
&lt;strong&gt;control-i&lt;/strong&gt; to get a tab.&lt;/p&gt;
&lt;p&gt;&lt;img alt="7" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-7.png"&gt;&lt;/p&gt;
&lt;p&gt;And that's it.&lt;/p&gt;
&lt;p&gt;&lt;img alt="8" src="http://c2888087.r87.cf2.rackcdn.com/some-vi-tips-8.png"&gt;&lt;/p&gt;
&lt;p&gt;Now all we got to do is save the file and do a copy and paste of its contents
into the map file on our production configuration. Of course, you'll want to
scroll through the file and make sure it looks correct and do a &lt;strong&gt;nginx
configtest&lt;/strong&gt; before &lt;strong&gt;nginx reload&lt;/strong&gt; to make sure it's valid.
&lt;img alt="atom" src="http://c2888087.r87.cf2.rackcdn.com/eol.png"&gt;&lt;/p&gt;</summary></entry></feed>
