Skip to content

Add snowflake-next preview emulator type - #464

Open
hovaesco wants to merge 3 commits into
mainfrom
lav-595-integration-with-lstk
Open

Add snowflake-next preview emulator type#464
hovaesco wants to merge 3 commits into
mainfrom
lav-595-integration-with-lstk

Conversation

@hovaesco

@hovaesco hovaesco commented Aug 18, 2026

Copy link
Copy Markdown

Motivation

lstk only knows the Python Snowflake emulator: type = "snowflake" means localstack/snowflake. Running the rewritten emulator meant hand-overriding image/tag and remapping env — a recipe that lived in a Slack thread, and which every tester had to rediscover.

Solution

Adds snowflake-next as a preview emulator type, so lstk -t snowflake-next just works and records the choice in config like any other type.

Two deliberate calls:

The picker does not offer it. SelectableEmulatorTypes still lists AWS/Snowflake/Azure, so a first-time user's choice stays a GA product; KnownEmulatorTypes() (selectable + previews) is what config and --type accept. At GA this type is retired and snowflake takes over the image.

lstk adapts the image instead of the image adapting to lstk. Unlike the other emulators it listens on 8080, ignores GATEWAY_LISTEN, and declares its own VOLUME:

Image default What lstk does
SNOWFLAKE_LISTEN_ADDR=0.0.0.0:8080 rewritten to the gateway port, so the published/health-checked/advertised port is real
VOLUME /var/lib/snowflake-rs bind-mounted from the managed volume dir
PGDATA=/var/lib/snowflake-rs/data left alone under --persist; pointed at a container-local path otherwise

Covering that VOLUME is not cosmetic: lstk recreates the container on every start and docker rm keeps anonymous volumes, so an uncovered declaration stranded a full PostgreSQL cluster per start. And because the cluster is always written to disk (there is no in-memory mode), --persist is expressed as where PGDATA points rather than an on/off switch — so the default still gives a clean emulator on restart, like the others.

Found and fixed along the way: the IaC proxies enumerated only the selectable types when naming a running non-AWS emulator, so lstk terraform against a running preview reported the misleading LocalStack AWS Emulator is not running. Same fix for the emulator list handed to extensions.

Verified end-to-end against localstack/snowflake-next:latest: health answers on the configured port over http and https, the snowflake. subdomain resolves, and the container self-validates its license. lstk -t snowflake-next, status (reports 0.1.0+a139ef6), volume path, and stop all exercised by hand on a fresh install.

Docs

User-facing, worth documenting:

  • New emulator type snowflake-next, a preview of the next Snowflake emulator. Selected with lstk start --type snowflake-next / lstk -t snowflake-next, or type = "snowflake-next" in config.toml. Displayed as "LocalStack Snowflake Preview Emulator".
  • Intentionally absent from the interactive first-run picker — reachable only by asking for it explicitly. Worth stating plainly in docs, since it is otherwise undiscoverable.
  • --type help text now lists it; lstk start --help gained a paragraph explaining it.
  • config.toml comment mentions it next to the supported types.
  • Persistence differs from the other emulators: state survives restarts only with --persist, and lstk volume clear resets it. Nothing for the user to do on Linux — the emulator runs as uid 1000 and lstk makes the state directory writable by it; if that ever fails, the start warns and names the directory.
  • --endpoint-url can target a remote snowflake-next on an image carrying localstack/snowflake-rs#2116. It is reported under the GA Snowflake emulator's name, since the two are indistinguishable from the health payload.
  • Migration note for early testers: the hand-rolled image/tag/SNOWFLAKE_LISTEN_ADDR/PGDATA config from the preview period is no longer needed and should be replaced by type = "snowflake-next". A custom image is still a hard error when switching types.

The default image is localstack/snowflake-next:latest, same tag convention as the other emulators.

Review

Human review advised — new user-facing emulator type, and the persistence/mount semantics are a judgement call worth a second opinion.

Todo

  • --endpoint-url against a remote snowflake-next: localstack/snowflake-rs#2116 (LAV-1678) is merged, so the preview now reports services: {"snowflake": "available"} — a shape lstk already classifies, no lstk change needed. Added an integration test pinning status against that payload and refreshed the now-stale comment in internal/endpoint/target.go. It resolves to the GA snowflake type: the two are indistinguishable from the payload, and every remote path treats them identically, so only the display name differs.
  • Confirm --persist on Linux where the host user is not uid 1000 — it was broken, and CI caught it: TestStartSnowflakeNextPersistsStateIntoVolumeWithPersist failed on ubuntu (uid 1001) while both non-persist siblings passed. lstk created the bind-mounted state dir 0755, so the emulator's uid 1000 could not create PGDATA in it and PostgreSQL never initialized. lstk cannot chown to a uid it does not own, so prepareNextStateDir now widens the mode and warns if that fails.

Towards LAV-595

Co-Authored-By: Claude noreply@anthropic.com

@hovaesco
hovaesco requested review from a team and peter-smith-phd as code owners August 18, 2026 14:19
@hovaesco hovaesco added semver: minor docs: needed Pull request requires documentation updates labels Aug 18, 2026
Co-Authored-By: Claude <noreply@anthropic.com>
@hovaesco
hovaesco force-pushed the lav-595-integration-with-lstk branch from 27d3311 to 796e607 Compare August 18, 2026 14:26

@gtsiolis gtsiolis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This wires the snowflake-next preview emulator type end to end — reachable only via --type/config (never the first-run picker), with its port remap and PostgreSQL persistence handled on the start path, plus integration tests and every non-compiler-checked map/slice touch-point updated.

  1. suggestion(non-blocking): on internal/emulator/snowflake/snowflake.go / internal/container/start.go — the --persist uid-1000 caveat on Linux native Docker is well documented in code, but has no user-facing signal: when it bites, the user sees only an opaque PostgreSQL "refuses to start" surfacing as a health-check timeout. A note/warning on --persist for snowflake-next on Linux pointing at host-directory ownership would save a confusing debugging session. Preview-acceptable to defer.
  2. question(non-blocking): on cmd/status.gosnowflake-next maps to snowflake.NewClient(), but per the comment you added in internal/endpoint/target.go the preview's health payload carries a version and no services map. Does lstk status render correctly against that payload, or could it under-report? I did not verify this against a running preview image (the licensed image wasn't available to me).
  3. praise: thorough TDD — the integration tests pin the port remap, both persistence paths, the first-run picker omission, and the IaC-rejection naming, and every entry the compiler can't catch (knownImages, emulatorHealthPaths, ContainerPort, SelfValidatesLicense, emulatorDisplayNames, the cmd/status.go client map, tipsForType) is covered.

Automated review on behalf of @gtsiolis.


Generated by Claude Code

hovaesco and others added 2 commits August 18, 2026 16:58
… user

Co-Authored-By: Claude <noreply@anthropic.com>
…leanup

Co-Authored-By: Claude <noreply@anthropic.com>

@peter-smith-phd peter-smith-phd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I understand what the change is trying achieve, but could we please have some upfront discussion on how to solve it? (Please forgive me if I missed the Slack message). I want to make sure we have a clean/elegant solution to this, rather than just asking Claude to make a whole ton of changes.

Some important questions I have:

  1. Are customers actively using the new Snowflake emulator, or is this for internal use only? What's the timeline for making the new Snowflake emulator the default?
  2. This PR touches 21 files and changes 601 lines of code, which seems excessive given that we're essentially making 3 small configuration changes. When I look at the code changes, I see a lot of places where a list of 3 things becomes a list of 4 things. That implies we should be centralizing those values in a common place, not just repeating the list everywhere.

@hovaesco

Copy link
Copy Markdown
Author
  1. The Snowflake Rust (Next) emulator is not yet being officially released to the public, but several prospects and customers are waiting to test it. We currently only have internal users and beta testers, and the lstk integration is one of the remaining pieces needed for a public release. The sooner we can launch the preview, the better. We’re tentatively targeting GA for early next year, depending on feedback from the preview phase.

  2. To be precise, the 603 added lines include approximately 406 lines of tests and snapshots. The runtime changes also extend beyond three configuration values. The accepted type set is centralized in KnownEmulatorTypes(), while the other occurrences represent behavior owned by different subsystems rather than duplicated copies of the same list. I’m open to consolidating more of this into a descriptor registry, but that would be a broader architectural refactor. If that is required for this PR, could you please suggest the specific abstraction you have in mind?

@claude claude 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.

This adds a snowflake-next preview emulator type, reachable only through --type/config and deliberately never offered by the first-run picker; the plumbing is careful and the genuinely tricky edge cases (uid-1000 state dir, anonymous-volume-per-start leak, remote-payload collapse) are each handled and pinned by a test. I verified CI is green on 5814071 and that discovery is centralized via KnownEmulatorTypes()/KnownImageRepos().

  1. suggestion(non-blocking): per-type behavior still fans out across many sites — emulatorDisplayNames, emulatorHealthPaths, ContainerPort, SelfValidatesLicense, the cmd/status.go client map, tipsForType, and knownImages. Discovery is now centralized, but adding a type remains a set of compiler-unchecked edits in ~7 files (echoing @peter-smith-phd); a follow-up folding per-type config into one table would make the next type a single entry.
  2. thought(non-blocking): prepareNextStateDir in internal/container/start.go chmods the state dir 0777 (world-writable) under the managed volume. The rationale is sound (the emulator runs as uid 1000 and lstk cannot chown to a uid it does not own), but on a shared host it exposes the PostgreSQL cluster dir to other local users — worth noting since it is a security-relevant widening, even if acceptable for a dev tool.
  3. thought(non-blocking): a remote preview instance collapses to EmulatorSnowflake in internal/endpoint/target.go, so lstk status --endpoint-url reports "Snowflake", not "Snowflake Preview", for a preview instance. It's documented and tested (TestStatusEndpointURLSnowflakePreviewPayload) and only the display name differs — just confirming that display divergence is intended.
  4. praise: the documentation and test coverage of the failure modes is exceptional — the anonymous-volume-per-start leak, the uid-1000 PGDATA initialization failure on native Linux Docker, and the widen-an-existing-dir upgrade path are each explained on the declaration and pinned by a dedicated test.

Automated review on behalf of @gtsiolis.


Generated by Claude Code

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

Labels

docs: needed Pull request requires documentation updates semver: minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants