feat(remote): route service checks through their hosting compute entity
A service check used to bake its hosting LXC's lan_ip and SSH it directly as root, which failed because the scheduler key is authorized on the Proxmox hosts but not inside every guest — leaving all 8 service process checks 'down' even after the guest routing and scripts were fixed. ResolveExecTargetForCheck now, for a non-guest target, walks the provides/runs-on/hosts edges to the compute entity that runs it and routes through that: pct/qm exec if the host is a guest, direct SSH with the host's correct user (workstation `user` attr) if it's a machine. The guest-resolution path is shared via resolveGuest, and the scheduler no longer needs an isMachine special case — one resolver handles guest, machine, and service.
This commit is contained in:
@@ -759,8 +759,11 @@ func checkSSHScript(ctx context.Context, pool *db.Pool, cd sqlcgen.ListEnabledCh
|
||||
scriptPath += " '" + strings.ReplaceAll(cfg.Args, "'", `'\''`) + "'"
|
||||
}
|
||||
|
||||
// Resolve the execution endpoint. Guests host-hop; host/workstation types
|
||||
// resolve their address + user live; everything else uses the baked config.
|
||||
// Resolve the execution endpoint. The resolver handles every target kind:
|
||||
// LXC/VM host-hop via pct/qm exec; hosts/workstations direct at their own
|
||||
// address; services route through their hosting compute entity (via the
|
||||
// provides edge) so a service check reaches the right machine with the
|
||||
// right user instead of baking an LXC lan_ip and SSHing it as root.
|
||||
host, port, user := cfg.Host, strconv.Itoa(oru(cfg.Port, 22)), orStr(cfg.User, sshUser)
|
||||
wrap := func(cmd string) string { return cmd }
|
||||
targetType := ""
|
||||
@@ -776,28 +779,22 @@ func checkSSHScript(ctx context.Context, pool *db.Pool, cd sqlcgen.ListEnabledCh
|
||||
targetType = ""
|
||||
}
|
||||
}
|
||||
if cd.TargetID != nil {
|
||||
switch {
|
||||
case remote.IsGuest(targetType):
|
||||
et, err := remote.ResolveExecTargetForCheck(ctx, pool, *cd.TargetID, targetType, sshUser)
|
||||
if err != nil {
|
||||
if cd.TargetID != nil && targetType != "" {
|
||||
et, err := remote.ResolveExecTargetForCheck(ctx, pool, *cd.TargetID, targetType, sshUser)
|
||||
if err != nil {
|
||||
if remote.IsGuest(targetType) {
|
||||
return checkResult{
|
||||
health: "down", signalKind: "ssh-script",
|
||||
evidence: fmt.Sprintf("route guest %s: %v", cd.EntitySlug, err), err: err,
|
||||
}
|
||||
}
|
||||
// Non-guest: log the resolution failure so an opaque ssh "down"
|
||||
// doesn't hide that the real cause was host/user resolution, then
|
||||
// fall back to the baked config below.
|
||||
slog.Warn("scheduler: target resolution failed, using baked config",
|
||||
"entity", cd.EntitySlug, "target_type", targetType, "error", err)
|
||||
} else {
|
||||
host, port, user, wrap = et.Host, "22", et.User, et.Wrap
|
||||
case isMachine(targetType):
|
||||
et, err := remote.ResolveExecTargetForCheck(ctx, pool, *cd.TargetID, targetType, sshUser)
|
||||
if err == nil {
|
||||
host, port, user, wrap = et.Host, "22", et.User, et.Wrap
|
||||
} else {
|
||||
// Log the resolution failure so an opaque ssh "down" doesn't
|
||||
// hide that the real cause was host/user resolution (e.g. a
|
||||
// missing attribute), then fall back to the baked config below.
|
||||
slog.Warn("scheduler: machine target resolution failed, using baked config",
|
||||
"entity", cd.EntitySlug, "target_type", targetType, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,18 +840,6 @@ func checkSSHScript(ctx context.Context, pool *db.Pool, cd sqlcgen.ListEnabledCh
|
||||
}
|
||||
}
|
||||
|
||||
// isMachine reports whether a target type is a physical/virtual machine that
|
||||
// should be reached by direct SSH at its own resolved address (rather than the
|
||||
// baked hosting-container address a service uses). These are the machine
|
||||
// subtypes in the ontology.
|
||||
func isMachine(entityType string) bool {
|
||||
switch entityType {
|
||||
case "proxmox-host", "standalone-server", "workstation", "appliance":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// oru returns v when nonzero, else def. orStr returns v when non-empty, else def.
|
||||
func oru(v, def int) int {
|
||||
if v != 0 {
|
||||
|
||||
Reference in New Issue
Block a user