---
# System prepended metadata

title: '[主題] Helm：用 `--api-versions` 驗證條件渲染'
tags: [k8s, K8s / Helm, HelmChart, .Capabilities.APIVersions.Has, Kubernetes, '--api-versions', Helm Chart]

---

[主題][helm template] 用 `--api-versions` 驗證條件渲染
===
###### tags: `K8s / Helm`
###### tags: `Kubernetes`, `k8s`, `Helm Chart`, `HelmChart`, `.Capabilities.APIVersions.Has`, `--api-versions`

<br>

[TOC]

<br>

> by gpt-5 (2025/10/13)

:::info
#### :books: 摘要
- 為什麼 `helm template` 無法渲染出某些東西？
- 為什麼 `helm template` 需要 `--api-versions` 才能渲染出條件資源？
- 在本機/叢集各自怎麼測？
:::

<br>

## 目標

* 了解 `.Capabilities.APIVersions.Has` 的行為
* 本機用 `helm template` 模擬 API 存在與否
* 在真實叢集用 `helm install/upgrade` 自動偵測 API

<br>

---

## 1) 建立最小 Chart
> **檔案清單**：
> ```
> ├── Chart.yaml
> ├── templates
> │   ├── configmap.yaml
> │   └── hello-cr.yaml   <- 如何做，才能渲染出該條件資源？
> └── values.yaml
> ```

```bash=
mkdir hello-apiversions && cd hello-apiversions

# Chart.yaml
cat > Chart.yaml <<'YAML'
apiVersion: v2
name: hello-apiversions
description: Hello world for .Capabilities and --api-versions
type: application
version: 0.1.0
appVersion: "0.1.0"
YAML

# -------------------------------------
# values.yaml
cat > values.yaml <<'YAML'
feature:
  enableHelloCR: true
YAML

mkdir -p templates

# -------------------------------------
# 一個一定會渲染的 ConfigMap
cat > templates/configmap.yaml <<'YAML'
apiVersion: v1
kind: ConfigMap
metadata:
  name: hello-config
data:
  message: "This always renders."
YAML

# -------------------------------------
# 一個「只有當 API 存在」才渲染的資源（假裝有個 CRD：example.com/v1, Kind=Hello）
cat > templates/hello-cr.yaml <<'YAML'
{{- if and (.Capabilities.APIVersions.Has "example.com/v1") .Values.feature.enableHelloCR }}
apiVersion: example.com/v1
kind: Hello
metadata:
  name: hello-conditional
spec:
  message: "Rendered because example.com/v1 is available."
{{- end }}
YAML
```

<br>

---

## 2) 本機渲染：觀察差異

### 2.1 不加 `--api-versions`

```bash
helm template hello .
```

你會只看到 `ConfigMap`，**看不到** `Hello`（因為本機離線渲染，`.Capabilities.APIVersions` 內沒有 `example.com/v1`）。

### 2.2 加上 `--api-versions`

```bash
helm template hello . \
  --api-versions example.com/v1
```

這次會多出：

```yaml
apiVersion: example.com/v1
kind: Hello
metadata:
  name: hello-conditional
...
```

因為你**手動告訴** Helm：「把 `example.com/v1` 視為存在」。

> 小結：`helm template` 離線、**不會探測叢集**，除非你用 `--api-versions` 模擬。

<br>

---

## 3) 在真實叢集的行為

### 3.1 若叢集**真的**有對應 CRD

* 用 `helm install/upgrade`（連線叢集），Helm 會自動將叢集支援的 API 放進 `.Capabilities.APIVersions`。
* 條件 `Has "example.com/v1"` 成立 → `Hello` 會被渲染與建立。
* 驗證：
    ```bash
    # 示意（若你確實安裝了對應 CRD）
    helm install hello . -n demo --create-namespace
    kubectl -n demo get configmap hello-config
    kubectl -n demo get hello hello-conditional   # 假設有 Hello 這種 CR
    ```

### 3.2 若叢集**沒有**該 CRD

* `Hello` 不會被渲染（避免部署失敗）
* `ConfigMap` 仍會照常建立
* 驗證：
    ```bash
    helm install hello . -n demo --create-namespace
    kubectl -n demo get configmap hello-config     # 有
    kubectl -n demo get hello hello-conditional    # 會找不到（因為沒被渲染/建立）
    ```

<br>

---

## 4) 只看特定模板（加碼技巧）

想「只輸出某檔案」檢查？

```bash
helm template hello . \
  --show-only templates/hello-cr.yaml

# 搭配：
helm template hello . \
  --api-versions example.com/v1 \
  --show-only templates/hello-cr.yaml
```

<br>

---

## 5) 套到常見情境（ServiceMonitor 範例）

很多 chart 會這樣寫：

```yaml
{{- if and (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") .Values.exporter.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata: { name: my-exporter }
spec: { /* ... */ }
{{- end }}
```

* `helm template` 本機：**要**加 `--api-versions monitoring.coreos.com/v1` 才看得到
* 叢集有 Prometheus Operator 時：`helm install/upgrade` 會自動偵測到，條件成立 → 會建立

如果你只想在本機預覽並且**只看** ServiceMonitor：

```bash
helm template my . \
  --api-versions monitoring.coreos.com/v1 \
  --set exporter.serviceMonitor.enabled=true \
  --show-only templates/servicemonitor.yaml
```

<br>

---

## 6) 常見 QA

### Q1：為什麼 `kubectl get` 看得到，但 `helm template` 看不到？**
A：`kubectl get` 查的是**叢集已存在**的資源；`helm template` 是**離線渲染**，不會連線叢集去偵測 API，因此條件不成立就不渲染。加 `--api-versions` 才能在本機模擬「API 存在」。

### Q2：我可以在 `helm install` 也用 `--api-versions` 嗎？**
A：不行，`helm install` 不支援 `--api-versions`，會有底下錯誤訊息：
```
Error: unknown flag: --api-versions
```

### Q3：我只想要渲染/檢視特定檔案
A：用 `--show-only templates/xxx.yaml`。

<br>

---

## 7) 清理

```bash
helm uninstall hello -n demo || true
kubectl delete ns demo || true
```


<br>

---

<br>

## 總結

### `helm template` vs `helm install/upgrade`

- ### 1. `helm template` 的行為
    - **本地渲染**，不連接 Kubernetes cluster
    - **無法自動偵測** cluster 的 API versions（`.Capabilities.APIVersions`）
    - 條件 `{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" }}` 會失敗
    - 因此 **ServiceMonitor 不會被渲染**

- ### 2. `helm install/upgrade` 的行為
    - **連接到 Kubernetes cluster**
    - **自動偵測** cluster 支援的 API versions
    - 如果 cluster 安裝了 Prometheus Operator，會偵測到 `monitoring.coreos.com/v1`
    - 條件通過，**ServiceMonitor 會被創建**

<br>

---

### 不同操作下的比較
| 指令 | ServiceMonitor 輸出 | 原因 |
|------|-------------------|------|
| `helm template` | ❌ 沒有 | 無法偵測 API versions |
| `helm template --api-versions monitoring.coreos.com/v1` | ✅ 有 | 手動指定 API version |
| `helm install` (實際部署) | ✅ 有 | 自動偵測 cluster 的 API versions |
| `kubectl get servicemonitor` | ✅ 有 | 已經在 cluster 中部署 |

<br>

---

### 梗概（背後原理）

* `.Capabilities.APIVersions`：Helm 在**連線叢集**時填入可用 API；在**離線**（`helm template`）時是空的，除非你用 `--api-versions` 補上。
* Chart 透過條件判斷，自動在「有/無對應 CRD」的環境**安全降級**，提升可攜性。


<br>

{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}