Add a docker compose Redash sandbox with seed data for trying rdsh end to end - #55
Merged
Conversation
rdsh's tests run against an in-process fake, so trying the CLI against a real Redash meant pointing it at a production instance. These services, behind a `redash` profile so no existing compose invocation picks them up, bring one up locally with a seeded database to query.
The stack needs the schema created before the server starts, an admin user, a fixed API key and a data source pointing at the seed database. Doing that by hand every time is what kept the sandbox from being useful, so the script does it, skips whichever steps are already done, and prints the two export lines that configure rdsh.
Where the fixed admin login, the mandatory --data-source and the teardown and version-switch commands live, so the values the script hard-codes are written down in the place a developer looks first.
Its admin password and API key are fixed values committed in the clear, and Redash connects to whatever host a data source names, so an instance answering the LAN is a pivot into the network it sits on. Nothing about the sandbox needs to be reachable from another machine.
The five-minute deadline is only checked between attempts, so a port that accepts a connection and then never answers — the state Redash passes through while it boots — could block one curl far longer than the deadline.
7 tasks
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
rdsh's tests all run against an in-process fake (
internal/redash/redash_test.go), so nothing in the repository exercises the CLI against a real Redash. Checking a change by hand meant pointing rdsh at a production instance. This adds a throwaway local Redash with a data source and some data already in it, sordsh run/rdsh query create/rdsh query updatecan be tried end to end on a laptop.What
compose.yaml—server,scheduler, oneworkerconsuming every queue,redis:7-alpineandpostgres:17-alpine, all behind aredashprofile so that no compose invocation that does not name the profile —docker compose run --rm lintabove all — picks them up. Trimmed from the official setup compose: no nginx, one worker instead of three, and fixed secrets in the clear, since the instance is local and disposable. Thelintservice and the header comment that pins golangci-lint are untouched.scripts/redash-seed.sql— mounted into the postgres image'sdocker-entrypoint-initdb.d. Creates atestdatadatabase withsignups(40 rows) andevents(120 rows), whose columns cover what--param-typecan express: a date, a timestamp, numbers and text.scripts/redash-up.sh— creates the schema before the server starts, the admin user, a fixed API key and the data source, skipping whatever is already done, then prints the twoexportlines.--resetthrows the Redash containers and volume away first.README.md— a### Redash sandboxsubsection under## Development.No Go code changed, so the output format, the exit codes,
skills/rdsh/SKILL.md, the cobra help strings and the plugin descriptions are all as they were..github/dependabot.ymlneeded no change either.Trying it
The first run pulls the Redash image, so give it a few minutes. Everything after that is seconds.
Decisions worth knowing about
The image tag is repeated on all three Redash services rather than living in the
x-redashanchor. Dependabot's docker-compose ecosystem readsservices.<name>.image; a tag hidden in a top-level extension field may never be bumped, and keeping the sandbox on the release users actually run is the reason these services live incompose.yamlat all instead of a separate file. Worth watching after this merges — the repository has no docker-compose Dependabot PR yet, so that behaviour is inferred from the documented ecosystem rather than observed here.The sandbox is published on
127.0.0.1:15000, not 5000. Redash's own port cannot move — the image's entrypoint hardcodesgunicorn -b [::]:5000— but 5000 is often already taken on a developer machine, and 15000 stays below the ephemeral range on both macOS and Linux. This is the one deviation from the issue, which named 5000.The loopback half of that is deliberate rather than incidental: the admin password and the API key are fixed values committed in the clear, and Redash connects to whatever host a data source names, so an instance answering the LAN is a pivot into the network around it. Nothing about a throwaway sandbox needs to be reachable from another machine.
All of the script's own output goes to stderr.
docker compose runforwards the container's output to the script's stdout, so redirecting each command individually would eventually leak a stray line intoeval.main "$@" >&2makes stdout structurally incapable of carrying anything but the twoexportlines;set -emeans a failure never reaches them at all.Two upstream details shape the script. The image's entrypoint passes
managearguments through an unquoted$*, so the data source's--optionsJSON must not contain a space. Andmanage users create_rootexits 1 when the user already exists, so its existence check has to gate the call rather than tolerate a failure — which is why all three setup steps are guarded by apsqlquery instead.--resetrunsdocker compose --profile redash downwithout-v, then removes the Redash volume by name.-vwould take thegomod-cacheandlint-cachevolumes with it. The volume carries a fixedname:in compose.yaml so it can be named on its own, rather than through a project prefix that follows the checkout directory.Rejected: a separate
e2e/compose.yaml(takes the images out of Dependabot's view and adds a second entry point; the profile gives the same isolation), an interpolated${REDASH_VERSION:-...}tag (Dependabot cannot bump one), and a Makefile or CONTRIBUTING.md for three one-line tasks.Verified
Every acceptance criterion was run on an arm64 machine with Docker.
Results
eval "$(scripts/redash-up.sh)" && rdsh run --data-source sandbox "SELECT count(*) FROM signups"on a clean checkout40, no config file involvedquery createwithsince=2026-01-01/--param-type since=datelatest_query_data_idset, the three expected rows on/api/queries/1/results.json), parameter stored as{"name":"since","type":"date","value":"2026-01-01"}--reseton an initialised stackrdsh_gomod-cacheandrdsh_lint-cachesurviveredash/redash:25.8.0+--resetmanage.py versionreports 25.8.0, criterion 1 passes; tag restored afterwardsdocker compose run --rm lint,go test -race ./...,yq '.services.lint.image' compose.yamlgolangci/golangci-lint:v2.12.2; no Redash service startsPOST /loginwithadmin@example.com/sandboxreturns 302 to/with a session cookie; a wrong password does notThe seed data was checked directly too: 40
signups, 120events, dates spanning 2025-11-04 to 2026-03-01 with 20 rows at or after 2026-01-01, so the>= '2026-01-01'filter in criterion 2 returns a proper subset rather than everything.Out of scope
Go end-to-end tests against this stack (
//go:build e2e) and a CI job that starts it, per the issue. Until those exist, a Dependabot bump of the Redash image is not verified automatically.Closes #54