Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/enhancement-vks-p8bowfym.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "enhancement",
"category": "vks",
"description": "Add --dry-run to the remaining mutating VKS commands (config-auto-healing, config-auto-upgrade, update-nodegroup-metadata, upgrade-nodegroup-version, generate-kubeconfig); it previews the request payload and exits without calling the API (works offline)"
}
17 changes: 12 additions & 5 deletions go/cmd/vks/auto_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func init() {
f.String("cluster-id", "", "Cluster ID (required)")
f.String("weekdays", "", "Days of the week, e.g. Mon,Wed,Fri (required)")
f.String("time", "", "Time of day in 24h format HH:mm, e.g. 03:00 (required)")
f.Bool("dry-run", false, "Preview the auto-upgrade config without executing")
setAutoUpgradeConfigCmd.MarkFlagRequired("cluster-id")
setAutoUpgradeConfigCmd.MarkFlagRequired("weekdays")
setAutoUpgradeConfigCmd.MarkFlagRequired("time")
Expand All @@ -45,21 +46,27 @@ func runSetAutoUpgradeConfig(cmd *cobra.Command, args []string) error {
clusterID, _ := cmd.Flags().GetString("cluster-id")
weekdays, _ := cmd.Flags().GetString("weekdays")
timeVal, _ := cmd.Flags().GetString("time")
dryRun, _ := cmd.Flags().GetBool("dry-run")

if err := validator.ValidateID(clusterID, "cluster-id"); err != nil {
return err
}

apiClient, err := createClient(cmd)
if err != nil {
return err
}

body := map[string]interface{}{
"weekdays": weekdays,
"time": timeVal,
}

if dryRun {
cli.PrintDryRun("configure", fmt.Sprintf("auto-upgrade for cluster %s", clusterID), body)
return nil
}

apiClient, err := createClient(cmd)
if err != nil {
return err
}

result, err := apiClient.Put(
fmt.Sprintf("/v1/clusters/%s/auto-upgrade-config", clusterID), body,
)
Expand Down
8 changes: 8 additions & 0 deletions go/cmd/vks/config_auto_healing.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/spf13/cobra"
"github.com/vngcloud/greennode-cli/internal/cli"
"github.com/vngcloud/greennode-cli/internal/validator"
)

Expand All @@ -21,6 +22,7 @@ func init() {
f.String("max-unhealthy", "", "Max unhealthy nodes, e.g. \"30%\"")
f.String("unhealthy-range", "", "Unhealthy node count range as \"[min-max]\", e.g. \"[2-5]\"")
f.Int("timeout-unhealthy", 0, "Unhealthy timeout in seconds")
f.Bool("dry-run", false, "Preview the auto-healing config without executing")

configAutoHealingCmd.MarkFlagRequired("cluster-id")
configAutoHealingCmd.MarkFlagRequired("enable-auto-healing")
Expand All @@ -46,6 +48,7 @@ func runConfigAutoHealing(cmd *cobra.Command, args []string) error {
maxUnhealthy, _ := cmd.Flags().GetString("max-unhealthy")
unhealthyRange, _ := cmd.Flags().GetString("unhealthy-range")
timeoutUnhealthy, _ := cmd.Flags().GetInt("timeout-unhealthy")
dryRun, _ := cmd.Flags().GetBool("dry-run")

if err := validator.ValidateID(clusterID, "cluster-id"); err != nil {
return err
Expand All @@ -58,6 +61,11 @@ func runConfigAutoHealing(cmd *cobra.Command, args []string) error {
}
body := buildAutoHealingBody(enable, maxUnhealthy, unhealthyRange, timeoutUnhealthy, changed)

if dryRun {
cli.PrintDryRun("configure", fmt.Sprintf("auto-healing for cluster %s", clusterID), body)
return nil
}

apiClient, err := createClient(cmd)
if err != nil {
return err
Expand Down
8 changes: 8 additions & 0 deletions go/cmd/vks/generate_kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/spf13/cobra"
"github.com/vngcloud/greennode-cli/internal/cli"
"github.com/vngcloud/greennode-cli/internal/validator"
)

Expand All @@ -20,20 +21,27 @@ func init() {
f := generateKubeconfigCmd.Flags()
f.String("cluster-id", "", "Cluster ID (required)")
f.Int("expiration-days", 30, "Number of days until the kubeconfig expires")
f.Bool("dry-run", false, "Preview without requesting generation")

generateKubeconfigCmd.MarkFlagRequired("cluster-id")
}

func runGenerateKubeconfig(cmd *cobra.Command, args []string) error {
clusterID, _ := cmd.Flags().GetString("cluster-id")
expirationDays, _ := cmd.Flags().GetInt("expiration-days")
dryRun, _ := cmd.Flags().GetBool("dry-run")

if err := validator.ValidateID(clusterID, "cluster-id"); err != nil {
return err
}

body := map[string]interface{}{"expirationDays": expirationDays}

if dryRun {
cli.PrintDryRun("generate", fmt.Sprintf("kubeconfig for cluster %s", clusterID), body)
return nil
}

apiClient, err := createClient(cmd)
if err != nil {
return err
Expand Down
8 changes: 8 additions & 0 deletions go/cmd/vks/update_nodegroup_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/spf13/cobra"
"github.com/vngcloud/greennode-cli/internal/cli"
"github.com/vngcloud/greennode-cli/internal/validator"
)

Expand All @@ -21,6 +22,7 @@ func init() {
f.String("labels", "", "Node labels as key=value pairs (comma-separated)")
f.String("tags", "", "Tags as key=value pairs (comma-separated)")
f.String("taints", "", "Node taints as key=value:effect (comma-separated)")
f.Bool("dry-run", false, "Preview the metadata update without executing")

updateNodegroupMetadataCmd.MarkFlagRequired("cluster-id")
updateNodegroupMetadataCmd.MarkFlagRequired("nodegroup-id")
Expand All @@ -46,6 +48,7 @@ func runUpdateNodegroupMetadata(cmd *cobra.Command, args []string) error {
labelsStr, _ := cmd.Flags().GetString("labels")
tagsStr, _ := cmd.Flags().GetString("tags")
taintsStr, _ := cmd.Flags().GetString("taints")
dryRun, _ := cmd.Flags().GetBool("dry-run")

if err := validator.ValidateID(clusterID, "cluster-id"); err != nil {
return err
Expand All @@ -64,6 +67,11 @@ func runUpdateNodegroupMetadata(cmd *cobra.Command, args []string) error {
}
body := buildMetadataBody(labelsStr, tagsStr, taintsStr, changed)

if dryRun {
cli.PrintDryRun("update", fmt.Sprintf("metadata for node group %s", nodegroupID), body)
return nil
}

apiClient, err := createClient(cmd)
if err != nil {
return err
Expand Down
8 changes: 8 additions & 0 deletions go/cmd/vks/upgrade_nodegroup_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/spf13/cobra"
"github.com/vngcloud/greennode-cli/internal/cli"
"github.com/vngcloud/greennode-cli/internal/validator"
)

Expand All @@ -19,6 +20,7 @@ func init() {
f.String("cluster-id", "", "Cluster ID (required)")
f.String("nodegroup-id", "", "Node group ID (required)")
f.String("k8s-version", "", "Target Kubernetes version (required)")
f.Bool("dry-run", false, "Preview the upgrade without executing")

upgradeNodegroupVersionCmd.MarkFlagRequired("cluster-id")
upgradeNodegroupVersionCmd.MarkFlagRequired("nodegroup-id")
Expand All @@ -33,6 +35,7 @@ func runUpgradeNodegroupVersion(cmd *cobra.Command, args []string) error {
clusterID, _ := cmd.Flags().GetString("cluster-id")
nodegroupID, _ := cmd.Flags().GetString("nodegroup-id")
k8sVersion, _ := cmd.Flags().GetString("k8s-version")
dryRun, _ := cmd.Flags().GetBool("dry-run")

if err := validator.ValidateID(clusterID, "cluster-id"); err != nil {
return err
Expand All @@ -43,6 +46,11 @@ func runUpgradeNodegroupVersion(cmd *cobra.Command, args []string) error {

body := buildUpgradeNodegroupBody(k8sVersion)

if dryRun {
cli.PrintDryRun("upgrade", fmt.Sprintf("node group %s", nodegroupID), body)
return nil
}

apiClient, err := createClient(cmd)
if err != nil {
return err
Expand Down
21 changes: 21 additions & 0 deletions go/internal/cli/confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
"sort"
"strings"
)

Expand All @@ -14,6 +15,26 @@ func DryRunNotice(verb string) {
fmt.Printf("\nRun without --dry-run to %s.\n", verb)
}

// PrintDryRun prints a consistent --dry-run preview for a mutating request: a
// header, the target being changed, the request body (keys sorted for stable
// output), and the standard footer. verb is the action (e.g. "update",
// "upgrade", "configure").
func PrintDryRun(verb, target string, body map[string]interface{}) {
fmt.Println("=== DRY RUN ===")
if target != "" {
fmt.Printf("Would %s %s:\n", verb, target)
}
keys := make([]string, 0, len(body))
for k := range body {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Printf(" %s: %v\n", k, body[k])
}
DryRunNotice(verb)
}

// Confirm asks the user to confirm a destructive action and reports whether to
// proceed. It returns true immediately when force is true. The prompt is shown
// as "<prompt> [y/N]: "; only "y" or "yes" (case-insensitive) proceeds.
Expand Down
Loading