In the course of a discussion with a handful of my colleagues this week, we identified a specific problem that I wanted to solve:
- launching instances in Amazon EC2 using human readable aliases instead of the 8-character hexadecimal identifiers, like ami-123456ab
I wanted a precise wrapper for ec2-run-instances, identical in every way, except I wanted to support mnemonic terms like "lucid", or "natty amd64" or "oneiric daily" instead of "ami-5ec3ba0c".
With the help of Scott's new Ubuntu Cloud Images API, this problem really comes down to:
- Scanning argv for such aliases
- Retrieving the manifest of available images from the web service
- Applying a set of rules to identify the "right" or "best matching" AMI
- Replacing the alias values in argv with the identifed AMI
- Calling execv() on the argv arguments and the right AMI
It was easy enough to install Go on Oneiric. Just sudo apt-get install golang. And so I put a good hour into writing the tool. I'm more than a little ashamed to admit how badly I struggled for the first hour. And so I gave up (for a bit) and just banged the tool in Python. In very little time, I had it working like a charm.
It was at that point (10pm?) that I then I ported it to Go. This was a much more pleasant experience! Rather than trying to design and implement a new tool in a new language, I was simply translating the logic I already had, into a new language. The problem was more about learning the language's semantics, than taking on the entire project at once. I learned a ton about Go in the process, and now have a more informed opinion on the technology (which I'll share below).
What you have here is a small program (under 200 lines) identical in behavior and functionality, which is implemented in both Python and Go! So open up your favorite multiplexing terminal or editor and split your screen vertically (Ctrl-F2 in Byobu), and check out these two programs side by side!
- It has a switch statement! Woohoo, take that, Python! :-)
- #!/usr/bin/gorun -- This is awesome! You have to jump through a couple of minor hoops (add a PPA, install a package, make a symlink by hand) right now, but if you do, you can just put that hash-bang at the top of your code and skip the compilation/linking steps, and run your code just like you would with Python/Shell/Perl. This provides a nice bridge, letting you do your fast, iterative development in an interpreted style language, but you can always compile your code too, for stripped, fast binaries.
- gofmt is also very cool! You can run this on your source code and it will normalize your indentation and formatting exactly to the language's recommended standard. I don't necessarily agree with all of the formatting rules, but that doesn't matter, since I can write how I want, and just run gofmt on my code before committing. It would be sweet to have one of these for every language!
- A function's return type is specified in the top of the function definition itself (albeit in a strange location on the line). I like this about C, C++, and Java and tend to miss it when reading someone else's code in Python, Perl, or Shell.
- _ is used, by convention, as a throwaway variable. It's kind of cool to just sort of have one of those around. I'm sure there are purists who'd disagree, and this is super minor, but I found it quite convenient.
- I was totally frustrated with how difficult "Go" is to search for. Try Googling for "go array example". Hint: you can get better results if you search for "golang array example", but it took me a while to figure that out (you're welcome!). I found this painfully ironic that it's Google who's behind Go in the first place :-) I just wish everyone would start calling it Golang (like Erlang) rather than Go. Side note: I also wish every language had official documentation and user contributed examples as useful and navigable as Php.net ;-)
- I found it very strange and counterintuitive to put variable type declarations after the variable name. So you would say "i int" rather than "int i". And an hash with strings as keys and integers as values would be "myhash map[string]int". To me, it was just a quirk and once I did it a few times, I got a little more used to it. This is really a very minor psychological hurdle, in the end.
- As cool as the gorun utility is, eventually, you'd probably want to eventually compile your code. As far as I can tell, the result is a static binary after compilation and linking. While this has some nice advantages for mobile, embedded devices that ship complete system/OS images, it seems like a huge nightmare for a distribution. If a bug or vulnerability is found in the http library I used in my example above, I would have to recompile my code with the new library. If Ubuntu shipped 100 such programs statically compiled, we'd have to recompile and SRU each and every one :-( So I'm a little uneasy on this point, until I understand a bit better how to use shared libraries and dynamic linking in Go.
- Perhaps the most unattractive aspect of Go I found was how completely unreadable panics (tracebacks) are. They look way too much like a Java shart, for my liking. I mean, it's better than a totally useless C segmentation fault, but traceback readability is really where Python shines, to me. Here's the first one I got from my program, last night: http://paste.ubuntu.com/658294/. 61 unreadable lines of text (which scrolled off of my screen) to tell me that I overran by buffer. Moreover, the linguistics here leave a little to be desired: "panic: runtime error: slice bounds out of range". I'm hoping that these are designed to be machine readable, and that a good debugger exists that can make more sense of these?
- The keyword "nil". Seriously? I mean, I'm even a lover of Latin, but this really demonstrates the length to which programming language authors go to differentiate their product. Just seems a little vain to me. What's wrong with null? :-) Okay, that one is totally comical. I don't actually care. It just seems silly to me.
A totally awesome learning experience that I recommend to any and all of my developer colleagues! It's been several years since I learned a new programming language. A big, sincere thanks to Gustavo for insisting that I give Go a try.
At this point, I don't really have any plans to start rewriting vast swaths of my Shell or Python code in Go. But the next time I encounter a problem that I really should solve in C, I think I'll try doing it in Go first. I don't yet quite see the massive advantages of Go over Shell or Python for distribution level development (which is my job at the end of the day), but I think I can see a few opportunities when we need something like C, C++, or Java.
So what about the tool you wrote???
So glad you asked! Actually, it's landed in Oneiric, as of today! It changed names, from ubuntu-run-instances, to ubuntu-ec2-run. And Scott Moser rewrote it to his liking, as maintainer of the cloud-utils package and the web service API it uses.
He opted for the Python implementation, though for two reasons... First, Go would have introduced a new build-dependency, and since cloud-utils is in Main, that would have required an MIR for the golang packages which are currently in Universe. Second, the aforementioned static binary would likely prove difficult to maintain, from a distribution perspective. We'd like to investigate this aspect of Go a bit more before heading down this route.