Skip to content

feat(bootstrap): add bootstrap command for first-time Greenhouse access - #83

Open
onuryilmaz wants to merge 4 commits into
mainfrom
feat/bootstrap-command
Open

onuryilmaz wants to merge 4 commits into
mainfrom
feat/bootstrap-command

Conversation

@onuryilmaz

Copy link
Copy Markdown
Contributor

Summary

Implements cloudctl bootstrap as described in #80, giving operators a single command to bootstrap first-time access to a Greenhouse cluster.

  • --data=<base64-kubeconfig> — decodes a kubeconfig downloaded from the Greenhouse Web UI (standard kubeconfig YAML, base64-encoded). Supports any auth type the UI provides: OIDC auth-provider, exec-plugin, token, or cert. All fields (certificate-authority-data, namespace, full auth-provider config) are preserved verbatim.
  • Individual OIDC flags — --greenhouse-server, --greenhouse-org, --greenhouse-idp-issuer-url, --greenhouse-client-id, --greenhouse-client-secret, --greenhouse-extra-scopes, --greenhouse-ca-data, --greenhouse-namespace. Produces the exact same auth-provider/oidc kubeconfig shape as the Greenhouse UI download, scriptable without a browser.
  • Interactive prompts for context name and --set-current-context on TTY; fully non-interactive when flags are provided.
  • Context rename: all three entries (cluster, user, context) are renamed atomically to the user-chosen name; namespace is preserved.
  • Existing unmanaged entries are never overwritten (idempotent).
  • --dry-run previews without writing.
  • Next-step hint printed on success: cloudctl sync -n <org>.

Test plan

  • TestBuildOIDCKubeconfig_* — unit tests for OIDC kubeconfig construction, including exact match against the real Greenhouse shape
  • TestRenameKubeconfigContext_* — rename of all three entries; multi-context blob only renames current
  • TestMergeBootstrapKubeconfig_* — add/skip/no-overwrite/preserve behaviour
  • TestResolveIncomingKubeconfig_* — both input modes, all missing-flag error paths
  • TestBootstrapCmd_* — 11 end-to-end cobra command tests: blob write, individual flags write, parity between modes, dry-run file unchanged, idempotency, context rename, preserves existing entries, creates file from scratch, JSON output, missing flags error, raw base64
make test   # all cmd and cmd/output tests pass

Closes #80

Adds `cloudctl bootstrap` which merges a Greenhouse kubeconfig into the
user's local kubeconfig so they can reach the Greenhouse API server with
kubectl and run `cloudctl sync`.

Two input modes are supported:

- `--data=<base64-kubeconfig>`: decodes a standard kubeconfig downloaded
  from the Greenhouse Web UI; supports any auth type (OIDC auth-provider,
  exec-plugin, token, cert) and preserves all fields verbatim.

- Individual OIDC flags: `--greenhouse-server`, `--greenhouse-org`,
  `--greenhouse-idp-issuer-url`, `--greenhouse-client-id`,
  `--greenhouse-client-secret`, `--greenhouse-extra-scopes`,
  `--greenhouse-ca-data`, `--greenhouse-namespace`. Produces the same
  auth-provider/oidc kubeconfig shape as the Greenhouse UI download.

Both modes:
- Ask interactively for context name and whether to set it as current
  context (skipped when `--context-name` / `--set-current-context` are
  given or when not on a TTY).
- Rename the context triple (cluster + user + context) to the chosen name.
- Never overwrite existing unmanaged kubeconfig entries.
- Are idempotent: running twice with the same input is safe.
- Support `--dry-run` to preview without writing.
- Print a next-step hint: `cloudctl sync -n <org>`.

Closes #80

Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 11:59
@onuryilmaz
onuryilmaz requested review from a team as code owners September 30, 2026 11:59

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Collision handling and insufficient input validation can produce broken kubeconfigs, and the required token-based mode is missing.

Review effort: Balanced
Findings: 4 High severity · 4 Medium severity · 2 Low severity

Open (10)
What changed in this PR

Adds first-time Greenhouse kubeconfig bootstrapping to the CLI.

Changes:

  • Adds blob and OIDC flag input modes, interactive prompts, merging, and dry-run support.
  • Adds structured bootstrap output and terminal formatting.
  • Adds comprehensive unit and command tests.
File Description
cmd/​bootstrap.go Implements the bootstrap command and kubeconfig merging.
cmd/​bootstrap_test.go Tests construction, merging, and command behavior.
cmd/​output/​types.go Defines structured bootstrap results.
cmd/​output/​plain_printer.go Formats plain-text bootstrap results.
cmd/​output/​interactive_printer.go Formats interactive terminal results.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cmd/bootstrap.go
Comment thread cmd/bootstrap.go
Comment thread cmd/bootstrap.go
Comment thread cmd/bootstrap.go
Comment thread cmd/bootstrap.go
Comment thread cmd/bootstrap.go
Comment thread cmd/output/interactive_printer.go Outdated
Comment thread cmd/output/plain_printer.go Outdated
Comment thread cmd/bootstrap.go Outdated
Comment thread cmd/output/types.go Outdated
…org hint

- fix misleading doc comment in buildOIDCKubeconfig (client-secret is always
  emitted, not omitted when empty)
- fix Added/Skipped JSON/YAML tags from omitempty to omitzero (Go 1.25 idiom)
- add CurrentContextUpdated field to BootstrapResult; printers now show
  "nothing new" only when no entries were added AND current-context was not
  changed
- capture org from the selected context's namespace before renameKubeconfigContext
  so the sync hint is correct when --context-name differs from greenhouse-<org>
- when --kubeconfig is not explicitly set, load through
  clientcmd.NewDefaultClientConfigLoadingRules to honour multi-file KUBECONFIG
- make renameKubeconfigContext safe for shared cluster/authinfo references:
  only delete old keys if no other context still references them
- validate blob context in resolveIncomingKubeconfig: error when current-context
  is missing or ambiguous, or when context references a non-existent cluster/user
- add tests: SharedClusterPreserved rename, DataBlobNoCurrentContext,
  DataBlobMissingClusterRef

Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>

Copilot AI 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.

Comment thread cmd/bootstrap.go
When KUBECONFIG starts with a path separator (e.g. :/second/config),
the first segment is empty and the write target is ambiguous. Return a
clear error matching the behaviour of resolveWriteTarget in sync.go.

Also reset cobra flag Changed state between test runs so that flag.Changed()
is accurate regardless of test ordering.

Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Rename collisions and non-atomic merging can overwrite or combine unrelated kubeconfig entries.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)

Comment thread cmd/bootstrap.go Outdated
… matches

When the incoming context key already equals targetName, the early return
skipped renaming the referenced cluster and authinfo entries. This caused
the three map entries to have inconsistent names after merge.

Separate the "context key is a no-op" case from "entries need renaming"
so cluster and authinfo are always aligned to targetName regardless of
whether the context key itself needed changing.

Signed-off-by: onuryilmaz <onur.yilmaz@sap.com>

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Context renaming and independent map merging can still overwrite incoming target-name entries or create mixed invalid configurations when names collide.

Review effort: Balanced
Findings: None

Resolved since last review (1)

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.

[FEAT cloudctl] - bootstrap procedure

2 participants