package host import ( "context" "errors" "log" "os" "path/filepath" "runtime" "sync" "sync/atomic" "time" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/appconfig" "github.com/carlosframework/platform/internal/cgroup" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rollout" "github.com/carlosframework/platform/internal/status" ) // The reason codes RolloutReport.Unavailable carries (zero-downtime deploys // spec §15, §8.4). They are codes, not member-facing text: plan 6 words // them. const ( // The flag is on but the box cannot contain generations, so the // scheduler is not armed and every leg stays on today's paths (swapArming). rolloutNoDelegation = "no-cgroup-delegation" rolloutNoGenControl = "no-generation-control" rolloutGenerationsOff = "generations-off" // The flag is on but host claims are on: claims-backed launches cannot // yet carry a swap's recovery choice, its execution evidence, the // generations file or the good build (#272, "claims-on swaps"), so the // box declines and stays on today's paths. rolloutHostClaims = "host-claims-on" // The flag is on and the box has delegation, but a test placement into // a scratch cgroup through the shim failed (placementCheck): generation // starts could not be placed inside their limits (#272, the second // dev-box check), so the box declines and stays on today's paths. rolloutPlacementFailed = "cgroup-placement-failed" // The flag is on and generations are on, but the scheduler's required // state is unavailable, so every managed start is refused // (refuseManaged). rolloutNoStore = "no-deploy-store" rolloutNoReleaseKey = "no-release-key" rolloutBookUnreadable = "failure-book-unreadable" ) // refuseTexts is the wake refusal each refuseManaged code carries. It reaches // a member through the activator's "wake failed:" event, so it names the // missing fact and never the raw error, which goes to the agent's log. The // unreadable book's text is the scheduler's own (rollout Task 5 refuses a // wake whose intent it cannot write with it), shared rather than copied. var refuseTexts = map[string]string{ rolloutNoStore: "this box cannot start instances right now: the deploy store is unavailable", rolloutNoReleaseKey: "this box cannot start instances right now: the release key is unavailable", rolloutBookUnreadable: rollout.BookUnreadableRefusal, } var rolloutMgr atomic.Pointer[rollout.Manager] // rolloutMaterialize is the exec backend's Materialize hook, handed to the // scheduler so a cold managed host is installed before its wake is planned // (Task 10). runAgent sets it before armRollout; nil refuses such a wake. var rolloutMaterialize func(ctx context.Context, host string) error // armedRollout is the box's rollout manager, nil unless CARLOS_SWAP=overlap // armed one. Every leg that would bounce an exec child asks it first. func armedRollout() *rollout.Manager { return rolloutMgr.Load() } // statusNudge asks the status loop for an early tick: a rollout transition // should reach status/boxes/