fix: pct_create make vmid optional, fix dhcp+gw, longer boot settle
Follow-ups found while verifying the approve→provision path end to end: - vmid is now optional: the early required-field check rejected vmid:0 before the cluster VMID guard could auto-assign a free id. Only hostname is required now; 0 (or a collision) resolves to `pvesh get /cluster/nextid`. - net0: use ip=dhcp with no gateway when no static IP is given (Proxmox rejects gw alongside dhcp); only attach gw for a static CIDR. - bump post-create settle to 10s so a DHCP lease is up before apt runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -230,10 +230,12 @@ func executeApprovedAction(ctx context.Context, pool *db.Pool, execID uuid.UUID,
|
|||||||
emitExecutionEvent(ctx, pool, execID, "failed", map[string]any{"target": targetSlug, "error": err.Error()})
|
emitExecutionEvent(ctx, pool, execID, "failed", map[string]any{"target": targetSlug, "error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cfg.VMID == 0 || cfg.Hostname == "" {
|
// Only hostname is required. vmid is optional — when 0 (or later found
|
||||||
|
// to collide) the VMID guard below assigns a free cluster id.
|
||||||
|
if cfg.Hostname == "" {
|
||||||
pool.Exec(ctx, `UPDATE executions SET status='failed', result=$2::jsonb WHERE entity_id=$1`,
|
pool.Exec(ctx, `UPDATE executions SET status='failed', result=$2::jsonb WHERE entity_id=$1`,
|
||||||
execID, `{"error":"pct_create: vmid and hostname are required"}`)
|
execID, `{"error":"pct_create: hostname is required"}`)
|
||||||
emitExecutionEvent(ctx, pool, execID, "failed", map[string]any{"target": targetSlug, "error": "missing vmid or hostname"})
|
emitExecutionEvent(ctx, pool, execID, "failed", map[string]any{"target": targetSlug, "error": "missing hostname"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cfg.Cores == 0 {
|
if cfg.Cores == 0 {
|
||||||
@@ -329,11 +331,23 @@ func executeApprovedAction(ctx context.Context, pool *db.Pool, execID uuid.UUID,
|
|||||||
nestingFlag = fmt.Sprintf(" --features %s", strings.Join(features, ","))
|
nestingFlag = fmt.Sprintf(" --features %s", strings.Join(features, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// net0: DHCP when no static IP is given (or ip=="dhcp"). Proxmox
|
||||||
|
// rejects a gateway alongside ip=dhcp, so only add gw for a static IP.
|
||||||
|
net0 := "name=eth0,bridge=vmbr0,"
|
||||||
|
if cfg.IP == "" || strings.EqualFold(cfg.IP, "dhcp") {
|
||||||
|
net0 += "ip=dhcp"
|
||||||
|
} else {
|
||||||
|
net0 += "ip=" + cfg.IP
|
||||||
|
if cfg.GW != "" {
|
||||||
|
net0 += ",gw=" + cfg.GW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
templatePath := fmt.Sprintf("/var/lib/vz/template/cache/%s", cfg.Template)
|
templatePath := fmt.Sprintf("/var/lib/vz/template/cache/%s", cfg.Template)
|
||||||
createCmd := fmt.Sprintf(
|
createCmd := fmt.Sprintf(
|
||||||
"pct create %d %s --hostname %s --cores %d --memory %d --rootfs %s:%d %s --net0 name=eth0,bridge=vmbr0,ip=%s,gw=%s%s --start 1",
|
"pct create %d %s --hostname %s --cores %d --memory %d --rootfs %s:%d %s --net0 %s%s --start 1",
|
||||||
cfg.VMID, templatePath, cfg.Hostname, cfg.Cores, cfg.Memory,
|
cfg.VMID, templatePath, cfg.Hostname, cfg.Cores, cfg.Memory,
|
||||||
cfg.Storage, cfg.DiskGB, privFlag, cfg.IP, cfg.GW, nestingFlag)
|
cfg.Storage, cfg.DiskGB, privFlag, net0, nestingFlag)
|
||||||
|
|
||||||
if cfg.Nameserver != "" {
|
if cfg.Nameserver != "" {
|
||||||
createCmd += fmt.Sprintf(" --nameserver %s", cfg.Nameserver)
|
createCmd += fmt.Sprintf(" --nameserver %s", cfg.Nameserver)
|
||||||
@@ -358,8 +372,9 @@ func executeApprovedAction(ctx context.Context, pool *db.Pool, execID uuid.UUID,
|
|||||||
// with a boot settle; failures are appended to output and mark the
|
// with a boot settle; failures are appended to output and mark the
|
||||||
// execution failed so the operator sees exactly which step broke.
|
// execution failed so the operator sees exactly which step broke.
|
||||||
if err == nil && (len(cfg.Services) > 0 || cfg.PostInstall != "") {
|
if err == nil && (len(cfg.Services) > 0 || cfg.PostInstall != "") {
|
||||||
// Give the container a moment to finish booting before exec.
|
// Give the container time to boot and (for DHCP) acquire a lease
|
||||||
steps := []string{fmt.Sprintf("sleep 5")}
|
// before apt needs the network.
|
||||||
|
steps := []string{"sleep 10"}
|
||||||
if len(cfg.Services) > 0 {
|
if len(cfg.Services) > 0 {
|
||||||
pkgs := strings.Join(sanitizePkgs(cfg.Services), " ")
|
pkgs := strings.Join(sanitizePkgs(cfg.Services), " ")
|
||||||
steps = append(steps, fmt.Sprintf("pct exec %d -- bash -lc 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq %s'", cfg.VMID, pkgs))
|
steps = append(steps, fmt.Sprintf("pct exec %d -- bash -lc 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq %s'", cfg.VMID, pkgs))
|
||||||
|
|||||||
Reference in New Issue
Block a user