Home avatar

A collection of dev guides, tutorials and thoughts on various tech stacks, tools and programming languages.

The iterator pattern in Go

This is a continuation of the common design patterns I found in my old code series, which I started in a previous post.

The iterator pattern is a frequently used design pattern in software and it’s very simple. It entails that a collection must provide an iterator that can be used to iterate through its objects.

To put it in simple terms:

c := MyCollection{}

for c.Next() {
    v := c.Value()
    ...
}

Though, I haven’t seen this used very often in Go (it doesn’t mean it’s true). I could only find a single instance of this while going through my code (a Firestore DocumentIterator).

The prototype pattern in Go

This is a continuation of the common design patterns I found in my old code series, which I started in a previous post.

While going through some of the code I found a couple of instances where I was making copies of some structs, but I wasn’t using the built in copy() method. I was, instead, using some custom copy logic.

The reason for that was that the struct had some properties that were slices of other structs and if I were to use copy(), it would get me in trouble as there was a possibility that the source struct could be mutated.

The factory method pattern in Go

This is a continuation of the common design patterns I found in my old code series, which I started in a previous post.

Another common pattern I found is the factory method pattern, which is a design pattern used to create different types of objects using the same interface.

This pattern is actually pretty common in Go. Some good example of this are the builtin I/O libraries:

package main

import (
  "bytes"
  "encoding/csv"
  "fmt"
  "io"
  "os"
)

func main() {
  f, _ := os.Open("avengers.csv")
  records, _ := parseCsv(f)
  fmt.Println("Records from file", records)
  
  data := []byte("name,surname\nJohn,Snow\n")
  r := bytes.NewReader(data)
  records, _ = parseCsv(r)
  fmt.Println("Records from bytes", records)
}

func parseCsv(r io.Reader) ([][]string, error) {
  cr := csv.NewReader(r)
  return cr.ReadAll()
}

Both the bytes.NewReader() and os.Open() implement the io.Reader interface which comes in handy for the parseCsv() method above as we can use it to parse data from multiple sources.

The singleton pattern in Go

I recently started going through some of my old code and I was trying to identify some common design patterns. I thought it could be a good memory exercise and refresher on software design patterns as it’s been quite some time since I last read through that.

And while I was doing that, I thought it might be a good idea to write about the patterns with the most occurrences.

Cross-compile gRPC for ARM with Docker

If you’re building a micro-service architecture you’ll most likely end up using some sort of networking lib to manage the communication between services.

This is where gRPC fits in. I’m not gonna go through why it’s a good choice, most of the time, but let’s just say that interoperability between different programming languages becomes a lot better.

There’s also a decent amount of documentation and a relatively large community where you can find help.

Cross-compile Go with C dependencies for ARM

Go has some pretty good support/tools for cross-compiling binaries for multiple platforms/architectures. Just check the $GOOS/$GOARCH env vars listed in the official documentation.

But sometimes, you may need to use some lib/code written in C that hasn’t been ported to Go. For this reason, there’s cgo, which you can use to call C code from your Go code. And while it can be straightforward to use when you run the binaries on the same platform you used to build them, it can be a bit of a mess when cross-compiling for platforms/architectures that are different.

Build Docker images on k8s nodes

In one of my previous posts, cross-compiling for Raspberry Pi with Docker, I wrote about and illustrated how the relatively new buildx command made it much easier to build and dump binaries on the host system.

Another useful feature of buildx is the ability to use different drivers when building images. As of today (10.10.2020), it supports 3 different drivers:

  1. docker - uses the docker daemon built in builder (default). You’d use this if you want to build and use images locally.
  2. docker-container - uses a BuildKit container spawned by docker. Useful when you want to build multi-platform images.
  3. kubernetes - uses k8s pods with defined buildkit container images. Similar to the docker-container driver, but instead of building on the host, it builds on a k8s node

I find the kubernetes driver quite useful when you’re low on resources on your host or when you just want your builds to be fast; there’s probably other good reasons to use it (scalability?), but I’m not going to try to convince you. You should just try it and see if it fits your needs or not.

How to enable experimental features for Docker in Github's workflow for Ubuntu 18.04?

If you’re using Github’s workflows for CI/CD and you need to use some of Docker’s experimental features, or you want to use buildx or maybe you just want to use some of the new dockerfile experimental syntaxes then you need to enable the experimental features for the CLI and probably the daemon too.

When running natively on Linux or macOS, it’s pretty easy.

To enable the experimental features for the CLI, you just need to add the following to your ~/.docker/config.json config:

How to install and run QEMU on K8s nodes?

If you find yourself having to emulate different CPU architectures in a Kubernetes environment you’ll probably end up running some version of binfmt as an init container or maybe manually run it once.

That would probably be ok in most cases, but wouldn’t work if, for example, you’re running multiple pod replicas on the same node concurrently (say, when you setup a CI that spawns pods for every job in your pipeline) or when nodes are autoscaled.

How to access private Github repos when building Docker images?

If you landed on this page, it means you probably have the same issue I’ve stumbled upon as well: I need to access some private Github repos while building a docker image but I don’t want any ssh keys or credentials to end up in the final image.

While there’s already ways to achieve this, the recent additions of buildx (first release was back in April 2019) and experimental dockerfile syntaxes (available from v18.06 onwards) make it much easier to do it (or just more elegant).