Puppet: System Administration Automated

LinuxWorld 2008


I'll be speaking at LinuxWorld this year, on Puppet of course. I'll be giving a reprise of my Velocity talk, I expect, but since it's the best Puppet talk I've ever given but was the first time I'd given that particular talk, I think this one will be even better. And, unfortunately, the Velocity talk wasn't recorded, so your only chance to see it is to show up at LinuxWorld.

If you're in SF and want to meet up, ping me and we can plan something on Thursday night. Maybe drinks at The Irish Bank?

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Wed, 30 Jul 2008 | Tags: , , ,


Content-Type Negotiation and REST


I'm working on a few enhancements for the development branch in Puppet, and in particular trying to add support for negotiating content-type. Strangely, I can't find much evidence of people talking about doing this. Or rather, I can find lots of people talking about it, but not many real implementations.

Most people, including Rails, seem to take the easy way out of just using file extensions. I don't want to do this in Puppet because I think file extensions are essentially evil, since you're embedding metadata into your file name, but also, it doesn't work because we need to be able to transfer files directly -- if the file ends in .pdf, we want to be able to transfer it even though we don't necessarily know anything about PDFs.

So, I want to use the content-type header (and apparently the Accept header, for clients). I was hoping to just be able to crib on someone else's work, but I guess I can't.

Most of it's going to be pretty easy -- do a bit of metaprogramming to convert the type to methods (e.g., to_xml and from_xml). However, we need to be able to list supported types, so I've been trying to decide how to provide a list of supported types. I guess the easiest way is to continue with the metaprogramming and just convert back from those method names to a type name. E.g., if your class has to_xml, from_xml, to_json, and from_json methods, then it supports the json and xml formats.

Also, I should probably be using the word representation instead of format, but, well, that's a lot of typing. :)

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Mon, 21 Jul 2008 | Tags: , , ,


OATV StartupCamp and FooCamp


This post won't do nearly the justice that the last four days deserve, especially since I've barely posted this summer, but I figure better to start small than not at all.

Andrew and I just got back from OATV StartupCamp (as opposed to StartupCamp, which I've also been to ) and FooCamp. It was a great weekend -- we met a lot of great people at both events, including my personal hero Kathy Sierra, and we got a lot of advice and information on our startup.

The only thing tangible we really took away is that we laser-etched our laptops and my moleskin notebook (it went first as a guinea pig). Hopefully I'll take another picture soon; that one didn't exactly turn out. I'll probably end up scraping some color into my laptop or something, since the cuts don't show up as much as I'd like. If we ever do a conference booth, that's what we're going to show up with -- a laser etcher.

The etcher is owned by Make Magazine, but I was shown how to use it by Eric Wilhelm from Instructables. Yes, it was sweet.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Mon, 14 Jul 2008 | Tags: , , ,


Preparing for Velocity and the Puppet MiniConf


I finally got access to my blog again (laziness combined well with the SSH key exploit to lock me out for a bit), and it's long past time I posted an update.

Andrew and I will be at Velocity Monday and Tuesday, and I'll be giving a presentation there on Tuesday afternoon. We then have to hurry downtown to CloudCamp, and then Wednesday will be at Structure. Adam Jacob says we should all be sure to attend the Velocity Ignite! on Monday night, too.

Then, because hey, free time is for losers, Jay Aras is hosting a Puppet MiniConf on Thursday

Andrew has been working steadily on getting new Puppet logos going (you should see a new favicon on the Trac site), and he promises to update our CSS in time for the conference.

And, last but not least, we'll be showing up with fancy new Puppet t-shirts. We'll be trying to figure out over the course of the week how to dole out shirts, so if you've got any good ideas, send me a note.

Anyway, I hope to see many of you over the course of the week, and I also hope to be updating far more often.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Sat, 21 Jun 2008 | Tags: , , , , , , ,


Testing Cached Values


I'm currently in the middle of the largest refactoring effort I've ever done, while simultaneously learning tons about how to be a better developer. I'm constantly feeling a bit overwhelmed, a bit behind the curve, and like someone's going to look at my code one day and say, "hey nimwit, you just pull this string here and suddenly 2/3 of your code just goes away."

However, one thing I've really grokked recently is that if you're fighting your tools too much, you are on the wrong track, and one way in which it seems I'm constantly fighting my tools is the combination of cached values and testing.

For instance, I have an HttpPool class that knows how to set up Net::HTTP instances with all of the SSL information they need. This class caches SSL information like the certificate and key, so that each connection doesn't hit the disk for this info, which is obviously a pretty decent use of the cache. This caching generally takes this simple form:

def ssl_host
    unless defined?(@ssl_host)
        @ssl_host = Puppet::SSL::Host.new
    end
    @ssl_host
end

Yes, you could just do @ssl_host ||= Puppet::SSL::Host.new, but I've gotten some weird behaviour out of that in the past, and it also throws a warning for undefined variables.

So anyway, this works fine in unit tests; I test the caching, and I use mocks everywhere else. When I get to integration tests, though, it starts to really hurt, especially since I try not to do much mocking in my integration tests.

For instance, say I've got two unrelated tests that do an ssl connection. They each create some certificates, start a daemon, and try to connect. In this situation, the first one caches the ssl information, and the second one uses the cached values instead of its own new certificate, and you get invalid certificates.

After talking to Rick Bradley on #nashdl on IRC (gotta represent!), I've decided on at least an initial course of action. I'm going to create a module that provides both a caching and cache-clearing interface; anyone using cached values would use this caching interface instead of caching the values themselves, and the module itself would give you a single point of entry for clearing all caches on the system.

My first instinct was to create a Cache class or struct and keep a list of them in the caching module, so they can be cleared as necessary, but my recent work with TTLs has made me realize that time-based concepts of cache dirtiness are much better than actively cleaning.

So now, I'm thinking that the caching module will just have a timestamp, and only cached values created after that time will be valid. Before the Cache struct returns any values, it will always check that time, and it will know whether the value it has is still good or should be discarded.

This keeps us from maintaining global lists of caches, and it also makes clearing caches insanely cheap -- just reset a timestamp. Given that Puppet already sometimes has onerous memory requirements, I also like that it makes the caches themselves more likely to get garbage collected, since only the caching instance ever knows about the actual cache itself.

So my new method would look something like this:

def ssl_host
    cache(:ssl_host) { Puppet::SSL::Host.new }
end

That bit with the block is something I just thought of; if we've got a value, it's not called, but if we need a value, it's there for us. Pretty sweet.

Now just to implement it.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Wed, 07 May 2008 | Tags: , ,


Jay and I converge on testing


Those four people who have been reading this blog for a while know I've been struggling to think and program like Jay Fields. In particular, he seems to have presented a few rules in the past that don't like to be used together:

Now, let's do a simple combinatorial exercise, and put these three rules together:

It's pretty clear that, like the old saw about programming ("All programs can be reduced to one line of code with a bug in it"), Jay is pointing us toward tests that can largely only be one line of code. Yeah, I know sometimes setup methods don't involve any mocking, but often they do.

And, since your tests can only be one line of code, they can't test very much, which means that all of your methods need to also be one line of code, else they aren't testable. (Yes, I'm being a touch extreme here, but that is where the arrow is pointed, anyway.)

You can see how this would kinda drive me bonkers. Some local dev friends have been trying to help me see the error of my ways, mostly so my code would stop looking like such crap; I've learned a helluva lot in the last 8 months or so, and most of it has actually made my code look more like Jay would recommend. I will say it's blindingly obvious Jay is doing internal development at enterprises, rather than developing as part of a consistent team producing software that is distributed to the wider world.

But one can only go so far, and the three rules above, in combination, are just way too far. I've often kind of sputtered expasperatedly at Jay's posts, especially his announcement of his new testing tool, expectations. Again, I can kind of see where he's going with that, but you've got another thing coming if you think I'm using it, especially given how happy (at least, relative to test/unit) I am with RSpec.

Also, I think it's just stupid having all setup code inline. DRY ("don't repeat yourself") is just as true in your test code as anywhere else, and having a maintainable test code base is, IMO, more important than having your normal code base be maintainable, because tests are kind of unnecessary. If you have good, readable, maintainable tests, then people who contribute will also contribute tests. If your tests are all 50 lines long and have lots of repetition, then 1) you've got 5x the amount of code you should, which is wicked expensive, and 2) you've got so much code no one will look at it. Yay, never getting patches with tests in them. My favorite example of this is Steve Yegge's rant Code's Worst Enemy; he describes his 500k line Java project with no tests, which is a lot of code but much less code than if it had tests. I've experienced in Puppet that test code seems to be much harder to maintain that normal code (although maybe it's just own crap test code, not normal test code), and having 5x test code than normal code would make me just quit writing unit tests entirely.

So, I am absolutely overjoyed to announce that Jay has changed one of his rules: He now recommends stubs over mocks. This is clearly just for setup code and such, but it's a big step. He even goes into using stub_everything, which I find is the only way to build tests that aren't fragile. For instance, say you start with this code:

class MyClass
    def go
        start()
        finish()
    end
end

describe MyClass do
    before
        @me = MyClass.new
        @me.stubs(:start)
        @me.stubs(:finish)
    end

    it "should start when going" do
        @me.expects(:start)
        @me.go
    end

    it "should finish when going" do
        @me.expects(:finish)
        @me.go
    end
end

Now you find you need a validation method, so you add this test:

it "should validate when going" do
    @me.expects(:validate)
    @me.go
end

Update: Fixed code to actually call @me.go in the validate test.

Oops. Now your single test passes, but your two old tests break, because you were only stubbing start and finish, instead of using stub_everything. Your setup code needs to be modified to take this new call into account (or, if you're Jay, you need to modify every test in your suite; yay). This comes up constantly. If you specifically mock or stub methods during setup, then you are almost guaranteed to have cascading failures when you expand your code.

Anyway, the point is, if I tried to follow Jay's rules, then the above trivial change -- I add one line of code to a very simple method -- would result in me adding a test for that line, plus at least one line of code in every other test in that suite. Instead, if I use stub_everything, then I add my new test and I'm done. (Well, kind of; notice I'm not actually testing the order of the method calls, which is actually pretty tough.)

My recommendation is to read Jay, since he's clearly thinking and talking about aspects of testing that not many others are, but read him with a skeptical eye, and be willing to say "That's just nuts!" and write your own relatively abstracted test code. And if you're working with people who can't think to look at their setup code when a test fails, then you need to find a different job.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Tue, 06 May 2008 | Tags: , , , , ,


Ruby has a distribution problem


I've been doing a better job of reading development books recently (e.g., Domain Driven Design), and something has really begun to stick out at me. There seems to be a split between those developers who write software that is expected to run in one place and those who write software that is expected to run in many places.

If you, as a developer, know that your software will really only be installed at a single customer (whether that customer is your employer, a consulting client, or whatever), then your life is drastically easier -- you don't usually have to worry about cross-platform issues, and you don't have to worry about different users having different needs, because you only have one user.

Obviously there's no inherent problem with having the simpler life of a developer with only one user, but it seems to me that the Ruby community is, as a group, largely adopting that perspective as the default. This is worrying to me, because I'm building an application that I expect to be installed in thousands of locations (in fact, it's probably already installed in thousands of locations). I'd like to take as much advantage of existing Ruby code as possible, but it's not exactly easy.

For example, rubygems (probably my least favorite ruby software of all time) basically require that you always try to load them, because their design stupidly requires that you know whether a given piece of software is installed via rubygems or some other mechanism. For instance, if you've installed the Facter gem, then this code doesn't work:

ruby -rfacter -e 'Facter.to_hash'

Instead, you have to do this:

ruby -rubygems -rfacter -e 'Facter.to_hash'

The reason is that rubygems installs in a location that Ruby doesn't search by default. The reason for that is that apparently this one guy, somewhere, wanted to have multiple versions of a given package installed at once. Who wants this? Let's just say it's not the guys who are distributing hundreds and thousands of copies of their software.

The truth is, most Rubyists don't even seem to use gems this way -- they tend to create a vendor subdirectory in their project, and then install their gems there. This is a clear example of how little they expect to have their projects distributed. These gems might be compiled, they might conflict with installed software, they might require installed software -- you have no idea, because it's an entirely separate repository of packages.

This is basically anathema to how I think about management, yet it's the standard, recommended practice in the Rails community, because it makes it easy to "guarantee" behaviour in a given environment. Of course, your guarantee is only good if no one ever tries to run the software anywhere except an exact duplicate of where you run it.

I tried to have a conversation about this at the Ruby Hoedown last year -- my claim was it was difficult to turn a Rails project into a native package, especially with the tendency toward requiring all kinds of random gems. Quite a few people kind of stared blankly at me and said, multiple times, "I just put it in vendor." Since then, this has become my go-to phrase for describing the Ruby way of solving distribution problems: "I just put it in vendor." I keep waiting for someone to try to put their kernel or web browser in vendor: "We only support the Firefox copy in vendor, sorry."

I don't know if other communities are any better at this. From what I can tell, this is basically how the Java community behaves, too. They have pathologically bad distribution systems, and as a language it seems to be most influenced by consulting shops developing huge, worthless software projects for large enterprises, rather than developing distributed applications that will be installed in thousands of locations.

I'd like to think that Puppet would have some counter-affect to this. It's one of the largest and most sophisticated publicly available Ruby projects, it's already installed in at least hundreds and probably thousands of places, and it does a pretty good job of working nearly everywere. However, I keep getting blank stares when I talk about this with other Rubyists, half the time I'm called a troll for even bringing it up, and when I explain why Puppet exists to most Rubyists, they just say, "I just put it in vendor", or, maybe, "Why not just use Capistrano?" To that I ask, how do you install Capistrano, but you know what they say to that.

I think Rails is a big part of the problem. Rails is clearly created by a company that will never distribute its software, and the Rails philosophy is again almost pathologically opposed to the idea of turning your software into a package. Imagine trying to make a Rails project LSB compliant -- your database.yaml file would need to be in /etc, your log directory would need to be in /var, and your actual code would need to be in /usr. There went all of your fancy Rails "convention over configuration", and you're suddenly fighting Rails instead of using it, and everyone you ask for help just tells you to "put it in vendor".

I'm looking at creating a new application that I'm planning on distributing, and one of my big goals is to be able to distribute the core in one package and various additional pieces of functionality as separate packages. I'll need to simultaneously support as many of my customer platforms as I can and provide a consistent operating environment for my packages. The only way to do this is to have supported operating environments with well-defined dependencies, such as you can almost trivially build in Debian or Red Hat.

For those of you who are thinking, "you could just put it in vendor", or "you could at least use gems", No, I couldn't. Take a trivial example: Say I want to use RRD support in my application (which is likely, in this case). There is Ruby support for RRD, but not in Gem form. Even if there were a gem, though, it would require a native RRDTool package, and, of course, Gems can't specify dependencies on native packages, so I'd be telling my customers, "well, install X gems and Y packages".

Instead, if I use native packages (say, those for Debian and Red Hat, to cover most cases), I can define clear dependencies for all cases. I know Debian provides everything I need, and in the rare case it doesn't, I can provide my own apt repository (and the same for yum). Gems, on the other hand, can really only do Ruby stuff. No, I don't actually want to put glibc in vendor, thanks.

I don't see a solution to this, other than getting more Rubyists distributing their software, but I'd really like to see this issue begin to be approached by the community. I feel like a wolf howling in the wilderness at this point, and if often feels like I'm fighting against my community in order to produce software that hundreds or thousands of people will install, as opposed to just use over the web.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Mon, 05 May 2008 | Tags: , , ,


Git branch in your bash prompt


Kevin Barnes has posted his mechanism for getting the current branch of the git repository into his bash prompt.

He mentions color in his article, and it turns out I'm the person who added the color, so I figured I'd post my version.

Here are the functions I have:

git_current_branch()
{
  git branch 2>/dev/null | sed -n '/^\*/ s/^\* //p'
}

git_display()
{
  br=$(git_current_branch)
  if [ -n br ]; then
      echo $br | BRANCH="$br" GIT_COLOR=$(git_color) awk '{if ($1) { print ENVIRON["GIT_COLOR"] ENVIRON["BRANCH"] " " } }'
  fi
}

git_color()
{
    git status 2>/dev/null | grep -c : | awk '{if ($1 > 0) { print ENVIRON["ORANGE"] } else { print ENVIRON["PINK"] } }'
}

And then here's my prompt:

title="033]0;h:W007" PS1="$title[$(git_display)$GREENw$NOCOLOR]nu@h("'$?'") $ "

First, the git bits. As Kevin mentions, I color the branch name; I use orange if I've got modified files, and pink if I don't (these are names that I map elsewhere to terminal codes). The three functions provide the three, um, functions: See what branch I'm on, see whether there are uncommitted files, and colorize the branch name.

Now, the bash bits.

First, you'll notice I have a multi-line prompt. I first started this when I switched to a color prompt, because for a while there bash didn't like the hidden characters that add color. I got to choose between a multi-line prompt, or a prompt that wrapped in broken ways. Since I really only wanted color in the path, I put that on the upper line, avoiding most of the wrapping problems. It was worth it, because (especially when I was still a sysadmin by trade) having color, almost any color, in the prompt makes it easy to pick out my commands from command output.

Now that I've had my prompt this way for about 4 years, I'm pretty fond of it. I think the bash wrapping problems are mostly fixed now, but I'm keepin git.

The $title sets the terminal title (which is slightly useful).

So, that's how I add the git branch, colored by whether I have uncommitted files, to my prompt, with a bit more prompt info thrown in for kicks.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Mon, 05 May 2008 | Tags: , ,


Reductive Labs should fill out the Little 4


Michael Cote and John Willis have been talking for a while about the Little 4 in management software, and it looks like Qlusters is no longer on the list.

I'd love to be able to comfortably say that Reductive Labs deserves to fill that fourth slot (you can have your say too). The truth is, of course, that all of those companies are far larger than Reductive Labs, and they've all successfully gotten investment while we have not (although we haven't tried all that hard). The products of the companies are pretty dissimilar, too -- they're mostly more focused on monitoring rather than what I would call management, as far as I can tell.

On the other hand, Puppet has a lot of traction, and is a clear leader in its space. We've been profitable since almost the beginning (which is to say, profitable enough to pay my meagre wages), and we've got a great and growing community. Now that Andrew Shafer has joined the company full-time as a partner, I do think we're going to start growing, and it's well-timed in terms of how the community is developing.

I do hope we grow this year, but I don't really know how we will. I'm still considering how hard we should be seeking investment, but it seems that VCs are pretty uninterested in infrastructure (or maybe they're just uninterested in me). Really, I'm hoping I can just get a big enough customer base that Andrew and I can build a bigger development team and start doing some of the almost-obvious but really interesting projects to enhance the Puppet ecosystem, like change control applications.

But the summary is, I want to deserve to be in the Little 4, and I think Puppet is popular enough that we just might, but there's still lots more to do.

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Mon, 21 Apr 2008 | Tags: ,


Closing the Whisky Experiment


It's been a bit more than a month since I started my Double Blind Whisky Taste Test, and I've decided it's time to call it. I've drunk a scarily large amount of the whisky, but science is a harsh mistress and she will not be denied. Those of you who are sitting at home complaining that I haven't fixed your bug, it's because I was drunk from too much experimentation.

I've posting my reviews to Twitter, which in some ways adds a third blindness: I didn't know how I reviewed a given Scotch previously.

I'm writing this having not yet looked at the key that will tell me which Scotch is which. Here are my reviews so far (using my arbitrary decanter names). I'm actually taking a small drink of each as I write, just to remind myself and do one last review. These reviews are in order of perceived strength, so that I'm going from weakest to strongest.

My comments will also try to mention what I would do with these whiskies; this is because I've still got decanters with a lot of whisky in them, and really, I probably won't drink them all straight, which is all I would normally do with a single malt.

My primary goal in this review was to find that single peaty Scotch that I could put in my liquor cabinet (currently virtual, since the liquor is kind of scattered wherever there is storage).

Okay, so now we know what I think about each of the whiskies, it's time to get the key out and see what is what. First, though, I will give my guesses as to what is what. Here's my estimate:

And now, without further ado, here's the real key:

So, I got two correct but mixed two of them. The clear winner in the "best peaty Islay malt" category is Lagavulin, which is unfortunate, since it's about $20 more per bottle than any of the others (assuming you can find Caol Ila, which I had to buy at the Duty Free in Melbourne). The Caol Ila gets an honorable mention as the easiest to drink.

Looks like I've been harshing on the Ardbeg this whole time, and it's the Laphroaig that has the sourness I didn't particularly like.

And with that, the experiment closes. Now I can finally drink some of the Caol Ila 18yr I have -- I couldn't drink it while the experiment was on, since it would likely have clued me into which decanter had the 12yr.

In the name of Science (and great whisky), Luke

[1]This decanter has a square bottom and a kind of oval top, so it looks kind of like a D sitting on its back. Really, though, it gets its name from the movie 8 Mile -- I have a friend from Detroit who said it was an accurate portrayal of Detroit, and ever since I've harassed him about how Eminem calls the city "The D".

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Sun, 13 Apr 2008 | Tags: ,


[1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27  >>