You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Testing an experimental Cassandra build (a git branch, or a tarball from the existing build-cassandra-ref.yml Action, #729) currently requires adding it to cassandra_versions.yaml and baking an entirely new AMI. install_cassandra_version() (packer/cassandra/install/install_cassandra.sh:143) already knows how to install a single version — download tarball, or git-clone+ant-build — but it's only ever invoked from a loop over every entry in /etc/cassandra_versions.yaml, gated by INSTALL_CASSANDRA=1 and run exactly once, at packer bake time (install_cassandra.sh:126-127, :290-300). There's no way to install one additional version onto a node that's already running.
cassandra use <version> (UseCassandra.kt) only switches which pre-baked version is active on a node — it never installs anything.
Scope
In scope:
Extract install_cassandra_version() so it can run standalone, against a single version, outside the bake-time loop — invoked remotely via RemoteOperationsService (not ProcessBuilder).
New cassandra install <version> subcommand: installs one version onto an already-provisioned, running cluster (all Cassandra nodes, or a --hosts-filtered subset, same mechanism as UseCassandra). Supports both existing install modes: git branch clone+ant-build, and tarball url: download — same schema fields (version, java, url/branch, ant_flags) cassandra_versions.yaml entries already use.
Writes the version's entry into /etc/cassandra_versions.yaml on the targeted node(s) before installing — this file is currently baked in once at packer time and only ever read down from a node (Up.kt:630-647), never pushed to; this is the first push-up path. packer/cassandra/bin/set-java-version:27-31 is existing precedent for an in-place yq -i mutation of this file on a live node.
lazy: true field on a cassandra_versions.yaml entry: declared/tracked in the repo's cassandra_versions.yaml (so it ships to every node's /etc/cassandra_versions.yaml), but skipped by the packer bake-time install loop — costs nothing at AMI-build time — until cassandra install <version> is explicitly run against a live node.
Extend ListVersions.kt (cassandra list) to show lazy-declared-but-not-yet-installed versions, so they're discoverable without already knowing the name from cassandra_versions.yaml.
Update openspec/specs/cassandra/spec.md (REQ-CA-001 currently requires multi-version support "on the same AMI" — needs updating/extending for the runtime-install path) and relevant docs in docs/.
Out of scope (decided, do not relitigate):
No CI orchestration.cassandra install does not trigger/dispatch build-cassandra-ref.yml and does not poll GitHub Actions for a build result. Run that Action manually, or point cassandra install at a git branch to build directly on the node (existing clone+build path).
No persistence across cluster lifecycle. A runtime-installed version is not synced to S3 via ClusterBackupService and does not need to survive down/up or node replacement/addition (not currently supported anyway). If a node is lost, re-run cassandra install.
cassandra use auto-installing a missing lazy version — use stays a fast, predictable symlink swap; running it against a not-yet-installed version fails loudly with a "run cassandra install <version> first" message.
--force/reinstall-in-place support — re-running cassandra install for an already-installed version is a safe no-op (skip, log "already present"). Can be added later as a fast-follow if reinstalling the same version name (e.g. new commits pushed to the same branch) turns out to be needed.
Acceptance criteria
WHEN cassandra install <version> is run with a git repo URL + branch for a version not yet present on the targeted node(s), THEN the branch is cloned and built with ant on that node, the built artifact lands at /usr/local/cassandra/<version>, and the command reports success per targeted host.
WHEN cassandra install <version> is run with a tarball url: (e.g. a build-cassandra-ref.yml release asset) for a version not yet present, THEN the tarball is downloaded and extracted to /usr/local/cassandra/<version> on the targeted node(s).
WHEN --hosts is passed to filter a subset of Cassandra nodes, THEN only those nodes have /etc/cassandra_versions.yaml updated and the version installed — untargeted nodes are unaffected.
WHEN install succeeds on a node, THEN cassandra use <version> on that node works unchanged — no changes needed to use-cassandra or UseCassandra.kt.
WHEN cassandra install <version> is run again for a version already installed on a node, THEN it does not error and does not re-download/re-build — safe no-op, logs "already present."
WHEN install fails (bad branch name, unreachable/404 tarball URL, ant build failure), THEN the command fails loudly with a clear per-host error identifying the version and the failure reason — no silent partial state, no fallback to another version.
WHEN a cassandra_versions.yaml entry has lazy: true and packer bakes a new AMI, THEN the bake-time install loop skips that entry entirely (no download/clone/build time spent), while the entry still appears in the resulting AMI's /etc/cassandra_versions.yaml so cassandra install can read its url/branch/java/ant_flags fields later without the operator re-specifying them.
WHEN cassandra use <version> is run for a version that isn't installed on that node, THEN it fails with a clear message directing the operator to run cassandra install <version> first — never a confusing/generic error.
WHEN cassandra list is run, THEN lazy-declared-but-not-yet-installed versions are shown, distinguishable from versions actually installed on the node.
Notes / context
packer/cassandra/install/install_cassandra.sh:143-268 — install_cassandra_version(), the function to extract. Handles three install modes: version-prefix-only (download_cassandra_version), url: ending in .tar.gz, and git url+branch (clone + ant realclean && ant -Dno-checkstyle=true $ANT_FLAGS).
packer/cassandra/install/install_cassandra.sh:83-91 — INSTALL_CASSANDRA gate; :126-127 / :290-300 — bake-time loop over every entry, parallel background jobs. This is where the lazy: true skip needs to land.
UseCassandra.kt — reference pattern for HostsMixin/--hosts targeting, remoteOps.executeRemotely, and @TriggerBackup/@RequireProfileSetup annotations to consider for the new command.
packer/cassandra/bin/use-cassandra — reads /etc/cassandra_versions.yaml via yq for java/python per version; the runtime-written entry must satisfy the same fields.
src/main/kotlin/com/rustyrazorblade/easydblab/commands/Up.kt:630-647 (downloadCassandraVersions) — confirms /etc/cassandra_versions.yaml today is only ever read down from a node, never pushed up.
cassandra_versions.yaml (repo root) vs. packer/cassandra/cassandra_versions.yaml — schema fields (version, java, python, ant_flags, url) the new command's entries must match. The two files currently drift slightly (not this issue's problem to fix); packer/cassandra/cassandra_versions.yaml is authoritative for AMI bake (wired via packer/cassandra/cassandra.pkr.hcl:172-185).
src/main/kotlin/com/rustyrazorblade/easydblab/providers/ssh/RemoteOperationsService.kt — executeRemotely/upload/download; use these, not ProcessBuilder.
openspec/specs/cassandra/spec.md:9-11 (REQ-CA-001) and :62-70 (REQ-CA-004, existing per-host-targeting precedent this issue extends).
Problem
Testing an experimental Cassandra build (a git branch, or a tarball from the existing
build-cassandra-ref.ymlAction, #729) currently requires adding it tocassandra_versions.yamland baking an entirely new AMI.install_cassandra_version()(packer/cassandra/install/install_cassandra.sh:143) already knows how to install a single version — download tarball, or git-clone+ant-build — but it's only ever invoked from a loop over every entry in/etc/cassandra_versions.yaml, gated byINSTALL_CASSANDRA=1and run exactly once, at packer bake time (install_cassandra.sh:126-127,:290-300). There's no way to install one additional version onto a node that's already running.cassandra use <version>(UseCassandra.kt) only switches which pre-baked version is active on a node — it never installs anything.Scope
In scope:
install_cassandra_version()so it can run standalone, against a single version, outside the bake-time loop — invoked remotely viaRemoteOperationsService(notProcessBuilder).cassandra install <version>subcommand: installs one version onto an already-provisioned, running cluster (all Cassandra nodes, or a--hosts-filtered subset, same mechanism asUseCassandra). Supports both existing install modes: git branch clone+ant-build, and tarballurl:download — same schema fields (version,java,url/branch,ant_flags)cassandra_versions.yamlentries already use./etc/cassandra_versions.yamlon the targeted node(s) before installing — this file is currently baked in once at packer time and only ever read down from a node (Up.kt:630-647), never pushed to; this is the first push-up path.packer/cassandra/bin/set-java-version:27-31is existing precedent for an in-placeyq -imutation of this file on a live node.lazy: truefield on acassandra_versions.yamlentry: declared/tracked in the repo'scassandra_versions.yaml(so it ships to every node's/etc/cassandra_versions.yaml), but skipped by the packer bake-time install loop — costs nothing at AMI-build time — untilcassandra install <version>is explicitly run against a live node.ListVersions.kt(cassandra list) to show lazy-declared-but-not-yet-installed versions, so they're discoverable without already knowing the name fromcassandra_versions.yaml.openspec/specs/cassandra/spec.md(REQ-CA-001 currently requires multi-version support "on the same AMI" — needs updating/extending for the runtime-install path) and relevant docs indocs/.Out of scope (decided, do not relitigate):
cassandra installdoes not trigger/dispatchbuild-cassandra-ref.ymland does not poll GitHub Actions for a build result. Run that Action manually, or pointcassandra installat a git branch to build directly on the node (existing clone+build path).ClusterBackupServiceand does not need to survivedown/upor node replacement/addition (not currently supported anyway). If a node is lost, re-runcassandra install.build-cassandra-ref.ymlitself or its fork-support hardening (ci: harden build-cassandra-ref fork support (private forks, tag disambiguation, docs) #765, unrelated).cassandra useauto-installing a missinglazyversion —usestays a fast, predictable symlink swap; running it against a not-yet-installed version fails loudly with a "runcassandra install <version>first" message.--force/reinstall-in-place support — re-runningcassandra installfor an already-installed version is a safe no-op (skip, log "already present"). Can be added later as a fast-follow if reinstalling the same version name (e.g. new commits pushed to the same branch) turns out to be needed.Acceptance criteria
cassandra install <version>is run with a git repo URL + branch for a version not yet present on the targeted node(s), THEN the branch is cloned and built with ant on that node, the built artifact lands at/usr/local/cassandra/<version>, and the command reports success per targeted host.cassandra install <version>is run with a tarballurl:(e.g. abuild-cassandra-ref.ymlrelease asset) for a version not yet present, THEN the tarball is downloaded and extracted to/usr/local/cassandra/<version>on the targeted node(s).--hostsis passed to filter a subset of Cassandra nodes, THEN only those nodes have/etc/cassandra_versions.yamlupdated and the version installed — untargeted nodes are unaffected.cassandra use <version>on that node works unchanged — no changes needed touse-cassandraorUseCassandra.kt.cassandra install <version>is run again for a version already installed on a node, THEN it does not error and does not re-download/re-build — safe no-op, logs "already present."cassandra_versions.yamlentry haslazy: trueand packer bakes a new AMI, THEN the bake-time install loop skips that entry entirely (no download/clone/build time spent), while the entry still appears in the resulting AMI's/etc/cassandra_versions.yamlsocassandra installcan read itsurl/branch/java/ant_flagsfields later without the operator re-specifying them.cassandra use <version>is run for a version that isn't installed on that node, THEN it fails with a clear message directing the operator to runcassandra install <version>first — never a confusing/generic error.cassandra listis run, THEN lazy-declared-but-not-yet-installed versions are shown, distinguishable from versions actually installed on the node.Notes / context
packer/cassandra/install/install_cassandra.sh:143-268—install_cassandra_version(), the function to extract. Handles three install modes: version-prefix-only (download_cassandra_version),url:ending in.tar.gz, and giturl+branch(clone +ant realclean && ant -Dno-checkstyle=true $ANT_FLAGS).packer/cassandra/install/install_cassandra.sh:83-91—INSTALL_CASSANDRAgate;:126-127/:290-300— bake-time loop over every entry, parallel background jobs. This is where thelazy: trueskip needs to land.UseCassandra.kt— reference pattern forHostsMixin/--hoststargeting,remoteOps.executeRemotely, and@TriggerBackup/@RequireProfileSetupannotations to consider for the new command.packer/cassandra/bin/use-cassandra— reads/etc/cassandra_versions.yamlviayqforjava/pythonper version; the runtime-written entry must satisfy the same fields.src/main/kotlin/com/rustyrazorblade/easydblab/commands/Up.kt:630-647(downloadCassandraVersions) — confirms/etc/cassandra_versions.yamltoday is only ever read down from a node, never pushed up.cassandra_versions.yaml(repo root) vs.packer/cassandra/cassandra_versions.yaml— schema fields (version,java,python,ant_flags,url) the new command's entries must match. The two files currently drift slightly (not this issue's problem to fix);packer/cassandra/cassandra_versions.yamlis authoritative for AMI bake (wired viapacker/cassandra/cassandra.pkr.hcl:172-185).src/main/kotlin/com/rustyrazorblade/easydblab/providers/ssh/RemoteOperationsService.kt—executeRemotely/upload/download; use these, notProcessBuilder.openspec/specs/cassandra/spec.md:9-11(REQ-CA-001) and:62-70(REQ-CA-004, existing per-host-targeting precedent this issue extends).build-cassandra-ref.yml— this issue'surl:mode consumes its output; its design doc explicitly deferred "wiring the lab to auto-deploy the produced artifacts," which this issue delivers) and ci: build Cassandra tarballs per-version (reuse build-cassandra-ref) + add 6.0 to cassandra_versions.yaml #763/feat: add Cassandra 6.0 to cassandra_versions.yaml (nightly tarball) #775/ci: make nightly Cassandra build artifact names consistent (6.0 → 6.0-HEAD) and wire into cassandra_versions.yaml #821/ci: tag nightly Cassandra GHCR images with the stable version label (:5.0-HEAD / :6.0-HEAD / :trunk) #824 (hand-wired specific nightly versions intocassandra_versions.yaml— this issue generalizes that into a reusable command).build-cassandra-ref.ymlitself — don't fold in).