---
# System prepended metadata

title: golang

---

---
title: golang
---
# ![](https://i.imgur.com/4cxxIyS.png)

[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)
}
```