Intro to the Go programming language (Golang)

Presented by
Jonathan Seth Mainguy
Systems Administrator @ Bandwidth

Go (often referred to as Golang) is a programming language created by Google in 2009 by Robert Griesemer, Rob Pike, and Ken Thompson (Ken wrote Unix, he is kind of a big deal).

Go is a statically typed, compiled language in the tradition of C

The mascot for Go is the Gopher, you can make your own at https://gopherize.me

The best place to start is the Go tour, it is fabulous.

https://tour.golang.org

 

The community on IRC Freenode is amazing for questions as well. 

Freenode: #go-nuts

 

Say hi to jmainguy if you go on there.

Why should I learn another language? Bash and python do everything I need.

Bash is my first love and has its place. It really sucks at parsing things like json.

Python is great at parsing, but requires additional packages be installed on each server your script will run on. It is hard to keep track of all the additional packages your script needs.

Golang compiles all its dependencies into a single binary, which you can then ship around and run without worry about it failing (Assuming you compiled on same relative OS, it uses some C libraries and such, so if GLIBC is really different it could be a problem)

Golang can import packages like python, except it can also do it straight from a git repo, like 

package main

import (
	"github.com/Jmainguy/zenoss"
)

func main() {
	zenoss.CreateAlarm("https://example.com",
		"jmainguy", "password",
		"neat", "12345")
}

Golang wont let you import modules, or define variables which you don't end up using. I think that is pretty cool.

[jmainguy@jmainguy-fedora test]$ cat main.go 
package main

import (
	"fmt" // I don't end up using fmt anywhere so this fails
	"github.com/Jmainguy/zenoss"
)

func main() {
	Uuid, Success := zenoss.CreateAlarm("https://example.com",
		"jmainguy", "password",
		"neat", "12345")
}
[jmainguy@jmainguy-fedora test]$ go build
# _/home/jmainguy/go/test
./main.go:4:2: imported and not used: "fmt"

I fixed my code to finally use fmt, but I missed using the Success variable for anything.

[jmainguy@jmainguy-fedora test]$ cat main.go 
package main

import (
	"fmt"
	"github.com/Jmainguy/zenoss"
)

func main() {
	Uuid, Success := zenoss.CreateAlarm("https://example.com",
		"jmainguy", "password",
		"neat", "12345")
	fmt.Println(Uuid)
}
[jmainguy@jmainguy-fedora test]$ go build
# _/home/jmainguy/go/test
./main.go:9:8: Success declared and not used

If you dont want a variable, don't define it. You can also use _ if a function returns more variables then you care about.

[jmainguy@jmainguy-fedora test]$ cat main.go 
package main

import (
	"fmt"
	"github.com/Jmainguy/zenoss"
)

func main() {
	Uuid, _ := zenoss.CreateAlarm("https://example.com",
		"jmainguy", "password",
		"neat", "12345")
	fmt.Println(Uuid)
}
[jmainguy@jmainguy-fedora test]$ go build
[jmainguy@jmainguy-fedora test]$ 
[jmainguy@jmainguy-fedora test]$ ls
main.go  test
[jmainguy@jmainguy-fedora test]$ file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), 
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 
with debug_info, not stripped

Go build finished succesefully and now I have a binary I can run, named after the directory I am in.

You can split your functions out over as many files as you want.

[jmainguy@jmainguy-fedora test]$ cat random.go 
package main

import (
	"math/rand"
)

func random(min, max int) int {
	return rand.Intn(max-min) + min
}
[jmainguy@jmainguy-fedora test]$ 
[jmainguy@jmainguy-fedora test]$ cat main.go 
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())
	fmt.Println("Rolling 1d20")
	roll := random(1, 20)
	fmt.Printf("You rolled a %d\n", roll)
}
[jmainguy@jmainguy-fedora test]$ ./test 
Rolling 1d20
You rolled a 8
[jmainguy@jmainguy-fedora test]$ ./test 
Rolling 1d20
You rolled a 13

Golang is very specific about types, you must declare them, and use them appropriately. In func random, I am declaring both min, and max inputs will be integers, and the return will be an integer as well.

[jmainguy@jmainguy-fedora test]$ cat random.go 
package main

import (
	"math/rand"
)

func random(min, max int) int {
	return rand.Intn(max-min) + min
}
package main

import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
	"time"
)

func main() {
	welcome()
	fmt.Println("What would you like to do?")
	reader := bufio.NewReader(os.Stdin)
	choice, _ := reader.ReadString('\n')
	if choice == "Fight\n" {
		rand.Seed(time.Now().UnixNano())
		fmt.Println("Rolling 1d20")
		roll := random(1, "twenty")
		fmt.Printf("You rolled a %d\n", roll)
		if roll < 10 {
			fmt.Println("You missed")
			fmt.Println("The Orc is taken back")
		} else {
			fmt.Println("You slice the Ork in two")
			fmt.Println("You are a murderer")
		}
	} else {
		fmt.Printf("You chose to %s\n", choice)
	}
}
[jmainguy@jmainguy-fedora test]$ go build
# _/home/jmainguy/go/test
./main.go:19:21: cannot use "twenty" (type string) as type int in argument to random

I passed a string, to a func expecting an int, so it errored out.

Once I changed "twenty" to 20, it compiled again.

[jmainguy@jmainguy-fedora test]$ ./test 
You see a Orc carrying an axe, walking directly toward you
What would you like to do?
Fight
Rolling 1d20
You rolled a 12
You slice the Ork in two
You are a murderer

It creates large binaries, because it contains all its dependencies.

[jmainguy@jmainguy-fedora test]$ ls -ltrh
total 2.1M
-rw-rw-r--. 1 jmainguy jmainguy  106 Sep 19 11:34 random.go
-rw-rw-r--. 1 jmainguy jmainguy  136 Sep 19 11:55 welcome.go
-rw-rw-r--. 1 jmainguy jmainguy  605 Sep 19 12:09 main.go
-rwxrwxr-x. 1 jmainguy jmainguy 2.1M Sep 19 12:10 test

Golang programs make great daemons, as they are quick to create

 

Here is a daemon that prints a message every 5 seconds until somone kills the process.

    for {
        fmt.Println("Look, I am a daemon")
        // Sleep for 5 seconds, restart loop
        time.Sleep(5 *time.Second)
    }

Maybe you want to destroy someones cpu in the background. Use a go routine.

package main

import (
	"crypto/sha512"
	"fmt"
	"time"
)

func sha() {
	input := "Jon is the cooooolest"
	sha_512 := sha512.New()
	sha_512.Write([]byte(input))
}

func eat() {
	for {
		sha()
	}
}

func main() {
	go eat()
	for {
		fmt.Println("I am being a good program I swear")
		time.Sleep(5 * time.Second)
	}
}

[jmainguy@jmainguy-fedora example]$ ./example 
I am being a good program I swear
I am being a good program I swear


12684 jmainguy  20   0   32848  29144   1168 S 121.3  0.2   0:38.40 example

You can download go binaries using go, to use as commands on your workstation. Here I am downloading gotty-client, and connecting to a shell on https://soh.re 

[jmainguy@jmainguy-fedora example]$ go get github.com/moul/gotty-client/cmd/gotty-client
[jmainguy@jmainguy-fedora example]$ ~/go/bin/gotty-client https://soh.re
[joshua@2ed43af67a04 ~]$ echo "Look, I am in a docker container, via soh.re website"
Look, I am in a docker container, via soh.re website
[joshua@2ed43af67a04 ~]$ ls
blog.txt  chat.txt  email.txt  github.txt  projects.MD  resume.txt  rhca_certified.txt

Interact with API's by creating structs

first, get an idea of what the result looks like

[jmainguy@jmainguy-fedora example]$ curl -k https://es-master-01a.example1:9200
{
  "name" : "lab-example-es-master-01a",
  "cluster_name" : "lab-example-es-01",
  "cluster_uuid" : "randomstring",
  "version" : {
    "number" : "6.2.2",
    "build_hash" : "10b1edd",
    "build_date" : "2018-02-16T19:01:30.685723Z",
    "build_snapshot" : false,
    "lucene_version" : "7.2.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"

Use a sweet golang package to convert this into a golang struct.

Now we can use this struct to get the only field we care about

[jmainguy@jmainguy-fedora example]$ ls
example  main.go  structs.go
[jmainguy@jmainguy-fedora example]$ cat main.go 
package main

import (
    "net/http"
    "io/ioutil"
    "encoding/json"
    "fmt"
    "crypto/tls"
)

func main() {

    http.DefaultTransport.(*http.Transport).TLSClientConfig = 
        &tls.Config{InsecureSkipVerify: true}
    // Generated by curl-to-Go: https://mholt.github.io/curl-to-go
    resp, _ := http.Get("https://es-master-01a.example:9200/_cluster/health")
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    clusterHealth := ClusterHealth{}
    json.Unmarshal(body, &clusterHealth)
    fmt.Println(clusterHealth.Status)
}

[jmainguy@jmainguy-fedora example]$ ./example 
green

Questions?

Thank you for reading / listening to all this

Here are some good sites you should checkout

https://tour.golang.org

https://gopherize.me/

https://golang.org/pkg/
https://gobyexample.com/

https://soh.re (all code is in golang on this site)

intro to golang

By jsmainguy

intro to golang

An introduction to Golang

  • 372