シリーズ
Go勉強(1) mac+VSCode+Go環境を設定
Go勉強(2) kubernetes client-goのexamplesをbuildする
Go勉強(3) kubernetes client-goのexamplesを読んでみる
[Go勉強(4) kubernetes client-goでPodのwatcher(TUI)を書いてみる]
(https://qiita.com/oruharo/items/668d708cead0ad261346)
[Go勉強(5) kubernetes client-goでPodのwatcher(TUI)を書いてみる2]
(https://qiita.com/oruharo/items/b7a131bf3eda36dd08a3)
はじめに
Golang自体の勉強をかねて、kubernetesへのアクセスライブラリclient-go
を使ったサンプルソースを読んでみます。
■参考
・初めてのGo
・他言語プログラマがgolangの基本を押さえる為のまとめ
・他言語から来た人がGoを使い始めてすぐハマったこととその答え
・Goで学ぶポインタとアドレス
・「例外」がないからGo言語はイケてないとかって言ってるヤツが本当にイケてない件
サンプルコード
https://github.com/kubernetes/client-go/blob/master/examples/out-of-cluster-client-configuration/main.go
このコード読みます。 全行掲載します。
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Note: the example only works with the code within the same release/branch.
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"time"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
//
// Uncomment to load all auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth"
//
// Or uncomment to load specific auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func main() {
var kubeconfig *string
if home := homeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err.Error())
}
// create the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
for {
pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
// Examples for error handling:
// - Use helper functions like e.g. errors.IsNotFound()
// - And/or cast to StatusError and use its properties like e.g. ErrStatus.Message
namespace := "default"
pod := "example-xxxxx"
_, err = clientset.CoreV1().Pods(namespace).Get(pod, metav1.GetOptions{})
if errors.IsNotFound(err) {
fmt.Printf("Pod %s in namespace %s not found\n", pod, namespace)
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
fmt.Printf("Error getting pod %s in namespace %s: %v\n",
pod, namespace, statusError.ErrStatus.Message)
} else if err != nil {
panic(err.Error())
} else {
fmt.Printf("Found pod %s in namespace %s\n", pod, namespace)
}
time.Sleep(10 * time.Second)
}
}
func homeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
}
return os.Getenv("USERPROFILE") // windows
}
コードリーディング
コードを見ながら気になるところをコメントしていきます。
import パッケージ
importされいるパケージです。 リファレンスはhttps://godoc.org/ から検索できるようです。
パッケージ | 説明 |
---|---|
flag | コマンドラインフラグ解析を実装します。 |
fmt | Cのprintfおよびscanfに類似した機能を持つフォーマットされたI / Oを実装します。 「動詞」の形式はCから派生していますが、より単純です。 |
os | オペレーティングシステムの機能に対するプラットフォームに依存しないインターフェイスを提供します。エラー処理はGoに似ていますが、設計はUnixに似ています。失敗した呼び出しは、エラー番号ではなくエラータイプの値を返します。 |
path/filepath | ターゲットオペレーティングシステム定義のファイルパスと互換性のある方法でファイル名パスを操作するためのユーティリティルーチンを実装します。 |
time | 時間を測定および表示する機能を提供します。 |
k8s.io/apimachinery/pkg/api/errors | APIフィールド検証の詳細なエラータイプを提供します。 |
k8s.io/apimachinery/pkg/apis/meta/v1 | すべてのバージョンに共通のAPIタイプが含まれています。 |
k8s.io/client-go/kubernetes | 自動生成されたクライアントセットです。 |
k8s.io/client-go/tools/clientcmd | 固定構成、.kubeconfigファイル、コマンドラインフラグ、またはマージされた組み合わせから作業クライアントを構築するためのワンストップショッピングを提供します。 |
main関数
mainパッケージのmain関数がエントリーポイントとなって最初に実行されます。 javaのstatic main関数と同じですね。
func main() {
ポインタ
C言語ではかなり苦しめられるポインタですが、Goの場合はポインタ渡し(参照渡し)・値渡しくらいのことを理解しとけばそんなに難しくなさそうです。
var kubeconfig *string
:
kubeconfig = flag.String( ... )
:
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
↑flag
が返す文字列のポインタを受け取って(ポインタ渡し)、clientcmd
に文字列のコピーを渡してます(値渡し)。
↓ こうすると文字列のコピーを受け取って(値渡し)、さらにコピーを渡す(値渡し)ことになりますね。
文字列にポインタ渡しを使うのは無駄なコピー処理やメモリーを節約するのが目的でしょうね。
ポインタ渡しされた文字列を変更できるのかな。C言語的な考えだと文字列伸ばすと後ろのメモリが壊れるからダメなような。(後で調査)
var kubeconfig string
:
kubeconfig = *flag.String( ... )
:
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if文 初期化ステートメント
if home := homeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
}
このif home := homeDir();
の部分。 見慣れない構文です。 「初期化ステートメント」と言うそうです。
Goではswitch文にも同様に記述できます。
他言語でよくあるfor(int i=0; i<10; i++) {
のi=0
の部分と同じ位置付けのものです。
home := homeDir()
if home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
}
他言語だったら↑こんな感じで書きますね。 初期化ステートメントは1行減らすための構文?って最初思いましたが、初期化ステートメントによって変数のスコープをif文内に閉じ込めることができるんですね。
if errors.IsNotFound(err) {
:
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
:
} else {
:
}
この部分↑が初期化ステートメントじゃなかったら、↓こんな感じにネストすることになってイヤですね。
if errors.IsNotFound(err) {
:
} else {
statusError, isStatus := err.(*errors.StatusError);
if isStatus {
:
} else {
:
}
}
Flag
Pythonなんかにもあるコマンドライン パラメータを受け取ったり、usageを表示します。
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
flag.Parse()
helpを出すとこんな感じになります。kubernetesのconfigファイルを指定する引数を渡せます。
$ ./app --help
Usage of ./app:
-kubeconfig string
(optional) absolute path to the kubeconfig file (default "/Users/haruo/.kube/config")
多値返却
pythonのように複数の戻り値を簡単に返せて簡単に受け取れます。
使わない戻り値は"_"で受けとります。
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
:
clientset, err := kubernetes.NewForConfig(config)
:
_, err = clientset.CoreV1().Pods(namespace).Get(pod, metav1.GetOptions{})
ん? これだとerr
が二重宣言でビルドエラーじゃないの? と思いましたが、
試してみたら:=
を使う時、左辺に新しく宣言する変数が一つでもあればエラーにならないみたいです。
エラー処理
C → java,C# → Goの順に経験すると、Cのif (rc != 0)
だらけのコードに逆戻りするような感じがしてしまうのですが、Goのそれは一周まわった完成形って感じでしょうか。
if err != nil {
panic(err.Error())
}
試しに誤ったconfigファイルを渡して無理やりエラーを発生させると、以下のような結果になりました。
$ ./app -kubeconfig /Users/xxxxx/.ssh/config
panic: error loading config file "/Users/xxxxx/.ssh/config": couldn't get version/kind; json parse error: json: cannot unmarshal string into Go value of type struct { APIVersion string "json:\"apiVersion,omitempty\""; Kind string "json:\"kind,omitempty\"" }
goroutine 1 [running]:
main.main()
/Users/xxxxx/go/src/out-of-cluster-client-configuration/main.go:54 +0x83a
繰り返し
繰り返しを表すキーワードはfor
のみ。while
は無し。シンプルですね。
for {
}
これは無限ループです。 多言語だったらwhile(true)
やfor(;;)
って書いたりします。
キャスト
キャストとキャストできるかの判定を一発でできちゃいます。
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
:
}
err
がerrors.StatusError
にキャストできるかをisStatus
に格納、
err
をerrors.StatusError
にキャストした結果をstatusErrorに格納します。
} else if (err instanceof StatusError) {
StatusError statusError = (StatusError) err;
:
}
javaだったら↑こんな感じで書きます。 StatusErrorを3回も書かないといけないです。
でもisStatusは書かなくて済んでる。んー。
time
time.Sleep(10 * time.Second)
time.Secondは以下のようにnano秒単位の倍数で定義されてます。
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
kubernetes
Client-goに関する部分です。 まだよくわかってないのでリファレンスとかだけ貼っておきます。
もっとわかってきたら書き足します。
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
clientset, err := kubernetes.NewForConfig(config)
kube/configファイルのパスを渡してClientset
を取得します。
pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
pod, err = clientset.CoreV1().Pods(namespace).Get(pod, metav1.GetOptions{})
Podの情報を取得します。
終わりに
短いコードですけど、Goの特徴がいくつも出てきました。
Goの理解はまだまだですけど、次からコード書いていきます。