目錄
1.背景
在初學kuberentes的時候,筆者作爲碼農就非常好奇它的代碼實現。於是開始git clone代碼,首先就被kuberentes的代碼倉庫搞蒙了,kuberentes項目有好多倉庫,包括kubernetes、client-go、api、apimachinery...,我該從哪兒看起?索性就從kubernetes的API對象的定義開始看吧,跟api有關的倉庫有兩個api、apimachinery,同時在apimachinery倉庫中還有api、apis兩個包,貌似他們都有types.go、meta.go、interface.go。此時筆者的心情真是難以言表,但是好奇心又慫恿自己弄明白原理,於是也就有了這篇類似總結的文章。希望本文能夠幫助讀者初步瞭解kubernetes代碼,成爲一篇比較基礎的入門文章。
2.分析
以前,在筆者印象中API都是各種接口,比較典型的就是操作系統的API。一提到API默認就是函數定義,直到REST API流行起來,API的雖然形式上不再是函數,但是原理上是一樣的,只是接口的服務端從本地變成了遠程。其實筆者一直都忽略了API的另一個重點,那就是接口類型,也就是接口函數的參數類型,它也是API的一部分。筆者認爲:操作系統的API強調方法,爲方法設計參數類型居多,而Rest API的強調資源,而方法就那麼幾個。在瞭解一個系統的Rest API時首先就要看有哪些資源,這些資源如何定義,支持哪些操作等等。
kubernetes對外提供的就是Rest API,只是這部分被client-go這個項目封裝成了類似SDK的形態。而apisever提供的就是基於API對象的各種操作,本文目標是探究一下API對象在kubernetes內部是如何定義以及實現的。
2.1api
在kubernetes裏提供了非常多的API對象,它們被定義在k8s.io/api這個倉庫中,這也是本章節命名爲api的原因。Pod應該是最爲基礎的對象之一,在初學kubernetes時我相信大部分同學都寫過類似下面的代碼:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
通過命令kubectl create -f xxx.yaml在kubernetes中創建了一個名字爲myapp-pod的Pod對象(此處忽略namespace)。
用編程的角度分析上面的流程:在kubernetes中需要有一個Pod的類型,每次執行kubectl create -f xxx.yaml創建Pod對象的時候需要實例化Pod,並把xxx.yaml中的參數賦值到Pod對象中。
現在就來看看kubernetes中Pod類型是如何定義的:
// 代碼源自k8s.io/api/core/v1/types.go
// kubernetes的API對象是單獨的git倉庫(https://github.com/kubernetes/api.git),可見API對象
// 在kubernetes項目中的重要程度。
type Pod struct {
// metav1是"k8s.io/apimachinery/pkg/apis/meta/v1"的重命名。額...,apimachinery又是
// 什麼鬼?apis包又是幹啥的?起初筆者被這些名字搞得雲裏霧裏,但所有的這些迷霧都會在本文揭開,此處
// 讀者只需要知道這個是類型的meta。那什麼又是類型的meta呢?以普通類型int32爲例,類型名稱
// “int32”、類型佔用內存空間4字節就是類型的meta,簡單的說就是類型屬性的描述。而kubernetes的
// API對象的類型meta是如何定義的,後面會有單獨章節詳細說明。
metav1.TypeMeta `json:",inline"`
// 同樣是metav1包,這回是對象的meta。同樣以int32爲例,對象的地址就屬於對象meta。在kubernetes
// 中,API對象都有自己的屬性,並且他們都有相同的屬性,例如名字、命名空間、標籤等等。kuberentes把
// 這些公共的屬性提取出來就是metav1.ObjectMeta,成爲了API對象類型的父類。
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// 從名字上看還是比較好理解的,就是Pod規格,最爲代表性的就是CPU、內存的資源使用。它和xxx.yaml中
// spec是關聯的。PodSpec不作爲本文的說明重點,所以不再詳細解釋。
Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// Pod的狀態,比如是運行還是掛起、Pod的IP、啓動時間等等。
Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
從Pod的定義來看,它繼承了metav1.TypeMeta和metav1.ObjectMeta兩個類型,同時還定義了Spec和Status兩個成員變量。其實kubernetes絕大部分API對象的類型都是這個結構,他們都繼承metav1.TypeMeta和metav1.ObjectMeta,前者用於定義類型的屬性,後者用於定義對象的公共屬性;Spec用於定義API對象類型的私有屬性,也是API對象之間的區別所在(例如Pod和Deployment雖然都繼承了兩個父類,但是他們二者的區別就是通過Spec實現的。就像是同一對父母的兩個孩子,有像的地方,更多的還是不像的地方讓他們成爲了兩個獨立的個體,這就是繼承的魅力所在);Status則是用於描述每個對象的狀態的,這和每個對象的類型緊密相關的。細心的讀者不難發現,metav1.TypeMeta和metav1.ObjectMeta對應的是xxx.yaml中的kind、apiVersion和metadata字段,Spec對應xxx.yaml中spec字段。這一點在代碼註釋`json:...`可以證實,這裏也可以得出另一個結論,那就是xxx.yaml就是Pod類型yaml的序列化。所以,kubectl create -f xxx.yaml就等同於new(Pod)。
此處要對metav1.TypeMeta和metav1.ObjectMeta多說兩句,可以把他們兩個看做是kubernetes全部API對象的基類,類似java中的Object類。語言因爲有編譯器的存在,類似metav1.TypeMeta的東西被編譯屏蔽了,所以開發者看到的所有的類繼承於Object。但在kubernetes中,每個API對象都需要metav1.TypeMeta字段用於描述自己是什麼類型,這樣才能構造相應類型的對象,所以相同類型的所有對象的metav1.TypeMeta字段都是相同的。但是metav1.ObjectMeta則不同,它是定義對象的公共屬性,即所有對象都應該具備的屬性。這部分就是和對象本身相關,和類型無關,所以相同類型的所有對象的metav1.ObjectMeta可能是不同的。
在kubernetes的API對象中除了單體對象外,還有對象列表類型,用於描述一組對象,等同於golang中的slice。對象列表的典型應用場景就是列舉,對象列表就可以表達一組對象。可能有些讀者會問爲什麼不用對象的slice,例如[]Pod,伴隨着筆者對對象列表的解釋讀者就會理解,此處以PodList爲例進行分析:
// 代碼源自k8s.io/api/core/v1/types.go
type PodList struct {
// PodList同樣需要繼承metav1.TypeMeta,畢竟對象列表也好、單體對象也好都需要類型屬性。
// PodList比[]Pod類型在yaml或者json表達上多了類型描述,當需要根據yaml構建對象列表的時候,
// 就可以根據類型描述反序列成爲PodList。而[]Pod則不可以,他必須確保yaml就是[]Pod序列化的
// 結果,否則就會報錯。這就無法實現一個通用的對象序列化/反序列化。
metav1.TypeMeta `json:",inline"`
// 與Pod不同,PodList繼承了metav1.ListMeta,metav1.ListMeta是所有對象類表類型的父類,
// 他定義了所有列表類型公共屬性。
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Items就是PodList定義的本質了,其實就是Pod的slice。說白了PodList就是[]Pod基礎上加了一些
// 跟類型和類表相關的信息,這些信息的作用會在後面的章節做詳細解釋。
Items []Pod `json:"items" protobuf:"bytes,2,rep,name=items"`
}
前面已經解釋了Pod的定義,PodList就不多解釋了。此處做一個小結:
- metav1.TypeMeta和metav1.ObjectMeta是所有API單體對象的父類;
- metav1.TypeMeta和metav1.ListMeta是所有API列表對象的父類;
- metav1.TypeMeta纔是所有API對象的父類,這也很好理解,畢竟所有的對象都要說明自己是什麼類型;
2.2metav1
metav1是k8s.io/apimachinery/pkg/apis/meta/v1的縮寫,後文會簡稱爲metav1。
2.2.1MetaType
作爲所有API對象的父類,是時候揭開它的真面目了:
// 代碼源自:k8s.io/apimachinery/pkg/apis/meta/v1/types.go
// 同樣來自apimachinery倉庫,ObjectMeta是xxx.yaml中metadata字段,平時我們填寫的metadata
// 一般只有name、label,其實ObjectMeta字段還是有很多內容了,讓筆者逐一介紹一下。
type ObjectMeta struct {
// 對象的名字應該不用介紹了。
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
// 如果Name爲空,系統這爲該對象生成一個唯一的名字。
GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`
// 命名空間,在平時學習、調試的時候很少用,但是在發佈的時候會經常用。
Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
// 對象的URL,由系統生成。
SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`
// 對象的唯一ID,由系統生成。
UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`
// 資源版本,這是一個非常有意思且變量,版本可以理解爲對象在時間軸上的一個時間節點,代表着對象最後
// 一次更新的時刻。如果說Name是在Namespace空間下唯一,那麼ResourceVersion則是同名、同類型
// 對象時間下唯一。因爲同名對象在不同時間可能會更新、刪除再添加,在比較兩個對象誰比較新的情況
// 非常有用,比如Watch。
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`
// 筆者很少關注這個值,所以也不太瞭解是幹什麼的,讀者感興趣可以自己瞭解一下。
Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`
// 對象創建時間,由系統生成。
CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`
// 對象刪除時間,指針類型說明是可選的,當指針不爲空的時候說明對象被刪除了,也是由系統生成
DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`
// 對象被刪除前允許優雅結束的時間,單位爲秒。
DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`
// 對象標籤,這個是我們經常用的,不用多解釋了
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
// 批註,這個和標籤很像,但是用法不同,比如可以用來做配置。
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
// 該對象依賴的對象類表,如果這些依賴對象全部被刪除了,那麼該對象也會被回收。如果該對象對象被
// 某一controller管理,那麼類表中有一條就是指向這個controller的。例如Deployment對象的
// OwnerReferences有一條就是指向DeploymentController的。
OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`
// 下面這幾個變量筆者沒有了解過,等筆者知道了再來更新文章吧。
Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`
ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"`
}
// 代碼源自k8s.io/apimachinery/pkg/apis/meta/v1/meta.go,GetObjectMeta是MetaAccessor
// 的接口函數,這個函數說明了ObjectMeta實現了MetaAccessor。
func (obj *ObjectMeta) GetObjectMeta() Object { return obj }
// 下面所有的函數是接口Object的ObjectMeta實現,可以看出來基本就是setter/getter方法。納尼?
// 又一個Object(這個Object是metav1.Object,本章節簡寫爲Object)?Object是API對象公共屬性
// (meta信息)的抽象,下面的函數是Object所有函數的實現,因爲功能比較簡單,筆者就不一一註釋了。
func (meta *ObjectMeta) GetNamespace() string { return meta.Namespace }
func (meta *ObjectMeta) SetNamespace(namespace string) { meta.Namespace = namespace }
func (meta *ObjectMeta) GetName() string { return meta.Name }
func (meta *ObjectMeta) SetName(name string) { meta.Name = name }
func (meta *ObjectMeta) GetGenerateName() string { return meta.GenerateName }
func (meta *ObjectMeta) SetGenerateName(generateName string) { meta.GenerateName = generateName }
func (meta *ObjectMeta) GetUID() types.UID { return meta.UID }
func (meta *ObjectMeta) SetUID(uid types.UID) { meta.UID = uid }
func (meta *ObjectMeta) GetResourceVersion() string { return meta.ResourceVersion }
func (meta *ObjectMeta) SetResourceVersion(version string) { meta.ResourceVersion = version }
func (meta *ObjectMeta) GetGeneration() int64 { return meta.Generation }
func (meta *ObjectMeta) SetGeneration(generation int64) { meta.Generation = generation }
func (meta *ObjectMeta) GetSelfLink() string { return meta.SelfLink }
func (meta *ObjectMeta) SetSelfLink(selfLink string) { meta.SelfLink = selfLink }
func (meta *ObjectMeta) GetCreationTimestamp() Time { return meta.CreationTimestamp }
func (meta *ObjectMeta) SetCreationTimestamp(creationTimestamp Time) {
meta.CreationTimestamp = creationTimestamp
}
func (meta *ObjectMeta) GetDeletionTimestamp() *Time { return meta.DeletionTimestamp }
func (meta *ObjectMeta) SetDeletionTimestamp(deletionTimestamp *Time) {
meta.DeletionTimestamp = deletionTimestamp
}
func (meta *ObjectMeta) GetDeletionGracePeriodSeconds() *int64 { return meta.DeletionGracePeriodSeconds }
func (meta *ObjectMeta) SetDeletionGracePeriodSeconds(deletionGracePeriodSeconds *int64) {
meta.DeletionGracePeriodSeconds = deletionGracePeriodSeconds
}
func (meta *ObjectMeta) GetLabels() map[string]string { return meta.Labels }
func (meta *ObjectMeta) SetLabels(labels map[string]string) { meta.Labels = labels }
func (meta *ObjectMeta) GetAnnotations() map[string]string { return meta.Annotations }
func (meta *ObjectMeta) SetAnnotations(annotations map[string]string) { meta.Annotations = annotations }
func (meta *ObjectMeta) GetFinalizers() []string { return meta.Finalizers }
func (meta *ObjectMeta) SetFinalizers(finalizers []string) { meta.Finalizers = finalizers }
func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference { return meta.OwnerReferences }
func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
meta.OwnerReferences = references
}
func (meta *ObjectMeta) GetClusterName() string { return meta.ClusterName }
func (meta *ObjectMeta) SetClusterName(clusterName string) { meta.ClusterName = clusterName }
func (meta *ObjectMeta) GetManagedFields() []ManagedFieldsEntry { return meta.ManagedFields }
func (meta *ObjectMeta) SetManagedFields(managedFields []ManagedFieldsEntry) {
meta.ManagedFields = managedFields
}
ObjectMeta每個成員變量是幹什麼用的對於理解本文並沒有什麼幫助,對於讀者來說只需要知道這些屬性是所有API對象都有的公共屬性。所以筆者對於每個成員變量的註釋也僅僅停留在表面定義,並沒有做深入的解釋。
此時,可以得出一個結論:ObjectMeta實現了Object和MetaAccessor兩個interface,而kubernetes所有單體對象都繼承了ObjectMeta,那麼所有的API對象就都實現了Object和MetaAccessor。kubernetes中有很多地方訪問API對象的這些meta信息並且不區分對象類型,Object是一個不錯的選擇。
2.2.3ListMeta
和ObjectMeta功能類似,ListMeta定義了所有列表對象的公共屬性:
// 代碼源自:k8s.io/apimachinery/pkg/apis/meta/v1/types.go
type ListMeta struct {
// 下面這兩個變量在ObjectMeta相同,不多解釋
SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,1,opt,name=selfLink"`
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,2,opt,name=resourceVersion"`
// 在列舉對象的時候可能會有非常多的對象,kubernetes支持分頁獲取,類似於SQL的limit,當對象總量
// 多於單頁的總量的時候,這個變量就會被設置。它用來告知用戶需要繼續獲取,並且它包含了下次獲取的
// 起始位置。
Continue string `json:"continue,omitempty" protobuf:"bytes,3,opt,name=continue"`
// 從字面意思也能理解,就是還剩多少個對象,這個和Continue是配合使用的,當Continue被設置了
// 這個變量就不會爲空,用來告訴用戶還有多少對象沒有獲取。
RemainingItemCount *int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"`
}
// 代碼源自k8s.io/apimachinery/pkg/apis/meta/v1/meta.go
// 下面所有的函數是接口ListInterface的ListMeta實現,ListInterface是API對象列表公共屬性(meta)
// 的抽象。
func (meta *ListMeta) GetResourceVersion() string { return meta.ResourceVersion }
func (meta *ListMeta) SetResourceVersion(version string) { meta.ResourceVersion = version }
func (meta *ListMeta) GetSelfLink() string { return meta.SelfLink }
func (meta *ListMeta) SetSelfLink(selfLink string) { meta.SelfLink = selfLink }
func (meta *ListMeta) GetContinue() string { return meta.Continue }
func (meta *ListMeta) SetContinue(c string) { meta.Continue = c }
func (meta *ListMeta) GetRemainingItemCount() *int64 { return meta.RemainingItemCount }
func (meta *ListMeta) SetRemainingItemCount(c *int64) { meta.RemainingItemCount = c }
此處做一個小結,在metav1包中,爲API單體對象和對象列表的公共屬性(meta)做了抽象,分別爲metav1.Object和 metav1.ListInterface。同時,metav1包爲這兩個抽象做了實現,他們分別爲metav1.ObjectMeta和metav.listMeta,API對象類型可以通過繼承這些類實現抽象,是不是有一點apimachinery所定義的那樣“類型”、“基礎設施”的意思了?
2.3runtime
2.3.1schema
前文提到了metav1.TypeMeta實現了schema.ObjectKind(本節簡稱ObjectKind),並且metav1.TypeMeta已經非常直觀的和xxx.yaml的相應字段對應上了,那ObjectKind又是幹什麼的呢?
// 代碼源自k8s.io/apimachinery/pkg/runtime/schema/interfaces.go
// ObjectKind是接口,兩個接口函數是GroupVersionKind類型的setter和getter
type ObjectKind interface {
SetGroupVersionKind(kind GroupVersionKind)
GroupVersionKind() GroupVersionKind
}
// GroupVersionKind纔是kubernetes的API對象類型真身,他包括Kind、Version和Group。其中Kind和
// Version還比較好理解,Group又是什麼?其實Group/Version纔是xxx.yaml的apiVersion字段。
// 在kuberentes中API對象是分組的,像Pod、Service、ConfigMap都屬於core分組,而core分組的對象
// 無需在apiVersion字段明文寫出來,系統會默認將這類的對象歸爲core分組,正如文章開始那個Pod的例子。
// 詳情可以看下面的代碼實現。
type GroupVersionKind struct {
Group string
Version string
Kind string
}
// 這個函數在metav1.TypeMeta實現GroupVersionKind()接口的時候調用了,該函數調用了ParseGroupVersion
// 實現從apiVersion解析Group和Version。
func FromAPIVersionAndKind(apiVersion, kind string) GroupVersionKind {
if gv, err := ParseGroupVersion(apiVersion); err == nil {
return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
}
return GroupVersionKind{Kind: kind}
}
// 從apiVersion解析Group和Version。
func ParseGroupVersion(gv string) (GroupVersion, error) {
// 這種不報錯是什麼道理?什麼情況下會有對象沒有Group和Version?
if (len(gv) == 0) || (gv == "/") {
return GroupVersion{}, nil
}
// 數apiVersion中有幾個‘/’
switch strings.Count(gv, "/") {
// 沒有'/',就像文章開始的Pod的例子,那麼Group就是空字符串,系統默認會把空字符串歸爲core
case 0:
return GroupVersion{"", gv}, nil
// 有一個'/',那麼就以'/'分割apiVersion,左邊爲Group,右邊爲Version。
case 1:
i := strings.Index(gv, "/")
return GroupVersion{gv[:i], gv[i+1:]}, nil
// 其他則爲格式錯誤。
default:
return GroupVersion{}, fmt.Errorf("unexpected GroupVersion string: %v", gv)
}
}
這就爲什麼Deployment的apiVersion是apps/v1或者extension/v1beta1了,至此可以總結如下:
- schema.ObjecKind是所有API對象類型meta的抽象;
- metav1.TypeMeta是schema.ObjecKind的一個實現,API對象類型通過繼承metav1.TypeMeta實現schema.ObjecKind;
2.3.2Object
如果說schema.ObjecKind是所有API對象類型的抽象,配合metav1.Object作爲所有API單體對象公共屬性的抽象,似乎已經找到了所有API對象的根。但是有沒有感覺怪怪的,如果想通過一個基類的指針指向任意API單體對象,schema.ObjecKind和metav1.Object感覺都不合適,因爲他們所能訪問的域是有限。如果有一個函數需要訪問任何API對象的類型和公共屬性,那麼就要傳入同一個對象的兩個指針(schema.ObjecKind和metav1.Object),這就太讓人難以接受了。有沒有一個類型作爲API單體對象的統一的基類呢?這就是本節要討論的:runtime.Object(本章節簡稱 Object)。
// 代碼源自k8s.io/apimachinery/pkg/runtime/interfaces.go
type Object interface {
// 有了這個函數,就可以訪問對象的類型域
GetObjectKind() schema.ObjectKind
// deepcopy是golang深度複製對象的方法,至於什麼是深度複製本文就不解釋了。這是個不錯的函數,
// 可以通過這個接口複製任何API對象而無需類型依賴。
DeepCopyObject() Object
// 就這麼兩個函數了麼?那如果需要訪問對象的公共屬性域怎麼辦?不應該有一個類似GetObjectMeta()
// 的接口麼?這一點,kubernetes是通過另一個方式實現的,見下面的代碼。
}
// 代碼源自k8s.io/apimachinery/pkg/api/meta/meta.go,注意是api包,不是apis
// Accessor()函數可以把obj安全的轉換爲metav1.Object,這樣也就避免了每個API對象類型都需要實現
// 類似GetObjectMeta()的接口了。有的讀者肯定會問:所有的API對象都繼承了metav1.ObjectMeta,
// 這個類型不是實現了GetObjectMeta()麼?筆者就要在這裏做出說明:筆者提到是類似GetObjectMeta(),
// 如果接口名字是ObjectMeta(),那豈不是繼承metav1.ObjectMeta就沒用了?一個頂層的類型抽象定義不
// 應該依賴於相對底層類型的實現。
func Accessor(obj interface{}) (metav1.Object, error) {
// 使用了golang的switch type語法
switch t := obj.(type) {
// 因爲API對象類型都繼承了metav1.ObjectMeta,也就自然實現了metav1.Object。
case metav1.Object:
return t, nil
// 在ObjectMeta章節筆者提到了,metav1.ObjectMeta實現了metav1.ObjectMetaAccessor,
// 所以API對象也自然實現了metav1.ObjectMetaAccessor。但是API對象會在上一個case就返回
// 了,這個case是給誰用的呢?筆者也比較疑惑,筆者感覺是那些沒有直接繼承metav1.ObjectMeta
// 卻實現了metav1.ObjectMetaAccessor的類型,筆者暫時還沒找到相關類型定義。
case metav1.ObjectMetaAccessor:
if m := t.GetObjectMeta(); m != nil {
return m, nil
}
return nil, errNotObject
default:
return nil, errNotObject
}
}
等下,爲什麼沒有看到API對象實現runtime.Object.DeepCopyObject()?那是因爲deep copy是具體API對象類型需要實現的,存在類型依賴,作爲API對象類型的父類不能實現。此處還是以Pod爲例,看看Pod是如何實現DeepCopyObject()的。
// +genclient
// +genclient:method=GetEphemeralContainers,verb=get,subresource=ephemeralcontainers,result=EphemeralContainers
// +genclient:method=UpdateEphemeralContainers,verb=update,subresource=ephemeralcontainers,input=EphemeralContainers,result=EphemeralContainers
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// 上面+k8s:deepcopy-gen:....就是告訴代碼生成工具爲下面的類型生成runtime.Object接口的
// DeepCopyObject()函數實現。因爲所有的API對象類型都要實現DeepCopyObject()函數,這是一個相當
// 大量的重複工作,所以kubernetes用代碼生成工具來實現。至於如何實現的不作爲本文討論重點,只要讀者
// 知道deep copy的目的就可以了。
type Pod struct {
......
}
3.總結
至此,前面各章節的總結都可以忘掉了,因爲那些總結都是基於當時的知識背景做的總結,可能缺乏全局性的考慮做出錯誤的結論,所以在此做出通盤的總結,如下圖所示:
- runtime.Object是所有API單體對象的根類(interface);
- schema.ObjectKind是對API對象類型的抽象(interface);
- metav1.Object是對API對象公共屬性的抽象(interface);
- metav1.ListInterface是對API對象列表公共屬性的抽象(interface);
- metav1.TypeMeta是schema.ObjectKind的一個實現,API對象類型繼承之;
- metav1.ObjectMeta是metav1.Object的一個實現,API對象類型繼承之;
- metav1.ListMeta是metav1.ListInterface的一個實現,API對象列表繼承之;