golang

module
v0.0.0-...-4b984f7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2024 License: Apache-2.0

README

= Golang Boot Camp
:toc: manual

== Getting started

* https://golang.org/doc/

=== Check go version

[source, go]
----
# go version
go version go1.21.5 linux/arm64
----

=== package, import, main

[source, go]
----
package main
  
import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
----

=== Run main methods

[source, go]
----
$ go run hello.go 
Hello, World!
----

=== Call External Methods

[source, go]
----
package main
  
import "fmt"

import "rsc.io/quote"

func main() {
    fmt.Println("Hello, World!")
    fmt.Println(quote.Glass())
    fmt.Println(quote.Go())
    fmt.Println(quote.Hello())
    fmt.Println(quote.Opt())
}
----

== Work with Go standard project

=== Project Structure

[source, go]
----
# tree greetings/
greetings/
├── cmd
│   └── greetings
│       └── main.go
└── pkg
    └── greetings
        ├── greetings.go
        └── greetings_test.go
----

A standard Go project structure can vary depending on the size and nature of the project, but there are some common conventions that many Go developers follow:

* *cmd*: The `cmd` directory is for the main applications of your project. Each application can have its own subdirectory. For example, myapp could be the main entry point for your application
* *internal*: The `internal` directory is for packages that are only used within your project, not meant for external use.
* *pkg*: The pkg directory contains libraries and packages that are meant to be used by other projects. Each package within pkg can have its own subdirectory.

=== Init and Setup Dependencies

[source, go]
----
go mod init github.com/kylinsoong/golang/greetings
go mod tidy
----

=== Run

[source, go]
----
go run cmd/greetings/main.go
----

=== How main module call pkg module

[source, go]
----
package main

import (
    "github.com/kylinsoong/golang/greetings/pkg/greetings"
)

func main() {
    names := []string{"Gladys", "Samantha", "Darrin", "Kylin"}
    messages, err := greetings.Hellos(names)
}
----

=== Run Unit Test

[source, go]
----
go test ./pkg/greetings/ 
----

=== Build

[source, go]
----
go build -o a.out cmd/greetings/*.go
----

=== Run Binary File

[source, go]
----
# ./a.out
----

== Data Struct

=== simple map

[source, go]
----
    processedResources := make(map[string]bool)

    processedResources["foo.yaml"] = true
    processedResources["bar.yaml"] = false
    processedResources["zoo.yaml"] = false

    for key, value := range processedResources {
        fmt.Printf("%s: %v\n", key, value)
    }

    fmt.Println(processedResources["zoo.yaml"])

    value, exists := processedResources["coo.yaml"]
    if exists {
        fmt.Printf("coo.yaml: %v\n", value)
    } else {
        fmt.Println("coo.yaml not exist")
    }
----

=== simple struct

[source, go]
----
type WatchedNamespaces struct {
    Namespaces     []string
    NamespaceLabel string
}

func main() {
    watchedNamespaces := WatchedNamespaces{
        Namespaces:     []string{"namespace1", "namespace2"},
        NamespaceLabel: "watched",
    }

    fmt.Println(watchedNamespaces.Namespaces)
    fmt.Println(watchedNamespaces.NamespaceLabel)
}
----

=== struct with func field

Using a Go struct with a function field offers flexibility and allows you to encapsulate behavior within the struct while enabling dynamic customization.

[source, go]
----
type Manager struct {
    queueLen            int
    processAgentLabels  func(map[string]string, string, string) bool
}

func customProcessAgentLabels(labels map[string]string, namespace string, name string) bool {
    fmt.Printf("Custom Processing Agent Labels: %v, Namespace: %s, Name: %s\n", labels, namespace, name)
    return true
}

func main() {
    appMgr := Manager{
        queueLen:           10,
        processAgentLabels: customProcessAgentLabels,
    }
    appMgr.processAgentLabels(map[string]string{"key": "value"}, "exampleNamespace", "exampleName")
}
----

== Fundamentals

=== object-oriented programming: interface composition

Go does not support traditional interface inheritance like some other object-oriented programming languages. Instead, Go uses a concept called "interface composition" or "embedding" to achieve similar goals without relying on classical inheritance.

In Go, you can embed one interface within another to create a new interface that includes the methods of the embedded interface. 

[source, go]
.*Interface*
----
type Interface interface {
	Add(item interface{})
	Len() int
	Get() (item interface{}, shutdown bool)
	Done(item interface{})
	ShutDown()
	ShutDownWithDrain()
	ShuttingDown() bool
}
----

[source, go]
.*DelayingInterface*
----
type DelayingInterface interface {
	Interface
	AddAfter(item interface{}, duration time.Duration)
}
----

[source, go]
.*RateLimitingInterface*
----
type RateLimitingInterface interface {
	DelayingInterface
	AddRateLimited(item interface{})
	Forget(item interface{})
	NumRequeues(item interface{}) int
}
----

== Multi-threads

=== goroutine

The goroutine is a lightweight thread of execution managed by the Go runtime. Goroutines enable concurrent programming in a way that is more efficient and scalable compared to traditional threads.

[source, go]
----
package main

import (
        "fmt"
        "time"
)

func printNumbers() {
    for i := 1; i <= 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Printf("%d \n", i)
    }
}

func main() {
    go printNumbers()

    for i := 1; i <= 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Printf("A%d \n", i)
    }
}
----

=== channel: send and receive data

Channels are a typed conduit through which you can send and receive values with the channel operator *<-*:

* ch <- v           send v to channel
*  v := <-ch         receive from channel, and assign value to v

[source, go]
----
func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // send sum to c
}

func Test_Send_Receive(t *testing.T) {
	s := []int{7, 2, 8, -9, 4, 0}
	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c
	fmt.Println(x, y, x+y)
}
----


=== channel: communication and synchronization between goroutines

In Go, channels are a powerful mechanism for communication and synchronization between goroutines. They provide a way for one goroutine to send data to another goroutine. 

[source, go]
----
func numberGenerator(ch chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 1; i <= 5; i++ {
        ch <- i // Send numbers 1 to 5 to the channel
    }
    close(ch) // Close the channel to signal no more data will be sent
}

func squareCalculator(ch chan int, resultCh chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for num := range ch {
        square := num * num
        resultCh <- square // Send squared result to the resultCh channel
    }
    close(resultCh) // Close the resultCh channel to signal no more results will be sent
}

func resultPrinter(resultCh chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for result := range resultCh {
        fmt.Println("Squared Result:", result)
    }
}

func main() {
    numberCh := make(chan int)
    resultCh := make(chan int)
    var wg sync.WaitGroup
    wg.Add(3)
    go numberGenerator(numberCh, &wg)
    go squareCalculator(numberCh, resultCh, &wg)
    go resultPrinter(resultCh, &wg)
    wg.Wait()
}
----

=== channel: multiple chanels with select case statement

The select statement in Go is used to choose from multiple communication operations. It allows a goroutine to wait on multiple communication operations, blocking until one of them can proceed.

[source, go]
----
func simple_worker(c chan string) {
	c <- fmt.Sprintf("Hello from Channel %v", c)
}

func Test_Multiple_Chan_With_Select(t *testing.T) {
	ch1 := make(chan string)
	ch2 := make(chan string)
	go simple_worker(ch1)
	go simple_worker(ch2)
	select {
	case msg1 := <-ch1:
		fmt.Println(msg1)
	case msg2 := <-ch2:
		fmt.Println(msg2)
	case <-time.After(3 * time.Second):
		fmt.Println("Timed out waiting for messages.")
	}
}
----

=== channel: signal completion of sub goroutine

[source, go]
----
func worker(ch chan struct{}) {
    fmt.Println("Worker is starting...")
    time.Sleep(2 * time.Second)
    fmt.Println("Worker is done!")
    ch <- struct{}{}
}

func main() {
    doneCh := make(chan struct{})
    go worker(doneCh)
    <-doneCh
    fmt.Println("Main function exiting.")
}
----

=== using signal to control application interruptiong and termination

In Go, the `os/signal` package provides a way to intercept signals sent to the program, such as termination signals (SIGINT for interrupt and SIGTERM for terminate). The signal usually wrapped with a channel that can be used to control application interruptiong and termination.

[source, go]
----
func main() {
    fmt.Println("Started to run tasks...")
    signals := make(chan os.Signal, 1)
    signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
    sig := <-signals
    fmt.Printf("Received signal: %v\n", sig)
}
----

=== defer statements a Last In, First Out (LIFO)  execution order

[source, go]
----
func main() {
    defer fmt.Println("This will be executed third.")
    defer fmt.Println("This will be executed second.")
    defer fmt.Println("This will be executed first.")
    fmt.Println("Hello, Go!")
}
----

=== thread-safe via defer and sync

[source, go]
----
type Counter struct {
    value int
    mu    sync.Mutex
}

func (c *Counter) increment() {
    c.mu.Lock()
    defer c.mu.Unlock() 
    c.value++
}

func (c *Counter) getValue() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}
----

== K8S

=== How to repeatedly calls a provided function

The `k8s.io/apimachinery/pkg/util/wait` provides utilities for waiting and timing operations. Specifically, `wait.Until` is a function that repeatedly calls a provided function until the stop channel is closed or a timeout is reached.

[source, go]
.*wait.Until*
----
func exampleWork() {
    fmt.Println("Doing some work...")
    time.Sleep(2 * time.Second)
}

func main() {
    stopCh := make(chan struct{})
    go wait.Until(exampleWork, time.Second, stopCh)
    time.Sleep(5 * time.Second)
    close(stopCh)
    time.Sleep(1 * time.Second)
    fmt.Println("Main goroutine exiting...")
}
----

=== How to use kubernetes rate limited workqueue

Refer to link:#object-oriented-programming-interface-composition[object-oriented programming: interface composition] for more details about `k8s.io/client-go/util/workqueue` and `RateLimitingInterface` implemenration.

=== How to create kubernetes client

There are 2 stps necessary to create a kubeClient:

* Create Kubernetes Rest Config, If your application run in Kubernetes, the use the certifications keys in Namespace default ServiceAccount, if your application run outside Kubernetes, then you need pass `~/.kube/config` file to create Rest Config
* Create Kubernetes Client via Kubernetes Rest Config 

[source, go]
----
import "k8s.io/client-go/kubernetes"
import "k8s.io/client-go/rest"
import "k8s.io/client-go/tools/clientcmd"

var kubeClient  kubernetes.Interface 
var config      *rest.Config
var err         error


if *inCluster {
    config, err = rest.InClusterConfig()
} else {
    config, err = clientcmd.BuildConfigFromFlags("", *kubeConfig)
}
if err != nil {
    log.Fatalf("[INIT] error creating configuration: %v", err)
}

kubeClient, err = kubernetes.NewForConfig(config)
if err != nil {
    log.Fatalf("[INIT] error connecting to the client: %v", err)
}
----

=== How to use kubernetes client-side caching mechanism and cache api create a configmap watcher

`k8s.io/client-go/tools/cache` is a client-side caching mechanism. It is useful for reducing the number of server calls you'd otherwise need to make. Reflector watches a server and updates a Store. Two stores are provided; one that simply caches objects (for example, to allow a scheduler to list currently available nodes), and one that additionally acts as a FIFO queue (for example, to allow a scheduler to process incoming pods).

[source, go]
----
	cfgMapInformer = cache.NewSharedIndexInformer(
		cache.NewFilteredListWatchFromClient(
			restClientv1,
			Configmaps,
			namespace,
			everything,
		),
		&v1.ConfigMap{},
		resyncPeriod,
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
	)

	cfgMapInformer.AddEventHandlerWithResyncPeriod(
		&cache.ResourceEventHandlerFuncs{
			AddFunc:    func(obj interface{}) { enqueueConfigMap(obj, OprTypeCreate) },
			UpdateFunc: func(old, cur interface{}) { enqueueConfigMap(cur, OprTypeUpdate) },
			DeleteFunc: func(obj interface{}) { enqueueConfigMap(obj, OprTypeDelete) },
		},
		resyncPeriod,
	)
----

Directories

Path Synopsis
as3-benchmark
cmd/as3-benchmark
How to run?
How to run?
as3-mock
bigip-ctlr
-
greetings module
k8s-bigip-ctlr-2.16.1
config/apis/cis/v1
Package v1 is the v1 version of the API.
Package v1 is the v1 version of the API.
config/client/clientset/versioned
This package has the automatically generated clientset.
This package has the automatically generated clientset.
config/client/clientset/versioned/fake
This package has the automatically generated fake clientset.
This package has the automatically generated fake clientset.
config/client/clientset/versioned/scheme
This package contains the scheme of the automatically generated clientset.
This package contains the scheme of the automatically generated clientset.
config/client/clientset/versioned/typed/cis/v1
This package has the automatically generated typed clients.
This package has the automatically generated typed clients.
config/client/clientset/versioned/typed/cis/v1/fake
Package fake has the automatically generated clients.
Package fake has the automatically generated clients.
-
pkg/vlogger
Package vlogger implements an interface around basic logging features so that end-user and library writers can code to the interface without worrying about the specific type of logging package that is being used.
Package vlogger implements an interface around basic logging features so that end-user and library writers can code to the interface without worrying about the specific type of logging package that is being used.
k8s-bigip-ctlr-2.9.1-1
config/apis/cis/v1
Package v1 is the v1 version of the API.
Package v1 is the v1 version of the API.
config/client/clientset/versioned
This package has the automatically generated clientset.
This package has the automatically generated clientset.
config/client/clientset/versioned/fake
This package has the automatically generated fake clientset.
This package has the automatically generated fake clientset.
config/client/clientset/versioned/scheme
This package contains the scheme of the automatically generated clientset.
This package contains the scheme of the automatically generated clientset.
config/client/clientset/versioned/typed/cis/v1
This package has the automatically generated typed clients.
This package has the automatically generated typed clients.
config/client/clientset/versioned/typed/cis/v1/fake
Package fake has the automatically generated clients.
Package fake has the automatically generated clients.
-
pkg/vlogger
Package vlogger implements an interface around basic logging features so that end-user and library writers can code to the interface without worrying about the specific type of logging package that is being used.
Package vlogger implements an interface around basic logging features so that end-user and library writers can code to the interface without worrying about the specific type of logging package that is being used.
pkg/vlogger/console
log_console.go:
log_console.go:
k8s-client-test
nim-mock
nim-rest-client
map
net

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL