Skip to content

feat(local): add hops local listmonk provider bootstrap#51

Merged
patrickleet merged 1 commit into
mainfrom
feat/local-listmonk-bootstrap
May 25, 2026
Merged

feat(local): add hops local listmonk provider bootstrap#51
patrickleet merged 1 commit into
mainfrom
feat/local-listmonk-bootstrap

Conversation

@patrickleet
Copy link
Copy Markdown
Collaborator

@patrickleet patrickleet commented May 25, 2026

Summary

Mirrors hops local zitadel / hops local github. Bootstraps the
hops-ops/provider-listmonk Crossplane provider + a cluster-scoped
ProviderConfig pointing at a Listmonk instance via Basic-Auth
credentials (JSON Secret).

Credential waterfall

  1. Explicit --endpoint / --username / --token
  2. LISTMONK_{ENDPOINT,USERNAME,TOKEN} env vars
  3. Read from the chart-bootstrapped Secret on a source cluster
    (default pat-local/marketing/marketing-listmonk-provider-creds
    with keys username + token — the exact shape produced by
    listmonk-chart v0.2.0's post-install api-user-bootstrap hook).

Endpoint derivation

When --endpoint is not explicitly set:

  • Inferred from the source secret name (<release>-provider-creds
    → release <release> → in-cluster http://<release>.<ns>.svc.cluster.local:9000).

Test plan

  • cargo test --bin hops-cli — 3 new unit tests pass
    (credentials shape, ProviderConfig YAML uses cluster-scoped
    apiVersion, Provider YAML uses v0.0.3 package default)
  • cargo build clean
  • ./target/debug/hops-cli local listmonk --help renders cleanly
  • Smoke-test verified manually on pat-local: hops local listmonk
    → Provider Healthy → ProviderConfig Ready → UserRole MR
    reconciles → DB row created.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Listmonk provider configuration command supporting flexible credential input via CLI flags, environment variables, or Kubernetes Secrets.
  • Breaking Changes

    • The source_context parameter for the Zitadel command is now required and must be explicitly provided (no default value).

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be2a586e-25c9-48f1-af17-e660d89a22f6

📥 Commits

Reviewing files that changed from the base of the PR and between 6e084d0 and 35c4563.

📒 Files selected for processing (3)
  • src/commands/local/listmonk.rs
  • src/commands/local/mod.rs
  • src/commands/local/zitadel.rs

📝 Walkthrough

Walkthrough

This PR adds a new Listmonk provider command with credential resolution, Kubernetes manifest orchestration, and CRD polling. It integrates the command into the local subcommand dispatcher and updates Zitadel to require an explicit source_context argument instead of using a default.

Changes

Listmonk Provider Command

Layer / File(s) Summary
Listmonk CLI interface and orchestration
src/commands/local/listmonk.rs (1-145)
ListmonkArgs struct defines endpoint, username, token, secret, namespace, and provider name fields. Main run function orchestrates conditional workflow: --refresh mode updates only the credentials Secret, otherwise applies Provider → polls ProviderConfig CRD readiness → applies Secret → applies ProviderConfig.
Credential resolution and Kubernetes utilities
src/commands/local/listmonk.rs (147-270)
Resolution functions implement precedence chain (CLI flags → env vars → source Secret) for username, token, endpoint. Utilities read Secret values via kubectl with templating and base64 decoding. Endpoint can be auto-derived from secret name by stripping -provider-creds suffix and constructing service DNS URL. Polling waits up to 60 times (5s intervals) for ProviderConfig CRD availability.
Manifest generation and serialization
src/commands/local/listmonk.rs (272-314)
Builders serialize credentials JSON and generate Provider/Secret/ProviderConfig YAML manifests. Secret embeds credentials JSON under stringData.credentials. Formatting utility provides YAML indentation.
Module integration and tests
src/commands/local/listmonk.rs (315-357), src/commands/local/mod.rs (5, 69-87)
Module declares listmonk submodule, extends LocalCommands enum with Listmonk variant, wires dispatcher. Unit tests validate credentials JSON keys, ProviderConfig/Provider YAML structure, apiVersion/kind/name/namespace/package defaults.

Zitadel Refinement

Layer / File(s) Summary
Zitadel source_context as required argument
src/commands/local/zitadel.rs (34-36)
Removes default value from source_context field in ZitadelArgs; now a required argument with updated help text indicating no platform-name default.

Possibly related PRs

  • hops-ops/hops-cli#49: Introduces the Zitadel local command that this PR refines by making source_context required instead of defaulting to "pat-local".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A Listmonk hops into the fold,
With secrets resolved, both new and old,
Manifests dance through the Kube-API,
CRDs await with patient sigh—
The local commands grow bold!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/local-listmonk-bootstrap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Mirrors `hops local zitadel` / `hops local github`. Bootstraps the
hops-ops/provider-listmonk Crossplane provider + a cluster-scoped
ProviderConfig pointing at a Listmonk instance via Basic-Auth
credentials (JSON Secret).

Credential resolution waterfall:
  1. Explicit --endpoint / --username / --token flags
  2. LISTMONK_{ENDPOINT,USERNAME,TOKEN} env vars
  3. Read from the chart-bootstrapped Secret on a source cluster
     (with keys `username` + `token` — the shape produced by
     listmonk-chart v0.2.0's post-install api-user-bootstrap hook)

Endpoint is derived from the source Secret name when not explicitly
set: `<release>-provider-creds` → in-cluster service
`http://<release>.<source-namespace>.svc.cluster.local:9000`.

Default upjet provider package: ghcr.io/hops-ops/provider-listmonk:v0.0.3.

Also drops the `pat-local` default from `--source-context` on BOTH
this command and `hops local zitadel` — that hardcoded value bakes
the implementer's personal cluster name into a tool meant for
multiple users. Required positional flag now; users explicitly pass
their own source context.

Verified end-to-end on pat-local 2026-05-25:
- Provider install + Healthy
- ProviderConfig applied
- UserRole MR reconciled (Crossplane → upjet → TF provider →
  Listmonk REST API → users / roles table)
- User MR reconciled with cross-resource userRoleIdRef → numeric
  userRoleId (typed-reference resolution works end-to-end)
- AppSettings MR reconciled (no-op write of current values; round-
  trip lossless)
@patrickleet patrickleet force-pushed the feat/local-listmonk-bootstrap branch from 8e7adcd to 35c4563 Compare May 25, 2026 08:50
@patrickleet patrickleet merged commit cfc515e into main May 25, 2026
1 of 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.

1 participant