Skip to content

Adds named profiles for kinetic CLI configuration#212

Open
JyotinderSingh wants to merge 2 commits intomainfrom
profiles
Open

Adds named profiles for kinetic CLI configuration#212
JyotinderSingh wants to merge 2 commits intomainfrom
profiles

Conversation

@JyotinderSingh
Copy link
Copy Markdown
Collaborator

Summary

Introduces kinetic profile — a command group for managing named infrastructure-target bundles (project, zone, cluster, namespace) so users can switch between configurations without re-exporting KINETIC_* env vars for every shell.

Profiles slot into the existing config precedence chain as a new low-priority layer:

CLI flag  >  KINETIC_* env var  >  active profile  >  built-in default

No existing command bodies or option signatures change. Profiles are additive: if no profile is saved or selected, today's env-var + prompt flow is preserved exactly.

New commands

Command Purpose
kinetic profile create [NAME] Save a new profile. Reads missing fields from KINETIC_* env vars, prompts for anything still unset. First profile created becomes active.
kinetic profile ls List profiles; * marks the active one, with an (override; stored: X) hint when --profile / KINETIC_PROFILE points at a non-current profile.
kinetic profile use NAME Mark a profile as the persistent active one.
kinetic profile show [NAME] Show a profile's settings. Default target respects --profile / KINETIC_PROFILE, falling back to the stored active profile.
kinetic profile rm NAME [--yes] Delete a profile. Clears the active pointer if the removed one was active.

Global flag (root group):

  • --profile NAME / KINETIC_PROFILE=NAME — one-shot override that selects a profile for this invocation without changing the stored active profile. Must appear before the subcommand: kinetic --profile prod up.

kinetic config show now surfaces the resolved active profile and a per-setting source column (profile / env var / default), making precedence discoverable.

Design notes

  • Storage: single JSON file at ~/.kinetic/profiles.json with {"current": ..., "profiles": {...}}. Atomic writes via tempfile + os.replace. Stdlib only (no PyYAML dep added).
  • Wiring: the root cli group callback resolves the active profile once and injects its fields into ctx.default_map for every subcommand. Click's built-in precedence order means CLI flags and KINETIC_* env vars still win — no existing command had to change.
  • profile group carve-out: the resolved profile is stashed on ctx.obj["active_profile"] for every subcommand including profile show/ls, but default_map is not injected into profile create — we don't want a new profile to auto-inherit values from the currently-active one.
  • Out of scope for this PR (explicit non-goals): workspaces, health badges in profile ls, profile validate (use kinetic doctor --profile foo), profile edit, and richer profile fields (accelerator family, output policy). These can layer on cleanly.

User Flow

$ kinetic profile ls
No profiles saved. Create one with 'kinetic profile create <name>'.

$ kinetic profile create test-profile
╭─ Create kinetic profile ─╮
GCP project ID: kinetic-team
GCP zone [us-central1-a]:
GKE cluster name [kinetic-cluster]:
Kubernetes namespace [default]:
Saved profile 'test-profile'.
Profile 'test-profile' is now active.

$ kinetic profile create test-profile-2
╭─ Create kinetic profile ─╮
GCP project ID: kinetic-team
GCP zone [us-central1-a]: us-central1-b
GKE cluster name [kinetic-cluster]: test-cluster
Kubernetes namespace [default]:
Saved profile 'test-profile-2'.
Run 'kinetic profile use test-profile-2' to activate it.

$ kinetic profile ls
┏━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃   ┃ NAME           ┃ PROJECT      ┃ ZONE          ┃ CLUSTER         ┃ NAMESPACE ┃
┡━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ * │ test-profile   │ kinetic-team │ us-central1-a │ kinetic-cluster │ default   │
│   │ test-profile-2 │ kinetic-team │ us-central1-b │ test-cluster    │ default   │
└───┴────────────────┴──────────────┴───────────────┴─────────────────┴───────────┘
Active profile: test-profile

$ kinetic profile use test-profile-2
Active profile: test-profile-2
   Profile: test-profile-2
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Setting   ┃ Value         ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ Project   │ kinetic-team  │
│ Zone      │ us-central1-b │
│ Cluster   │ test-cluster  │
│ Namespace │ default       │
└───────────┴───────────────┘

$ kinetic profile use test-profile-3
Error: Profile 'test-profile-3' does not exist.

$ kinetic profile rm test-profile-2
Delete profile 'test-profile-2' (kinetic-team/us-central1-b/test-cluster)? [y/N]: y
Removed profile 'test-profile-2'.

$ kinetic profile show
No active profile. Pass a NAME or run 'kinetic profile use <name>'.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a named profiles feature to the Kinetic CLI, allowing users to manage and switch between different infrastructure configurations (project, zone, cluster, and namespace) without manually setting environment variables. It adds a new profile command group for CRUD operations, implements persistent storage in ~/.kinetic/profiles.json, and updates the root CLI to inject profile settings as defaults for all relevant subcommands. Feedback was provided regarding code organization, specifically suggesting moving a local import to the top-level module imports.

Comment on lines +8 to +17
from kinetic.cli.profiles import (
Profile,
ProfileError,
get_profile,
list_profiles,
remove_profile,
set_current,
upsert_profile,
validate_name,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add load_store to the top-level imports from kinetic.cli.profiles to avoid the need for a local import inside profile_create.

Suggested change
from kinetic.cli.profiles import (
Profile,
ProfileError,
get_profile,
list_profiles,
remove_profile,
set_current,
upsert_profile,
validate_name,
)
from kinetic.cli.profiles import (
Profile,
ProfileError,
get_profile,
list_profiles,
load_store,
remove_profile,
set_current,
upsert_profile,
validate_name,
)

Copy link
Copy Markdown
Member

@jeffcarp jeffcarp May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the auto-suggestion above (if valid)

Copy link
Copy Markdown
Member

@jeffcarp jeffcarp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This should provide the foundation for more easily searching for spot capacity across clusters. Left some nits/suggestions

console.print(" export KINETIC_CLUSTER=kinetic-cluster")
console.print(" export KINETIC_NAMESPACE=my-namespace")
console.print(
"Precedence: CLI flag > KINETIC_* env var > active profile > default."
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great to mention here, do you want to link to the docs where we elaborate on this behavior?
https://kinetic.readthedocs.io/en/latest/configuration.html#precedence

Comment on lines +8 to +17
from kinetic.cli.profiles import (
Profile,
ProfileError,
get_profile,
list_profiles,
remove_profile,
set_current,
upsert_profile,
validate_name,
)
Copy link
Copy Markdown
Member

@jeffcarp jeffcarp May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the auto-suggestion above (if valid)


@click.group()
def profile():
"""Manage named kinetic profiles (project/zone/cluster/namespace bundles)."""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need a ... or pass here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting it seems to be a feature of @click.group(). Looks weird but as long as it works 👍

Comment thread kinetic/cli/profiles.py
CLI flag > KINETIC_* env var > active profile field > built-in default
"""

from __future__ import annotations
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this in the project

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.

2 participants