vmm-cli update --compose with --env-file silently drops compose changes when allowed_envs differs
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 72/100
調査の方向性
vmm/src/vmm-cli.py の update_vm() から始め、特に 1051–1124 行付近の compose ブランチと env-file ブランチを確認し、issue に記載された結合更新を再現してください。両方のブランチで処理中の compose データを再利用し、保存される結果に新しい docker_compose_file と更新された allowed_envs が保持されるようにしてから、結合コマンドがどちらの変更も失わずに成功を報告することを確認してください。
索引モデルが issue の本文から書いたものです。
説明
Summary
vmm-cli.py update <vm_id> --compose new.yaml --env-file new.env --kms-url ... silently drops the --compose update when the env-file's keys differ from the VM's current allowed_envs. The resulting VMM-stored compose_file keeps the old docker_compose_file but with the new allowed_envs. vmm-cli update exits 0 and reports success.
Reproduction
Any combined update where --env-file introduces (or removes) any env var changes allowed_envs. For us this surfaced when adding LAUNCHER_CHANNEL to the env list alongside a new service in the compose YAML — the new service was silently dropped on two hosts.
Root cause
vmm/src/vmm-cli.py, update_vm() (current master, lines 1051–1124): two unrelated branches both write to upgrade_params["compose_file"], and the env-file branch runs last:
# Branch 1 — compose update (line 1051)
if needs_compose_update:
vm_configuration = vm_info_response["info"].get("configuration") or {}
compose_file_content = vm_configuration.get("compose_file")
app_compose = json.loads(compose_file_content) if compose_file_content else {}
if docker_compose_content:
app_compose["docker_compose_file"] = docker_compose_content # ← inserts NEW YAML
...
upgrade_params["compose_file"] = json.dumps(app_compose, ...)
# Branch 2 — env-file (line 1088)
if env_file:
envs = parse_env_file(env_file)
if envs:
...
if compose_file_content:
app_compose = json.loads(compose_file_content) # ← RE-READS ORIGINAL (no new YAML)
...
if app_compose.get("allowed_envs") != allowed_envs:
app_compose["allowed_envs"] = allowed_envs
compose_changed = True
...
if compose_changed:
upgrade_params["compose_file"] = json.dumps(app_compose, ...) # ← OVERWRITES branch 1's result
Branch 2 reloads compose_file_content from vm_info_response (pre-update state) instead of continuing to mutate the app_compose dict already built by branch 1. When allowed_envs differs, compose_changed=True and branch 2's upgrade_params["compose_file"] = json.dumps(app_compose, ...) clobbers the new YAML.
Why it's hard to notice
vmm-cli updateexits 0 and prints success- The resulting
compose_filestill has the newallowed_envs, so subsequent env operations look correct - The KMS hash registered by the operator (computed from
app-compose.json) matches what VMM stores — both are wrong-but-internally-consistent - The CVM boots fine; the missing service simply… never existed
Suggested fix
Have branch 2 reuse the app_compose dict built by branch 1 instead of reloading from vm_configuration. Sketch:
app_compose = None # accumulated across both branches
if needs_compose_update or env_file:
vm_info_response = self.rpc_call("GetInfo", {"id": vm_id})
...
if needs_compose_update:
vm_configuration = vm_info_response["info"].get("configuration") or {}
compose_file_content = vm_configuration.get("compose_file")
try:
app_compose = json.loads(compose_file_content) if compose_file_content else {}
except json.JSONDecodeError:
app_compose = {}
if docker_compose_content:
app_compose["docker_compose_file"] = docker_compose_content
updates.append("docker compose")
# ... prelaunch_script, swap_size ...
upgrade_params["compose_file"] = json.dumps(app_compose, ...)
if env_file:
envs = parse_env_file(env_file)
if envs:
...
# Reuse the in-flight app_compose if branch 1 ran;
# otherwise load from current VMM state.
if app_compose is None:
vm_configuration = vm_info_response["info"].get("configuration") or {}
compose_file_content = vm_configuration.get("compose_file")
try:
app_compose = json.loads(compose_file_content) if compose_file_content else {}
except json.JSONDecodeError:
app_compose = {}
compose_changed = False
allowed_envs = list(envs.keys())
if app_compose.get("allowed_envs") != allowed_envs:
app_compose["allowed_envs"] = allowed_envs
compose_changed = True
# ... launch_token_hash ...
if compose_changed or needs_compose_update:
upgrade_params["compose_file"] = json.dumps(app_compose, ...)
Two key changes: (a) app_compose is shared across both branches; (b) when branch 1 ran, always re-serialize the merged result so the env updates don't drop the compose changes.
Workaround (no upstream change needed)
Split the single update into two sequential vmm-cli update calls:
vmm-cli update <vm_id> --env-file new.env --kms-url ...— settlesallowed_envsandencrypted_envvmm-cli update <vm_id> --compose new.yaml --vcpu ... --image ... --kms-url ...— applies the new compose against an already-matchingallowed_envs, so branch 2 seescompose_changed=Falseand doesn't clobber
Environment
Reproduced on a downstream install (/usr/bin/vmm-cli.py, md5 da37c6fecd4219363e4c43076ca4fc30); upstream master at vmm/src/vmm-cli.py has the same code path. Hosts in question were built from a dstack release using dstack-nvidia-0.5.5.
- 主要言語
- Rust
- スター
- 551
- フォーク
- 97
- 平均マージ
- 1日 8時間
- マージ済み PR(30日)
- 182
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Dstack-TEE/dstack のほかの issue
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
Dstack-TEE/dstack#1384 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 30/100
Dstack-TEE/dstack#1301 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 55/100
Dstack-TEE/dstack#1300 ·
-
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
Dstack-TEE/dstack#1299 ·
-
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
Dstack-TEE/dstack#1298 ·
Dstack-TEE/dstack の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
-
bug core
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
-
JIT-compiled number -> Decimal conversion silently overflows instead of raising DECIMAL_OVERFLOW オープンfuzz
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
ClickHouse/ClickHouse#122114 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
linebender/vello_svg#90 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100