Monday, October 20, 2014

Serving up static files from Go / Gorilla mux

I've been playing with Go lately, just serving up a non-trivial API to kick the tires a bit.

One of the things I wanted to do was use the Swagger (https://helloreverb.com/developers/swagger) framework to document that API (I've done this before in Ruby with grape and it was a breeze).  (BTW - it was considerably more tedious to write the specification (even using the awesome https://github.com/wordnik/swagger-editor) but that's a different post...)

I needed to host the swagger-ui files (which they helpfully bundle into a 'dist' folder) and serve them up from my Go application.  The directory structure looked like this:

├── server.go
├── swagger-ui/
│   ├── css/
│   ├── images/
│   ├── index.html
│   ├── lib/

It ended up being fairly trivial to serve static files but took a little experimentation to get the Gorilla mux piece working correctly.  Here's what I ended up doing, inside 'server.go'

func main() {
router = mux.NewRouter()
router.HandleFunc("/", indexHandler)
router.HandleFunc("/api-docs", apiDocHandler)
router.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir("./swagger-ui/"))))
http.Handle("/", router)
err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", router)
if err != nil {
log.Fatalln(err)
}
}

Important parts to realize:
1) you should use the PathPrefix so that the mux picks up everything inside that folder
2) you need to strip out the path prefix so that your files are served up from the correct directory, otherwise you'll end up making requests for /swagger-ui/swagger-ui/ which won't work.

Tuesday, June 3, 2014

Docker and Google Compute Engine

Participated in a hackathon (hackforchange.org) this past weekend and there were some Google credits available so I figured I'd see how easy it was to get Docker running on Compute Engine. Turns out, very easy thanks to this post from Google: Containers on Google Cloud Platform.
The only real gotcha that I ran into was that I ran out of disk space almost immediately after installing Docker registry. What?! Yup, since Compute Engine starts you out with an 8GB disk you need to:
  1. Provision a bigger disk and attach it
  2. Move Docker onto the bigger disk
Attaching more disk is thoroughly documented by Google (see: Disks) but it took quite a bit of searching to figure out how to move Docker so I figured I'd post with what I did.

We're going to assume I added a bigger disk and mounted it to /docker-storage.
  1. Stop docker - 'service docker stop'
  2. Edit /etc/default/docker - you're looking for the line that sets DOCKER_OPTS.  You want to add a -g flag and where you want Docker to run from.  In my example, I prepended
    -g=/docker-storage/docker
  3. Move the files from the default Docker location (/var/lib/docker) to the new mount:  'cd /var/lib; mv docker /docker-storage/'
  4. Restart docker - 'service docker start'
That should be all it takes.

Friday, May 2, 2014

Quick tidbits

One of the reasons I like Project Euler is that it gives you a chance to expand your knowledge of whatever language you're using...

For instance, today I learned about Fixnum#fdiv (forces float division instead of integer division) in Ruby:
2/4       # 0
2.div(4)  # 0
2.fdiv(4) # 0.5
Sure there's a ton of other ways to do it, but this was succinct and to the point.

And the RSpec =~ operator for array equality - it's basically the same thing as ==, except that order doesn't matter.

[1,2] == [2,1] # false
[1,2] =~ [2,1] # true
Two simple things that I'd not had a reason to use before. Will I use them all the time, nope. But I'm glad to have found them.

Monday, January 27, 2014

Logging sql queries from korma

Wanted to see what sql is actually getting executed from a webapp running korma - seems fairly easy since I stumbled upon blackwater.
Inside your project.clj, add blackwater and clj-time...
(defproject foo "0.1.0-SNAPSHOT" 
  :description "Zap bug tracker"
  :dependencies [
    ;; other deps omitted
    [blackwater "0.0.9"]
    [clj-time "0.6.0"]]
Inside your ns:
(ns.foo
  (:use korma.db korma.core)
  (:require [black.water.korma :refer [decorate-korma!]]))

(decorate-korma!)
You'll get nice output like:
SELECT "project".* FROM "project"| took:31ms
SELECT "project".* FROM "project" WHERE ("project"."id" = ?)| took:4ms

Reloading lein repl

So, I'm working through the book Seven Web Frameworks in Seven Weeks from the awesome Pragmatic Programmers and I'm on the Clojure section (keep in mind that I don't know Clojure ^^) but quitting/restarting to get the repl to pick up changes just plain sucks.

Here's a solution I found on SO:

Add tools.namespace to your project.clj dependencies.

e.g.
:dependencies [
  # ...
  [org.clojure/tools.namespace "0.2.4"]]

After you start up the lein repl:
user=> (use '[clojure.tools.namespace.repl :only (refresh)])
nil

... make some changes to a clj file ...

user=> (refresh)
:reloading (hello.core hello.core-test zap.models)
:ok
It's not 100% automatic, but it beats quitting/restarting. Though it does appears you have to type that use statement every time after you refresh...

Tuesday, June 11, 2013

How do I debug/run an app in Android Studio to a Nexus 7

So, it turns out developer mode is a little tricky to find on a Google Nexus 7. Go to "Settings" -> "About Tablet" then click EXACTLY 7 TIMES on the "Build Number". (After three presses, you'll get a message "You are four steps from being a developer”) This will open up "Developer Options" in the menu. Choose that. Enable "USB debugging"

Thursday, May 16, 2013

Chrome - Network error - Failed

So, let's say you're downloading a large file in Chrome and it fails... what do you do?

Well, if you're at home or work or somewhere with a fast network, you probably just start the download again (there's no resume functionality).

But what if you're at a conference or coffee shop or somewhere without a fast internet connection.  Starting the download from the beginning could cost you a significant amount of time.
Instead:

Open your downloads folder (e.g. ~/Downloads in OSX if you haven't changed the defaults)
Locate the file you were trying to download, it will have the extension .crdownload
Rename the file, removing the ".crdownload" extension (e.g. myfoo.zip.crdownload should be renamed to  myfoo.zip)
Open a terminal window in the same directory
Use wget to resume the file (the -c flag will continue the download you're already started)
wget -c <url_of_the_file_you_were_downloading>