0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes: CRDのバリデーションには CEL format ライブラリが便利

Posted at

この投稿では、Kubernetes の CRD でフィールドのバリデーションを行う際に活用できる CEL format ライブラリについて紹介します。metadata.name に使える形式や、ラベルの値として許容される形式を検証したいとき、正規表現を自前で組む代わりにこのライブラリを使うと、車輪の再発明を避けられます。

この投稿で知れること

  • CEL format ライブラリとは何か
  • 利用可能なフォーマットの種類と用途
  • CRD の x-kubernetes-validations での具体的な使い方

CEL format ライブラリとは

Kubernetes 1.30 以降では、CRD の x-kubernetes-validations(CEL ルール)から利用できる format ライブラリ が提供されています。このライブラリを使うと、DNS ラベルや UUID、日付といった一般的な文字列フォーマットのバリデーションを、Kubernetes 本体と同じルールで簡単に行うことができます。

公式ドキュメントでは簡単な説明しかないため、「具体的にどんな文字列が OK で、どんな文字列が NG なのか」が分かりづらいところがあります。この投稿では、ソースコードの実装を踏まえながら、各フォーマットの仕様を整理していきます。

基本的な使い方

フォーマットの取得

フォーマットを取得するには2つの方法があります。

// 方法1: 直接フォーマット関数を呼び出す
format.dns1123Label()

// 方法2: format.named() で名前から取得
format.named("dns1123Label")  // Optional<Format> を返す

バリデーションの実行

validate() メソッドを呼び出すと、検証結果が Optional 型で返ってきます。

format.dns1123Label().validate("my-name")
// → 有効な場合: optional.none
// → 無効な場合: optional([エラーメッセージのリスト])

CRD での実践的な使用例

バリデーションルールでは、hasValue() を使って結果を判定します。

// バリデーションルールでの使用例
rule: !format.dns1123Label().validate(self.metadata.name).hasValue()
//    ^否定演算子を忘れずに!

// messageExpression でエラーメッセージを表示
messageExpression: format.dns1123Label().validate(self.metadata.name).orValue([]).join("\n")

validate()optional.none を返す(つまり hasValue()false になる)場合が「有効」であることに注意してください。

Kubebuilder でのコメントアノテーション

Kubebuilder を使って CRD を開発している場合は、Go の構造体にコメントアノテーションを記述することでバリデーションルールを定義できます。+kubebuilder:validation:XValidation マーカーを使用します。

type MyResourceSpec struct {
    // Name は DNS-1123 ラベル形式である必要があります
    // +kubebuilder:validation:XValidation:rule="!format.dns1123Label().validate(self).hasValue()",message="must be a valid DNS-1123 label"
    Name string `json:"name"`

    // Host は DNS-1123 サブドメイン形式である必要があります
    // +kubebuilder:validation:XValidation:rule="!format.dns1123Subdomain().validate(self).hasValue()",message="must be a valid DNS-1123 subdomain"
    Host string `json:"host"`
}

利用可能なフォーマット一覧

format ライブラリで利用可能なフォーマットは以下の13種類です。

  • dns1123Label
  • dns1123Subdomain
  • dns1035Label
  • dns1123LabelPrefix
  • dns1123SubdomainPrefix
  • dns1035LabelPrefix
  • qualifiedName
  • labelValue
  • uri
  • uuid
  • byte
  • date
  • datetime

以下では、よく使うフォーマットについて詳しく見ていきます。

DNS 関連フォーマット

DNS 関連のフォーマットは、Pod 名や Namespace 名、Service 名などのリソース名を検証する際に使用します。共通の特徴として、すべて ASCII の小文字(a-z)のみを前提としており、大文字やアンダースコアは使用できません。

dns1123Label

Kubernetes で最も一般的に使われるフォーマットです。RFC 1123 に基づいており、最大 63 文字まで許容されます。

使用可能な文字は、小文字英字(a-z)、数字(0-9)、ハイフン(-)です。先頭と末尾は英小文字または数字である必要があり、ドット(.)は使用できません。

!format.dns1123Label().validate(self.metadata.name).hasValue()

my-servicenginxabc-123 といった値は有効ですが、My-Service(大文字)、-nginx(先頭がハイフン)、my.service(ドットを含む)といった値は無効になります。

dns1123Subdomain

ドメイン名のような形式で、ドットで区切られた複数のラベルを含むことができます。最大 253 文字まで許容されます。

!format.dns1123Subdomain().validate(self.spec.host).hasValue()

example.comapi.example.com といった値は有効です。

dns1035Label

RFC 1035 に基づくより厳格なラベル形式です。dns1123Label との違いは、先頭が必ず英小文字でなければならないという点です。数字で始まる値は許可されません。

!format.dns1035Label().validate(self.metadata.name).hasValue()

nginxmy-service1 は有効ですが、1service(先頭が数字)は無効になります。Pod や Service の名前など、リソースの種類によっては dns1035Label が要求されるケースがあります。

dns1123Label と dns1035Label の違い

これらは混同しやすいので、違いを整理しておきます。

dns1123Label は先頭に英小文字または数字を許容するため、123-abc のような値が有効です。一方、dns1035Label は先頭に英小文字のみを許容するため、123-abc は無効になります。

どちらを使うべきか

基本的には dns1123Label を使えば問題ありません。Kubernetes の多くのリソース(ConfigMap、Secret、Namespace など)は dns1123Label の形式を採用しています。

では、dns1035Label はどのような場面で使うべきでしょうか。主に以下のケースが該当します。

dns1035Label を使うべき代表的なケースは、そのフィールドの値が Service の metadata.name として使われる場合です。Service 名はクラスタ内 DNS でホスト名として解決されるほか、環境変数名のプレフィックスとしても使われるため、RFC 1035 に従って先頭が英字である必要があります。CRD のフィールドが最終的に Service 名になるような設計であれば、dns1035Label でバリデーションしておくと安全です。

Prefix 系フォーマット

dns1123LabelPrefixdns1123SubdomainPrefixdns1035LabelPrefix は、generateName のように「接頭辞 + 自動生成サフィックス」という形式で使われることを想定したフォーマットです。通常のフォーマットでは末尾のハイフンが許可されませんが、Prefix 系では末尾のハイフンが許容されます。

!format.dns1123LabelPrefix().validate(self.metadata.generateName).hasValue()

my-app- のような値がプレフィックスとして有効になります。

ラベル関連フォーマット

labelValue

Kubernetes のラベルの値として使用できる形式です。最大 63 文字で、空文字列も有効であるという点が特徴的です。

使用可能な文字は、英字(大文字・小文字)、数字、ハイフン(-)、アンダースコア(_)、ドット(.)です。空でない場合は、先頭と末尾が英数字である必要があります。

!format.labelValue().validate(self.metadata.labels['app']).hasValue()

空文字列、valuemy.value_1v1-2-3 といった値は有効です。-value(先頭がハイフン)や value-(末尾がハイフン)は無効になります。

qualifiedName

アノテーションやラベルのキー名で使用される形式です。オプションで DNS サブドメインのプレフィックスを持つことができ、[prefix/]name という構造になっています。

!format.qualifiedName().validate(key).hasValue()

my-nameMy.Name_1(大文字も OK)、example.com/MyName といった値は有効です。/my-name(プレフィックス部分が空)や example.com/(名前部分が空)は無効になります。

その他のフォーマット

uri

Go の url.ParseRequestURI を使用して URI を検証します。スキーム(http://https:// など)が必須です。

!format.uri().validate(self.spec.callbackURL).hasValue()

uuid

UUID の形式を検証します。大文字・小文字は区別せず、ハイフンはあってもなくても有効です。

!format.uuid().validate(self.spec.id).hasValue()

date / datetime

dateYYYY-MM-DD 形式の日付を検証します。datetime は RFC 3339 形式の日時を検証しますが、タイムゾーン指定(Z+09:00 など)が必須である点に注意が必要です。

!format.date().validate(self.spec.startDate).hasValue()
!format.datetime().validate(self.spec.expireAt).hasValue()

2021-01-01T00:00:00Z は有効ですが、2021-01-01T00:00:00(タイムゾーンなし)は無効になります。

format.named の使いどころ

format.named(name) は、文字列から動的にフォーマットを選びたい場合に便利です。

format.named(self.spec.formatName).
  map(f, f.validate(self.spec.value)).
  orValue(optional.of(["unknown format"])).
  value()

サポートされていない名前の場合は optional.none になるため、orValue などでフォールバック処理を書くことができます。

所感

CRD のバリデーションを書くとき、正規表現を自前で組むのは意外と手間がかかりますし、Kubernetes 本体の実装と微妙にずれてしまうリスクもあります。CEL format ライブラリを使えば、Kubernetes 本体と同じルールを再利用する形になるため、挙動の一貫性を保つことができます。

特に dns1123Labeldns1035Label の違いや、labelValue が空文字列を許容するといった細かい仕様は、自分で実装すると見落としがちです。このライブラリを活用して、車輪の再発明を避けていきたいところです。

最後までお読みくださりありがとうございました。Twitterでは、Qiitaに書かない技術ネタなどもツイートしているので、よかったらフォローしてもらえると嬉しいです:relieved:Twitter@suin

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?