---
title: golang
---
# 
[Go](https://golang.org/) is a compiled language designed by Google as a simpler and more productive C++.
One can experiment with the language at [Go playground](https://play.golang.org/).
There is an official package hub as well -- [pkg.go.dev](https://pkg.go.dev/).
It is said that *the cloud* is built with Go.
Go has great support for networking, concurrency, and APIs.
However, it boasts a few sharp edges that cuts developers familiar with C/C++.
A github repo for ranting can even be [found](https://github.com/ksimka/go-is-not-good).
I started with these:
1. [Go: the Good, the Bad and the Ugly](https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/)
2. [Go is a terrible language](https://pasztor.at/blog/go-is-terrible/)
Don't take the negativity to hard, as they are presented here to balance the praises of Go coming ahead.
The post *[Eleven Years of Go](https://blog.golang.org/11years)* provides keywords and a very brief overview of current status.
## Installation
[Installation guide](https://golang.org/doc/install)
## Hello World
```go=
package main
import "fmt"
func main() {
fmt.Println("Hello Go")
}
```
## go command line
### command `go run`
Execute source code directly. Must run in *main package*.
```bash=
go run hello.go
```
### command `go mod init`
Create go module. First, create a `greetings` directory for your Go module source code. Then, create a `go.mod` file using `go mod init` command.
```bash=
mkdir greetings
cd greetings
go mod init example.com/greetings
```
### file `go.mod`
`go.mod` includes the name of your module and the Go version your code supports. As you add more and more dependencies, the `go.mod` file will list the specific module versions to use. This keeps builds reproducible and gives you direct control over which module versions to use.
Example `go.mod`:
```go=
module hello
go 1.14
replace example.com/greetings => ../greetings
```
### command `go build`
Command `go build` makes Go locate the modules and add it as a dependency to the `go.mod` file.
We can Look at `go.mod` again to see the changes made by go build, including the require directive Go added.
```go=
module hello
go 1.14
replace example.com/greetings => ../greetings
require example.com/greetings v0.0.0-00010101000000-000000000000
```
### command `go fmt`
Format go source code. To make spacing and position of brace uncontroversial.
```bash=
go fmt main.go
```
### command `go test`
The command `go test` is a built-in support for unit testing, which makes testing easier. To test as you go, create a file called `greetings_test.go`:
```go=
func TestHelloName(t *testing.T) {
name := "Gladys"
want := regexp.MustCompile(`\b`+name+`\b`)
msg, err := Hello("Gladys")
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
}
}
```
At the command line in `greetings` directory, run `go test` command to execute the unit test:
```bash=
$ go test
PASS
ok example.com/greetings 0.364s
```
### Installation
Change the installation directory using `go env`
```bash=
go env -w GOBIN=/path/to/your/bin
```
Once you've updated the shell path, run the `go install` command to compile and install the package.
```bash=
go install
```
## Package
Every Go program is made up of packages. Programs start running in `package main`. By convention, the package name is the same as the last element of the import path.
## Imports
```go
import (
"fmt"
"math"
)
```
or
```go
import "fmt"
import "math"
```
## Exports
In Go, a name is exported if it **begins with a capital letter**. For example, `Pi` is an exported value from the `math` package.
## Variables
```go=
// go's type goes after identifier
var foo int = 87
// Variables declared without an explicit initial value are given their zero value.
var foo int
// omit type if two parameters are of same type
var foo, bar int = 87, 9487
var foo = 87
// implicit typing
foo := 87
```
:::warning
Outside a function, every statement begins with a keyword (var, func, and so on). So `:=` is not available outside a function.
:::
Constants
```go=
/*
Constants cannot be declared using the := syntax
*/
const constant = "This is a constant"
/*
Numeric constants are high-precision values
*/
const (
Small = 1 >> 99
Big = Small << 99
)
/*
In golang, iota can be used to represents successive integer constants.
*/
const (
_ = iota
a
b
c = 1 << iota
d
)
fmt.Println(a, b, c, d)
// result: 1 2 8 16
```
## Functions
```go=
func functionName() {}
func functionName(param1 string, param2 int) {}
/* omit type if two parameters are of same type */
func functionName(param1, param2 int) {}
func functionName() int {
return 87
}
func returnMultiple() (int, string) {
return 87, "foo_bar"
}
var x, str = returnMultiple()
/*
Named return function
A return statement without arguments returns the named return values.
Go's return values may be named.
If so, they are treated as variables defined at the top of the function.
*/
func returnMultiple2() (n int, s string) {
n = 87
s = "foo_bar"
return
}
var x, str = returnMultiple2()
```
## Basic Types
* `bool`
* `string`
* `int int8 int16 int32 int64`
* `uint uint8 uint16 uint32 uint64 uintptr`
:::info
size of `int` and `uint` is platform specific.
:::
* `byte` is alias of `uint8`; `type byte uint8`
* `rune` is alias of `int32` -- represents a Unicode code point; `type rune int32`
* `float32 float64`
* `complex64, complex128`
* Default type for literals:
* Integers, e.g., `-22, 0`, are of type `int`
* Floats, e.g., `4.0, -23.55`, are of type `float64`
* Complex numbers, e.g., `2.3 + 6.0i, -7.777 - 99.3i`, are of type `complex128`
### Type conversions
```go=
i := 42
f := float64(i)
u := uint(f)
```
### Print type
```go=
i := 1 + 2i
fmt.Println("%T", i)
```
## Custom Types and Methods
A custom type is a collection of other types.
The order of definition is irrelevant.
Let's call the following block **example-ABCD**.
```go
import ("fmt";"reflect")
type D struct {
d int `tag as\t"raw string"`
e int "tag as\t\"normal string\""
}
type A struct {
C // embedded/anonymous/promoted field
x, y int
z *float64
w B
D // embedded/anonymous/promoted field
}
type Bfunc func (int, *int64, func (int64, int32) (int64)) (string, error)
type B struct {
f Bfunc
n int64
}
type C struct {
int a, b
}
```
Type/struct `A` has two fields specified only by type.
This sort of field is called embedded, anonymous, or promoted.
Normally, fields are access through:
```go
var a A
a.x
a.y
a.z
a.w.f
a.w.n
```
For embedded fields, access is gained with:
```go
a.C.a
a.C.b
a.a
a.b
a.D.d
a.D.e
a.d
a.e
```
A trailing string in a type-field definition is called a *tag*.
Tags can be access with the aid of the `reflect` package.
They can even be iterated.
Note that the `reflect` package is much more powerful than this; it is an extensive materialization of [reflective programming](https://blog.golang.org/laws-of-reflection).
```go
func main() {
var d D
fmt.Println(reflect.TypeOf(d).Field(0).Tag)
fmt.Println(reflect.TypeOf(d).Field(1).Tag)
v := reflect.TypeOf(d)
for i := 0; i < v.NumField(); i++ {
fmt.Println(v.Field(i).Tag)
}
}
```
A method for some type `T` takes one of the two forms:
```go
func (self T) f(<args>) (<returns>)
func (self *T) g(<args>) (<returns>)
```
They could be called with:
```go
T t
<returns> := t.f(<args>)
<returns> := t.g(<args>)
```
This resembles C/python-style OOP.
Note that in Go, pointers are synonymous to references, unlike C++; in fact, Go only defines the concept of pointers.
Hence, the last line of the previous code block could be effectively written in two ways:
```go
<returns> := (&t).g(<args>)
<returns> := t.g(<args>)
```
Here's a demonstration with **example-ABCD**:
```go
func F (p1 int, p2 *int64, p3 func (int64, int32) (int64)) (string, error) {
r := p3(*p2, int32(p1))
return fmt.Sprintf("%T: %v", r, r), nil
}
func (self *B) g(G func (int64, int32) (int64)) string {
str, err := self.f(-13, &(self.n), G)
if err != nil {
panic("How is this possible?")
}
return fmt.Sprintf("Called method g: %v", str)
}
func main() {
b := B{F, 19}
fmt.Println(b.g(func (x int64, y int32) int64 {
return x * int64(y)
}))
}
```
# Kubernetes Scheduling Framework
## Usage
https://github.com/kubernetes/enhancements/blob/master/keps/sig-scheduling/624-scheduling-framework/README.md#custom-scheduler-plugins-out-of-tree
```go
import (
scheduler "k8s.io/kubernetes/cmd/kube-scheduler/app"
)
func main() {
command := scheduler.NewSchedulerCommand(
scheduler.WithPlugin("example-plugin1", ExamplePlugin1),
scheduler.WithPlugin("example-plugin2", ExamplePlugin2))
if err := command.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
```
More elaborate:
https://github.com/kubernetes-sigs/scheduler-plugins/blob/master/cmd/scheduler/main.go
```go
import ( // Ensure scheme package is initialized.
_ "sigs.k8s.io/scheduler-plugins/pkg/apis/config/scheme"
)
func main() {
rand.Seed(time.Now().UnixNano()) // import ("math/rand"; "time")
// import "k8s.io/kubernetes/cmd/kube-scheduler/app"
command := app.NewSchedulerCommand(
// import "sigs.k8s.io/scheduler-plugins/pkg/*"
app.WithPlugin(coscheduling.Name, coscheduling.New),
app.WithPlugin(noderesources.AllocatableName, noderesources.NewAllocatable),
// Sample plugins below.
app.WithPlugin(crossnodepreemption.Name, crossnodepreemption.New),
app.WithPlugin(podstate.Name, podstate.New),
app.WithPlugin(qos.Name, qos.New),
)
// import "k8s.io/component-base/logs"
logs.InitLogs()
defer logs.FlushLogs()
if err := command.Execute(); err != nil {
os.Exit(1) // import "os"
}
}
```
Root of source is: https://github.com/kubernetes/kubernetes/blob/v1.19.5-rc.0/
`/pkg/scheduler/framework/runtime/registry.go`
```go=29
type PluginFactory = func(configuration runtime.Object, f v1alpha1.FrameworkHandle) (v1alpha1.Plugin, error)
```
`/cmd/kube-scheduler/app/server.go`
```go=64
func NewSchedulerCommand(registryOptions ...Option) *cobra.Command
```
```go=288
func WithPlugin(name string, factory runtime.PluginFactory) Option {
return func(registry runtime.Registry) error {
return registry.Register(name, factory)
}
}
```
`k8s.io/pkg/scheduler/framework/runtime/framework.go`
```go=124
type frameworkOptions struct {
clientSet clientset.Interface
eventRecorder events.EventRecorder
informerFactory informers.SharedInformerFactory
snapshotSharedLister framework.SharedLister
metricsRecorder *metricsRecorder
profileName string
podNominator framework.PodNominator
extenders []framework.Extender
runAllFilters bool
captureProfile CaptureProfile
}
// Option for the frameworkImpl.
type Option func(*frameworkOptions)
```
`k8s.io/apimachinery/pkg/runtime/interfaces.go`
```go=299
type Object interface {
GetObjectKind() schema.ObjectKind
DeepCopyObject() Object
}
```
https://github.com/kubernetes/kubernetes/blob/v1.19.5-rc.0/pkg/scheduler/framework/v1alpha1/interface.go
```go=209
type Plugin interface {
Name() string
}
```
Core: `/pkg/scheduler/framework/v1alpha1`
How to write a plugin: https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler/framework/plugins
Sample scoring plugin:
`sigs.k8s.io/scheduler-plugins/pkg/podstate/pod_state.go`
```go=
package podstate
import (
"context"
"fmt"
"math"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
)
type PodState struct {
handle framework.FrameworkHandle
}
var _ = framework.ScorePlugin(&PodState{})
const Name = "PodState"
func (ps *PodState) Name() string {
return Name
}
// Score invoked at the score extension point.
func (ps *PodState) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
nodeInfo, err := ps.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
if err != nil {
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
}
// pe.score favors nodes with terminating pods instead of nominated pods
// It calculates the sum of the node's terminating pods and nominated pods
return ps.score(nodeInfo)
}
// ScoreExtensions of the Score plugin.
func (ps *PodState) ScoreExtensions() framework.ScoreExtensions {
return ps
}
func (ps *PodState) score(nodeInfo *framework.NodeInfo) (int64, *framework.Status) {
// get nominated Pods for node from nominatedPodMap
nominatedPodNum := int64(len(ps.handle.PreemptHandle().NominatedPodsForNode(nodeInfo.Node().Name)))
var terminatingPodNum int64
for _, p := range nodeInfo.Pods {
// Pod is terminating if DeletionTimestamp has been set
if p.Pod.DeletionTimestamp != nil {
terminatingPodNum++
}
}
return terminatingPodNum - nominatedPodNum, nil
}
func (ps *PodState) NormalizeScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {
// Find highest and lowest scores.
lowest := scores[0].Score
highest := lowest
for _, nodeScore := range scores {
if highest < nodeScore.Score {
highest = nodeScore.Score
}
if lowest > nodeScore.Score {
lowest = nodeScore.Score
}
}
// Transform the highest to lowest score range to fit the framework's min to max node score range.
oldRange := highest - lowest
newRange := framework.MaxNodeScore - framework.MinNodeScore
for i, nodeScore := range scores {
scores[i].Score = framework.MinNodeScore
if oldRange != 0 {
scores[i].Score += (nodeScore.Score - lowest) * newRange / oldRange
}
}
return nil
}
// New initializes a new plugin and returns it.
func New(_ runtime.Object, h framework.FrameworkHandle) (framework.Plugin, error) {
return &PodState{handle: h}, nil
}
```
## Plugins
:::success
All codes in this *Plugins* section originates from [this](https://github.com/kubernetes/kubernetes/blob/v1.19.5-rc.0/pkg/scheduler/framework/v1alpha1/interface.go).
The commencing line number of each code snippet is loyal, but lines that follow could be modified without affecting correctness.
:::
`Plugin` is the ancestor of all the scheduling framework plugins.
```go=209
type Plugin interface {
Name() string
}
```
`Status` indicates the result of running a plugin.
When the status code is not `Success`, the reasons should explain why.
Note that a `nil` Status is considered as `Success`.
```go=98
type Status struct {
code Code
reasons []string
}
func (s *Status) Code() Code {
if s == nil {
return Success
}
return s.code
}
func (s *Status) Reasons() []string {
return s.reasons
}
func (s *Status) Message() string {
if s == nil {
return ""
}
return strings.Join(s.reasons, ", ")
}
```
```go=52
type Code int
const(
Success Code = iota
Error
Unschedulable
UnschedulableAndUnresolvable
Wait
Skip
)
var codes = []string{ // This should be identical to the above.
"Success",
"Error",
"Unschedulable",
"UnschedulableAndUnresolvable",
"Wait",
"Skip",
}
func (c Code) String() string {
return codes[c]
}
```
### QueueSort Plugins
Only **one** QueueSort plugin may be enabled at a time.
Pods in the scheduling queue are sorted according to `Less`.
```go=219
type QueueSortPlugin interface {
Plugin
Less(*QueuedPodInfo, *QueuedPodInfo) bool
}
```
### PreFilter Plugins
All PreFilter plugins must return success or the pod will be rejected.
`PreFilterExtensions` returns a `PreFilterExtensions` interface if the plugin implements one, or `nil` if it does not.
```go=239
type PreFilterPlugin interface {
Plugin
PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status
PreFilterExtensions() PreFilterExtensions
}
```
`PreFilterExtensions` allow specifying callbacks to make incremental updates to its supposedly pre-calculated state.
The framework calls `AddPod`/`RemovePod` to evaluate the impact of adding/removing `podToAdd`/`podToRemove` to/from the node while scheduling `podToSchedule`.
`AddPod`/`RemovePod` will only be called after `PreFilter`, possibly on a cloned `CycleState`, and may call those functions more than once before calling `Filter` again on a specific node.
```go=228
type PreFilterExtensions interface {
AddPod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *NodeInfo) *Status
RemovePod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToRemove *v1.Pod, nodeInfo *NodeInfo) *Status
}
```
### Filter Plugins (archaic: Predicates)
`Filter` should return a `Status` of value `Success`, `Unschedulable`, `Error`, or the like; anything other than `Success` will prevent the pod from running on the node.
If `Filter` doesn't return `Success`, refer to `scheduler/algorithm/predicates/error.go` for setting error messages.
The logic of `Filter` should be based **solely** on the `nodeInfo` parameter and **never** `NodeInfoSnapshot`.
```go=260
type FilterPlugin interface {
Plugin
Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status
}
```
### PostFilter Plugins
These plugins are called after a pod cannot be scheduled.
Returned `Status` should be one of:
`Unschedulable`: the plugin gets executed successfully but the pod cannot be made schedulable.
`Success`: the plugin gets executed successfully and the pod can be made schedulable.
`Error`: the plugin aborts due to some internal error.
Informational plugins should be configured first, and always return `Unschedulable` status.
A non-`nil` `PostFilterResult` may be returned along with a `Success` status. E.g., a preemption plugin may choose to return `nominatedNodeName`, so that framework can reuse that to update the preemptor pod's `.spec.status.nominatedNodeName` field.
```go=279
type PostFilterPlugin interface {
Plugin
PostFilter(ctx context.Context, state *CycleState, pod *v1.Pod, filteredNodeStatusMap NodeToStatusMap) (*PostFilterResult, *Status)
}
```
```go=527
type PostFilterResult struct {
NominatedNodeName string
}
```
### PreScore Plugins
`PreScore` is called after a list of nodes passed the filtering phase.
All *PreScore* plugins must return `Success` or the pod will be rejected.
*PreScore* is an informational extension point; a plugin may use this data to update internal state or to generate logs/metrics.
```go=298
type PreScorePlugin interface {
Plugin
PreScore(ctx context.Context, state *CycleState, pod *v1.Pod, nodes []*v1.Node) *Status
}
```
### Score Plugins (archaic: Priorities)
`Score` is called on each filtered node; it must return `Success` and an integer score for a node.
All scoring plugins must return `Success` or the pod will be rejected.
`ScoreExtensions` returns a `ScoreExtensions` interface if it implements one, or `nil` if does not.
```go=316
type ScorePlugin interface {
Plugin
Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status)
ScoreExtensions() ScoreExtensions
}
```
A successful run of `NormalizeScore` will update the `scores` list and return a `Success` status.
```go=307
type ScoreExtensions interface {
NormalizeScore(ctx context.Context, state *CycleState, p *v1.Pod, scores NodeScoreList) *Status
}
```
```go=36
type NodeScoreList []NodeScore
type NodeScore struct {
Name string // Node name.
Score int64 // Node score.
}
```
The output of a *Score* plugin must be an integer between `MinNodeScore` and `MaxNodeScore`; if not, the scheduling cycle is aborted.
This is the output after running the optional `NormalizeScore` function of the plugin.
If `NormalizeScore` is not provided, the output of `Score` must be in this range.
After the optional `NormalizeScore`, the scheduler will combine node scores from all plugins according to the configured plugin weights.
```go=84
const (
// The minimum score a Score plugin is expected to return.
MinNodeScore int64 = 0
// The maximum score a Score plugin is expected to return.
MaxNodeScore int64 = 100
// The maximum total score.
MaxTotalScore int64 = math.MaxInt64
)
```
### Reserve Plugins (archaic: Assumptions)
Anything other than the `Success` status will lead to rejection of the pod.
`Reserve` is called when the scheduler cache is updated; if this method fails, the scheduler will call `Unreserve` for all enabled `ReservePlugins`.
`Unreserve` must be idempotent and may be called by the scheduler even if the corresponding `Reserve` method for the same plugin was not called.
```go=333
type ReservePlugin interface {
Plugin
Reserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
Unreserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string)
}
```
### Permit Plugins
*Permit* plugins are called before *PreBind* plugins to prevent or delay the binding of a Pod.
A permit plugin can do one of three things.
1. **Approve**:
Once all *Permit* plugins approve a pod, it is sent for binding.
2. **Deny**:
If any *Permit* plugin denies a pod, it is returned to the scheduling queue.
This will trigger the `Unreserve` methods in the *Reserve* phase.
3. **Wait (with a timeout)**:
If a permit plugin returns `Wait`, then the pod is kept in the permit phase until a plugin approves it.
If a timeout occurs, wait becomes **deny** and the pod is returned to the scheduling queue, triggering the `Unreserve` methods in the *Reserve* phase.
```go=369
type PermitPlugin interface {
Plugin
Permit(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (*Status, time.Duration)
}
```
### PreBind Plugins
`PreBind` is called before binding a pod.
All *PreBind* plugins must return `Success` or the pod will be rejected and won't be sent for binding.
```go=358
type PreBindPlugin interface {
Plugin
PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
}
```
### Bind Plugins
*Bind* plugins will be called in the configured order after all *PreBind* plugins have completed.
A *Bind* plugin may choose whether or not to handle the given Pod.
- If a bind plugin chooses to handle a Pod, the remaining bind plugins are skipped.
- If a bind plugin does not handle a pod, it must return the `Skip` status code.
- If a bind plugin returns `Error`, the pod is rejected and will not be bound.
```go=382
type BindPlugin interface {
Plugin
Bind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
}
```
### PostBind Plugins
`PostBind` is called after a pod is successfully bound.
These plugins are informational.
If a plugin needs to clean up its state after a pod is scheduled and bound, *PostBind* is the extension point that it should register.
```go=358
type PostBindPlugin interface {
Plugin
PostBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string)
}
```