Skip to content

fix(template): probe envd readiness before persisting envd capability#741

Open
zyl1121 wants to merge 1 commit into
TencentCloud:masterfrom
zyl1121:fix/template-envd-probe
Open

fix(template): probe envd readiness before persisting envd capability#741
zyl1121 wants to merge 1 commit into
TencentCloud:masterfrom
zyl1121:fix/template-envd-probe

Conversation

@zyl1121

@zyl1121 zyl1121 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Motivation

In #566, create-time env injection started relying on cube.master.components.envd.version as the envd capability signal, using the mechanism introduced in #650. During review, we agreed that the main envd readiness guarantee should move to template construction time instead of rediscovering the same readiness race on every sandbox creation.

Before this change, the template envd annotation could be written after only collecting envd --version. That proved the binary existed, but not that the envd service was reachable and ready on the sandbox runtime endpoint.

As a result, downstream sandbox create flows could treat a template as envd-capable even when envd was absent, stopped, or still starting.

Summary

  • Cubelet now confirms envd readiness via http://<sandboxIP>:49983/health before reporting an envd version during template snapshot flows. This uses a dedicated bounded HTTP probe instead of the generic telnet.Telnet helper so the readiness path can honor parent context cancellation and reject HTTP redirects explicitly.
  • The same readiness check applies to both AppSnapshot and CommitSandbox, keeping image-based template creation and runtime commit paths consistent.
  • CubeMaster persists cube.master.components.envd.version only when all READY replicas report the same valid envd version; otherwise, it suppresses the annotation instead of publishing an optimistic capability signal.
  • If redo later re-runs the template on the same templateID and the stricter convergence check fails, CubeMaster now clears any previously persisted envd annotation instead of leaving stale capability data behind.

Validation

  • go test -vet=off ./pkg/templatecenter -run 'Envd|Template' -count=1
  • go test -vet=off ./services/cubebox -run 'Envd|Probe' -count=1

Manual validation on Linux:

  • success path: without the temporary REJECT rule, template info showed:

    "cube.master.components.envd.version": "0.5.11"
    

    After enabling INFO logs, Cubelet also logged:

    collect envd version: readiness confirmed, version=0.5.11
    
  • failure path: with a temporary REJECT rule blocking Cubelet -> sandboxIP:49983, template build still succeeded, but the envd annotation was omitted. Cubelet logged:

    collect envd version: envd readiness probe failed on <sandboxIP>:49983 ...
    
  • redo path: running cubemastercli cubebox template redo against the same templateID also updated the annotation as expected. A template that previously carried cube.master.components.envd.version lost it after a redo under the REJECT rule, confirming that redo re-evaluates and reconciles the envd capability signal instead of leaving the previous value sticky.

Scope

This change is limited to the template/snapshot paths that produce and persist the envd capability annotation. It does not change sandbox create-time env var propagation semantics from #566.

Follow-up

A later follow-up can make envd capability suppression visible to users, so a missing annotation can distinguish between "envd is absent" and "envd was detected but not ready during template-time probing". That would let users understand why the template is not marked envd-capable and decide whether to run redo for transient startup jitter.

Probe envd /health during template snapshot flows before reporting an envd
version from Cubelet. This makes the envd capability annotation mean that the
template has a usable envd service, not just an envd binary that may be stopped
or still starting.

Apply the same readiness check to both AppSnapshot and CommitSandbox.

Only persist cube.master.components.envd.version when all READY replicas report
the same valid, ready envd version. Otherwise, omit the annotation so later
sandbox create flows do not rely on an optimistic capability signal.

Signed-off-by: zhengyilei <zheng_yilei@qq.com>
@zyl1121 zyl1121 changed the title fix(template): probe envd readiness before persisting capability fix(template): probe envd readiness before persisting envd capability Jul 3, 2026
@cubesandboxbot

cubesandboxbot Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Summary for PR #741

This PR improves envd capability detection by adding an HTTP readiness probe before persisting the envd version annotation. The code is clean and well-tested overall.

Notable Issues

1. runProbeCall goroutine leak on context cancellation (envd_version.go:93-116)
When runProbeCall context expires it returns immediately but the spawned goroutine continues executing. Three call sites per collectEnvdVersion invocation.

2. Serial latency on snapshot/commit paths (appsnapshot.go:169 template_ops.go:187)
The readiness probe adds up to 15s of serial blocking latency. Consider an in-memory TTL cache per sandboxID.

3. Test coverage gap: reconcileTemplateEnvdVersion persist/no-op paths (store_test.go)
Only the clear stale annotation path is tested. The persist and no-op paths are untested.

4. Missing IP validation (envd_version.go:319-328)
lookupSandboxIP returns cb.IP without net.ParseIP(). Inline comment posted.

5. Fragile sentinel (envd_version.go:193)
sandboxIP == "" check relies on specific string formatting. Inline comment posted.

6. ProbeConfig mutation (envd_version.go:213-224)
Defaults written directly into caller cfg struct. Inline comment posted.

Positive Highlights

  • finalizeReadyEnvdVersion with injected dependencies is a clean testable pattern
  • HTTP redirect rejection via ErrUseLastResponse is solid SSRF defense
  • Timer handling correctly drains channel on cancellation
  • Table-driven convergeEnvdVersion tests cover all behavioral cases

4 inline comments posted on specific code locations.


func (l *local) probeEnvdReadiness(ctx context.Context, sandboxIP string, port int) error {
sandboxIP = strings.TrimSpace(sandboxIP)
if sandboxIP == "" || sandboxIP == "<nil>" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "<nil>" string comparison is a fragile sentinel check — if the upstream source of sandboxIP changes its nil formatting (e.g. Go version upgrade, different serialization), this guard silently passes through a clearly invalid IP. Consider validating with net.ParseIP() here or (better) at the point of origin so the root cause is addressed rather than papered over.

if cb == nil {
return "", fmt.Errorf("cubebox %s not found", sandboxID)
}
return strings.TrimSpace(cb.IP), nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sandbox IP is returned without validation via net.ParseIP(). While the IP originates from an internal store (not direct user input), adding net.ParseIP() here as defense-in-depth would prevent a corrupted store entry from causing the Cubelet to make HTTP requests to unintended hosts. The IP flows directly into buildEnvdReadinessProbeformatURLhttp.Client.Do, making this a potential SSRF vector if the store is ever corrupted.

if client == nil {
client = newEnvdReadinessHTTPClient()
}
if cfg.Timeout <= 0 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probeEnvdReadinessWithClient mutates the caller's *ProbeConfig by writing default values into cfg.Timeout, cfg.ProbeTimeout, cfg.Period, and cfg.FailureThreshold when they are <= 0. This is a surprising side effect — if a caller ever reuses a ProbeConfig across multiple probes, the first call permanently overwrites zero-valued fields, masking what the original caller-supplied values were. Consider using local variables for defaults instead of mutating cfg.

@zyl1121 zyl1121 marked this pull request as draft July 3, 2026 11:53
@kinwin-ustc

Copy link
Copy Markdown
Collaborator

Perhaps we just need to use the probe-path parameter of cubemastercli? For example, --probe 49983 --probe-path /health

@zyl1121 zyl1121 marked this pull request as ready for review July 3, 2026 12:08
@zyl1121

zyl1121 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Perhaps we just need to use the probe-path parameter of cubemastercli? For example, --probe 49983 --probe-path /health

I considered that too, but I think it would change the CLI contract too much.

Today, --probe / --probe-path represents the user-facing readiness probe for the workload being built into the template. If we reuse it for envd (49983 /health), envd readiness becomes a hard template-build gate, and users also lose the ability to specify their own service's readiness probe.

This PR intentionally keeps those two concerns separate: the existing probe continues to represent workload readiness, while the envd probe is an internal capability check whose only effect is whether CubeMaster publishes the envd capability annotation.

If we want to express envd readiness through probe configuration in the future, I think we should first add support for multiple probes, or introduce a dedicated internal probe slot, rather than overloading the existing user-facing one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants