Skip to content

feat(env_aws): support clickhouse clusters and keepers - #283

Draft
ianaya89 wants to merge 8 commits into
masterfrom
feat/clickhouse-clusters-aws
Draft

feat(env_aws): support clickhouse clusters and keepers#283
ianaya89 wants to merge 8 commits into
masterfrom
feat/clickhouse-clusters-aws

Conversation

@ianaya89

@ianaya89 ianaya89 commented Aug 25, 2026

Copy link
Copy Markdown
Member

Exposes the ClickHouse clusters and Keepers that the public API now carries in the env spec. AWS only; the other five envs have the same fields and are still unwired.

  • clickhouse_clusters / clickhouse_keepers on altinitycloud_env_aws — they live in the env spec, there is no standalone mutation
  • Removal is explicit: Update diffs plan vs state to fill the API's *ToDelete lists. Renaming an entry deletes it and creates a new one, so the plan warns before every deletion
  • password_value is sensitive and never refreshed — the API never returns passwords
  • Plan-time checks for immutable attributes, node group reservations, and the fields the update API cannot carry on a newly added entry

The e2e config exercises the new fields but has not been run against the dev control plane.

@ianaya89
ianaya89 marked this pull request as draft August 25, 2026 12:31
The input types were already generated; only the read fragments were
missing the fields, so reads never returned them.
Clusters and Keepers are part of the environment spec in the public API,
with no standalone mutation, so they are exposed as `clickhouse_clusters`
and `clickhouse_keepers` attributes on the environment resource.

Unlike the rest of the spec, which is sent wholesale under
UpdateStrategy REPLACE, these entries are patched by name and removal is
explicit: Update diffs plan against state to fill the API's *ToDelete
lists, at both the top level and inside each cluster.

The shared mapping lives in env/common and works on plain spec structs;
each env only lowers its own gqlgenc fragment types into them, since
those are generated per env.

Refresh semantics worth noting:

- Passwords are never returned by the API, so `password_value` is kept
  from the configuration and never refreshed. `password_type` follows it,
  because the API can report PLAIN_TEXT, which is not a valid input.
- Empty lists and empty strings coming back from the API map to null, so
  an omitted attribute does not drift to `[]` or `""`.
- A null keeper in a read means detached, not absent: the configured name
  is preserved since the API ignores it while disabled.

Immutability is checked in ModifyPlan by pairing entries by name rather
than with the ImmutableString modifier, which compares by index and would
report a false positive whenever an entry is removed from the middle of
the list.
Adds a full example and a section covering the behaviors that are not
obvious from the schema: removal deletes, the node group reservations a
cluster and a Keeper need, which attributes are immutable, and that
passwords are never returned.

`make docs` now passes --provider-name explicitly, since tfplugindocs
otherwise derives it from the directory name and fails in a git worktree.
Five defects found reviewing the initial implementation:

- Entries added to an existing environment go through the update API, whose
  input carries no mode, zones or storage class, so they would silently come
  up with the defaults and fail the post-apply consistency check. Setting any
  of the three on a newly added cluster, Keeper or volume is now rejected at
  plan time. An explicit STANDARD mode is still allowed, since that is what
  would be created anyway.
- Zones were stored in the order the API returned them, which diffs forever
  against an immutable list attribute. They now follow the configured order,
  the same way env and node group zones already do.
- Unknown values were read as empty strings, so two entries whose names are
  only known at apply time collided as duplicates, an unknown mode failed the
  SWARM check, and an unknown zone element shortened the list and read as a
  removed zone. Each rule now defers instead.
- The name pattern made its trailing group optional, so a one-character name
  passed a validator documented as 2-15.
- Documentation claimed renaming an entry was rejected at plan time. It never
  was, and it cannot be: a rename is a delete and a create. The plan now warns
  about every entry that is about to be deleted, and the docs say so.

Also cross-validates instance types against node group reservations, which is
knowable from the configuration and otherwise only surfaces as a failed
environment reconcile.
…butes

Two defects that between them broke the documented example.

ValidateConfig reads the raw configuration, before schema defaults are
applied, so an omitted `keeper.enabled` arrives null rather than as its
Default(true). ValueBool() reports false for a null Bool, so every cluster
that left the attribute out was rejected as a STANDARD cluster running
without a Keeper.

The pointer accessors only map null to nil: on an unknown value they hand
back a pointer to the zero value. Every Optional+Computed attribute without
a schema default is unknown in the plan, so create sent `mode: ""` — not a
valid enum literal — along with an empty storage class and zeroed iops,
throughput, profile and quota. They are now omitted, and `mode` carries a
STANDARD default like the other Optional+Computed enums in the provider.

Both paths were invisible to the tests, which only ever built models out of
known values and never exercised an omitted attribute.
@ianaya89
ianaya89 force-pushed the feat/clickhouse-clusters-aws branch from e420241 to c1f4b35 Compare August 25, 2026 14:25
Closes the rest of the review findings.

A refresh used to copy every cluster and Keeper the API returned into state,
including ones created outside Terraform. The configuration declares none, so
the next plan proposed removing them and the update deleted them along with
their data. State now only tracks what prior state already managed, the way
custom domains do; the data source keeps reporting everything. Reserved
platform users are dropped for the same reason: the configuration can never
declare them, so keeping them made the applied state differ from the plan on
an attribute that is not Computed.

The scoped read only rejected wholly-unknown list elements, so an unknown
nested `disk` or `users` still reached reflection into a Go struct and failed
as a provider error. It now walks nested objects, lists, sets and maps. When
values are that unsettled the plan-time checks cannot run at all, which used
to happen silently even though the deletion warning is the only notice before
data goes away; the plan now says so.

Also:

- `keeper.name` is optional, so a SWARM cluster running without a Keeper no
  longer has to invent a name the API documents as ignored. It is still
  required, and still checked against `clickhouse_keepers`, whenever the
  Keeper is enabled.
- `password_value_from_secret` requires `password_type`, which the API needs
  whenever a password value is set in either form. The example set neither.
- Comments in the new files are back to one line, per the project convention.
ValidateClickHousePlan walked only the planned `additional_disks`, so a volume
present in state and absent from the plan was never looked at. It still landed
in `AdditionalDisksToDelete`, so dropping or renaming one destroyed the volume
and its data with no plan-time diagnostic at all — clusters and Keepers already
warned, volumes did not.

The three `name` attributes described themselves as "Immutable", which reads as
a promise that the plan rejects a change. It does not: the entry is deleted and
a new, empty one is created. They now say so.
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.

1 participant