この記事では、Kubebuilder/controller-runtimeなどでControllerReference(OwnerReference)をセットしたいときに便利なイディオムについて紹介します。
便利なヘルパー関数
次のようなヘルパー関数を定義しておくと、所有権関係の設定が簡単になります:
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func setOwnerReference(parent *myprojv1.SomeResource, child metav1.Object) {
child.SetOwnerReferences([]metav1.OwnerReference{
*metav1.NewControllerRef(parent, parent.GroupVersionKind()),
})
}
この関数は親リソースから子リソースへの所有権関係(ControllerReference)を設定します。親リソース削除時に小リソースを自動削除(いわゆるカスケード削除、GCなんて呼ばれたりするやつ)できたり、Owns()
で子リソースを監視(watch)できたり便利になります。
重要な注意点
parent.GroupVersionKind()
メソッドは、client.Get()
などのKubernetesクライアント操作で取得したオブジェクトの場合のみ、正しいGroupVersionKindが設定されていることが保証されます。それ以外の方法(例:新規作成したオブジェクトや、自前でUnmarshalしたオブジェクト)の場合、GroupVersionKindがゼロ値になっている可能性があります。
そのため、より安全な実装としては、明示的にGroupVersionKindを指定する方法が推奨されます:
func setOwnerReference(parent *myprojv1.SomeResource, child metav1.Object) {
child.SetOwnerReferences([]metav1.OwnerReference{
*metav1.NewControllerRef(parent, myprojv1.GroupVersion.WithKind("SomeResource")),
})
}
この方法であれば、親オブジェクトの取得方法に関わらず、常に正しいGroupVersionKindが設定されることが保証されます。
ただ、書き方が冗長になるので、「クライアントから取得したオブジェクトしか使わない」というケースでは最初に紹介したイディオムでもいいと思っています。
ちなみに、ControllerReferenceとOwnerReferenceの違いとは
OwnerReferenceは単にリソース間の所有権関係を示しますが、ControllerReferenceは「Controller」フィールドがtrue
に設定された特殊なOwnerReferenceです。OwnerReferenceの一種ということですね。
一つのリソースは複数のOwnerReferenceを持つことができますが、ControllerReferenceは最大で1つしか持てません。
これは、リソースを実際に制御する権限を持つコントローラーは一つだけであるべきという原則に基づいています。この区別により、複数のコントローラーが同一リソースを管理しようとする際の競合を防げるわけです。