fix: handle Parallels poweroff snapshot restores
This commit is contained in:
parent
71a79bdf5c
commit
095a9f6e1d
@ -42,10 +42,13 @@ pnpm test:parallels:macos \
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Snapshot target: closest to `macOS 26.3.1 fresh`.
|
- Snapshot target: closest to `macOS 26.3.1 fresh`.
|
||||||
|
- Snapshot resolver now prefers matching `*-poweroff*` clones when the base hint also matches. That lets the harness reuse disk-only recovery snapshots without passing a longer hint.
|
||||||
|
- If Windows/Linux snapshot restore logs show `PET_QUESTION_SNAPSHOT_STATE_INCOMPATIBLE_CPU`, drop the suspended state once, create a `*-poweroff*` replacement snapshot, and rerun. The smoke scripts now auto-start restored power-off snapshots.
|
||||||
- Harness configures Discord inside the guest; no checked-in token/config.
|
- Harness configures Discord inside the guest; no checked-in token/config.
|
||||||
- Use the `openclaw` wrapper for guest `message send/read`; `node openclaw.mjs message ...` does not expose the lazy message subcommands the same way.
|
- Use the `openclaw` wrapper for guest `message send/read`; `node openclaw.mjs message ...` does not expose the lazy message subcommands the same way.
|
||||||
- Write `channels.discord.guilds` in one JSON object (`--strict-json`), not dotted `config set channels.discord.guilds.<snowflake>...` paths; numeric snowflakes get treated like array indexes.
|
- Write `channels.discord.guilds` in one JSON object (`--strict-json`), not dotted `config set channels.discord.guilds.<snowflake>...` paths; numeric snowflakes get treated like array indexes.
|
||||||
- Avoid `prlctl enter` / expect for long Discord setup scripts; it line-wraps/corrupts long commands. Use `prlctl exec --current-user /bin/sh -lc ...` for the Discord config phase.
|
- Avoid `prlctl enter` / expect for long Discord setup scripts; it line-wraps/corrupts long commands. Use `prlctl exec --current-user /bin/sh -lc ...` for the Discord config phase.
|
||||||
|
- Full 3-OS sweeps: the shared build lock is safe in parallel, but snapshot restore is still a Parallels bottleneck. Prefer serialized Windows/Linux restore-heavy reruns if the host is already under load.
|
||||||
- Harness cleanup deletes the temporary Discord smoke messages at exit.
|
- Harness cleanup deletes the temporary Discord smoke messages at exit.
|
||||||
- Per-phase logs: `/tmp/openclaw-parallels-smoke.*`
|
- Per-phase logs: `/tmp/openclaw-parallels-smoke.*`
|
||||||
- Machine summary: pass `--json`
|
- Machine summary: pass `--json`
|
||||||
|
|||||||
@ -14,6 +14,9 @@ INSTALL_VERSION=""
|
|||||||
TARGET_PACKAGE_SPEC=""
|
TARGET_PACKAGE_SPEC=""
|
||||||
JSON_OUTPUT=0
|
JSON_OUTPUT=0
|
||||||
KEEP_SERVER=0
|
KEEP_SERVER=0
|
||||||
|
SNAPSHOT_ID=""
|
||||||
|
SNAPSHOT_STATE=""
|
||||||
|
SNAPSHOT_NAME=""
|
||||||
|
|
||||||
MAIN_TGZ_DIR="$(mktemp -d)"
|
MAIN_TGZ_DIR="$(mktemp -d)"
|
||||||
MAIN_TGZ_PATH=""
|
MAIN_TGZ_PATH=""
|
||||||
@ -163,7 +166,7 @@ esac
|
|||||||
OPENAI_API_KEY_VALUE="${!OPENAI_API_KEY_ENV:-}"
|
OPENAI_API_KEY_VALUE="${!OPENAI_API_KEY_ENV:-}"
|
||||||
[[ -n "$OPENAI_API_KEY_VALUE" ]] || die "$OPENAI_API_KEY_ENV is required"
|
[[ -n "$OPENAI_API_KEY_VALUE" ]] || die "$OPENAI_API_KEY_ENV is required"
|
||||||
|
|
||||||
resolve_snapshot_id() {
|
resolve_snapshot_info() {
|
||||||
local json hint
|
local json hint
|
||||||
json="$(prlctl snapshot-list "$VM_NAME" --json)"
|
json="$(prlctl snapshot-list "$VM_NAME" --json)"
|
||||||
hint="$SNAPSHOT_HINT"
|
hint="$SNAPSHOT_HINT"
|
||||||
@ -171,28 +174,54 @@ resolve_snapshot_id() {
|
|||||||
import difflib
|
import difflib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
payload = json.loads(os.environ["SNAPSHOT_JSON"])
|
payload = json.loads(os.environ["SNAPSHOT_JSON"])
|
||||||
hint = os.environ["SNAPSHOT_HINT"].strip().lower()
|
hint = os.environ["SNAPSHOT_HINT"].strip().lower()
|
||||||
best_id = None
|
best_id = None
|
||||||
|
best_meta = None
|
||||||
best_score = -1.0
|
best_score = -1.0
|
||||||
|
|
||||||
|
def aliases(name: str) -> list[str]:
|
||||||
|
values = [name]
|
||||||
|
for pattern in (
|
||||||
|
r"^(.*)-poweroff$",
|
||||||
|
r"^(.*)-poweroff-\d{4}-\d{2}-\d{2}$",
|
||||||
|
):
|
||||||
|
match = re.match(pattern, name)
|
||||||
|
if match:
|
||||||
|
values.append(match.group(1))
|
||||||
|
return values
|
||||||
|
|
||||||
for snapshot_id, meta in payload.items():
|
for snapshot_id, meta in payload.items():
|
||||||
name = str(meta.get("name", "")).strip()
|
name = str(meta.get("name", "")).strip()
|
||||||
lowered = name.lower()
|
lowered = name.lower()
|
||||||
score = 0.0
|
score = 0.0
|
||||||
if lowered == hint:
|
for alias in aliases(lowered):
|
||||||
score = 10.0
|
if alias == hint:
|
||||||
elif hint and hint in lowered:
|
score = max(score, 10.0)
|
||||||
score = 5.0 + len(hint) / max(len(lowered), 1)
|
elif hint and hint in alias:
|
||||||
else:
|
score = max(score, 5.0 + len(hint) / max(len(alias), 1))
|
||||||
score = difflib.SequenceMatcher(None, hint, lowered).ratio()
|
else:
|
||||||
|
score = max(score, difflib.SequenceMatcher(None, hint, alias).ratio())
|
||||||
|
if str(meta.get("state", "")).lower() == "poweroff":
|
||||||
|
score += 0.5
|
||||||
if score > best_score:
|
if score > best_score:
|
||||||
best_score = score
|
best_score = score
|
||||||
best_id = snapshot_id
|
best_id = snapshot_id
|
||||||
|
best_meta = meta
|
||||||
if not best_id:
|
if not best_id:
|
||||||
sys.exit("no snapshot matched")
|
sys.exit("no snapshot matched")
|
||||||
print(best_id)
|
print(
|
||||||
|
"\t".join(
|
||||||
|
[
|
||||||
|
best_id,
|
||||||
|
str(best_meta.get("state", "")).strip(),
|
||||||
|
str(best_meta.get("name", "")).strip(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,10 +280,42 @@ guest_exec() {
|
|||||||
prlctl exec "$VM_NAME" "$@"
|
prlctl exec "$VM_NAME" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_vm_status() {
|
||||||
|
local expected="$1"
|
||||||
|
local deadline status
|
||||||
|
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
||||||
|
while (( SECONDS < deadline )); do
|
||||||
|
status="$(prlctl status "$VM_NAME" 2>/dev/null || true)"
|
||||||
|
if [[ "$status" == *" $expected" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_guest_ready() {
|
||||||
|
local deadline
|
||||||
|
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
||||||
|
while (( SECONDS < deadline )); do
|
||||||
|
if guest_exec /bin/true >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
restore_snapshot() {
|
restore_snapshot() {
|
||||||
local snapshot_id="$1"
|
local snapshot_id="$1"
|
||||||
say "Restore snapshot $SNAPSHOT_HINT ($snapshot_id)"
|
say "Restore snapshot $SNAPSHOT_HINT ($snapshot_id)"
|
||||||
prlctl snapshot-switch "$VM_NAME" --id "$snapshot_id" >/dev/null
|
prlctl snapshot-switch "$VM_NAME" --id "$snapshot_id" >/dev/null
|
||||||
|
if [[ "$SNAPSHOT_STATE" == "poweroff" ]]; then
|
||||||
|
wait_for_vm_status "stopped" || die "restored poweroff snapshot did not reach stopped state in $VM_NAME"
|
||||||
|
say "Start restored poweroff snapshot $SNAPSHOT_NAME"
|
||||||
|
prlctl start "$VM_NAME" >/dev/null
|
||||||
|
fi
|
||||||
|
wait_for_guest_ready || die "guest did not become ready in $VM_NAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap_guest() {
|
bootstrap_guest() {
|
||||||
@ -585,13 +646,16 @@ run_upgrade_lane() {
|
|||||||
UPGRADE_AGENT_STATUS="pass"
|
UPGRADE_AGENT_STATUS="pass"
|
||||||
}
|
}
|
||||||
|
|
||||||
SNAPSHOT_ID="$(resolve_snapshot_id)"
|
IFS=$'\t' read -r SNAPSHOT_ID SNAPSHOT_STATE SNAPSHOT_NAME <<<"$(resolve_snapshot_info)"
|
||||||
|
[[ -n "$SNAPSHOT_ID" ]] || die "failed to resolve snapshot id"
|
||||||
|
[[ -n "$SNAPSHOT_NAME" ]] || SNAPSHOT_NAME="$SNAPSHOT_HINT"
|
||||||
LATEST_VERSION="$(resolve_latest_version)"
|
LATEST_VERSION="$(resolve_latest_version)"
|
||||||
HOST_IP="$(resolve_host_ip)"
|
HOST_IP="$(resolve_host_ip)"
|
||||||
HOST_PORT="$(resolve_host_port)"
|
HOST_PORT="$(resolve_host_port)"
|
||||||
|
|
||||||
say "VM: $VM_NAME"
|
say "VM: $VM_NAME"
|
||||||
say "Snapshot hint: $SNAPSHOT_HINT"
|
say "Snapshot hint: $SNAPSHOT_HINT"
|
||||||
|
say "Resolved snapshot: $SNAPSHOT_NAME [$SNAPSHOT_STATE]"
|
||||||
say "Latest npm version: $LATEST_VERSION"
|
say "Latest npm version: $LATEST_VERSION"
|
||||||
say "Current head: $(git rev-parse --short HEAD)"
|
say "Current head: $(git rev-parse --short HEAD)"
|
||||||
say "Run logs: $RUN_DIR"
|
say "Run logs: $RUN_DIR"
|
||||||
|
|||||||
@ -21,6 +21,9 @@ DISCORD_TOKEN_ENV=""
|
|||||||
DISCORD_TOKEN_VALUE=""
|
DISCORD_TOKEN_VALUE=""
|
||||||
DISCORD_GUILD_ID=""
|
DISCORD_GUILD_ID=""
|
||||||
DISCORD_CHANNEL_ID=""
|
DISCORD_CHANNEL_ID=""
|
||||||
|
SNAPSHOT_ID=""
|
||||||
|
SNAPSHOT_STATE=""
|
||||||
|
SNAPSHOT_NAME=""
|
||||||
GUEST_OPENCLAW_BIN="/opt/homebrew/bin/openclaw"
|
GUEST_OPENCLAW_BIN="/opt/homebrew/bin/openclaw"
|
||||||
GUEST_OPENCLAW_ENTRY="/opt/homebrew/lib/node_modules/openclaw/openclaw.mjs"
|
GUEST_OPENCLAW_ENTRY="/opt/homebrew/lib/node_modules/openclaw/openclaw.mjs"
|
||||||
GUEST_NODE_BIN="/opt/homebrew/bin/node"
|
GUEST_NODE_BIN="/opt/homebrew/bin/node"
|
||||||
@ -291,7 +294,7 @@ cleanup_discord_smoke_messages() {
|
|||||||
discord_delete_message_id_file "$RUN_DIR/upgrade.discord-host-message-id"
|
discord_delete_message_id_file "$RUN_DIR/upgrade.discord-host-message-id"
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_snapshot_id() {
|
resolve_snapshot_info() {
|
||||||
local json hint
|
local json hint
|
||||||
json="$(prlctl snapshot-list "$VM_NAME" --json)"
|
json="$(prlctl snapshot-list "$VM_NAME" --json)"
|
||||||
hint="$SNAPSHOT_HINT"
|
hint="$SNAPSHOT_HINT"
|
||||||
@ -299,28 +302,54 @@ resolve_snapshot_id() {
|
|||||||
import difflib
|
import difflib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
payload = json.loads(os.environ["SNAPSHOT_JSON"])
|
payload = json.loads(os.environ["SNAPSHOT_JSON"])
|
||||||
hint = os.environ["SNAPSHOT_HINT"].strip().lower()
|
hint = os.environ["SNAPSHOT_HINT"].strip().lower()
|
||||||
best_id = None
|
best_id = None
|
||||||
|
best_meta = None
|
||||||
best_score = -1.0
|
best_score = -1.0
|
||||||
|
|
||||||
|
def aliases(name: str) -> list[str]:
|
||||||
|
values = [name]
|
||||||
|
for pattern in (
|
||||||
|
r"^(.*)-poweroff$",
|
||||||
|
r"^(.*)-poweroff-\d{4}-\d{2}-\d{2}$",
|
||||||
|
):
|
||||||
|
match = re.match(pattern, name)
|
||||||
|
if match:
|
||||||
|
values.append(match.group(1))
|
||||||
|
return values
|
||||||
|
|
||||||
for snapshot_id, meta in payload.items():
|
for snapshot_id, meta in payload.items():
|
||||||
name = str(meta.get("name", "")).strip()
|
name = str(meta.get("name", "")).strip()
|
||||||
lowered = name.lower()
|
lowered = name.lower()
|
||||||
score = 0.0
|
score = 0.0
|
||||||
if lowered == hint:
|
for alias in aliases(lowered):
|
||||||
score = 10.0
|
if alias == hint:
|
||||||
elif hint and hint in lowered:
|
score = max(score, 10.0)
|
||||||
score = 5.0 + len(hint) / max(len(lowered), 1)
|
elif hint and hint in alias:
|
||||||
else:
|
score = max(score, 5.0 + len(hint) / max(len(alias), 1))
|
||||||
score = difflib.SequenceMatcher(None, hint, lowered).ratio()
|
else:
|
||||||
|
score = max(score, difflib.SequenceMatcher(None, hint, alias).ratio())
|
||||||
|
if str(meta.get("state", "")).lower() == "poweroff":
|
||||||
|
score += 0.5
|
||||||
if score > best_score:
|
if score > best_score:
|
||||||
best_score = score
|
best_score = score
|
||||||
best_id = snapshot_id
|
best_id = snapshot_id
|
||||||
|
best_meta = meta
|
||||||
if not best_id:
|
if not best_id:
|
||||||
sys.exit("no snapshot matched")
|
sys.exit("no snapshot matched")
|
||||||
print(best_id)
|
print(
|
||||||
|
"\t".join(
|
||||||
|
[
|
||||||
|
best_id,
|
||||||
|
str(best_meta.get("state", "")).strip(),
|
||||||
|
str(best_meta.get("name", "")).strip(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,6 +406,20 @@ resolve_host_port() {
|
|||||||
printf '%s\n' "$HOST_PORT"
|
printf '%s\n' "$HOST_PORT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_vm_status() {
|
||||||
|
local expected="$1"
|
||||||
|
local deadline status
|
||||||
|
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
||||||
|
while (( SECONDS < deadline )); do
|
||||||
|
status="$(prlctl status "$VM_NAME" 2>/dev/null || true)"
|
||||||
|
if [[ "$status" == *" $expected" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
wait_for_current_user() {
|
wait_for_current_user() {
|
||||||
local deadline
|
local deadline
|
||||||
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
||||||
@ -458,6 +501,11 @@ restore_snapshot() {
|
|||||||
local snapshot_id="$1"
|
local snapshot_id="$1"
|
||||||
say "Restore snapshot $SNAPSHOT_HINT ($snapshot_id)"
|
say "Restore snapshot $SNAPSHOT_HINT ($snapshot_id)"
|
||||||
prlctl snapshot-switch "$VM_NAME" --id "$snapshot_id" >/dev/null
|
prlctl snapshot-switch "$VM_NAME" --id "$snapshot_id" >/dev/null
|
||||||
|
if [[ "$SNAPSHOT_STATE" == "poweroff" ]]; then
|
||||||
|
wait_for_vm_status "stopped" || die "restored poweroff snapshot did not reach stopped state in $VM_NAME"
|
||||||
|
say "Start restored poweroff snapshot $SNAPSHOT_NAME"
|
||||||
|
prlctl start "$VM_NAME" >/dev/null
|
||||||
|
fi
|
||||||
wait_for_current_user || die "desktop user did not become ready in $VM_NAME"
|
wait_for_current_user || die "desktop user did not become ready in $VM_NAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1017,13 +1065,16 @@ FRESH_MAIN_STATUS="skip"
|
|||||||
UPGRADE_STATUS="skip"
|
UPGRADE_STATUS="skip"
|
||||||
UPGRADE_PRECHECK_STATUS="skip"
|
UPGRADE_PRECHECK_STATUS="skip"
|
||||||
|
|
||||||
SNAPSHOT_ID="$(resolve_snapshot_id)"
|
IFS=$'\t' read -r SNAPSHOT_ID SNAPSHOT_STATE SNAPSHOT_NAME <<<"$(resolve_snapshot_info)"
|
||||||
|
[[ -n "$SNAPSHOT_ID" ]] || die "failed to resolve snapshot id"
|
||||||
|
[[ -n "$SNAPSHOT_NAME" ]] || SNAPSHOT_NAME="$SNAPSHOT_HINT"
|
||||||
LATEST_VERSION="$(resolve_latest_version)"
|
LATEST_VERSION="$(resolve_latest_version)"
|
||||||
HOST_IP="$(resolve_host_ip)"
|
HOST_IP="$(resolve_host_ip)"
|
||||||
HOST_PORT="$(resolve_host_port)"
|
HOST_PORT="$(resolve_host_port)"
|
||||||
|
|
||||||
say "VM: $VM_NAME"
|
say "VM: $VM_NAME"
|
||||||
say "Snapshot hint: $SNAPSHOT_HINT"
|
say "Snapshot hint: $SNAPSHOT_HINT"
|
||||||
|
say "Resolved snapshot: $SNAPSHOT_NAME [$SNAPSHOT_STATE]"
|
||||||
say "Latest npm version: $LATEST_VERSION"
|
say "Latest npm version: $LATEST_VERSION"
|
||||||
say "Current head: $(git rev-parse --short HEAD)"
|
say "Current head: $(git rev-parse --short HEAD)"
|
||||||
if discord_smoke_enabled; then
|
if discord_smoke_enabled; then
|
||||||
|
|||||||
@ -15,6 +15,9 @@ TARGET_PACKAGE_SPEC=""
|
|||||||
JSON_OUTPUT=0
|
JSON_OUTPUT=0
|
||||||
KEEP_SERVER=0
|
KEEP_SERVER=0
|
||||||
CHECK_LATEST_REF=1
|
CHECK_LATEST_REF=1
|
||||||
|
SNAPSHOT_ID=""
|
||||||
|
SNAPSHOT_STATE=""
|
||||||
|
SNAPSHOT_NAME=""
|
||||||
|
|
||||||
MAIN_TGZ_DIR="$(mktemp -d)"
|
MAIN_TGZ_DIR="$(mktemp -d)"
|
||||||
MAIN_TGZ_PATH=""
|
MAIN_TGZ_PATH=""
|
||||||
@ -194,7 +197,7 @@ ps_array_literal() {
|
|||||||
printf '@(%s)' "$joined"
|
printf '@(%s)' "$joined"
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_snapshot_id() {
|
resolve_snapshot_info() {
|
||||||
local json hint
|
local json hint
|
||||||
json="$(prlctl snapshot-list "$VM_NAME" --json)"
|
json="$(prlctl snapshot-list "$VM_NAME" --json)"
|
||||||
hint="$SNAPSHOT_HINT"
|
hint="$SNAPSHOT_HINT"
|
||||||
@ -202,28 +205,54 @@ resolve_snapshot_id() {
|
|||||||
import difflib
|
import difflib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
payload = json.loads(os.environ["SNAPSHOT_JSON"])
|
payload = json.loads(os.environ["SNAPSHOT_JSON"])
|
||||||
hint = os.environ["SNAPSHOT_HINT"].strip().lower()
|
hint = os.environ["SNAPSHOT_HINT"].strip().lower()
|
||||||
best_id = None
|
best_id = None
|
||||||
|
best_meta = None
|
||||||
best_score = -1.0
|
best_score = -1.0
|
||||||
|
|
||||||
|
def aliases(name: str) -> list[str]:
|
||||||
|
values = [name]
|
||||||
|
for pattern in (
|
||||||
|
r"^(.*)-poweroff$",
|
||||||
|
r"^(.*)-poweroff-\d{4}-\d{2}-\d{2}$",
|
||||||
|
):
|
||||||
|
match = re.match(pattern, name)
|
||||||
|
if match:
|
||||||
|
values.append(match.group(1))
|
||||||
|
return values
|
||||||
|
|
||||||
for snapshot_id, meta in payload.items():
|
for snapshot_id, meta in payload.items():
|
||||||
name = str(meta.get("name", "")).strip()
|
name = str(meta.get("name", "")).strip()
|
||||||
lowered = name.lower()
|
lowered = name.lower()
|
||||||
score = 0.0
|
score = 0.0
|
||||||
if lowered == hint:
|
for alias in aliases(lowered):
|
||||||
score = 10.0
|
if alias == hint:
|
||||||
elif hint and hint in lowered:
|
score = max(score, 10.0)
|
||||||
score = 5.0 + len(hint) / max(len(lowered), 1)
|
elif hint and hint in alias:
|
||||||
else:
|
score = max(score, 5.0 + len(hint) / max(len(alias), 1))
|
||||||
score = difflib.SequenceMatcher(None, hint, lowered).ratio()
|
else:
|
||||||
|
score = max(score, difflib.SequenceMatcher(None, hint, alias).ratio())
|
||||||
|
if str(meta.get("state", "")).lower() == "poweroff":
|
||||||
|
score += 0.5
|
||||||
if score > best_score:
|
if score > best_score:
|
||||||
best_score = score
|
best_score = score
|
||||||
best_id = snapshot_id
|
best_id = snapshot_id
|
||||||
|
best_meta = meta
|
||||||
if not best_id:
|
if not best_id:
|
||||||
sys.exit("no snapshot matched")
|
sys.exit("no snapshot matched")
|
||||||
print(best_id)
|
print(
|
||||||
|
"\t".join(
|
||||||
|
[
|
||||||
|
best_id,
|
||||||
|
str(best_meta.get("state", "")).strip(),
|
||||||
|
str(best_meta.get("name", "")).strip(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,12 +367,31 @@ restore_snapshot() {
|
|||||||
local snapshot_id="$1"
|
local snapshot_id="$1"
|
||||||
say "Restore snapshot $SNAPSHOT_HINT ($snapshot_id)"
|
say "Restore snapshot $SNAPSHOT_HINT ($snapshot_id)"
|
||||||
prlctl snapshot-switch "$VM_NAME" --id "$snapshot_id" >/dev/null
|
prlctl snapshot-switch "$VM_NAME" --id "$snapshot_id" >/dev/null
|
||||||
|
if [[ "$SNAPSHOT_STATE" == "poweroff" ]]; then
|
||||||
|
wait_for_vm_status "stopped" || die "restored poweroff snapshot did not reach stopped state in $VM_NAME"
|
||||||
|
say "Start restored poweroff snapshot $SNAPSHOT_NAME"
|
||||||
|
prlctl start "$VM_NAME" >/dev/null
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_windows_user_ready() {
|
verify_windows_user_ready() {
|
||||||
guest_exec cmd.exe /d /s /c "echo ready"
|
guest_exec cmd.exe /d /s /c "echo ready"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_vm_status() {
|
||||||
|
local expected="$1"
|
||||||
|
local deadline status
|
||||||
|
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
||||||
|
while (( SECONDS < deadline )); do
|
||||||
|
status="$(prlctl status "$VM_NAME" 2>/dev/null || true)"
|
||||||
|
if [[ "$status" == *" $expected" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
wait_for_guest_ready() {
|
wait_for_guest_ready() {
|
||||||
local deadline
|
local deadline
|
||||||
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
deadline=$((SECONDS + TIMEOUT_SNAPSHOT_S))
|
||||||
@ -830,13 +878,16 @@ run_upgrade_lane() {
|
|||||||
UPGRADE_AGENT_STATUS="pass"
|
UPGRADE_AGENT_STATUS="pass"
|
||||||
}
|
}
|
||||||
|
|
||||||
SNAPSHOT_ID="$(resolve_snapshot_id)"
|
IFS=$'\t' read -r SNAPSHOT_ID SNAPSHOT_STATE SNAPSHOT_NAME <<<"$(resolve_snapshot_info)"
|
||||||
|
[[ -n "$SNAPSHOT_ID" ]] || die "failed to resolve snapshot id"
|
||||||
|
[[ -n "$SNAPSHOT_NAME" ]] || SNAPSHOT_NAME="$SNAPSHOT_HINT"
|
||||||
LATEST_VERSION="$(resolve_latest_version)"
|
LATEST_VERSION="$(resolve_latest_version)"
|
||||||
HOST_IP="$(resolve_host_ip)"
|
HOST_IP="$(resolve_host_ip)"
|
||||||
HOST_PORT="$(resolve_host_port)"
|
HOST_PORT="$(resolve_host_port)"
|
||||||
|
|
||||||
say "VM: $VM_NAME"
|
say "VM: $VM_NAME"
|
||||||
say "Snapshot hint: $SNAPSHOT_HINT"
|
say "Snapshot hint: $SNAPSHOT_HINT"
|
||||||
|
say "Resolved snapshot: $SNAPSHOT_NAME [$SNAPSHOT_STATE]"
|
||||||
say "Latest npm version: $LATEST_VERSION"
|
say "Latest npm version: $LATEST_VERSION"
|
||||||
say "Current head: $(git rev-parse --short HEAD)"
|
say "Current head: $(git rev-parse --short HEAD)"
|
||||||
say "Run logs: $RUN_DIR"
|
say "Run logs: $RUN_DIR"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user