Wiki

Useful Go Snippets

Interface

To assert that a struct implements an interface:

1
var _ CoolInterface = (*CuteStruct)(nil)

Network

Serve http multiplexer under subpath

1
2
3
4
5
6
7
8
9
import 	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"

gwmux := runtime.NewServeMux()
if err := pb.RegisterSomeServiceHandler(ctx, gwmux, conn); err != nil {
    panic(err)
}

mux := http.NewServeMux()
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", gwmux))

Test server with any available port

If you hardcode the port number, your CI system might have trouble running two tests at once that both want the same port. Instead, use whichever port available:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
grpcLis, err := net.Listen("tcp", "localhost:0")
if err != nil {
    t.Fatal(err)
}
server := grpc.NewServer()
// ... pb.RegisterSomeServiceServer(s, svc)
go server.Serve(grpcLis)

conn, err := grpc.Dial(srv.grpcLis.Addr().String(), grpc.WithInsecure())
if err != nil {
    t.Fatalf("did not connect: %v", err)
}
defer conn.Close()

clnt := pb.NewSomeServiceClient(conn)

Struct

Validate Fields and Set Default Values

It gets verbose to validate configuration input or set default values, like the following. It gets worse when add more fields to Config.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Config struct {
    KubeClientSet kubernetes.Interface
    StopCh        <-chan struct{}
    Threadiness   int
}

func NewController(c *Config) (*Controller, error) {
    if c.KubeClientSet == nil {
        return nil, errors.New("KubeClientSet cannot be nil")
    }
    if c.StopCh == nil {
        return nil, errors.New("StopCh cannot be nil")
    }

    if c.Threadiness == 0 {
        c.Threadiness = 2
    }
    return &Controller{/* ... */}
}

I recommend using go-playground/validator for validation and creasty/defaults for setting defaults. Both packages leverage the field tags in struct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import (
    "github.com/creasty/defaults"
    "github.com/go-playground/validator/v10"
)


type Config struct {
    KubeClientSet kubernetes.Interface `validate:"required"`
    StopCh        <-chan struct{}      `validate:"required"`
    Threadiness   int                  `default:"2" validate:"gte=0,lte=10"`
}

func NewController(c *Config) (*Controller, error) {
    if err := validator.New().Struct(conf); err != nil {
        return nil, fmt.Errorf("invalid config: %v", err)
    }
    if err := defaults.Set(conf); err != nil {
        return nil, fmt.Errorf("could not set default values: %v", err)
    }
    return &Controller{/* ... */}
}

HashSet with Empty Struct

Empty struct takes exactly zero memory.

1
2
3
4
5
6
7
8
9
strSet := make(map[string]struct{})

// Add to set.
strSet["hello"] = struct{}{}

// Check if the set contains an element.
if _, ok := strSet["hello"]; ok {
    log.Println("set contains `hello'")
}

Functional Options

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
type Foo struct {
    Num int
    Str string
}

type Option func(f *Foo)

func WithNum(num int) Option {
  return func(f *Foo) {
    f.Num = num
  }
}

func WithStr(str string) Option {
  return func(f *Foo) {
    f.Str = str
  }
}

func New(opts ...Option) *Foo {
  foo := &Foo{
    num: 10,
    str: "hello",
  }

  for _, applyOpt := range opts {
    applyOpt(foo)
  }

  return &foo
}

func main() {
  foo := New()
  foo = New(WithNum(30))
  foo = New(WithNum(20), WithStr("world"))
}

For more details, see Parameters with Defaults in Go: Functional Options

Certificate Signing Requests with Email Address

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var oidEmailAddress = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}

func NewCSR(commonName string, key crypto.PrivateKey, w io.Writer) error {
	subj := pkix.Name{
		CommonName:         commonName,
		Country:            []string{"US"},
		Province:           []string{"California"},
		Locality:           []string{"Palo Alto"},
		Organization:       []string{"Pied Piper, Inc."},
		OrganizationalUnit: []string{"Best team"},
		ExtraNames: []pkix.AttributeTypeAndValue{
			// Crazy dance just to set the email address in CSR.
			// https://stackoverflow.com/questions/26043321/create-a-certificate-signing-request-csr-with-an-email-address-in-go.
			{
				Type: oidEmailAddress,
				Value: asn1.RawValue{
					Tag:   asn1.TagIA5String,
					Bytes: []byte("[email protected]"),
				},
			},
		},
	}

	// Don't add email addresses to the CertificateRequest EmailAddresses field,
	// which sets a SubjectAltName (SAN). Those are designed more for things
	// like signed emails. In the context of TLS certificates, SANs should be
	// used for alternatively valid hostnames and IP addresses.
	template := x509.CertificateRequest{
		Subject:            subj,
		SignatureAlgorithm: x509.SHA256WithRSA,
	}

	csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, key)
	if err != nil {
		return err
	}
	return pem.Encode(w, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
}

Operators

^ only means bit-wise XOR in Go. For logical XOR, one can use !=.

1
2
3
4
var a, b bool
if a != b {
    // only true if exactly one of {a, b} is true.
}