Wiki
* Scroll me *
PREFACE
About Wiki
GOLANG
Snippets
Pitfalls
Development
Production
SHELL
Shell Script
NETWORKING
Networking
KUBERNETES
Commands
Patterns
Potholes
ISTIO
Istio
VAULT
Vault
INFRA AS CODE
Terraform
Pulumi
Useful Go Snippets
Table of Contents
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.
}