# GSP Template Description
# metadata.yaml
說明 GSP 的 metadata.yaml 內各個欄位的定義方式
最基本的 metadata.yaml 只需要有 version 欄位
```yaml
version: '1.0.0'
```
除此之外還有其它選填的欄位可以定義
以下將針對這些欄位做簡單的敘述
#### 1. `version`
用途: 表示 gsp 的版本
附註: version 格式可自由決定,每次更新 gsp 時 version 必須比原本的還要新。此為必填
範例:
```yaml
version: '2021.01.1'
```
#### 2. `container_type`
用途: 表示 gsp 所開啟的服務類型
附註: 可支援的類型為 vm 及 docker 兩種。vm 只可被用於 Openstack 的平台,而 docker 只可被用於 Kubernetes 的平台。預設為 vm
範例:
```yaml
container_type: docker
```
#### 3. `module`
用途: 表示由此 gsp 所開啟的服務需要在平台中啟用的功能
附註: 目前可支援的功能類別為 IaaSVM, KubeShare 及 NativeResource
- 除了 IaaSVM 以外,都只能在 [container_type](#2-container_type) 為 docker 時才能使用。預設為 IaaSVM
- KubeShare 只在 GMN 及 Nomos 有支援
- NativeResource 目前只有 Nomos 支援
範例:
```yaml
module: KubeShare
```
#### 4. `goc_template_version`
用途: 表示 gsp 使用的語法版本
附註:
- 目前可支援版本為 1, 2 及 3。版本之間的差異可參考 [GSP Template
](https://gitlab.com/geminiopencloud/engineering/infra/gemini/-/wikis/GSP-Template)。預設為 1
- 不同版本可支援的 [module](#3-module) 也會有所不同
版本 1, 2 可支援 IaaSVM 及 KubeShare,而 3 只支援 NativeResource
範例:
```yaml
goc_template_version: '2'
```
#### 5. `os`
用途: 表示服務所使用的 image OS 類型
附註: 可支援的類型為 Linux 及 Windows。只有在 [container_type](#2-container_type) 為 vm 時才有作用。預設為 Linux
範例:
```yaml
os: Windows
```
#### 6. `resource_definitions`
用途: 此為 quota 計算所需之欄位,用於標明由此 gsp 所開出來的服務預估會佔用的資源量。當 [module](#3-module) 並非 NativeResource 時,這個欄位為必填
附註:
- **parameter**: 使用者開啟服務時所輸入的參數名稱。資源的預估量將會從使用者的輸入中取得對應名稱的參數值進行計算。此為必填
- **type**: 表示參數的資源類型。目前只支援 flavor 以及 floating_ip 兩種類型。其中 floating_ip 只會用於 [container_type](#2-container_type) 為 vm 的 gsp 中。此為必填
- **count**: 定義資源計算的方式,計算出來的結果會決定資源的預估使用量。可代入參數 (**params**) 進行計算。此為必填
- **params**: 定義在 **count** 中可用於計算資源使用量的參數,可自定義各個參數的值,也可從使用者的輸入中取得。從使用者輸入中取得值的定義方式為: {{ 參數名稱 }}
- **availability_zone**: 定義此資源會被使用於哪個 availability zone,用於決定開啟服務的請求會被送去哪個隊列中。可自定義 availability zone 的名稱,也可從使用者的輸入中取得。從使用者輸入中取得值的定義方式為: {{ 參數名稱 }}。此欄位只在 **type** 為 flavor 並且 [container_type](#2-container_type) 為 vm 時才有作用
範例:
```yaml
resource_definitions:
- parameter: flavor
type: flavor
count: 'replicas * parallelism + 1'
params:
replicas: {{ replicas }}
parallelism: 2
availability_zone: {{ availability_zone }}
```
# site.yaml
說明 GSP 的 site.yaml 內各個 label 的使用方法
各 Branch 能使用的 [goc_template_version](#4-goc_template_version) 之版本
**TWCC**: v1, v2
**TWGC**: v1
**GMN**: v1, v2
因為從 v3 開始 site.yaml 將不會額外定義 parameters 或 resources
所以接下來都將以 v2 為基礎說明
一般 site.yaml 定義參數會類似下面的樣子
```yaml
parameters:
image:
type: string
label: limited_options
description:
- allowed_values:
- nginx:1.7
- nginx:1.17
flavor:
type: string
label: public_flavor
```
## [OpenStack/Kubernetes]
1. `availability_zone`
用途: 自動列出所有可用的AZ選項, 以及可使用GPU卡的類型
附註: 僅TWCC會show GPU
範例:
```yaml
availability_zone:
type: string
label: availability_zone
```
Branch: TWCC,TWGC,GMN
Response:
```json
"availability-zone" : [
"(V100,T4)nova" # 該AZ存在有`V100`及`T4`GPU卡的hosts
"(Unknown,V100)az1", # Unknown為沒在config中設定名稱的GPU卡
"az2", # 該AZ不存在可使用GPU的hosts
]
```
2. `gpu_availability_zone`
用途: 自動列出所有可用的 GPU AZ選項, 以及可使用GPU卡的類型
附註: 無
範例:
```yaml
availability_zone:
type: string
label: gpu_availability_zone
```
Branch: TWCC
Response:
```json
"availability-zone" : [
"(V100,T4)nova", # 該AZ存在有`V100`及`T4`GPU卡的hosts
"(Unkown,V100)az1" # None為沒在config中設定名稱的GPU卡
]
```
---
## [OpenStack]
1. `flavor_list`
用途: 列出自定義, 且該Solution可使用的清單
附註: 效果同`private_flavor`
範例:
```yaml
flavor:
label: flavor_list
description:
- allowed_values:
- '0 GPU + 04 cores + 015GB memory + 005GB share memory'
- '0 GPU + 04 cores + 020GB memory'
constraints:
- custom_constraint: nova.flavor
```
Branch: TWCC,TWGC,GMN
Response:
```json
"flavor" : [
"0 GPU + 04 cores + 020GB memory"
]
```
2. `keypair`
用途: 自動列出可使用的keypair
附註: 無label, 由constraint從OpenStack撈
範例:
```yaml
keypair:
type: string
constraints:
- custom_constraint: nova.keypair
```
Branch: TWCC,TWGC,GMN
Response:
```json
"keypair" : [
"test_key"
]
```
3. `network`
用途: 自動列出該用戶可使用的network
附註: 無label, 由constraint從OpenStack撈
範例:
```yaml
public_network:
type: string
constraints:
- custom_constraint: neutron.network
private_network:
type: string
constraints:
- custom_constraint: neutron.network
```
Branch: TWCC,TWGC,GMN
Response:
```json
"private-network": [
"test_network"
]
```
4. `network_list`
用途: 自動列出該用戶可使用的network
附註: 支援用`,` create site 時一次給多個 network (`network1,network2`)
範例:
```yaml
private_network:
type: comma_delimited_list
label: network_list
```
Branch: TWGC,GMN
Response:
```json
"private-network": [
"test_network",
"network2"
]
```
Create Site Header:
`-H 'x-extra-property-private-network: test_network, network2'`
5. `image`
用途: 自動列出可使用的image
附註: 無label, 由constraint從OpenStack撈
範例:
```yaml
image:
type: string
constraints:
- custom_constraint: glance.image
```
Branch: TWCC,TWGC,GMN
Response:
```json
"image": [
"(public)image1",
"(private)image2"
]
```
6. `volume`
用途: 自動列出可使用的volume
附註: 無label, 由constraint從OpenStack撈
範例:
```yaml
volume:
type: string
constraints:
- custom_constraint: cinder.vtype
```
Branch: TWCC
Response:
```json
"volume": [
"volume_1"
]
```
7. `private_image`
用途: 自動列出可使用的private image
附註: 無
範例:
```yaml
image:
type: string
label: private_image
```
Branch: TWCC,GMN
Response:
```json
"image": [
"(private)private_img_1",
"(private)snapshot_1"
]
```
8. `private_flavor`
用途: 列出自定義, 且可使用的flavor
附註: 無
範例:
```yaml
flavor:
type: string
label: private_flavor
description:
- allowed_values:
- pvt_flavor_1
constraints:
- custom_constraint: nova.flavor
```
Branch: TWCC,GMN
Response:
```json
"flavor": [
"pvt_flavor_1"
]
```
9. `public_flavor`
用途: 自動列出可使用的public flavor
附註: 無
範例:
```yaml
flavor:
type: string
label: public_flavor
constraints:
- custom_constraint: nova.flavor
```
Branch: TWCC,GMN
Response:
```json
"flavor": [
"pub_flavor_1"
]
```
10. `hybrid_flavor`
用途: (private+public) 自動列出public flavor及有自定義且可使用的flavor
附註: 無
範例:
```yaml
flavor:
type: string
label: hybrid_flavor
description:
- allowed_values:
- pvt_flavor_1
constraints:
- custom_constraint: nova.flavor
```
Branch: TWCC,GMN
Response:
```json
"flavor": [
"pub_flavor_1",
"pvt_flavor_1"
]
```
11. `sg`
用途: 自動列出所有可使用的security group
附註: 無
範例:
```yaml
security_group:
type: string
constraints:
- custom_constraint: neutron.security_group
```
Branch: TWGC,GMN
Response:
```json
"security_group": [
"sg_1"
]
```
12. `bucket_list`
用途: 列出該使用者在該計畫中可使用的清單
附註:
- 需設定Ceph config
範例:
```yaml
bucket:
type: string
label: bucket_list
```
Branch: TWGC,GMN
Response:
```json
"bucket": [
{
"name": "{$bucket_name},
"mountpath": "{$mount_path}"
}
]
```
13. `bootable_volume`
用途: 列出該使用者在該計畫中可使用的 bootable volume 清單
附註:
- OS::Nova::Server 需設定好對應 vda mount point
範例:
```yaml
bootable_volume:
type: string
label: bootable_volume
```
Branch: TWGC, GMN, 桂華
Response:
```json
"bootable_volume": [
"(Linux)test"
]
```
---
## [Kubernetes]
1. `default_image_repo`
用途: 列出該使用者在default harbor下所有可使用的image
附註:
- 需設定harbor config
- default harobr為config中第一個
範例:
```yaml
image:
type: string
label: default_image_repo
```
Branch: TWCC,TWGC,GMN
Response:
```json
"image" : [
"{$harbor_ip}/{$image}:{$image_tag}"
]
```
2. `bucket_list`
用途: 列出該使用者在該計畫中可使用的清單
附註:
- 需設定Ceph config
範例:
```yaml
bucket:
type: string
label: bucket_list
```
Branch: TWCC,TWGC,GMN
Response:
```json
"bucket": [
{
"name": "{$bucket_name},
"mountpath": "{$mount_path}"
}
]
```
3. `flavor_list`
用途: 列出自定義, 且該Solution可使用的清單
附註A: 有設定 `allowed_values` 效果同 `private_flavor`
範例A:
```yaml
flavor:
label: flavor_list
description:
- allowed_values:
- '0 GPU + 04 cores + 015GB memory + 005GB share memory'
- '0 GPU + 04 cores + 020GB memory'
```
Branch: TWCC,TWGC,GMN
Response:
```json
"flavor" : [
"0 GPU + 04 cores + 020GB memory"
]
```
附註B: 沒設定 `allowed_values` 列出所有 private flavor
範例B:
```yaml
flavor:
label: flavor_list
```
Branch: GMN
Response:
```json
"flavor" : [
"0 GPU + 04 cores + 020GB memory",
"0 GPU + 04 cores + 015GB memory + 005GB share memory"
]
```
4. `harbor_image`
用途: 列出自定義harbor repository的可使用image
附註:
- 若沒指定ip, 則拿config裡第一個harbor
- **必須**將 image 的變數用 "" 包住
範例:
```yaml
image:
type: string
label: harbor_image
description:
- allowed_values:
- {{ project_name }}
...
spec:
containers:
- image: "{{ image }}"
```
Branch: TWCC,GMN
Response:
```json
"image": [
"${image}:${tag}"
]
```
5. `private_flavor`
用途: 列出自定義, 且可使用的flavor
附註: 無
範例:
```yaml
flavor:
type: string
label: private_flavor
description:
- allowed_values:
- pvt_flavor_1
```
Branch: TWCC,GMN
Response:
```json
"flavor": [
"pvt_flavor_1"
]
```
6. `public_flavor`
用途: 自動列出可使用的public flavor
附註: 無
範例:
```yaml
flavor:
type: string
label: public_flavor
```
Branch: TWCC,GMN
Response:
```json
"flavor": [
"pub_flavor_1"
]
```
7. `hybrid_flavor`
用途: (private+public) 自動列出public flavor及有自定義且可使用的flavor
附註: 無
範例:
```yaml
flavor:
type: string
label: hybrid_flavor
description:
- allowed_values:
- pvt_flavor_1
```
Branch: TWCC,GMN
Response:
```json
"flavor": [
"pub_flavor_1",
"pvt_flavor_1"
]
```
8. `gpu_list`
用途: 自動列出可使用GPU數量的選項
附註: 無
範例:
```yaml
gpu:
type: string
label: gpu_list
```
Branch: GMN
Response:
```json
"gpu": [
0,
1
]
```
9. `ceph_project`
用途: 自動建立PV&PVC
附註: 不需填form.yaml
範例:
```yaml
project_bucket:
type: string
label: 'ceph_project'
```
Branch: TWCC
Response: null
10. `gpfs_user`
用途: 取得request.username
附註: 不需填form.yaml
範例:
```yaml
username:
type: string
label: gpfs_user
```
Branch: TWCC,TWGC,GMN
Response: null
11. `project_name`
用途: 取得project_name
附註: 不需填form.yaml
範例:
```yaml
project_name:
type: string
label: 'project_name'
```
Branch: TWCC,GMN
Response: null
12. `ipvs`
用途: 隨機開port(50000~60000)
附註: 不需填form.yaml
範例:
```yaml
port1:
type: string
label: ipvs
```
Branch: TWCC,TWGC,GMN
Response: null
13. `public_ip`
用途: 取得public_ip
附註: 不需填form.yaml
範例:
```yaml
external_ip:
type: string
label: public_ip
```
Branch: TWCC,TWGC,GMN
Response: null
14. `link_service`
用途: 取得service_name
附註: 不需填form.yaml
範例:
```yaml
link_service:
type: string
label: link_service
```
Branch: TWCC,TWGC,GMN
Response: null
15. `env_list`
用途: 取得env
附註: 需填入 dictionary
範例:
```yaml
env_list:
type: string
label: env_list
```
Branch: TWCC
Response: null
16. `userid`
用途: 取得user.id
附註: 不需填form.yaml
範例:
```yaml
user_id:
type: string
label: userid
```
Branch: TWGC,GMN
Response: null
17. `keypair_list`
用途: 從OpenStack取得keypair列表
附註: 無
範例:
```yaml
keypair:
type: string
label: keypair_list
```
Branch: TWGC,GMN
Response:
```json
"keypair": [
"test_key"
]
```
18. `network_list`
用途: 從OpenStack取得network列表
附註: 無
範例:
```yaml
network:
type: string
label: network_list
```
Branch: TWGC,GMN
Response:
```json
"network": [
"test_net"
]
```
19. `sg_list`
用途: 從OpenStack取得security group清單
附註: 無
範例:
```yaml
sg:
type: string
label: sg_list
```
Branch: TWGC,GMN
Response:
```json
"sg" : [
"test_sg"
]
```
20. `limited_options`
用途: 直接列出自定義的選項
附註: 與已廢棄的 user_defined_image_list, menu_list 同效果,請以 limited_options 為主
範例:
```yaml
replica:
type: string
label: limited_options
description:
- allowed_values:
- 1
- 2
- 3
```
Branch: TWCC,TWGC,GMN
Response:
```json
"replica" : [
"1",
"2",
"3"
]
```
21. `site_id`
用途: 取得 site ID
附註: 不需填form.yaml
範例:
```yaml
site_id:
type: string
label: 'site_id'
```
Branch: GMN
Response: null
22. `pull_secret`
用途: 選擇 secret 以作為 image pull secret
附註: 只會顯示 docker-registry type 的 secret 列表
範例:
```yaml
secret:
type: string
label: pull_secret
spec:
containers:
{%- if secret %}
imagePullSecrets:
- name: {{ secret }}
{%- endif %}
```
Branch: GMN
Response:
```json
"secret" : [
'secretA',
'secretB',
'secretC'
]
```
23. `secret_list`
用途: 選擇 secret 以在 container 建立 k8s secret volume
附註: 需填入 dictionary, readOnly 預設為 False
範例:
```yaml
secretvolume:
type: string
label: secret_list
spec:
containers:
- secretvolumes: {{ secretvolume }}
```
Branch: GMN
Response:
```json
"secretvolume" : [
{
"mountPath": "",
"secret": "secretA",
"readOnly": False
},
]
```
24. `env_list`
用途: GMN 版的 env 用 label,決定額外設定於 container 的 env,支援 secret
附註: 需填入 dictionary,若是採用 secret,value 需填入 "secret.<key_name>"
範例:
```yaml
env:
type: string
label: env_list
spec:
containers:
- env:
- name: BUFFER
value: {{ env }}
```
Branch: GMN
Response:
```json
"env" : [
{
"key": "",
"secret (optional)": "secretA",
"value": ""
},
]
```
25. `persistent_volume_list`
用途: 選擇 mount 到 container 裡的 persistent volume
附註: 需填入 dictionary
範例:
```yaml
persistentvolumes:
type: string
label: persistent_volume_list
spec:
containers:
- persistentvolumes: {{ persistentvolumes }}
```
Branch: GMN
Response:
```json
"persistentvolumes" : [
{
"persistent_volume": "volumeA",
"mountPath": ""
}
]
```
## [補充 - v3 額外支援的參數類型]
1. `native_resource`
用途: 選擇透過 native resource API 建立出來的資源名稱
附註: 無
範例:
```yaml
components:
- key: pull_secret
type: select
label: "${lang.pullSecret}"
options:
type: native_resource
description:
- allowed_values:
- Secret # native resource kind
required: false
```
Branch: Nomos
Response:
```json
"options" : [
{
"label": "secret-1",
"value": "secret-1"
}
]
```
> 雖然沒特別禁止使用,但 v3 目前基本上只支援 `harbor_image` 和 `native_resource` 這兩個類型
# forms.yaml
說明 GSP 的 forms.yaml 內參數的定義方式
不同的 [goc_template_version](#4-goc_template_version) 對於 forms.yaml 的格式定義也會有所不同
因此以下針對 forms.yaml 的介紹將會根據不同版本進行說明
### \*goc_template_version 1 and 2
一般 forms.yaml 定義參數會類似下面的樣子
```yaml
create_site:
- parameter: private_network
- parameter: image
- parameter: flavor
```
create_site 下每筆資料中的 parameter 將會作為參數名稱被使用
而除了參數名稱之外,在 TWGC, GMN 及 Nomos 下還可以針對各個參數定義額外的屬性
1. `label`
用途: 給參數定義一個容易閱讀的名稱,取得 solution 的參數選項時會顯示在 display_name 這個欄位
附註: 預設值為 parameter 的值
範例:
```yaml
create_site:
- parameter: private_network
label: Private Network
- parameter: image
- parameter: flavor
```
Response:
```json
"site_extra_prop": [
{
"name": "default",
"parameters": [
{
"display_name": "Private Network",
"description": "",
"format": "txt",
"hidden": false,
"name": "private-network",
"required": true,
"value": [
"default_network"
]
},
{
"display_name": "image",
"description": "",
"format": "txt",
"hidden": false,
"name": "image",
"required": true,
"value": [
"cirros"
]
},
{
"display_name": "flavor",
"description": "",
"format": "txt",
"hidden": false,
"name": "flavor",
"required": true,
"value": [
"cirros256"
]
}
]
}
]
```
2. `format`
用途: 方便 UI 判斷要以何種形式呈現參數選項
附註: 預設值為 txt
範例:
```yaml
create_site:
- parameter: image
- parameter: replicas
format: number
```
Response:
```json
"site_extra_prop": [
{
"name": "default",
"parameters": [
{
"display_name": "image",
"description": "",
"format": "txt",
"hidden": false,
"name": "image",
"required": true,
"value": [
"cirros"
]
},
{
"display_name": "replicas",
"description": "",
"format": "number",
"hidden": false,
"name": "replicas",
"required": true,
"value": ""
}
]
}
]
```
3. `required`
用途: 設定參數是否為必填
附註: 預設值為 true
範例:
```yaml
create_site:
- parameter: image
- parameter: desc
required: false
```
Response:
```json
"site_extra_prop": [
{
"name": "default",
"parameters": [
{
"display_name": "image",
"description": "",
"format": "txt",
"hidden": false,
"name": "image",
"required": true,
"value": [
"cirros"
]
},
{
"display_name": "desc",
"description": "",
"format": "txt",
"hidden": false,
"name": "desc",
"required": false,
"value": ""
}
]
}
]
```
4. `hidden`
用途: 設定參數是否要隱藏,之後取得 site 的建立參數時不會顯示 hidden 設定為 true 的參數
附註: 預設值為 false
範例:
```yaml
create_site:
- parameter: image
- parameter: password
hidden: true
```
Response:
```json
"site_extra_prop": [
{
"name": "default",
"parameters": [
{
"display_name": "image",
"description": "",
"format": "txt",
"hidden": false,
"name": "image",
"required": true,
"value": [
"cirros"
]
},
{
"display_name": "password",
"description": "",
"format": "txt",
"hidden": true,
"name": "password",
"required": true,
"value": ""
}
]
}
]
```
Get Site Parameters:
```json
{
"image": "cirros"
}
```
5. `parameter_groups`
用途: 將參數分類到不同的群組中,在取得 solution 的參數選項時會依照所屬群組分別顯示
附註: 沒有定義群組時,會將所有參數統一歸類為 default 下
範例:
```yaml
parameter_groups:
- label: required
parameters:
- private_network
- image
- flavor
- label: optional
parameters:
- desc
create_site:
- parameter: private_network
- parameter: image
- parameter: flavor
- parameter: desc
required: false
```
Response:
```json
"site_extra_prop": [
{
"name": "required",
"parameters": [
{
"display_name": "private_network",
"description": "",
"format": "txt",
"hidden": false,
"name": "private-network",
"required": true,
"value": [
"default_network"
]
},
{
"display_name": "image",
"description": "",
"format": "txt",
"hidden": false,
"name": "image",
"required": true,
"value": [
"cirros"
]
},
{
"display_name": "flavor",
"description": "",
"format": "txt",
"hidden": false,
"name": "flavor",
"required": true,
"value": [
"cirros256"
]
}
]
},
{
"name": "optional",
"parameters": [
{
"display_name": "desc",
"description": "",
"format": "txt",
"hidden": false,
"name": "desc",
"required": false,
"value": ""
}
]
}
]
```
6. `description`
用途: 對於參數的描述
附註: 預設值為空值
範例:
```yaml
create_site:
- parameter: ports
description: 22,8080
- parameter: image
- parameter: flavor
```
Response:
```json
"site_extra_prop": [
{
"name": "default",
"parameters": [
{
"display_name": "Security Group Ports",
"description": "22,8080",
"format": "txt",
"hidden": false,
"name": "ports",
"required": false,
"value": ""
},
{
"display_name": "image",
"description": "",
"format": "txt",
"hidden": false,
"name": "image",
"required": true,
"value": [
"cirros"
]
},
{
"display_name": "flavor",
"description": "",
"format": "txt",
"hidden": false,
"name": "flavor",
"required": true,
"value": [
"cirros256"
]
}
]
}
]
```
### \*goc_template_version 3
一般 forms.yaml 定義參數會類似下面的樣子
```yaml
create:
- label: "${lang.basicInfo}"
components:
- key: name
type: text
label: "${lang.name}"
format: "/^[a-z]([-a-z0-9]*[a-z0-9])?$/"
required: true
list:
- key: id
type: String
label: ID
{% if raw is mapping %}
value: {{ raw.metadata.labels["geminiopencloud.com/uuid"] }}
{% endif %}
- key: name
type: String
label: "${lang.name}"
{% if raw is mapping %}
value: "{{ raw.metadata.name }}"
{% endif %}
```
forms.yaml 分為兩個 section
- **create**
以群組的方式分開定義可用的參數與額外屬性
每個群組都會有 label 及 components 兩個欄位
- **label** 定義群組名稱
- **components** 定義屬於該群組的參數規格
關於參數更詳細的規格可參考 [GSP-Spec. forms.yaml](https://gitlab.com/geminiopencloud/product-management2/2.-requirements/goc-nomos/-/wikis/GSP-Spec.#formsyaml)
- **list**
透過 API 取得資源列表時,定義要顯示的欄位及資料來源
- **key**: 辨識資料用的唯一值
- **label**: UI 呈現在 table 上的名稱
- **type**: 標示資料的類型,以便 UI 可以根據類型做相對應的處理
- **value**: 要呈現的資料內容,可使用 jinja 語法從 raw 這個 context 中取得該資源的原始資料
另外務必將 value 用 {% if raw is mapping %}{% endif %} 包起來