Skip to content

feat: add opt-in idle browser rebuilds - #114

Merged
IlyaasK merged 8 commits into
mainfrom
fix/rebuild-idle-browsers-on-config-update
Aug 4, 2026
Merged

feat: add opt-in idle browser rebuilds#114
IlyaasK merged 8 commits into
mainfrom
fix/rebuild-idle-browsers-on-config-update

Conversation

@IlyaasK

@IlyaasK IlyaasK commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add rebuild_idle_browsers_on_update to kernel_browser_pool, defaulting to false
  • send discard_all_idle=true only when the option is enabled and profile, proxy, extensions, Chrome policy, viewport, headless, kiosk, stealth, or start URL changes
  • warn during planning when known or unresolved values may cause idle browsers to be discarded
  • suppress the update-specific warning for creates, destroys, replacement plans, disabled preferences, and non-launch updates
  • keep name, size, fill-rate, timeout, and local-preference-only updates non-disruptive
  • persist local-preference-only changes without an empty PATCH or unnecessary read
  • document that the setting is provider-local and imported pools default to false unless configured otherwise

Why

Kernel browser-pool updates normally change the template for future browsers without replacing existing instances. This gives customers an explicit Terraform opt-in to rebuild browsers that are already idle when launch configuration changes.

Discarding idle browsers can temporarily reduce ready capacity while the pool refills. The plan warning exposes that effect before approval, including when interpolated launch inputs are not known until apply. Browsers that are warming or currently leased are not rebuilt, matching the Kernel API contract.

Acquire and release remain outside the Terraform provider surface. The acceptance test uses the SDK only to verify the resulting browser configuration.

Example

resource "kernel_browser_pool" "example" {
  name    = "example"
  size    = 10
  stealth = true

  rebuild_idle_browsers_on_update = true
}

Verification

  • go test -race ./internal/resources/browserpool -run 'Test(ModifyPlanWarnsBeforeIdleBrowserRebuild|ExpandUpdateParamsRejectsUnknownLaunchComparisonValuesBeforeDiscard)' -count=20
  • go test -short -timeout=2m ./...
  • go vet ./...
  • gofmt -l cmd internal
  • bash scripts/check-docs.sh
  • bash scripts/check-markdown-links.sh
  • bash scripts/check-examples.sh
  • hosted Go and docs passed at caa0c1a

The browser-pool lifecycle acceptance test passed earlier in this PR in 64.99 seconds. It changed stealth from false to true, acquired a rebuilt browser with stealth=true, and released it with reuse=false. It was not rerun after the plan-warning follow-up because that change does not alter the API payload or lifecycle behavior; the warning and unknown-value paths are covered by focused framework, unit, and race tests.


Note

Medium Risk
Updates can discard idle pool browsers and temporarily reduce ready capacity when opted in; behavior is gated and well-tested but affects live browser-pool runtime.

Overview
Adds rebuild_idle_browsers_on_update to kernel_browser_pool (optional/computed, default false, not stored by Kernel—imports default to false unless set in config).

When the flag is true and launch-related fields change (profile, proxy, extensions, chrome policy, viewport, headless/kiosk/stealth, start URL), pool updates include discard_all_idle=true so idle browsers are replaced with the new template; warming/leased browsers are unchanged. Name, size, timeouts, fill rate, and toggling the flag alone stay non-disruptive—preference-only updates persist in state without an API PATCH.

ModifyPlan emits a plan warning when rebuild may run (including unknown launch inputs), and skips it for create/destroy, project replacement, disabled preference, or non-launch edits.

Docs and acceptance coverage are updated: the browser-pool lifecycle test opts into rebuild, waits for capacity, verifies stealth on an acquired browser (SDK acquire/release with reuse=false), and ignores the provider-local attribute on import verify.

Reviewed by Cursor Bugbot for commit a6bf679. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread internal/resources/browserpool/expand.go
@IlyaasK
IlyaasK force-pushed the fix/rebuild-idle-browsers-on-config-update branch from fbed696 to cec6189 Compare August 3, 2026 15:19
@IlyaasK IlyaasK changed the title fix: rebuild idle browsers on pool config updates feat: add opt-in idle browser rebuilds Aug 3, 2026
@IlyaasK
IlyaasK force-pushed the fix/rebuild-idle-browsers-on-config-update branch from cec6189 to 0905dc6 Compare August 3, 2026 15:24
Comment thread internal/resources/browserpool/flatten.go Outdated
Comment thread internal/resources/browserpool/expand.go
@IlyaasK

IlyaasK commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up review items are addressed in 6bf94ae and e311fe4. For the viewport question, I experimentally confirmed with Terraform CLI that a known viewport object can contain an unknown width. The provider intentionally fails closed rather than treating an unknown required dimension as unchanged: expandViewport returns a path-specific diagnostic, expandUpdateParams returns hasPatch=false and a zero SDK payload, and the resource does not call Kernel. Permanent table coverage now proves this for both width and height with idle rebuilding enabled. The other accepted changes add opt-out/no-launch/null-to-set coverage, document exact trigger fields and import behavior, disable acceptance-client retries, align the fixture, and extend only the failure timeout. Runtime Acquire/Release remain outside internal/kernelclient to preserve its durable-only boundary.

@IlyaasK
IlyaasK requested a review from Sayan- August 3, 2026 19:58

@Sayan- Sayan- left a comment

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 unknown handling is right, and I checked the viewport case you flagged: requireKnownOptional tests the object, not its attributes, so a known viewport carrying an unknown width does reach browserViewportChanged, returns true, sets discard_all_idle, and then expandViewport errors before any API call happens. Fail-closed, exactly as you described.

Worth noting explicitly why the rest is safe, since it's load-bearing and not obvious from the diff: validateUpdateKnownValues requires known values for every string and collection field that browserLaunchConfigurationChanged compares with a raw .Equal, which is what prevents an unknown from being read as "changed" and triggering a discard with no corresponding patch. The three bools aren't in that list, which is why knownBoolChanged has to exist. Those two lists now have to stay in sync — if a future field is added to the launch-change check without a matching known-value guard, the failure mode is discarding idle browsers on a no-op update. The comment above the function covers the patch list; it's worth extending it to say that too.

One thing I'd add before this ships: a plan-time warning. Today a user with rebuild_idle_browsers_on_update = true who flips stealth sees a plan reading stealth: false -> true and nothing else. Nothing in that output says applying it will discard the pool's idle browsers. It isn't data loss — leased and warming browsers are untouched — but it is a capacity and cost event, and a pool of 200 draining and refilling is worth knowing about before you type yes. ModifyPlan can append a warning diagnostic when the flag is set and browserLaunchConfigurationChanged is true, which is the normal way providers surface a destructive side effect that isn't visible as a resource replacement. That's the difference between an intended rebuild and a surprising one.

Minor, take it or leave it: planAttrs["refresh_rate"].(types.Int64) is an unchecked type assertion. It can't fail given the declared object type, but if the viewport shape ever changes it panics the provider instead of returning a diagnostic.

IlyaasK added 6 commits August 4, 2026 14:40
Cover each launch-setting classification independently, assert the non-disruptive schema default, and give acceptance browser cleanup a fresh release deadline.
Surface the possible idle-capacity impact during planning, including unknown launch inputs, while suppressing the update warning for replacement plans. Add fail-closed update and plan-hook regression coverage.
@IlyaasK
IlyaasK force-pushed the fix/rebuild-idle-browsers-on-config-update branch from e311fe4 to caa0c1a Compare August 4, 2026 19:01

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit caa0c1a. Configure here.

Comment thread internal/resources/browserpool/expand.go Outdated
IlyaasK added 2 commits August 4, 2026 15:16
Use Terraform configuration to distinguish omitted computed launch fields from unresolved configured values. Preserve conservative warnings for real unknown inputs and cover both planning cases.
Keep the planning classifier aligned with every launch field and prove invalid clear operations suppress the capacity warning because no update can proceed.
@IlyaasK
IlyaasK requested a review from Sayan- August 4, 2026 19:35

@Sayan- Sayan- left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning gate looks right. Reading req.Config to separate an omitted Optional+Computed value (null) from an unresolved expression (unknown) is the correct distinction, and keeping the plan-time comparison separate from the apply-time one is the right call since they need opposite unknown handling. Skipping the warning on replacement and on updates that would already fail validation are both good catches.

@IlyaasK
IlyaasK merged commit 0bd6092 into main Aug 4, 2026
2 checks passed
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