Go for Infrastructure Engineers cheat sheet
59 commands from every lesson of Go for Infrastructure Engineers, on one page.
Every day
go mod init github.com/me/kaudit | Start a module (the import path of your project) |
go run . | Compile and run the package in this folder |
go build -o bin/kaudit . | Build a binary |
go test ./... | Run all tests in the module |
go vet ./... && gofmt -l . | Static checks and unformatted files |
go get k8s.io/client-go@v0.34.0 | Add or change a dependency version |
go mod tidy | Add missing and remove unused dependencies |
Build for elsewhere
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o kaudit-arm64 . | Static Linux binary for ARM64, from any machine |
go build -ldflags='-s -w -X main.version=1.2.0' . | Smaller binary, version stamped at build time |
go env GOPATH GOCACHE | Where modules and build cache live |
Syntax at a glance
x := 3 | Declare and assign (type inferred) inside functions |
var nodes []string | Declare with the zero value (nil slice, ready to append) |
nodes = append(nodes, "n1") | Add to a slice |
counts := map[string]int{} | Empty map ready to use |
for i, n := range nodes { } | Loop over a slice with index and value |
if v, ok := counts["n1"]; ok { } | Read a map key and check it exists |
type Node struct { Name string; Ready bool } | Define a struct |
func (n Node) String() string { } | A method on a type |
Patterns
if err != nil { return fmt.Errorf("read %s: %w", path, err) } | Add context and keep the original error |
if errors.Is(err, os.ErrNotExist) { } | Is it (or does it wrap) this specific error? |
var nf *NotFoundError; if errors.As(err, &nf) { } | Is it (or does it wrap) this error type? Get it |
defer f.Close() | Run clean-up when the function returns, whatever path it takes |
var ErrNoNodes = errors.New("no nodes") | A sentinel error callers can test for |
Building blocks
flag.String("cluster", "", "cluster name") | Standard-library flag |
go get github.com/spf13/cobra@latest | Add cobra for subcommands |
slog.New(slog.NewJSONHandler(os.Stderr, nil)) | Structured JSON logs to stderr |
signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) | A context cancelled on Ctrl-C or SIGTERM |
os.Exit(2) | Exit with a specific code (deferred calls don't run!) |
Patterns
client := &http.Client{Timeout: 10 * time.Second} | Never use a client without a timeout |
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) | Request that stops when ctx is cancelled |
defer resp.Body.Close() | Always close bodies (connection reuse) |
json.NewDecoder(resp.Body).Decode(&out) | Decode JSON straight from the response |
if resp.StatusCode >= 300 { } | Errors from the server are not Go errors: check the status |
Patterns
var wg sync.WaitGroup; wg.Add(1); go func() { defer wg.Done() }() | Start work and wait for it |
g, ctx := errgroup.WithContext(ctx); g.SetLimit(10) | Run tasks, at most 10 at once, cancel all on first error |
select { case v := <-ch: case <-ctx.Done(): } | Wait for whichever happens first |
var mu sync.Mutex; mu.Lock(); defer mu.Unlock() | Protect shared state |
go test -race ./... / go run -race . | Detect data races |
Set-up
go get k8s.io/client-go@v0.34.0 k8s.io/apimachinery@v0.34.0 | client-go and apimachinery, versions matching your clusters' minor |
clientcmd.BuildConfigFromFlags("", kubeconfigPath) | Config from a kubeconfig file |
rest.InClusterConfig() | Config inside a pod (ServiceAccount token) |
kubernetes.NewForConfig(cfg) | The typed clientset |
Calls
cs.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) | List nodes |
cs.CoreV1().Pods("").List(ctx, metav1.ListOptions{FieldSelector: "status.phase=Pending"}) | Pending pods in all namespaces |
cs.CoreV1().Nodes().Patch(ctx, name, types.MergePatchType, data, metav1.PatchOptions{}) | Patch a node (labels, annotations) |
apierrors.IsNotFound(err) | Was it a 404? |
Kubebuilder
kubebuilder init --domain platform.example.com --repo github.com/me/team-operator | Scaffold a project |
kubebuilder create api --group platform --version v1alpha1 --kind TeamNamespace | A CRD type and its controller |
make manifests generate | Regenerate CRD YAML, RBAC and deep-copy code after editing types |
make install run | Install the CRD into the current cluster and run the controller locally |
make test | Unit and envtest-based tests |
make docker-build deploy IMG=registry.example.com/team-operator:0.1.0 | Build and deploy into the cluster |
Quality
go test ./... -race -cover | All tests, race detector, coverage |
go test -run TestParse/empty -v ./internal/audit | One sub-test, verbose |
golangci-lint run | Many linters at once (errcheck, staticcheck, govet…) |
govulncheck ./... | Known vulnerabilities in code paths you actually call |
Ship
CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o kaudit . | Small static binary |
docker build -t registry.example.com/kaudit:0.1.0 . | Multi-stage build into a distroless image |
goreleaser release --clean | Multi-platform binaries, checksums and release notes from a tag |