Go for operations

By Yaron Tal

TODO

  • About Appfactory
  • About me
  • Go basics
  • Building a tool
    • CLI
    • GIT
    • Tests
    • HTTP/XML
    • Concurrency (Bonus)

About Appfactory

  • Continuous Delivery Pipeline
  • Intelligent Platform
  • Feedback from whole stack

About me

  • Yaron Tal
  • Web-Developer, sysadmin, Operations
  • KPN Appfactory
  • ytal.nl
  • Slides: https://github.com/yaron/go-ops
  • Slides: https://yaron.github.io/go-ops
  • Code: https://github.com/yaron/go-ops-code

Go basics

package main

import "fmt"

func main() {
	fmt.Println("Hello Linux")
}
$ go run hello.go
Hello Linux
package main

import "fmt"

func main() {
	fmt.Println("1+2=")
	fmt.Println(add(1, 2))
}

func add(a, b int) int {
	return a + b
}
$ go run function.go
1+2=
3
package main

import "fmt"

type myConfig struct {
	APIKey string
	Secret string
}

func main() {
	config := myConfig{
		APIKey: "wo9wmfojw4rfwsmef",
		Secret: "so9jfsofjsgsg",
	}
	fmt.Println(config.APIKey)
}
type myError struct { Message string }

func (e myError) Error() string {
	return e.Message
}

func errorFunc() (string, error) {
	return "", myError{Message: "Sorry..."}
}

func main() {
	value, err := errorFunc()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(value)
}
type error interface {
    Error() string
}

Building a tool

CLI: https://github.com/urfave/cli
$ go get github.com/urfave/cli
import (
	"fmt"
	"os"

	"github.com/urfave/cli"
)
func main() {
	app := cli.NewApp()
	err := app.Run(os.Args)
	if err != nil {
		fmt.Println(err)
	}
}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "url, u",
			Value: "https://github.com/yaron/go-ops.git",
			Usage: "URL to git clone",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:    "clone",
			Aliases: []string{"c"},
			Usage:   "Clone a git url",
			Action: func(c *cli.Context) error {
				fmt.Printf("URL: %q", c.String("url"))
				return nil
			},
		},
	}
$ go run cli.go -h
NAME:
   cli - A new cli application

USAGE:
   cli [global options] command [command options] [arguments...]

VERSION:
   0.0.0

COMMANDS:
     clone, c  Clone a git url
     help, h   Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --url value, -u value  URL to git clone (default: "https://github.com/yaron/go-ops.git")
   --help, -h             show help
   --version, -v          print the version
GIT: https://github.com/src-d/go-git (or os.exec)
$ go get gopkg.in/src-d/go-git.v4
_, err := git.PlainClone("/tmp/go-ops", false, &git.CloneOptions{
  URL:      "https://github.com/yaron/go-ops.git",
  Progress: os.Stdout,
})

if err != nil {fmt.Println(err)}
func TestAdd(t *testing.T) {
	testTables := []struct {
		a int
		b int
		t int
	}{
		{1, 1, 2},
		{2, 2, 4},
	}

	for _, testTable := range testTables {
		total := add(testTable.a, testTable.b)
		if total != testTable.t {
			t.Errorf("Sum of (%d+%d) was incorrect, got: %d, want: %d.", testTable.a, testTable.b, total, testTable.t)
		}
	}
}
<xml>
  <album>
    <title>The Resistance</title>
    <year>2009</year>
    <song>
      <title>Uprising</title>
      <artist>Muse</artist>
      <duration>5:05</duration>
    </song>
    <song>
      <title>Resistance</title>
      <artist>Muse</artist>
      <duration>5:47</duration>
    </song>
  </album>

  <album>
    <title>Get Born</title>
    <year>2003</year>
    <song>
      <title>Are You Gonna Be My Girl</title>
      <artist>Jet</artist>
      <duration>3:34</duration>
    </song>
    <song>
      <title>Timothy</title>
      <artist>Jet</artist>
      <duration>4:30</duration>
    </song>
  </album>
</xml>
type musicData struct {
	Data []album `xml:"album"`
}

type album struct {
	Title string `xml:"title"`
	Year  int    `xml:"year"`
	Songs []song `xml:"song"`
}

type song struct {
	Title    string `xml:"title"`
	Artist   string `xml:"artist"`
	Duration string `xml:"duration"`
}
	func main() {
		url := "https://gist.githubusercontent.com/yaron/1a1df8cdc71612c24fedf8562f4c0083/raw/0b9fa04ecd96581abaea478eb6799c387d56aa16/gistfile1.txt"
		resp, err := http.Get(url)
		if err != nil {
			fmt.Println(err)
			return
		}

		defer resp.Body.Close()
		
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println(err)
			return
		}
	
		var data musicData
		err = xml.Unmarshal([]byte(body), &data)
		if err != nil {
			fmt.Printf("error: %v", err)
			return
		}
	
for _, album := range data.Data {
	for _, song := range album.Songs {
	  fmt.Printf("%s by %s on album %s\n",
			song.Title,
			song.Artist,
			album.Title
		)
	}
}
func main() {
	fmt.Println("I have a problem and I think concurrency"
		+"is the solution")

	sentence := []string{"And", "now", "I", "have",
		"two", "problems", ".\n"}

	for i := 0; i < len(sentence); i++ {
		printFunc(" " + sentence[i])
	}
}

func printFunc(s string) {
	time.Sleep(
		time.Duration(rand.Intn(1000)) * time.Microsecond
	)
	fmt.Print(s)
}
func main() {
	fmt.Println("I have a problem and I think concurrency"
		+"is the solution")

	sentence := []string{"And", "now", "I", "have",
		"two", "problems", ".\n"}

	for i := 0; i < len(sentence); i++ {
		go printFunc(" " + sentence[i])
	}
}

func printFunc(s string) {
	time.Sleep(
		time.Duration(rand.Intn(1000)) * time.Microsecond
	)
	fmt.Print(s)
}
$ go run concurrency.go
I have a problem and I think concurrency is the solution
var wg sync.WaitGroup

func main() {
	fmt.Println("I have a problem and I think concurrency"
		+" is the solution")

	sentence := []string{"And", "now", "I", "have",
		"two", "problems", ".\n"}

	wg.Add(len(sentence))
	for i := 0; i < len(sentence); i++ {
		go printFunc(" " + sentence[i])
	}

	fmt.Println("Now we wait")
	wg.Wait()
}

func printFunc(s string) {
	defer wg.Done()
	time.Sleep(
		time.Duration(rand.Intn(1000)) * time.Microsecond
	)
	fmt.Print(s)
}
		$ go run concurrency.go
I have a problem and I think concurrency is the solution
Now we wait
 have .
 I problems two And now 
	
func main() {
	fileLocations := make(chan string, 2)

	go fetchFile("http://www.example.com/file1", fileLocations)
	go fetchFile("http://www.example.com/file2", fileLocations)

	file1 := <-fileLocations
	file2 := <-fileLocations
	fmt.Println(file1)
	fmt.Println(file2)
}

func fetchFile(url string, location chan<- string) {
	time.Sleep(time.Duration(rand.Intn(1000)) * time.Microsecond)
	urlSplit := strings.Split(url, "/")
	location <- "/tmp/filedir/" + urlSplit[len(urlSplit)-1]
}