Commits

Kelsey Hightower committed 3a1077a

Move labs under docs

  • Participants
  • Parent commits ec3fc56

Comments (0)

Files changed (27)

 # Hands on Exercises
 
-- [Installing Go](installing_go.md)
-- [Hello World](hello_world.md)
-- [Code Walkthrough](pinger.md)
-- [Documenting with GoDoc](godoc.md)
-- [Package to convert CSV to JSON](csv2json_package.md)
-- [Testing csv2json ](csv2json_package_tests.md)
-- [CLI tool to convert CSV to JSON](csv2json_cli.md)
-- [Managing Dependencies with Godep](godep.md)
-- [HTTP API to convert CSV to JSON](csv2json_server.md)
-- [Testing csv2json-server](csv2json_server_tests.md)
-- [Cross Compiling](cross_compiling.md)
-- [Deploy with Docker](docker.md)
+- [Installing Go](docs/installing_go.md)
+- [Hello World](docs/hello_world.md)
+- [Code Walkthrough](docs/pinger.md)
+- [Documenting with GoDoc](docs/godoc.md)
+- [Package to convert CSV to JSON](docs/csv2json_package.md)
+- [Testing csv2json ](docs/csv2json_package_tests.md)
+- [CLI tool to convert CSV to JSON](docs/csv2json_cli.md)
+- [Managing Dependencies with Godep](docs/godep.md)
+- [HTTP API to convert CSV to JSON](docs/csv2json_server.md)
+- [Testing csv2json-server](docs/csv2json_server_tests.md)
+- [Cross Compiling](docs/cross_compiling.md)
+- [Deploy with Docker](docs/docker.md)

labs/cross_compiling.md

-# Cross Compiling
-
-## Requirements
-
-- gcc
-
-## Build the Tool Chains
-
-#### Change Directory
-
-    cd /usr/local/go/src
-
-#### Run
-
-    for os in linux windows darwin; do GOOS=${os} GOARCH=amd64 CGO_ENABLED=0 ./make.bash —no-clean; done
-
-
-## Cross Compile csv2json_server
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json-server
-
-#### Run
-
-    GOOS=darwin GOARCH=amd64 go build -o csv2json-server main.go
-
-
-## Exercise
-
-### Cross Compile for linux, windows, and darwin

labs/csv2json_cli.md

-# csv2json-cli
-
-CLI tool to convert CSV to JSON
-
-#### Create
-
-    mkdir ${GOPATH}/src/github.com/${username}/csv2json-cli
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json-cli
-
-#### Edit
-
-    main.go
-
--
-
-	package main
-
-	import (
-		"flag"
-		"fmt"
-		"log"
-		"os"
-
-		"github.com/kelseyhightower/csv2json"
-	)
-
-	var (
-		infile string
-		columns = []string{"Name","Date","Title"}
-	)
-
-	func init() {
-		flag.StringVar(&infile, "infile", "", "input file")
-	}
-
-	func main() {
-		flag.Parse()
-		if infile == "" {
-			log.Fatal("Input file required")
-		}
-		f, err := os.Open(infile)
-		if err != nil {
-			log.Fatal(err)
-		}
-		defer f.Close()
-		jsonData, err := csv2json.Convert(f, columns)
-		if err != nil {
-			log.Fatal(err)
-		}
-		fmt.Println(string(jsonData))
-	}
-
-#### Edit
-
-    ${HOME}/famous-gophers.csv
-
--
-
-	Mac, 1947, The Goofy Gophers
-	Tosh, 1947, The Goofy Gophers
-	Samuel J. Gopher, 1966, Winnie the Pooh
-	Chief Running Board, 1968, Go Go Gophers
-	Ruffled Feathers, 1968, Go Go Gophers
-
-#### Build
-
-    go build -o csv2json .
-
-#### Run
-
-    ./csv2json -infile ${HOME}/famous-gophers.csv
-
-#### Version
-
-    git init .
-    git add main.go
-    git commit -m "first commit"
-

labs/csv2json_package.md

-# csv2json
-
-Package to convert CSV to JSON
-
-#### Create
-
-    mkdir ${GOPATH}/src/github.com/${username}/csv2json
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json
-
-#### Edit
-
-    csv2json.go
-
--
-
-	package csv2json
-
-	import (
-		"encoding/csv"
-		"encoding/json"
-		"io"
-	)
-
-	func Convert(r io.Reader, columns []string) ([]byte, error) {
-		rows := make([]map[string]string, 0)
-		csvReader := csv.NewReader(r)
-		csvReader.TrimLeadingSpace = true
-		for {
-			record, err := csvReader.Read()
-			if err == io.EOF {
-				break
-			}
-			if err != nil {
-				return nil, err
-			}
-			row := make(map[string]string)
-			for i, n := range columns {
-				row[n] = record[i]
-			}
-			rows = append(rows, row)
-		}
-		data, err := json.MarshalIndent(&rows, "", "  ")
-		if err != nil {
-			return nil, err
-		}
-		return data, nil
-	}
-
-#### Build
-
-    go install .
-
-#### Inspect
-
-    file ${GOPATH}/pkg/darwin_amd64/github.com/${username}/csv2json.a
-
-#### Version
-
-    git init .
-    git add .
-    git commit -m "first commit"

labs/csv2json_package_exercise_answers.md

-# Exercise Answers
-
-## Write an additional test
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json
-
-#### Edit
-
-    csv2json_test.go
-
--
-
-	package csv2json
-
-	import (
-		"bytes"
-		"io/ioutil"
-		"os"
-		"testing"
-	)
-
-	var columns = []string{"name", "email", "phone"}
-	var want = `[
-	  {
-		"email": "ken@google.com",
-		"name": "Ken Thompson",
-		"phone": "555-5550"
-	  },
-	  {
-		"email": "rob@google.com",
-		"name": "Rob Pike",
-		"phone": "555-5551"
-	  },
-	  {
-		"email": "robert@google.com",
-		"name": "Robert Griesemer",
-		"phone": "555-5552"
-	  }
-	]`
-
-	var input = `
-	Ken Thompson, ken@google.com, 555-5550
-	Rob Pike, rob@google.com, 555-5551
-	Robert Griesemer, robert@google.com, 555-5552
-	`
-
-	func TestConvertWithBuffer(t *testing.T) {
-		var b bytes.Buffer
-		_, err := b.WriteString(input)
-		if err != nil {
-			t.Error(err)
-		}
-		got, err := Convert(bytes.NewReader(b.Bytes()), columns)
-		if err != nil {
-			t.Error(err)
-		}
-		if string(got) != want {
-			t.Errorf("TestConvertWithBuffer(f, %s) = %s; want %s",
-				columns, string(got), want)
-		}
-	}
-
-	func TestConvertWithFile(t *testing.T) {
-		tf, err := ioutil.TempFile("", "")
-		if err != nil {
-			t.Error(err)
-		}
-		defer os.Remove(tf.Name())
-
-		err = ioutil.WriteFile(tf.Name(), []byte(input), 0644)
-		if err != nil {
-			t.Error(err)
-		}
-
-		f, err := os.Open(tf.Name())
-		if err != nil {
-			t.Error(err)
-		}
-		defer f.Close()
-
-		got, err := Convert(f, columns)
-		if err != nil {
-			t.Error(err)
-		}
-		if string(got) != want {
-			t.Errorf("TestConvertWithFile(f, %s) = %s; want %s", columns, string(got), want)
-		}
-	}
-
-#### Version
-
-    git add csv2json_test.go
-    git commit -m "add more tests" 

labs/csv2json_package_tests.md

-# Testing the csv2json package
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json
-
-#### Edit 
-
-    csv2json_test.go
-
--
-
-	package csv2json
-
-	import (
-		"bytes"
-		"testing"
-	)
-
-	var columns = []string{"name", "email", "phone"}
-	var want = `[
-	  {
-		"email": "ken@google.com",
-		"name": "Ken Thompson",
-		"phone": "555-5550"
-	  },
-	  {
-		"email": "rob@google.com",
-		"name": "Rob Pike",
-		"phone": "555-5551"
-	  },
-	  {
-		"email": "robert@google.com",
-		"name": "Robert Griesemer",
-		"phone": "555-5552"
-	  }
-	]`
-
-	var input = `
-	Ken Thompson, ken@google.com, 555-5550
-	Rob Pike, rob@google.com, 555-5551
-	Robert Griesemer, robert@google.com, 555-5552
-	`
-
-	func TestConvertWithBuffer(t *testing.T) {
-		var b bytes.Buffer
-		_, err := b.WriteString(input)
-		if err != nil {
-			t.Error(err)
-		}
-		got, err := Convert(bytes.NewReader(b.Bytes()), columns)
-		if err != nil {
-			t.Error(err)
-		}
-		if string(got) != want {
-			t.Errorf("TestConvertWithBuffer(f, %s) = %s; want %s",
-				columns, string(got), want)
-		}
-	}
-
-#### Run
-
-    go test
-
-
-Run with the -v flag for verbose output:
-
-    go test -v
-
-#### Version
-
-    git add csv2json_test.go
-    git commit -m "add tests"
-
-## Exercise
-
-### Write an additional test
-
-Create an additional test named TestConvertWithFile.
-
-#### Hint
-
-	func TestConvertWithFile(t *testing.T) {
-		tf, err := ioutil.TempFile("", "")
-		if err != nil {
-			t.Error(err)
-		}
-		defer os.Remove(tf.Name())
-
-		err = ioutil.WriteFile(tf.Name(), []byte(input), 0644)
-		if err != nil {
-			t.Error(err)
-		}
-
-		f, err := os.Open(tf.Name())
-		if err != nil {
-			t.Error(err)
-		}
-		defer f.Close()
-
-		// Test your results
-	}
-

labs/csv2json_server.md

-# csv2json-server
-
-HTTP API to convert CSV to JSON
-
-#### Create
-
-    mkdir ${GOPATH}/src/github.com/${username}/csv2json-server
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json-server
-
-#### Edit
-
-    main.go
-
--
-
-	package main
-
-	import (
-		"log"
-		"net/http"
-
-		"github.com/kelseyhightower/csv2json"
-	)
-
-	var (
-		columns = []string{"Name", "Date", "Title"}
-	)
-
-	func csv2JsonServer(w http.ResponseWriter, req *http.Request) {
-		jsonData, err := csv2json.Convert(req.Body, columns)
-		defer req.Body.Close()
-		if err != nil {
-			http.Error(w, "Could not convert csv to json", 503)
-		}
-		w.Write(jsonData)
-	}
-
-	func main() {
-		http.HandleFunc("/csv2json", csv2JsonServer)
-		err := http.ListenAndServe(":8080", nil)
-		if err != nil {
-			log.Fatal("ListenAndServe: ", err)
-		}
-	}
-
-#### Build
-
-    go build -o csv2json-server .
-
-#### Run
-
-    ./csv2json-server
-
-Test with curl:
-
-    curl -X POST http://localhost:8080/csv2json --data-binary @${HOME}/famous-gophers.csv
-
-#### Version
-
-    git init .
-    git add main.go
-    git commit -m "first commit"
-
-#### Vendor
-
-    godep save
-    git add Godeps
-    git commit -m "Manage dependencies with godep"
-
-## Exercise
-
-### Make the listen port configurable via the environment
-
-#### Hint
-
-    import (
-        "os"
-        "net"
-    )
-
-    func main() {
-        listenAddr := net.JoinHostPort(host, port)
-    }
-
-### Improve logging
-
-#### Hint
-
-    import (
-        "log"
-    )

labs/csv2json_server_tests.md

-# Testing csv2json-server
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json-server
-
-#### Edit
-
-    api_test.go
-
--
-
-	package main
-
-	import (
-		"bytes"
-		"testing"
-		"net/http"
-		"net/http/httptest"
-	)
-
-	type httpTestResponse struct {
-		statusCode int
-		body       string
-	}
-
-	var csv2JsonServerPostBodyTest = `
-	Mac, 1947, The Goofy Gophers
-	Tosh, 1947, The Goofy Gophers
-	Samuel J. Gopher, 1966, Winnie the Pooh
-	Chief Running Board, 1968, Go Go Gophers
-	Ruffled Feathers, 1968, Go Go Gophers
-	`
-
-	var csv2JsonServerWantBodyTest = `[
-	  {
-		"Date": "1947",
-		"Name": "Mac",
-		"Title": "The Goofy Gophers"
-	  },
-	  {
-		"Date": "1947",
-		"Name": "Tosh",
-		"Title": "The Goofy Gophers"
-	  },
-	  {
-		"Date": "1966",
-		"Name": "Samuel J. Gopher",
-		"Title": "Winnie the Pooh"
-	  },
-	  {
-		"Date": "1968",
-		"Name": "Chief Running Board",
-		"Title": "Go Go Gophers"
-	  },
-	  {
-		"Date": "1968",
-		"Name": "Ruffled Feathers",
-		"Title": "Go Go Gophers"
-	  }
-	]`
-
-	func TestCsv2JsonServer(t *testing.T) {
-		body := bytes.NewBufferString(csv2JsonServerPostBodyTest)
-		req, err := http.NewRequest("POST", "http://example.com/csv2json", body)
-		if err != nil {
-			t.Error(err)
-		}
-		w := httptest.NewRecorder()
-		csv2JsonServer(w, req)
-
-		want :=  httpTestResponse{http.StatusOK, csv2JsonServerWantBodyTest}
-		got := httpTestResponse{w.Code, w.Body.String()}
-		if got != want {
-			t.Errorf("Want: %#v, got %#v", want, got)
-		} 
-	}
-
-#### Run
-
-    go test -v
-
-Run using godep
-
-    godep go test -v
-
-#### Version
-
-    git add api_test.go
-    git commit -m "add api tests"
-

labs/docker.md

-# Deploy with Docker
-
-## Install Docker
-
-Installation Instructions: http://docs.docker.io/en/latest/installation
-
-#### Change Directory
-
-    cd ${HOME}
-
-#### Download
-
-    curl -o docker https://get.docker.io/builds/Darwin/x86_64/docker-latest
-
-#### Install
-	
-	chmod +x docker
-    sudo cp docker /usr/local/bin/
-
-#### Configure
-
-    export DOCKER_HOST=tcp://${docker_host}:4243
-
-
-## Create a Dockerfile
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json-server
-
-#### Edit
-
-    Dockerfile
-
--
-
-	# csv2json-server
-	#
-	# VERSION 0.0.1
-
-	FROM       ubuntu
-	EXPOSE     8080
-	ADD        csv2json-server /usr/local/bin/csv2json-server
-	ENTRYPOINT ["/usr/local/bin/csv2json-server"]
-
-#### Build
-
-    GOOS=linux go build -o csv2json-server .
-
-## Build the Docker Image
-
-#### Run
-
-    docker build -t ${username}/csv2json-server .
-
-#### Inspect
-
-    docker images
-
-## Run the Docker Image
-
-#### Run
-
-    docker run -d -P ${username}/csv2json-server
-
-## Testing with curl
-
-#### Inspect
-
-    docker port ${container} 8080
-
-#### Run
-
-    curl -X POST http://${docker_host}:${port}/csv2json --data-binary @${HOME}/famous-gophers.csv
-

labs/docs/cross_compiling.md

+# Cross Compiling
+
+## Requirements
+
+- gcc
+
+## Build the Tool Chains
+
+#### Change Directory
+
+    cd /usr/local/go/src
+
+#### Run
+
+    for os in linux windows darwin; do GOOS=${os} GOARCH=amd64 CGO_ENABLED=0 ./make.bash —no-clean; done
+
+
+## Cross Compile csv2json_server
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json-server
+
+#### Run
+
+    GOOS=darwin GOARCH=amd64 go build -o csv2json-server main.go
+
+
+## Exercise
+
+### Cross Compile for linux, windows, and darwin

labs/docs/csv2json_cli.md

+# csv2json-cli
+
+CLI tool to convert CSV to JSON
+
+#### Create
+
+    mkdir ${GOPATH}/src/github.com/${username}/csv2json-cli
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json-cli
+
+#### Edit
+
+    main.go
+
+-
+
+	package main
+
+	import (
+		"flag"
+		"fmt"
+		"log"
+		"os"
+
+		"github.com/kelseyhightower/csv2json"
+	)
+
+	var (
+		infile string
+		columns = []string{"Name","Date","Title"}
+	)
+
+	func init() {
+		flag.StringVar(&infile, "infile", "", "input file")
+	}
+
+	func main() {
+		flag.Parse()
+		if infile == "" {
+			log.Fatal("Input file required")
+		}
+		f, err := os.Open(infile)
+		if err != nil {
+			log.Fatal(err)
+		}
+		defer f.Close()
+		jsonData, err := csv2json.Convert(f, columns)
+		if err != nil {
+			log.Fatal(err)
+		}
+		fmt.Println(string(jsonData))
+	}
+
+#### Edit
+
+    ${HOME}/famous-gophers.csv
+
+-
+
+	Mac, 1947, The Goofy Gophers
+	Tosh, 1947, The Goofy Gophers
+	Samuel J. Gopher, 1966, Winnie the Pooh
+	Chief Running Board, 1968, Go Go Gophers
+	Ruffled Feathers, 1968, Go Go Gophers
+
+#### Build
+
+    go build -o csv2json .
+
+#### Run
+
+    ./csv2json -infile ${HOME}/famous-gophers.csv
+
+#### Version
+
+    git init .
+    git add main.go
+    git commit -m "first commit"
+

labs/docs/csv2json_package.md

+# csv2json
+
+Package to convert CSV to JSON
+
+#### Create
+
+    mkdir ${GOPATH}/src/github.com/${username}/csv2json
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json
+
+#### Edit
+
+    csv2json.go
+
+-
+
+	package csv2json
+
+	import (
+		"encoding/csv"
+		"encoding/json"
+		"io"
+	)
+
+	func Convert(r io.Reader, columns []string) ([]byte, error) {
+		rows := make([]map[string]string, 0)
+		csvReader := csv.NewReader(r)
+		csvReader.TrimLeadingSpace = true
+		for {
+			record, err := csvReader.Read()
+			if err == io.EOF {
+				break
+			}
+			if err != nil {
+				return nil, err
+			}
+			row := make(map[string]string)
+			for i, n := range columns {
+				row[n] = record[i]
+			}
+			rows = append(rows, row)
+		}
+		data, err := json.MarshalIndent(&rows, "", "  ")
+		if err != nil {
+			return nil, err
+		}
+		return data, nil
+	}
+
+#### Build
+
+    go install .
+
+#### Inspect
+
+    file ${GOPATH}/pkg/darwin_amd64/github.com/${username}/csv2json.a
+
+#### Version
+
+    git init .
+    git add .
+    git commit -m "first commit"

labs/docs/csv2json_package_exercise_answers.md

+# Exercise Answers
+
+## Write an additional test
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json
+
+#### Edit
+
+    csv2json_test.go
+
+-
+
+	package csv2json
+
+	import (
+		"bytes"
+		"io/ioutil"
+		"os"
+		"testing"
+	)
+
+	var columns = []string{"name", "email", "phone"}
+	var want = `[
+	  {
+		"email": "ken@google.com",
+		"name": "Ken Thompson",
+		"phone": "555-5550"
+	  },
+	  {
+		"email": "rob@google.com",
+		"name": "Rob Pike",
+		"phone": "555-5551"
+	  },
+	  {
+		"email": "robert@google.com",
+		"name": "Robert Griesemer",
+		"phone": "555-5552"
+	  }
+	]`
+
+	var input = `
+	Ken Thompson, ken@google.com, 555-5550
+	Rob Pike, rob@google.com, 555-5551
+	Robert Griesemer, robert@google.com, 555-5552
+	`
+
+	func TestConvertWithBuffer(t *testing.T) {
+		var b bytes.Buffer
+		_, err := b.WriteString(input)
+		if err != nil {
+			t.Error(err)
+		}
+		got, err := Convert(bytes.NewReader(b.Bytes()), columns)
+		if err != nil {
+			t.Error(err)
+		}
+		if string(got) != want {
+			t.Errorf("TestConvertWithBuffer(f, %s) = %s; want %s",
+				columns, string(got), want)
+		}
+	}
+
+	func TestConvertWithFile(t *testing.T) {
+		tf, err := ioutil.TempFile("", "")
+		if err != nil {
+			t.Error(err)
+		}
+		defer os.Remove(tf.Name())
+
+		err = ioutil.WriteFile(tf.Name(), []byte(input), 0644)
+		if err != nil {
+			t.Error(err)
+		}
+
+		f, err := os.Open(tf.Name())
+		if err != nil {
+			t.Error(err)
+		}
+		defer f.Close()
+
+		got, err := Convert(f, columns)
+		if err != nil {
+			t.Error(err)
+		}
+		if string(got) != want {
+			t.Errorf("TestConvertWithFile(f, %s) = %s; want %s", columns, string(got), want)
+		}
+	}
+
+#### Version
+
+    git add csv2json_test.go
+    git commit -m "add more tests" 

labs/docs/csv2json_package_tests.md

+# Testing the csv2json package
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json
+
+#### Edit 
+
+    csv2json_test.go
+
+-
+
+	package csv2json
+
+	import (
+		"bytes"
+		"testing"
+	)
+
+	var columns = []string{"name", "email", "phone"}
+	var want = `[
+	  {
+		"email": "ken@google.com",
+		"name": "Ken Thompson",
+		"phone": "555-5550"
+	  },
+	  {
+		"email": "rob@google.com",
+		"name": "Rob Pike",
+		"phone": "555-5551"
+	  },
+	  {
+		"email": "robert@google.com",
+		"name": "Robert Griesemer",
+		"phone": "555-5552"
+	  }
+	]`
+
+	var input = `
+	Ken Thompson, ken@google.com, 555-5550
+	Rob Pike, rob@google.com, 555-5551
+	Robert Griesemer, robert@google.com, 555-5552
+	`
+
+	func TestConvertWithBuffer(t *testing.T) {
+		var b bytes.Buffer
+		_, err := b.WriteString(input)
+		if err != nil {
+			t.Error(err)
+		}
+		got, err := Convert(bytes.NewReader(b.Bytes()), columns)
+		if err != nil {
+			t.Error(err)
+		}
+		if string(got) != want {
+			t.Errorf("TestConvertWithBuffer(f, %s) = %s; want %s",
+				columns, string(got), want)
+		}
+	}
+
+#### Run
+
+    go test
+
+
+Run with the -v flag for verbose output:
+
+    go test -v
+
+#### Version
+
+    git add csv2json_test.go
+    git commit -m "add tests"
+
+## Exercise
+
+### Write an additional test
+
+Create an additional test named TestConvertWithFile.
+
+#### Hint
+
+	func TestConvertWithFile(t *testing.T) {
+		tf, err := ioutil.TempFile("", "")
+		if err != nil {
+			t.Error(err)
+		}
+		defer os.Remove(tf.Name())
+
+		err = ioutil.WriteFile(tf.Name(), []byte(input), 0644)
+		if err != nil {
+			t.Error(err)
+		}
+
+		f, err := os.Open(tf.Name())
+		if err != nil {
+			t.Error(err)
+		}
+		defer f.Close()
+
+		// Test your results
+	}
+

labs/docs/csv2json_server.md

+# csv2json-server
+
+HTTP API to convert CSV to JSON
+
+#### Create
+
+    mkdir ${GOPATH}/src/github.com/${username}/csv2json-server
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json-server
+
+#### Edit
+
+    main.go
+
+-
+
+	package main
+
+	import (
+		"log"
+		"net/http"
+
+		"github.com/kelseyhightower/csv2json"
+	)
+
+	var (
+		columns = []string{"Name", "Date", "Title"}
+	)
+
+	func csv2JsonServer(w http.ResponseWriter, req *http.Request) {
+		jsonData, err := csv2json.Convert(req.Body, columns)
+		defer req.Body.Close()
+		if err != nil {
+			http.Error(w, "Could not convert csv to json", 503)
+		}
+		w.Write(jsonData)
+	}
+
+	func main() {
+		http.HandleFunc("/csv2json", csv2JsonServer)
+		err := http.ListenAndServe(":8080", nil)
+		if err != nil {
+			log.Fatal("ListenAndServe: ", err)
+		}
+	}
+
+#### Build
+
+    go build -o csv2json-server .
+
+#### Run
+
+    ./csv2json-server
+
+Test with curl:
+
+    curl -X POST http://localhost:8080/csv2json --data-binary @${HOME}/famous-gophers.csv
+
+#### Version
+
+    git init .
+    git add main.go
+    git commit -m "first commit"
+
+#### Vendor
+
+    godep save
+    git add Godeps
+    git commit -m "Manage dependencies with godep"
+
+## Exercise
+
+### Make the listen port configurable via the environment
+
+#### Hint
+
+    import (
+        "os"
+        "net"
+    )
+
+    func main() {
+        listenAddr := net.JoinHostPort(host, port)
+    }
+
+### Improve logging
+
+#### Hint
+
+    import (
+        "log"
+    )

labs/docs/csv2json_server_tests.md

+# Testing csv2json-server
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json-server
+
+#### Edit
+
+    api_test.go
+
+-
+
+	package main
+
+	import (
+		"bytes"
+		"testing"
+		"net/http"
+		"net/http/httptest"
+	)
+
+	type httpTestResponse struct {
+		statusCode int
+		body       string
+	}
+
+	var csv2JsonServerPostBodyTest = `
+	Mac, 1947, The Goofy Gophers
+	Tosh, 1947, The Goofy Gophers
+	Samuel J. Gopher, 1966, Winnie the Pooh
+	Chief Running Board, 1968, Go Go Gophers
+	Ruffled Feathers, 1968, Go Go Gophers
+	`
+
+	var csv2JsonServerWantBodyTest = `[
+	  {
+		"Date": "1947",
+		"Name": "Mac",
+		"Title": "The Goofy Gophers"
+	  },
+	  {
+		"Date": "1947",
+		"Name": "Tosh",
+		"Title": "The Goofy Gophers"
+	  },
+	  {
+		"Date": "1966",
+		"Name": "Samuel J. Gopher",
+		"Title": "Winnie the Pooh"
+	  },
+	  {
+		"Date": "1968",
+		"Name": "Chief Running Board",
+		"Title": "Go Go Gophers"
+	  },
+	  {
+		"Date": "1968",
+		"Name": "Ruffled Feathers",
+		"Title": "Go Go Gophers"
+	  }
+	]`
+
+	func TestCsv2JsonServer(t *testing.T) {
+		body := bytes.NewBufferString(csv2JsonServerPostBodyTest)
+		req, err := http.NewRequest("POST", "http://example.com/csv2json", body)
+		if err != nil {
+			t.Error(err)
+		}
+		w := httptest.NewRecorder()
+		csv2JsonServer(w, req)
+
+		want :=  httpTestResponse{http.StatusOK, csv2JsonServerWantBodyTest}
+		got := httpTestResponse{w.Code, w.Body.String()}
+		if got != want {
+			t.Errorf("Want: %#v, got %#v", want, got)
+		} 
+	}
+
+#### Run
+
+    go test -v
+
+Run using godep
+
+    godep go test -v
+
+#### Version
+
+    git add api_test.go
+    git commit -m "add api tests"
+

labs/docs/docker.md

+# Deploy with Docker
+
+## Install Docker
+
+Installation Instructions: http://docs.docker.io/en/latest/installation
+
+#### Change Directory
+
+    cd ${HOME}
+
+#### Download
+
+    curl -o docker https://get.docker.io/builds/Darwin/x86_64/docker-latest
+
+#### Install
+	
+	chmod +x docker
+    sudo cp docker /usr/local/bin/
+
+#### Configure
+
+    export DOCKER_HOST=tcp://${docker_host}:4243
+
+
+## Create a Dockerfile
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json-server
+
+#### Edit
+
+    Dockerfile
+
+-
+
+	# csv2json-server
+	#
+	# VERSION 0.0.1
+
+	FROM       ubuntu
+	EXPOSE     8080
+	ADD        csv2json-server /usr/local/bin/csv2json-server
+	ENTRYPOINT ["/usr/local/bin/csv2json-server"]
+
+#### Build
+
+    GOOS=linux go build -o csv2json-server .
+
+## Build the Docker Image
+
+#### Run
+
+    docker build -t ${username}/csv2json-server .
+
+#### Inspect
+
+    docker images
+
+## Run the Docker Image
+
+#### Run
+
+    docker run -d -P ${username}/csv2json-server
+
+## Testing with curl
+
+#### Inspect
+
+    docker port ${container} 8080
+
+#### Run
+
+    curl -X POST http://${docker_host}:${port}/csv2json --data-binary @${HOME}/famous-gophers.csv
+

labs/docs/godep.md

+# Managing Dependencies with Godep
+
+## Install Godep
+
+#### Change Directory
+
+    cd ${HOME}
+
+#### Run
+
+    go get github.com/tools/godep
+
+## Save Dependencies
+
+#### Change Directory
+
+    cd ${GOPATH}/src/github.com/${username}/csv2json-cli
+
+#### Inspect
+
+    go list -json
+
+#### Vendor
+
+    godep save
+
+#### Inspect 
+
+    cat Godeps/Godeps.json
+
+#### Inspect
+
+    ls -R Godeps
+
+#### Version
+
+    git add Godeps
+    git commit -m "Manage dependencies with Godep"
+
+
+## Building with Godep
+
+#### Run
+
+    godep go build -o csv2json .
+
+## Testing with Godep
+
+#### Run
+
+    godep go test

labs/docs/godoc.md

+# Documenting with GoDoc
+
+## Clone the Pinger Repo
+
+#### Change Directory
+
+    cd ${GOPATH}/src
+
+#### Run
+
+    git clone git@github.com:kelseyhightower/pinger.git
+
+## Add Comments
+
+#### Change Directory
+
+    cd ${GOPATH}/src/pinger
+
+#### Edit
+
+    ping/pinger.go
+
+-
+
+	package ping
+
+	import (
+		"fmt"
+		"net/http"
+		"time"
+	)
+
+	// Objects implementing the Pinger interface can be used to contact
+    // a particular website and return roundtrip results.
+	type Pinger interface {
+		Ping() (*Result, error)
+	}
+
+	// Result represents the results of a website ping.
+	type Result struct {
+		Url      string
+		Duration time.Duration
+	}
+
+	// Target represents a target website.
+	type Target struct {
+		url string
+	}
+
+	// NewTarget returns a *Target
+	func NewTarget(url string) *Target {
+		return &Target{url}
+	}
+
+	// Ping makes an HTTP GET request to a target identified by t.url
+	// and records total roundtrip duration.
+	func (t *Target) Ping() (*Result, error) {
+		startTime := time.Now()
+		res, err := http.Get(t.url)
+		duration := time.Since(startTime)
+
+		if err != nil {
+			return nil, err
+		}
+		defer res.Body.Close()
+
+		if res.StatusCode != http.StatusOK {
+			return nil, fmt.Errorf("ping error from %s bad status: %d", t.url, res.StatusCode)
+		}
+		return &Result{t.url, duration}, nil
+	}
+
+#### Edit
+
+    ping/doc.go 
+
+-
+
+    // Package ping provides Pinger.
+    package ping
+
+#### Edit
+
+    ping/example_test.go
+
+-
+
+	package ping_test
+
+	import (
+		"fmt"
+
+		. "pinger/ping"
+	)
+
+	func ExampleNewTarget() {
+		t := NewTarget("http://google.com")
+		res, err := t.Ping()
+		if err != nil {
+			fmt.Print(err)
+		}
+		fmt.Printf("%s - %s", res.Url, res.Duration)
+	}
+
+#### Run
+
+    godoc -http=":6060"
+
+#### Visit
+
+    http://localhost:6060/pkg

labs/docs/hello_world.md

+# Hello World
+
+#### Create
+
+    mkdir ${GOPATH}/src/hello
+
+#### Change Directory
+
+    cd ${GOPATH}/src/hello
+
+#### Edit
+
+    main.go
+
+-
+
+	package main
+
+	import (
+		"fmt"
+	)
+
+	func main() {
+		fmt.Println("Hello World")
+	}
+
+#### Build
+
+    go build -o hello .
+
+#### Run
+
+    ./hello

labs/docs/installing_go.md

+# Installing Go
+
+## Installation
+
+#### Change Directory
+
+    cd ${HOME}
+
+### Install a binary distribution
+ 
+#### Run
+
+    wget https://go.googlecode.com/files/go1.2.linux-amd64.tar.gz
+    tar -xvf go1.2.linux-amd64.tar.gz -C /usr/local
+
+## Setup the Workspace
+
+### GOPATH
+
+#### Edit
+
+    ${HOME}/.bashrc
+
+-
+
+    export GOPATH="${HOME}/go"
+    export PATH="/usr/local/go/bin:${GOPATH}/bin:$PATH"
+
+#### Activate:
+
+    source ${HOME}/.bashrc
+
+### Workspace Directories
+
+#### Create the directories on Unix
+
+    mkdir -p ${GOPATH}/src
+    mkdir -p ${GOPATH}/pkg
+    mkdir -p ${GOPATH}/bin
+    mkdir -p ${GOPATH}/src/github.com/${username}
+
+### Check the Go Environment
+
+    go env
+
+-
+
+	GOARCH="amd64"
+	GOBIN=""
+	GOCHAR="6"
+	GOEXE=""
+	GOHOSTARCH="amd64"
+	GOHOSTOS="darwin"
+	GOOS="darwin"
+	GOPATH="/Users/kelseyhightower/go"
+	GORACE=""
+	GOROOT="/usr/local/go"
+	GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
+	TERM="dumb"
+	CC="clang"
+	GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fno-common"
+	CXX="clang++"
+	CGO_ENABLED="1"

labs/docs/pinger.md

+# pinger
+
+Log response times from a list of websites
+
+#### Create
+
+	mkdir ${GOPATH}/src/pinger
+	mkdir ${GOPATH}/src/pinger/ping
+
+#### Change Directory
+
+    cd ${GOPATH}/src/pinger
+
+#### Edit
+
+	main.go
+
+-
+
+	package main
+
+	import (
+		"sync"
+
+		"pinger/ping"
+	)
+
+	func main() {
+		targets := []string{
+			"http://google.com",
+			"http://puppetlabs.com",
+			"http://newrelic.com",
+		}
+		work := make(chan ping.Pinger)
+		result := make(chan *ping.Result)
+
+		var wg sync.WaitGroup
+		wg.Add(2)
+		go pinger(work, result, &wg)
+		go printer(result, &wg)
+
+		for _, t := range targets {
+			work <- ping.NewTarget(t)
+		}
+		close(work)
+		wg.Wait()
+	}
+
+#### Edit
+
+    pinger.go
+
+-
+
+	package main
+
+	import (
+		"log"
+		"sync"
+
+		"pinger/ping"
+	)
+
+	func pinger(work chan ping.Pinger, result chan *ping.Result, wg *sync.WaitGroup) {
+		for {
+			w, ok := <-work
+			if !ok {
+				break
+			}
+			res, err := w.Ping()
+			if err != nil {
+				log.Printf("pinger: %s", err)
+				continue
+			}
+			result <- res
+		}
+		close(result)
+		wg.Done()
+	}
+
+	func printer(result chan *ping.Result, wg *sync.WaitGroup) {
+		for {
+			res, ok := <-result
+			if !ok {
+				break
+			}
+			log.Printf("ping %s %s", res.Url, res.Duration)
+		}
+		wg.Done()
+	}
+
+#### Edit
+
+    ping/pinger.go
+
+-
+    package ping
+
+	import (
+		"fmt"
+		"net/http"
+		"time"
+	)
+
+	type Pinger interface {
+		Ping() (*Result, error)
+	}
+
+	type Result struct {
+		Url      string
+		Duration time.Duration
+	}
+
+	type Target struct {
+		url string
+	}
+
+	func NewTarget(url string) *Target {
+		return &Target{url}
+	}
+
+	func (t *Target) Ping() (*Result, error) {
+		startTime := time.Now()
+		res, err := http.Get(t.url)
+		duration := time.Since(startTime)
+
+		if err != nil {
+			return nil, err
+		}
+		defer res.Body.Close()
+
+		if res.StatusCode != http.StatusOK {
+			return nil, fmt.Errorf("ping error from %s bad status: %d", t.url, res.StatusCode)
+		}
+		return &Result{t.url, duration}, nil
+	}
+
+#### Build
+
+    go build -o pinger .
+
+#### Run
+
+    ./pinger 

labs/godep.md

-# Managing Dependencies with Godep
-
-## Install Godep
-
-#### Change Directory
-
-    cd ${HOME}
-
-#### Run
-
-    go get github.com/tools/godep
-
-## Save Dependencies
-
-#### Change Directory
-
-    cd ${GOPATH}/src/github.com/${username}/csv2json-cli
-
-#### Inspect
-
-    go list -json
-
-#### Vendor
-
-    godep save
-
-#### Inspect 
-
-    cat Godeps/Godeps.json
-
-#### Inspect
-
-    ls -R Godeps
-
-#### Version
-
-    git add Godeps
-    git commit -m "Manage dependencies with Godep"
-
-
-## Building with Godep
-
-#### Run
-
-    godep go build -o csv2json .
-
-## Testing with Godep
-
-#### Run
-
-    godep go test

labs/godoc.md

-# Documenting with GoDoc
-
-## Clone the Pinger Repo
-
-#### Change Directory
-
-    cd ${GOPATH}/src
-
-#### Run
-
-    git clone git@github.com:kelseyhightower/pinger.git
-
-## Add Comments
-
-#### Change Directory
-
-    cd ${GOPATH}/src/pinger
-
-#### Edit
-
-    ping/pinger.go
-
--
-
-	package ping
-
-	import (
-		"fmt"
-		"net/http"
-		"time"
-	)
-
-	// Objects implementing the Pinger interface can be used to contact
-    // a particular website and return roundtrip results.
-	type Pinger interface {
-		Ping() (*Result, error)
-	}
-
-	// Result represents the results of a website ping.
-	type Result struct {
-		Url      string
-		Duration time.Duration
-	}
-
-	// Target represents a target website.
-	type Target struct {
-		url string
-	}
-
-	// NewTarget returns a *Target
-	func NewTarget(url string) *Target {
-		return &Target{url}
-	}
-
-	// Ping makes an HTTP GET request to a target identified by t.url
-	// and records total roundtrip duration.
-	func (t *Target) Ping() (*Result, error) {
-		startTime := time.Now()
-		res, err := http.Get(t.url)
-		duration := time.Since(startTime)
-
-		if err != nil {
-			return nil, err
-		}
-		defer res.Body.Close()
-
-		if res.StatusCode != http.StatusOK {
-			return nil, fmt.Errorf("ping error from %s bad status: %d", t.url, res.StatusCode)
-		}
-		return &Result{t.url, duration}, nil
-	}
-
-#### Edit
-
-    ping/doc.go 
-
--
-
-    // Package ping provides Pinger.
-    package ping
-
-#### Edit
-
-    ping/example_test.go
-
--
-
-	package ping_test
-
-	import (
-		"fmt"
-
-		. "pinger/ping"
-	)
-
-	func ExampleNewTarget() {
-		t := NewTarget("http://google.com")
-		res, err := t.Ping()
-		if err != nil {
-			fmt.Print(err)
-		}
-		fmt.Printf("%s - %s", res.Url, res.Duration)
-	}
-
-#### Run
-
-    godoc -http=":6060"
-
-#### Visit
-
-    http://localhost:6060/pkg

labs/hello_world.md

-# Hello World
-
-#### Create
-
-    mkdir ${GOPATH}/src/hello
-
-#### Change Directory
-
-    cd ${GOPATH}/src/hello
-
-#### Edit
-
-    main.go
-
--
-
-	package main
-
-	import (
-		"fmt"
-	)
-
-	func main() {
-		fmt.Println("Hello World")
-	}
-
-#### Build
-
-    go build -o hello .
-
-#### Run
-
-    ./hello

labs/installing_go.md

-# Installing Go
-
-## Installation
-
-#### Change Directory
-
-    cd ${HOME}
-
-### Install a binary distribution
- 
-#### Run
-
-    wget https://go.googlecode.com/files/go1.2.linux-amd64.tar.gz
-    tar -xvf go1.2.linux-amd64.tar.gz -C /usr/local
-
-## Setup the Workspace
-
-### GOPATH
-
-#### Edit
-
-    ${HOME}/.bashrc
-
--
-
-    export GOPATH="${HOME}/go"
-    export PATH="/usr/local/go/bin:${GOPATH}/bin:$PATH"
-
-#### Activate:
-
-    source ${HOME}/.bashrc
-
-### Workspace Directories
-
-#### Create the directories on Unix
-
-    mkdir -p ${GOPATH}/src
-    mkdir -p ${GOPATH}/pkg
-    mkdir -p ${GOPATH}/bin
-    mkdir -p ${GOPATH}/src/github.com/${username}
-
-### Check the Go Environment
-
-    go env
-
--
-
-	GOARCH="amd64"
-	GOBIN=""
-	GOCHAR="6"
-	GOEXE=""
-	GOHOSTARCH="amd64"
-	GOHOSTOS="darwin"
-	GOOS="darwin"
-	GOPATH="/Users/kelseyhightower/go"
-	GORACE=""
-	GOROOT="/usr/local/go"
-	GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
-	TERM="dumb"
-	CC="clang"
-	GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fno-common"
-	CXX="clang++"
-	CGO_ENABLED="1"

labs/pinger.md

-# pinger
-
-Log response times from a list of websites
-
-#### Create
-
-	mkdir ${GOPATH}/src/pinger
-	mkdir ${GOPATH}/src/pinger/ping
-
-#### Change Directory
-
-    cd ${GOPATH}/src/pinger
-
-#### Edit
-
-	main.go
-
--
-
-	package main
-
-	import (
-		"sync"
-
-		"pinger/ping"
-	)
-
-	func main() {
-		targets := []string{
-			"http://google.com",
-			"http://puppetlabs.com",
-			"http://newrelic.com",
-		}
-		work := make(chan ping.Pinger)
-		result := make(chan *ping.Result)
-
-		var wg sync.WaitGroup
-		wg.Add(2)
-		go pinger(work, result, &wg)
-		go printer(result, &wg)
-
-		for _, t := range targets {
-			work <- ping.NewTarget(t)
-		}
-		close(work)
-		wg.Wait()
-	}
-
-#### Edit
-
-    pinger.go
-
--
-
-	package main
-
-	import (
-		"log"
-		"sync"
-
-		"pinger/ping"
-	)
-
-	func pinger(work chan ping.Pinger, result chan *ping.Result, wg *sync.WaitGroup) {
-		for {
-			w, ok := <-work
-			if !ok {
-				break
-			}
-			res, err := w.Ping()
-			if err != nil {
-				log.Printf("pinger: %s", err)
-				continue
-			}
-			result <- res
-		}
-		close(result)
-		wg.Done()
-	}
-
-	func printer(result chan *ping.Result, wg *sync.WaitGroup) {
-		for {
-			res, ok := <-result
-			if !ok {
-				break
-			}
-			log.Printf("ping %s %s", res.Url, res.Duration)
-		}
-		wg.Done()
-	}
-
-#### Edit
-
-    ping/pinger.go
-
--
-    package ping
-
-	import (
-		"fmt"
-		"net/http"
-		"time"
-	)
-
-	type Pinger interface {
-		Ping() (*Result, error)
-	}
-
-	type Result struct {
-		Url      string
-		Duration time.Duration
-	}
-
-	type Target struct {
-		url string
-	}
-
-	func NewTarget(url string) *Target {
-		return &Target{url}
-	}
-
-	func (t *Target) Ping() (*Result, error) {
-		startTime := time.Now()
-		res, err := http.Get(t.url)
-		duration := time.Since(startTime)
-
-		if err != nil {
-			return nil, err
-		}
-		defer res.Body.Close()
-
-		if res.StatusCode != http.StatusOK {
-			return nil, fmt.Errorf("ping error from %s bad status: %d", t.url, res.StatusCode)
-		}
-		return &Result{t.url, duration}, nil