k8s-rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE +21 -0
- data/README.md +201 -0
- data/docs/design.md +383 -0
- data/lib/k8s-rails.rb +124 -0
- data/lib/k8s_rails/client.rb +181 -0
- data/lib/k8s_rails/configuration.rb +35 -0
- data/lib/k8s_rails/crd.rb +55 -0
- data/lib/k8s_rails/errors.rb +43 -0
- data/lib/k8s_rails/normalizer.rb +36 -0
- data/lib/k8s_rails/resource.rb +94 -0
- data/lib/k8s_rails/version.rb +5 -0
- metadata +129 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 37dcb752eb0c0b10dd35af27b071e5dad054674e111cff16a60e49b78e7de64b
|
|
4
|
+
data.tar.gz: '0867da799bf1c8914940bccae887f22d976e67826271d05313d965bf6aec8f15'
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: '039460cf0b175fbb51f160ce454cb9abe54e0001fb5d974f5b9da24c4c55750aa9e39326157d7306c319e08c64556284dfe994618642b374ab00e03c258465ec'
|
|
7
|
+
data.tar.gz: 124d5016d3e90068fe6ca03241c6cba6364bf956ee564324cf6d0329426f824b1a27e916e9234f279aacc216ae8450178e3f4ec437a5ef311c8f2cce199b9f28
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
`k8s-rails` の全 notable な変更はこのファイルに記録する。
|
|
4
|
+
|
|
5
|
+
## 0.1.0
|
|
6
|
+
|
|
7
|
+
- **M0**: gem 骨子(gemspec / Gemfile / Rakefile / version / require 構造)
|
|
8
|
+
- **M1**: Configuration + Client(lazy connect・K1 BearerToken 橋渡し)・例外体系
|
|
9
|
+
(`Unavailable` / `NotFound` / `ApiError` + `ReadOnlyError` / `RedeclarationError`)
|
|
10
|
+
- **M2**: CRD 宣言 DSL + Resource(`list` / `find` / `find_or_nil` / `create` / `patch`、
|
|
11
|
+
`readonly` は明示 boolean、再宣言は `RedeclarationError`)
|
|
12
|
+
- **M3**: 計測(ActiveSupport::Notifications 経由、無効時 no-op)+ README + rubocop
|
|
13
|
+
- **M4**: consumer Rails アプリの K8s サービス移行で動作検証
|
|
14
|
+
(実クラスタ microk8s v1.33.13)
|
|
15
|
+
- 応答は常に文字列キー(K2)・純 Ruby `Normalizer`(ActiveSupport 非依存)
|
|
16
|
+
- 対応 Ruby: `>= 3.3, < 4.0`(下限は kruby 1.36.x の `>= 3.3`。上限は
|
|
17
|
+
未検証の Ruby 4.x を宣言から除外するため。4.0 / 3.5 対応は v0.2 以降
|
|
18
|
+
で検証の上宣言に含める)。
|
|
19
|
+
CI(GitHub Actions)で 3.3.0 / 3.3.8 / 3.4.10 の matrix 検証
|
|
20
|
+
- 対応 Kubernetes サーバ: v1.33.x で実クラスタ検証済み(README 参照)
|
|
21
|
+
- 公開導線: owner のローカル PC から手動 `gem push`(rake 全緑確認 +
|
|
22
|
+
CHANGELOG 確定後に実施。CI の自動公開は行わない)
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dorian - Takahiro Ishida
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# k8s-rails
|
|
2
|
+
A Kubernetes API / CRD convention layer for Rails applications.
|
|
3
|
+
|
|
4
|
+
`k8s-rails` provides the **connection, CRD access, and error-handling convention
|
|
5
|
+
layer** for Rails applications that talk to the Kubernetes API.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/doridoridoriand/k8s-rails/actions/workflows/test.yml)
|
|
8
|
+
[](https://rubygems.org/gems/k8s-rails)
|
|
9
|
+
|
|
10
|
+
For the design rationale, see the [design document (docs/design.md)](docs/design.md) (KBR-DESIGN-001).
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
| Item | Supported range | Notes |
|
|
15
|
+
|------|-----------------|-------|
|
|
16
|
+
| Ruby | `>= 3.3, < 4.0` | Floor: kruby 1.36.x requires Ruby 3.3. Upper bound: unverified Ruby 4.x is excluded from the declared range. CI verifies the declared range with a 3.3.0 / 3.3.8 / 3.4.10 matrix |
|
|
17
|
+
| kruby | `~> 1.36.0` | The official Kubernetes OpenAPI client |
|
|
18
|
+
| Kubernetes server | **Verified on v1.33.x** (a real cluster, microk8s v1.33.13, 2026-09-21) | kruby 1.36.x is a 1.36-series client. Newer servers (1.36, etc.) use the same API (CustomObjects API v1), so compatibility is expected, but has not yet been verified against a real cluster |
|
|
19
|
+
| Dependencies | **kruby only** at runtime | ActiveSupport is used only for optional [instrumentation](#instrumentation-optional-activesupport) (no-op when absent) |
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# Gemfile
|
|
25
|
+
gem "k8s-rails", "~> 0.1"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
require "k8s-rails"
|
|
30
|
+
K8sRails::VERSION # => "0.1.0"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`require` is side-effect-free and needs no cluster. kruby itself is loaded
|
|
34
|
+
lazily on the first `K8sRails.client` / `K8sRails.connected?` / CRD operation
|
|
35
|
+
(**lazy connect**), so the gem can be required even when no cluster is
|
|
36
|
+
reachable.
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
# config/initializers/k8s-rails.rb
|
|
42
|
+
K8sRails.configure do |config|
|
|
43
|
+
config.namespace = "team-a" # default namespace (can be overridden per declaration or per call)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Declare the CRD you work with (group / version / plural / kind are never guessed)
|
|
47
|
+
Workflow = K8sRails.crd(
|
|
48
|
+
group: "argoproj.io",
|
|
49
|
+
version: "v1alpha1",
|
|
50
|
+
plural: "workflows",
|
|
51
|
+
kind: "Workflow",
|
|
52
|
+
readonly: false, # default true; false enables create/patch
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
Workflow.list # => [{"name" => "...", "labels" => {...}}, ...]
|
|
58
|
+
Workflow.find("wf-1") # same shape / raises K8sRails::NotFound when absent
|
|
59
|
+
Workflow.find_or_nil("wf-1") # same, but returns nil instead of raising
|
|
60
|
+
Workflow.create({ metadata: { name: "wf-1" } }) # readonly: false only
|
|
61
|
+
Workflow.patch("wf-1", [{ op: "replace", path: "/spec/a", value: 2 }]) # readonly: false only
|
|
62
|
+
|
|
63
|
+
K8sRails.connected? # true on success; raises on failure
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
K8sRails.configure do |config|
|
|
70
|
+
config.namespace = "team-a" # default namespace (default: "default")
|
|
71
|
+
# config.connection = my_config # pass a Kubernetes::Configuration directly (optional)
|
|
72
|
+
# config.instrumentation = false # disable instrumentation (default true; only effective when ActiveSupport is present)
|
|
73
|
+
# config.api_client = stub # test-only: inject a transport (see [Testing](#testing))
|
|
74
|
+
end
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- `configure` is effective **only once**. A second call prints a warning and is
|
|
78
|
+
ignored (use `K8sRails.reset!` to reset the configuration, the cached
|
|
79
|
+
transport, and declared CRDs — primarily for tests).
|
|
80
|
+
- Connection resolution order: `config.api_client` (test injection) →
|
|
81
|
+
`config.connection` → `Kubernetes::Configuration.default_config`
|
|
82
|
+
(automatic in-cluster → KUBECONFIG detection).
|
|
83
|
+
|
|
84
|
+
## CRD declaration and access
|
|
85
|
+
|
|
86
|
+
`plural` / `kind` are **never guessed** (many CRDs do not follow the obvious
|
|
87
|
+
naming convention). Re-declaring the same kind raises
|
|
88
|
+
`K8sRails::RedeclarationError` (configuration-mistake detection).
|
|
89
|
+
|
|
90
|
+
Return values are **always string-keyed hashes**. kruby returns symbol keys,
|
|
91
|
+
but Rails-side JSON/views work with string keys, so the gem normalizes
|
|
92
|
+
internally in pure Ruby (no ActiveSupport dependency). Every method accepts a
|
|
93
|
+
`namespace:` argument to override the namespace from the declaration.
|
|
94
|
+
|
|
95
|
+
`readonly` must be an **explicit boolean** (`nil` or other values raise
|
|
96
|
+
`ArgumentError`). Writes are enabled **only** by `readonly: false`, so a
|
|
97
|
+
missing flag can never fail open.
|
|
98
|
+
|
|
99
|
+
## Connectivity check
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
K8sRails.connected? # true on success; raises K8sRails::Unavailable / ApiError on failure
|
|
103
|
+
# a lightweight /version-equivalent check
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
It never returns `false` — a connection failure surfaces as an exception
|
|
107
|
+
(handle it with `rescue`). The actual API connection is established lazily on
|
|
108
|
+
the first API call.
|
|
109
|
+
|
|
110
|
+
## Exception hierarchy
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
K8sRails::Error < StandardError
|
|
114
|
+
├── K8sRails::Unavailable # transport-layer failure (DNS/timeout/connection refused; kruby 1.36.x reports it as ApiError code 0)
|
|
115
|
+
├── K8sRails::NotFound # HTTP 404
|
|
116
|
+
├── K8sRails::ApiError # other API errors (401/403/409/422/5xx); holds #code and #response
|
|
117
|
+
├── K8sRails::ReadOnlyError # create/patch called on a readonly: true declaration
|
|
118
|
+
└── K8sRails::RedeclarationError # re-declaration of an already-declared CRD kind
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Recommended caller pattern:
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
begin
|
|
125
|
+
Workflow.list
|
|
126
|
+
rescue K8sRails::Unavailable
|
|
127
|
+
# cluster-side problem → "unable to load" fallback UI, etc.
|
|
128
|
+
rescue K8sRails::ApiError => e
|
|
129
|
+
# inspect e.code / e.response to determine the cause
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Instrumentation (optional ActiveSupport)
|
|
134
|
+
|
|
135
|
+
When `config.instrumentation = true` (the default) and ActiveSupport is loaded,
|
|
136
|
+
each API call is published as a `k8s-rails.request` notification (no-op when
|
|
137
|
+
ActiveSupport is absent):
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
k8s-rails.request
|
|
141
|
+
payload: { operation:, group:, version:, plural:, namespace:, duration_ms:,
|
|
142
|
+
status: "ok" | "unavailable" | "api_error" }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
In a Rails app you can subscribe with `ActiveSupport::Notifications`:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
ActiveSupport::Notifications.subscribe("k8s-rails.request") do |name, start, finish, id, payload|
|
|
149
|
+
Rails.logger.info("[k8s-rails] #{payload[:operation]} #{payload[:plural]} (#{payload[:duration_ms]}ms) #{payload[:status]}")
|
|
150
|
+
end
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Testing
|
|
154
|
+
|
|
155
|
+
The test suite needs **no cluster**. Tests inject a transport stub via
|
|
156
|
+
`config.api_client`. The stub is wrapped internally by an adapter, so it just
|
|
157
|
+
implements the same four methods as kruby's `CustomObjectsApi`:
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
class StubTransport
|
|
161
|
+
def list_namespaced_custom_object(group, version, namespace, plural) = { items: [] }
|
|
162
|
+
def get_namespaced_custom_object(group, version, namespace, plural, name) = {}
|
|
163
|
+
def create_namespaced_custom_object(group, version, namespace, plural, body) = {}
|
|
164
|
+
def patch_namespaced_custom_object(group, version, namespace, plural, name, body) = {}
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
K8sRails.configure { |c| c.api_client = StubTransport.new }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
bundle install
|
|
174
|
+
bundle exec rake # rspec + rubocop
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Known issue: kruby 1.36 bearer-token key mismatch
|
|
178
|
+
|
|
179
|
+
kruby 1.36.x's in-cluster / KUBECONFIG configuration writes the bearer token
|
|
180
|
+
to `api_key['authorization']`, but `Configuration#auth_settings` reads
|
|
181
|
+
`api_key['BearerToken']` for the `Authorization` header. Because the keys do
|
|
182
|
+
not match, **the Authorization header ends up empty and requests fail with
|
|
183
|
+
401** when using kruby's configuration directly.
|
|
184
|
+
|
|
185
|
+
k8s-rails automatically copies `authorization` to `BearerToken` when building
|
|
186
|
+
its client (the design document calls this the "K1 bridge"), so connections
|
|
187
|
+
through k8s-rails are unaffected (it does not overwrite an already-set
|
|
188
|
+
`BearerToken`). If you see 401s from a cluster connection that relies on
|
|
189
|
+
kruby's own configuration behavior (outside this gem), check this token-key
|
|
190
|
+
issue first.
|
|
191
|
+
|
|
192
|
+
## Roadmap (v0.2+)
|
|
193
|
+
|
|
194
|
+
- Ruby 3.5 / 4.0 support (after verifying against the stable releases, then
|
|
195
|
+
widening the declared range and the CI matrix)
|
|
196
|
+
- CI-based E2E tests using kind
|
|
197
|
+
- watch (streaming) support, under consideration
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT ([LICENSE](LICENSE))
|
data/docs/design.md
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# k8s-rails 設計書
|
|
2
|
+
|
|
3
|
+
- 文書番号: KBR-DESIGN-001
|
|
4
|
+
- 版: 0.1.10(案)
|
|
5
|
+
- 日付: 2026-09-15
|
|
6
|
+
- 対象リポジトリ: k8s-rails(本設計の実装先)
|
|
7
|
+
- ライセンス: MIT(LICENSE は main に既存)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. 目的
|
|
12
|
+
|
|
13
|
+
Rails アプリが Kubernetes API を扱う際の**接続・CRD アクセス・障害処理の規約層**を
|
|
14
|
+
gem(`k8s-rails`)として提供する。
|
|
15
|
+
|
|
16
|
+
起点となった実際の Rails アプリ実装には、公式 `kruby` クライアントを
|
|
17
|
+
Rails アプリで実運用する過程で得られた知見が実装として固定されている:
|
|
18
|
+
|
|
19
|
+
| # | 知見(実運用で確認済み) |
|
|
20
|
+
|---|---|
|
|
21
|
+
| K1 | in-cluster(ServiceAccount)と KUBECONFIG の接続自動切替は `Kubernetes::Configuration.default_config` が担うが、**kruby 1.36 では in-cluster 時の `api_key['authorization']`(Bearer トークン)が `auth_settings` が読む `api_key['BearerToken']` に書かれず、Authorization ヘッダが欠落して 401 になる**。この橋渡しを忘れると本番(in-cluster)で必ず失敗する |
|
|
22
|
+
| K2 | kruby はシンボルキーの Hash を返す。Rails 側(JSON/ビュー)では文字列キーで扱うため、文字列キーへの統一変換が必須(gem 内部で処理: ActiveSupport 存在時は `deep_stringify_keys`、無ければ純 Ruby の再帰変換。Rails 無し環境でも成立、§5.2) |
|
|
23
|
+
| K3 | クラスタが到達不能な場合、呼び出し側(ビュー等)が生の `kruby` 例外を扱うと 500 になる。接続不能 / リソース不在 / API エラーを**gem 側の例外体系**で格納し、Rails 側はそれを素通し表示できる規約が必要 |
|
|
24
|
+
| K4 | read-only(get/list)と操作(create/patch/update)は障害時の影響度が異なるため、**フェーズごとに API を分離**する運用(read-only のみ → 操作追加)が事実上のベストプラクティス |
|
|
25
|
+
| K5 | CRD へのアクセスは group/version/plural をハードコードしがち。宣言でメソッドを生成すると typo による 404 を減らせる |
|
|
26
|
+
|
|
27
|
+
本 gem はこれらを**規約として標準化**し、(a) 新規 Rails アプリが 5 行程度の設定で
|
|
28
|
+
K8s CRD を扱えるようにし、(b) 実際の consumer アプリをこの gem に移行して両方向で検証する。
|
|
29
|
+
|
|
30
|
+
## 2. スコープ
|
|
31
|
+
|
|
32
|
+
### 2.1 本設計で扱うもの
|
|
33
|
+
|
|
34
|
+
- gem `k8s-rails` のアーキテクチャ・公開 API の設計
|
|
35
|
+
- 依存ポリシー(kruby pin、ActiveSupport の扱い)
|
|
36
|
+
- 例外体系・障害時の挙動定義
|
|
37
|
+
- テスト戦略(スタブによるユニットテスト、クラスタ不要)
|
|
38
|
+
- v0.1 / v0.2 のリリース境界
|
|
39
|
+
- consumer アプリへの移行手順(v0.1 検証の受け皿)
|
|
40
|
+
|
|
41
|
+
### 2.2 非スコープ(本設計の外)
|
|
42
|
+
|
|
43
|
+
- core v1 リソース(Pod / Deployment / Service 等)のフルサポート — CRD 中心の gem。core v1 は built-in リソースであり CustomObjects API では扱えない(`group=""`/`version="v1"` の宣言は**無効**)ため、別途 core API 経路が必要。v0.1 では対象外(将来候補、§11)
|
|
44
|
+
- **watch(ストリーム)** — v0.1 では非対応。kruby の watch は get/list より成熟度が低く、初版の API 保証範囲から外す(§11 で v0.2 候補)
|
|
45
|
+
- アプリの**デプロイ**(helm / kustomize 生成等) — `kuby-core` の領域
|
|
46
|
+
- RBAC 権限の付与・管理 — 呼び出しアプリ側の ClusterRole/Role の責務
|
|
47
|
+
- 複数クラスタ同時接続 — v0.1 は単一クラスタ前提(§11 展望)
|
|
48
|
+
|
|
49
|
+
### 2.3 前提
|
|
50
|
+
|
|
51
|
+
| # | 前提 |
|
|
52
|
+
|---|------|
|
|
53
|
+
| P1 | 実行環境は Kubernetes 1.27 以降を想定。API は 1.27〜1.31 系で動作確認 |
|
|
54
|
+
| P2 | クライアントは `kruby`(公式 OpenAPI クライアント系)。consumer アプリと同一の `~> 1.36.0` を基本 pin |
|
|
55
|
+
| P3 | 認証は (a) in-cluster ServiceAccount、(b) KUBECONFIG の 2 パターンのみを扱う。exec plugin / 他方式は v0.1 で保証しない |
|
|
56
|
+
| P4 | consumer アプリの K8s 利用(Argo Workflows / CronWorkflow 等の CRD)が、本 gem の移行検証の**最初かつ最低限のユースケース**である |
|
|
57
|
+
|
|
58
|
+
## 3. 全体構成
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
┌────────────────────────────────────────────────────┐
|
|
62
|
+
│ Rails アプリ(呼び出し側) │
|
|
63
|
+
│ K8sRails.configure { |c| c.namespace = "app" } │
|
|
64
|
+
│ K8sRails.crd(group:, version:, plural:, kind:) │
|
|
65
|
+
│ │
|
|
66
|
+
│ MyApp::Workflow.list / .find / .create / .patch │
|
|
67
|
+
└──────────────────────────┬─────────────────────────┘
|
|
68
|
+
│ 例外: K8sRails::Unavailable / ::NotFound / ::ApiError
|
|
69
|
+
┌──────────────────────────┴─────────────────────────┐
|
|
70
|
+
│ k8s-rails(本 gem) │
|
|
71
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
|
|
72
|
+
│ │ Config │ │ Client │ │ CRD │ │
|
|
73
|
+
│ │ (initializer) │→ │ (接続解決+橋 │→ │ Resource │ │
|
|
74
|
+
│ │ │ │ 渡し+計測) │ │ (メソッド生成)│
|
|
75
|
+
│ └──────────────┘ └──────┬───────┘ └─────┬─────┘ │
|
|
76
|
+
│ │ │ │
|
|
77
|
+
│ 例外体系: Unavailable / NotFound / ApiError│ │
|
|
78
|
+
│ 計測: ActiveSupport::Notifications (任意) │ │
|
|
79
|
+
└──────────────────────────┴────────────────┼────────┘
|
|
80
|
+
│ │
|
|
81
|
+
┌──────────────────────────┴────────────────┴────────┐
|
|
82
|
+
│ kruby (~> 1.36.0) → Kubernetes API Server │
|
|
83
|
+
│ (in-cluster SA token | KUBECONFIG) │
|
|
84
|
+
└────────────────────────────────────────────────────┘
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
データフローの原則:
|
|
88
|
+
|
|
89
|
+
- 呼び出しアプリは kruby に**直接触れない**(例外はテスト用注入のみ)。
|
|
90
|
+
`K8sRails::Client` を挟むことで、kruby の API 変化・キー種別問題は gem 内部に封じ込める。
|
|
91
|
+
- クラスタ未接続でも gem のロード・設定は**副作用なし**で完了する(lazy connect)。
|
|
92
|
+
接続は最初の API 呼び出し時(§5.2)。
|
|
93
|
+
|
|
94
|
+
## 4. ディレクトリ構成(gem 本体)
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
k8s-rails/
|
|
98
|
+
├── k8s-rails.gemspec
|
|
99
|
+
├── Gemfile # gemspec 参照 + 開発依存 (rspec, rubocop)
|
|
100
|
+
├── Rakefile
|
|
101
|
+
├── README.md
|
|
102
|
+
├── LICENSE # 既にある MIT(Dorian - Takahiro Ishida)
|
|
103
|
+
├── docs/
|
|
104
|
+
│ └── design.md # 本設計書
|
|
105
|
+
├── lib/
|
|
106
|
+
│ ├── k8s-rails.rb # エントリ。require 集 + モジュール定義
|
|
107
|
+
│ └── k8s_rails/
|
|
108
|
+
│ ├── version.rb # VERSION = "0.1.0"
|
|
109
|
+
│ ├── configuration.rb # Config: namespace, connection, 計測 ON/OFF
|
|
110
|
+
│ ├── client.rb # 接続解決・BearerToken 橋渡し・計測ラップ
|
|
111
|
+
│ ├── crd.rb # K8sRails.crd 宣言 → Resource 生成
|
|
112
|
+
│ ├── resource.rb # list/find/create/patch(生成先の基底クラス)
|
|
113
|
+
│ └── errors.rb # Unavailable / NotFound / ApiError
|
|
114
|
+
├── spec/
|
|
115
|
+
│ ├── spec_helper.rb
|
|
116
|
+
│ ├── k8s_rails_spec.rb # 設定 / lazy connect
|
|
117
|
+
│ ├── client_spec.rb # 橋渡し・計測・例外変換
|
|
118
|
+
│ ├── crd_spec.rb # 宣言 → メソッド生成
|
|
119
|
+
│ └── resource_spec.rb # list/find/create/patch の整形
|
|
120
|
+
└── examples/
|
|
121
|
+
└── rails-app/ # 最小 Rails 7.1 例(consumer 移行の雛形兼用)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 5. 公開 API 設計
|
|
125
|
+
|
|
126
|
+
### 5.1 設定
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
# config/initializers/k8s-rails.rb
|
|
130
|
+
K8sRails.configure do |config|
|
|
131
|
+
config.namespace = ENV.fetch("K8S_NAMESPACE", "default")
|
|
132
|
+
# 任意上書き(省略時は default_config の自動検出: in-cluster → KUBECONFIG)
|
|
133
|
+
# config.connection = Kubernetes::Configuration.default_config
|
|
134
|
+
# config.instrumentation = true # 既定 true(ActiveSupport 存在時のみ有効)
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
設定項目:
|
|
139
|
+
|
|
140
|
+
| キー | 既定 | 説明 |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `namespace` | `"default"` | CRD 宣言が namespace 未指定時のデフォルト |
|
|
143
|
+
| `connection` | `nil`(自動検出) | `Kubernetes::Configuration` インスタンス。認証を上書きする場合に指定 |
|
|
144
|
+
| `api_client` | `nil` | **テスト専用**: kruby の `CustomObjectsApi` と同型の 4 メソッド(`get_namespaced_custom_object` / `list_namespaced_custom_object` / `create_namespaced_custom_object` / `patch_namespaced_custom_object`)を実装する素のオブジェクト。指定時は `Client.build` が接続解決をスキープして `StringKeyedAdapter` で包んで使う(§5.2・§9) |
|
|
145
|
+
| `instrumentation` | `true` | `ActiveSupport::Notifications` で計測する(§8) |
|
|
146
|
+
|
|
147
|
+
- 設定は `K8sRails.configure` で**一度だけ**。再実行は警告(`Warning`)+ 無視。
|
|
148
|
+
- `K8sRails.reset!`(テスト用)で接続キャッシュ・宣言済 CRD を破棄できる。
|
|
149
|
+
|
|
150
|
+
### 5.2 接続
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
K8sRails::Client.build # → Kubernetes::CustomObjectsApi(lazy。初回呼び出し時に接続)
|
|
154
|
+
K8sRails.connected? # → 成功時は true。失敗は K8sRails::Unavailable / ApiError を raise
|
|
155
|
+
# (false を返す経路なし)。/version 相当の軽量確認
|
|
156
|
+
# (kruby 1.36.x の VersionApi#get_code(GET /version/)1 回)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`Client.build` が内部で行うこと(consumer アプリの K8s サービスの custom objects 生成部を移設):
|
|
160
|
+
|
|
161
|
+
0. `config.api_client` があれば(テスト注入、§5.1)それを直接返し、以降の接続解決をスキップ
|
|
162
|
+
1. `config.connection` があればそれ、なければ `Kubernetes::Configuration.default_config`
|
|
163
|
+
2. **K1 橋渡し**: `api_key['authorization']` が `api_key['BearerToken']` に書かれていなければ複製
|
|
164
|
+
3. `Kubernetes::ApiClient` → `Kubernetes::CustomObjectsApi` を生成し、**文字列キー化**(K2)を API レスポンス後に行う。ActiveSupport 非依存の gem 内部の純 Ruby 再帰変換(`K8sRails::Normalizer`)を使う(v0.1.1 以降: 常に Normalizer。`deep_stringify_keys` 経路は廃止)
|
|
165
|
+
4. **接続レベルの失敗**(DNS 失敗 / タイムアウト / 接続拒否等)は `K8sRails::Unavailable` に変換して `raise`(リトライはしない)。**kruby 1.36.x ではこれらの転送失敗は HTTP ステータスが無いため `Kubernetes::ApiError`(`code == 0`)として surfacing する**(§5.4 の変換表参照)。認可失敗(401/403)は §5.4 により `K8sRails::ApiError`
|
|
166
|
+
|
|
167
|
+
### 5.3 CRD 宣言
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# config/initializers/k8s-rails_crd.rb(またはアプリケーションクラス内)
|
|
171
|
+
Workflow = K8sRails.crd(
|
|
172
|
+
group: "argoproj.io",
|
|
173
|
+
version: "v1alpha1",
|
|
174
|
+
plural: "workflows",
|
|
175
|
+
kind: "Workflow",
|
|
176
|
+
namespace: K8sRails.config.namespace, # 省略可
|
|
177
|
+
readonly: false, # 既定 true。false で create/patch 有効化(K4)
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
宣言で生成されるメソッド(全て class メソッド。各メソッドは任意の `namespace:` 引数を受け取り、宣言時のデフォルト namespace を上書き可能):
|
|
182
|
+
|
|
183
|
+
| メソッド | 引数 | 戻り値 | readonly 制限 |
|
|
184
|
+
|---|---|---|---|
|
|
185
|
+
| `list` | `{}` | `[{"name" => "...", "labels" => {}, "spec" => {}, "status" => {}}, ...]`(**文字列キー**) | 常に有効 |
|
|
186
|
+
| `find(name)` | 必須 | 同型 or `K8sRails::NotFound`(raise) | 常に有効 |
|
|
187
|
+
| `create(attributes)` | CRD body hash | 作成済みオブジェクト(文字列キー) | `readonly: false` のみ |
|
|
188
|
+
| `patch(name, operations)` | JSON Patch 操作配列 | 更新済みオブジェクト | `readonly: false` のみ |
|
|
189
|
+
|
|
190
|
+
- **戻り値は常に文字列キーの Hash**(K2 の規約を API 契約として固定)。
|
|
191
|
+
`find` は存在しない場合 `K8sRails::NotFound` を raise(consumer アプリ側が `return nil` にしていたのは
|
|
192
|
+
呼び出し側の都合。gem としては例外が明示的)。「存在しない場合は nil」が欲しい場合は
|
|
193
|
+
`find_or_nil(name)` を併設する。
|
|
194
|
+
- `plural` / `kind` は自動推測しない(`workflows` / `Workflow` 等、推測が外れる CRD が多い)。
|
|
195
|
+
宣言で必ず指定する(K5)。
|
|
196
|
+
- 同名 CRD の再宣言は `K8sRails::RedeclarationError`(設定ミス検出)。
|
|
197
|
+
|
|
198
|
+
### 5.4 例外体系(K3)
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
K8sRails::Error < StandardError
|
|
202
|
+
├── K8sRails::Unavailable # 接続不能・タイムアウト・DNS 失敗等(クラスタ起因)
|
|
203
|
+
├── K8sRails::NotFound # リソース不在(HTTP 404)
|
|
204
|
+
├── K8sRails::ApiError # その他の API エラー(401/403/409/422 等)。#code と #response を保持
|
|
205
|
+
├── K8sRails::ReadOnlyError # readonly: true 宣言で create/patch が呼ばれた(設定ミス)
|
|
206
|
+
└── K8sRails::RedeclarationError # 同名 CRD の再宣言(設定ミス)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
変換規則:
|
|
210
|
+
|
|
211
|
+
| kruby 側 | → gem 側 |
|
|
212
|
+
|---|---|
|
|
213
|
+
| kruby 転送層例外(DNS 失敗 / タイムアウト / 接続拒否等。kruby 1.36.x では **`ApiError`(`code == 0`)** として surfacing) | `Unavailable` |
|
|
214
|
+
| その他の `StandardError`(プログラミング/設定ミス、例: `NoMethodError`) | そのまま伝播(変換せず隠さない) |
|
|
215
|
+
| `Kubernetes::ApiError` code 404 | `NotFound` |
|
|
216
|
+
| `Kubernetes::ApiError` その他 | `ApiError`(code / response body を保持) |
|
|
217
|
+
| 宣言時に `readonly: true` で create/patch を呼ばれた | `K8sRails::ReadOnlyError`(**設定ミス**なので raise せずには済ませない) |
|
|
218
|
+
|
|
219
|
+
呼び出しアプリ(Rails)側の推奨パターン:
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
begin
|
|
223
|
+
workflows = Workflow.list
|
|
224
|
+
rescue K8sRails::Unavailable => e
|
|
225
|
+
render "k8s_unavailable" # consumer アプリの「K8s 未接続」バナー相当
|
|
226
|
+
rescue K8sRails::NotFound
|
|
227
|
+
redirect_to root_path, alert: "Workflow が見つかりません"
|
|
228
|
+
end
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## 6. 依存ポリシー
|
|
232
|
+
|
|
233
|
+
| 依存 | 制約 | 理由 |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| Ruby | `>= 3.3, < 4.0` | 下限: kruby 1.36.x が `required_ruby_version ">= 3.3"` を宣言(RubyGems API で実測 2026-09-21、1.36.0.1〜1.36.4.1 全バージョン)。上限: 「宣言した Ruby minor を必ず CI で検証する」方針(レビュー対応・2026-09-21)— 2026-09-21 時点で Ruby 4.0 は stable(v4.0.7)だが未検証、3.5 は preview(v3_5_0_preview1)のため、宣言範囲を 3.x に限定。4.0 / 3.5 対応は v0.2 以降で検証の上宣言に含める |
|
|
236
|
+
| `kruby` | `~> 1.36.0` | consumer アプリと同一 pin。`~> 1.36.0` は 1.36.x のみ許可(`~> 1.36` 形式は 1.37 以降も許容してしまうため使用しない)。新しめの kruby に対応する場合は §7 の確認事項(client.rb 4 メソッド・K1 橋渡し)を済ませてから明示的に上げ替える |
|
|
237
|
+
| `activesupport` | **任意**(`>= 7.0`) | `defined?(ActiveSupport::Notifications)` でガード(計測のみ、§8)。Rails 無し環境(Cron スクリプト等)でも動作する必要がある — レスポンスの文字列キー化(K2)はこれに依存せず、gem 内部の純 Ruby 変換で担う(§5.2) |
|
|
238
|
+
| `rspec` / `rubocop` | 開発依存 | spec / lint |
|
|
239
|
+
|
|
240
|
+
- kruby への依存は **`K8sRails::Client` に閉じ込める**(§7)。
|
|
241
|
+
kruby 上げ替え時の修正箇所を 1 ファイルに限定し、CHANGELOG に「対応 kruby」を明記する。
|
|
242
|
+
|
|
243
|
+
## 7. 実装規約(kruby 変化への耐性)
|
|
244
|
+
|
|
245
|
+
- `lib/k8s_rails/client.rb` **のみ**が `require "kubernetes"` してよい。
|
|
246
|
+
他のファイルは kruby 定数・クラスを参照しない。
|
|
247
|
+
- kruby の `CustomObjectsApi` メソッド呼び出しは `client.rb` 内の
|
|
248
|
+
`*_namespaced_custom_object` の 4 メソッド(`get_namespaced_custom_object` 等)に集約する。`resource.rb` は
|
|
249
|
+
`K8sRails.client.get(group, version, ns, plural, name)` のような **gem 内部 API** だけを使う。
|
|
250
|
+
- kruby 上げ替え時の作業は (1) client.rb 4 メソッドのシグネチャ確認、
|
|
251
|
+
(2) K1 橋渡しの要否確認、に収まることをテスト(§9)で担保する。
|
|
252
|
+
|
|
253
|
+
## 8. 計測(ActiveSupport 任意)
|
|
254
|
+
|
|
255
|
+
`instrumentation: true` かつ ActiveSupport 存在時、各 API 呼び出しを計測する:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
k8s-rails.request payload: { operation: :list, group:, version:, plural:, namespace:
|
|
259
|
+
# operation は symbol(:list / :find / :create / :patch)
|
|
260
|
+
duration_ms: # float(ミリ秒・小数点 2 桁)
|
|
261
|
+
status: "ok" | "unavailable" | "api_error" }
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- `status` は **文字列**(`"ok"` / `"unavailable"` / `"api_error"`)、
|
|
265
|
+
`operation` は **symbol**。例外は notification を発した上で **そのまま raise**
|
|
266
|
+
される(計測は swallow しない)。
|
|
267
|
+
|
|
268
|
+
- Rails アプリではこの notification を `ActiveSupport::Notifications` /
|
|
269
|
+
`log_subscription` で拾える(ログ・ダッシュボード表示)。
|
|
270
|
+
- ActiveSupport 無い環境(または `instrumentation: false`)では no-op。
|
|
271
|
+
この場合も **ブロックの戻り値はそのまま返る**(`nil` にはならない)。
|
|
272
|
+
|
|
273
|
+
## 9. テスト戦略(クラスタ不要)
|
|
274
|
+
|
|
275
|
+
| レイヤー | 手法 | 対象 |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| ユニット | `K8sRails.config.api_client` に**スタブ**(kruby `CustomObjectsApi` と同型の 4 メソッド `get_namespaced_custom_object` / `list_namespaced_custom_object` / `create_namespaced_custom_object` / `patch_namespaced_custom_object` を実装する素のオブジェクト。`StringKeyedAdapter` がこの形式を呼ぶ)を注入 | client(橋渡し・例外変換)、resource(整形・readonly 制限)、crd(メソッド生成) |
|
|
278
|
+
| 設定 | spec 間で `K8sRails.reset!` | 宣言の破棄・再接続 |
|
|
279
|
+
| 集積(任意) | GitHub Actions で **kind**(または既存 microk8s に接続するジョブ)で実クラスタ E2E | v0.1 の必須ではない。**推奨**: consumer アプリ移行時の検証を兼ねる |
|
|
280
|
+
|
|
281
|
+
- 本設計では CI は `rspec` + `rubocop` のみを必須とし、kind E2E は v0.2 以降で
|
|
282
|
+
GitHub Actions の追加として扱う(実クラスタへの接続 CI はネットワーク依存のため採用しない)。
|
|
283
|
+
|
|
284
|
+
## 10. リリース計画
|
|
285
|
+
|
|
286
|
+
| バージョン | 内容 | 出口基準 |
|
|
287
|
+
|---|---|---|
|
|
288
|
+
| **v0.1** | §5 の公開 API(CRD 宣言 / list / find / create / patch / 例外 / 計測 / スタブテスト)+ README | rspec 全緑 + **consumer アプリの K8s サービスを `k8s-rails` に移行して動作確認**(§12) |
|
|
289
|
+
| v0.2 | watch(`watch` メソッド、kruby の watch サポート上)、core v1 built-in リソース対応(CustomObjects API では不可なため別途 core API 経路、§2.2)、kind E2E の CI 化 | v0.1 運用 1 ヶ月後のフィードバック |
|
|
290
|
+
| v0.3 | (展望)複数クラスタ(ネームスペース化された client 集合)、リトライポリシー | — |
|
|
291
|
+
|
|
292
|
+
v0.1 の milestone 分割(開発セッション向けのタスク単位目安):
|
|
293
|
+
|
|
294
|
+
1. M0 — gem 骨子: gemspec / Gemfile / Rakefile / version / require 構造(rspec が回せる状態)
|
|
295
|
+
2. M1 — Configuration + reset!(K1 橋渡しを含む Client 実装、例外変換)
|
|
296
|
+
3. M2 — CRD 宣言 DSL + Resource(list / find / find_or_nil / create / patch、readonly 制限)
|
|
297
|
+
4. M3 — 計測 + README + rubocop 設定
|
|
298
|
+
5. M4 — consumer アプリへの移行と検証(§12)
|
|
299
|
+
|
|
300
|
+
## 11. 展望・検討事項(v0.1 では確定しない)
|
|
301
|
+
|
|
302
|
+
- **watch**: kruby の watch はストリーム処理であり、Rails のリクエスト応答型には不向き。
|
|
303
|
+
導入するなら「watch 開始 → メッセージをキュー / NotificationCenter 相当に流す」の
|
|
304
|
+
形で、ポーリング置き換えのユースケース(consumer アプリの 30 秒ポーリング等)から設計する。
|
|
305
|
+
- **複数クラスタ**: `K8sRails.cluster("prod") { ... }` のような名前付き client 集合。
|
|
306
|
+
現時点で需要がないため v0.1 では単一クラスタ。
|
|
307
|
+
- **retries / timeout**: kruby の `Kubernetes::Configuration` には接続タイムアウトが
|
|
308
|
+
設定できる。v0.1 は既定値 + README 記載のみで、gem 独自のバックオフは持たない
|
|
309
|
+
(Rails 側の middleware / sidekiq retry で吸収するのが慣習)。
|
|
310
|
+
- **OpenTelemetry**: `instrumentation` を notification 経由にしているため、
|
|
311
|
+
OTel instrumentation を別途足せる状態に留める(v0.1 で実装しない)。
|
|
312
|
+
|
|
313
|
+
## 12. consumer アプリへの移行(v0.1 検証)
|
|
314
|
+
|
|
315
|
+
実際の consumer Rails アプリの K8s サービス(kruby 直接利用)を `k8s-rails` に置き換えて動作を確認する:
|
|
316
|
+
|
|
317
|
+
1. `Gemfile` に `gem "k8s-rails", path: "../k8s-rails"`(開発期間限定。公開後は registry 版)
|
|
318
|
+
2. initializer に CRD 宣言(Argo Workflows / CronWorkflow 等の該当 CRD、
|
|
319
|
+
`readonly: false`(操作系があるため))
|
|
320
|
+
3. K8s サービス内の kruby 呼び出しを `K8sRails` 経由に置換。**整形メソッド
|
|
321
|
+
(summary 系)は consumer アプリ側に残す**
|
|
322
|
+
(アプリ固有の表示ロジックのため、gem には載せない)
|
|
323
|
+
4. 例外: consumer アプリの Unavailable 相当を `K8sRails::Unavailable` に alias/
|
|
324
|
+
rescue 統一
|
|
325
|
+
5. 検証: consumer アプリのテスト + K8s 読取が KUBECONFIG 経由で
|
|
326
|
+
従来通り表示されること(実クラスタへの手動確認)
|
|
327
|
+
|
|
328
|
+
移行後も K8s サービスを**整形ラッパーとして残す**(コントローラの呼び出し先を変えない、
|
|
329
|
+
PR の差分を最小化)。
|
|
330
|
+
|
|
331
|
+
## 13. 命名・公開
|
|
332
|
+
|
|
333
|
+
- **gem 名 / リポジトリ名: `k8s-rails`**(RubyGems で空きを確認済み 2026-09-21)。
|
|
334
|
+
当初の名 `kuberails` は **push 時の類似名チェックで却下**された
|
|
335
|
+
(RubyGems は名前のハイフンを無視して比較し、`kube-rails`(2015 年の旧 gem・
|
|
336
|
+
取得済み)と正規化すると同一文字列になるため使用不可)。命名規則は
|
|
337
|
+
**gem 名 = ハイフン**(`k8s-rails`)・**モジュール = CamelCase**(`K8sRails`)・
|
|
338
|
+
**ファイル / ディレクトリ = snake_case**(`lib/k8s_rails/`。エントリのみ
|
|
339
|
+
`lib/k8s-rails.rb` と require 名に合わせたハイフン)で統一する
|
|
340
|
+
- GitHub: `doridoridoriand/k8s-rails`(org `k8s-rails` は他者が使用済み。個人アカウント配下)
|
|
341
|
+
- 公開は v0.1 完成後、**ローカル PC から手動 `gem push`**(kruby と同様の運用方針・
|
|
342
|
+
2026-09-21 確定)。CI による自動公開は行わない。
|
|
343
|
+
- 手順(owner がローカル PC で実施):
|
|
344
|
+
1. `bundle exec rake`(spec + rubocop)が全緑であることを確認
|
|
345
|
+
2. CHANGELOG.md の該当バージョン節を確定(「Unreleased」のまま公開しない)
|
|
346
|
+
3. `gem signin`(RubyGems アカウント・未作成なら先に作成)
|
|
347
|
+
4. `gem build k8s-rails.gemspec` → `gem push k8s-rails-<VERSION>.gem`
|
|
348
|
+
- 未取得の gem は**初回 push が所有権の取得**(`gem owner` は `--add` による
|
|
349
|
+
**追加** owner のみで、位置引数に user を取る構文は存在しない・
|
|
350
|
+
gem 4.0.7 `gem owner --help` で実測 2026-09-21)
|
|
351
|
+
- 公開直前に tag を切って **remote へ push** すると追溯性が高い
|
|
352
|
+
(`git tag v<VERSION> && git push origin v<VERSION>`)。
|
|
353
|
+
GitHub 上でリリースコミットと tag が対応付けられ、
|
|
354
|
+
公開した gem のバージョンがどのコミットに基づくかを追跡できる。
|
|
355
|
+
tag 名と gemspec の `VERSION` は一致させる
|
|
356
|
+
- テスト CI(`.github/workflows/test.yml`)は push / PR 時に rspec + rubocop を実行。
|
|
357
|
+
gemspec の宣言範囲(`>= 3.3, < 4.0`)を matrix で検証:
|
|
358
|
+
3.3.0(下限・kruby 1.36.x の `>= 3.3`)・3.3.8(開発)・
|
|
359
|
+
3.4.10(3.x 系の最新 stable・2026-09-21 時点。3.5 は preview、
|
|
360
|
+
4.0 は宣言範囲外のため未検証・v0.2 以降で検討)。
|
|
361
|
+
**宣言範囲を常に matrix がカバーする**こと(`< 4.0` 上限により、
|
|
362
|
+
4.x のリリースは宣言範囲外。stable 化された新 3.x minor が出たら
|
|
363
|
+
matrix への追加を忘れないこと)。
|
|
364
|
+
テストはクラスタ不要(§9・スタブ注入)のため v0.1 は runner 上のユニットのみ。
|
|
365
|
+
kind / 実クラスタ E2E の CI 化は v0.2 対象(§9・§10)
|
|
366
|
+
- `README` に「kruby pin」「対応 k8s バージョン(実測 v1.33.x で検証済み)」「K1 橋渡しの背景」
|
|
367
|
+
を明記する(検索でヒットする重要な注意点のため)
|
|
368
|
+
|
|
369
|
+
## 14. 承認・変更履歴
|
|
370
|
+
|
|
371
|
+
| 版 | 日付 | 変更 | 承認 |
|
|
372
|
+
|---|---|---|---|
|
|
373
|
+
| 0.1 | 2026-09-15 | 初版(案)。実際の Rails アプリ実装の知見 K1–K5 を基に作成 | 未承認 |
|
|
374
|
+
| 0.1.1 | 2026-09-18 | PR #1 レビュー対応: 文字列キー化の純 Ruby 経路(ActiveSupport 非依存)、401/403→ApiError 統一、`throw`→`raise`、core v1 を built-in 扱いに修正、`~> 1.36.0` に統一、テスト注入の `api_client` 追加、例外ツリーに `ReadOnlyError`/`RedeclarationError` 追記、初期化子例を汎用化 | レビュー反映済み |
|
|
375
|
+
| 0.1.2 | 2026-09-18 | M1 実装にあたって kruby 1.36.2.1 を実機確認した差分を反映: `connected?` の endpoint を `VersionApi#get_code`(GET /version/)に修正、転送失敗(DNS/timeout/接続拒否)が `ApiError(code == 0)` として surfacing することを §5.2/§5.4 に明記、文字列キー化を常に `Normalizer`(`deep_stringify_keys` 経路廃止)に統一 | 実装反映済み |
|
|
376
|
+
| 0.1.3 | 2026-09-20 | M3 実装に伴う §8 の軽微明確化: notification の `operation` は symbol・`status` は文字列であること、例外は発火後そのまま raise(swallow しない)こと、no-op 時(AS 無 / instrumentation: false)もブロック値がそのまま返ること。加えて `connected?` の戻り値記述を実装に合わせ修正(false を返す経路なし・失敗は raise)、テストスタブのメソッド名を kruby `CustomObjectsApi` 形式(`*_namespaced_custom_object`)に修正 | 実装反映済み |
|
|
377
|
+
| 0.1.4 | 2026-09-21 | リリース準備(§13): 公開導線を手動 `gem push` から**タグ基準の GitHub Actions**(`test.yml` / `publish.yml`)に更新、README に検証済み k8s サーババージョン(v1.33.x / microk8s v1.33.13)を追記、CHANGELOG.md を同梱、gemspec に `source_code_uri` / `changelog_uri` / `allowed_push_host` メタ情報を追加 | 実装反映済み |
|
|
378
|
+
| 0.1.5 | 2026-09-21 | PR #11 レビュー対応(Codex P2 + Copilot M/L 3 系統): ①未取得 gem の owner 取得手順を「初回 push が所有権取得」に修正(`gem owner k8s-rails <user>` は無効構文・`gem owner --help` 実測、`--add` は追加のみ)・publish.yml / §13 ②テスト CI を Ruby 3.3.x matrix に(**`>= 3.2` は kruby 1.36.x の `>= 3.3` と非整合だったため、§6・gemspec・README の Ruby 下限を 3.3 に改訂**・RubyGems API で 1.36.x 全 7 バージョン実測)③tag 前の CHANGELOG 確定を手順化(publish workflow は CHANGELOG を書き換えないため) | 実装反映済み |
|
|
379
|
+
| 0.1.6 | 2026-09-21 | PR #11 レビュー第 2 波対応(Copilot ×2): ①publish workflow に `verify` job(サポート Ruby 全バージョンの rake matrix)を追加し push job を `needs: verify` でゲート化(独立 Test workflow は tag 時に gem push をブロックできないため)・§13 ②テスト matrix の下限を 3.3.1 から **3.3.0** に(gemspec `>= 3.3` は 3.3.0 を含むため、宣言された最低バージョンを実際に検証) | 実装反映済み |
|
|
380
|
+
| 0.1.7 | 2026-09-21 | PR #11 レビュー第 3 波対応(Copilot ×1): `>= 3.3` が Ruby 3.4+ も含むため、テスト / verify matrix に **3.4.10(最新 stable・ruby-lang.org 実測)** を追加(3.3.0 / 3.3.8 / 3.4.10 の 3 系統)。新しい stable minor が出た際の matrix 追加を §13 に手順として明記 | 実装反映済み |
|
|
381
|
+
| 0.1.8 | 2026-09-21 | PR #11 レビュー第 4 波対応(Copilot ×3): 指摘(「Ruby 3.5 が stable 化したため matrix に追加せよ」)を検証した結果 **3.5 は preview であり claim は誤り**(ruby/ruby タグ `v3_5_0_preview1`・2026-09-21 実測)と判明。ただし指摘の根本(宣言と検証範囲のズレ)は**Ruby 4.0 が stable(v4.0.7)だったため**実際に存在した。対策として宣言範囲を **`>= 3.3, < 4.0` に改訂**(gemspec / §6 / README / CHANGELOG)し、宣言範囲 = matrix 検証範囲(3.3.0 / 3.3.8 / 3.4.10)を一致。4.0 / 3.5 対応は v0.2 以降で検証の上宣言に含める方針 | 実装反映済み |
|
|
382
|
+
| 0.1.9 | 2026-09-21 | public リポジトリ化の準備: ①公開導線を**ローカル PC から手動 `gem push`** に変更(kruby と同様の運用方針・CI 自動公開は廃止、publish workflow を削除、test workflow の push/PR テストのみ残す)②§13 公開手順の手動化(tag は追溯性のため推奨)③内部 consumer アプリの名称・構造への言及を §1〜§14 全箇所から除去し「consumer アプリ」に一般化 | 実装反映済み |
|
|
383
|
+
| 0.1.10 | 2026-09-21 | PR #12 レビュー対応(Copilot): §13 の公開手順で tag の **remote への push**(`git push origin v<VERSION>`)が欠落しており、GitHub 上のリリースコミットとの対応付け(追溯性)が確保できないとの指摘を反映 | 実装反映済み |
|