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
1 change: 1 addition & 0 deletions cli/cmd/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Examples:
RunE: func(cmd *cobra.Command, args []string) error {
return deleteByID(cmd, args,
"This will permanently delete definition %s. Continue? (y/n): ",
passthroughID,
func(c *client.Client, id string) error { return c.DeleteDefinition(id) },
"Deleted definition %s",
)
Expand Down
131 changes: 66 additions & 65 deletions cli/cmd/override.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,23 @@ var overrideCmd = &cobra.Command{
// --- Value Overrides ---

var overrideListCmd = &cobra.Command{
Use: "list <instance-id>",
Use: "list <name|id>",
Short: "List value overrides for a stack instance",
Long: `List all value overrides for a stack instance.

Examples:
stackctl override list 42
stackctl override list 42 -o json
stackctl override list 42 -q`,
stackctl override list my-stack
stackctl override list my-stack -o json
stackctl override list my-stack -q`,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
instanceID, err := parseID(args[0])
c, err := newClient()
if err != nil {
return err
}

c, err := newClient()
instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}
Expand Down Expand Up @@ -88,25 +88,21 @@ Examples:
}

var overrideSetCmd = &cobra.Command{
Use: "set <instance-id> <chart-id>",
Use: "set <name|id> <chart-id>",
Short: "Set value overrides for a chart",
Long: `Set value overrides for a specific chart in a stack instance.

Provide values via --file (JSON or YAML file) or --set key=value (repeatable).
At least one of --file or --set is required.

Examples:
stackctl override set 42 1 --file values.json
stackctl override set 42 1 --file values.yaml
stackctl override set 42 1 --set replicas=3 --set image.tag=v2
stackctl override set 42 1 --file values.json --set replicas=5`,
stackctl override set my-stack 1 --file values.json
stackctl override set my-stack 1 --file values.yaml
stackctl override set my-stack 1 --set replicas=3 --set image.tag=v2
stackctl override set my-stack 1 --file values.json --set replicas=5`,
Args: cobra.ExactArgs(2),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
instanceID, err := parseID(args[0])
if err != nil {
return err
}
chartID, err := parseID(args[1])
if err != nil {
return err
Expand All @@ -132,9 +128,7 @@ Examples:
if err != nil {
return fmt.Errorf("reading file %s: %w", file, err)
}
// Try JSON first, then YAML
if err := json.Unmarshal(data, &values); err != nil {
// Try YAML
if yamlErr := yaml.Unmarshal(data, &values); yamlErr != nil {
return fmt.Errorf("invalid JSON/YAML in file %s (json: %v): %w", file, err, yamlErr)
}
Expand All @@ -154,6 +148,11 @@ Examples:
return err
}

instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}

override, err := c.SetValueOverride(instanceID, chartID, &types.SetValueOverrideRequest{
Values: values,
})
Expand All @@ -179,16 +178,16 @@ Examples:
}

var overrideDeleteCmd = &cobra.Command{
Use: "delete <instance-id> <chart-id>",
Use: "delete <name|id> <chart-id>",
Short: "Delete a value override",
Long: `Delete a value override for a specific chart in a stack instance.

This is a destructive operation. You will be prompted for confirmation
unless --yes is specified.

Examples:
stackctl override delete 42 1
stackctl override delete 42 1 --yes`,
stackctl override delete my-stack 1
stackctl override delete my-stack 1 --yes`,
Args: cobra.ExactArgs(2),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -207,22 +206,22 @@ var overrideBranchCmd = &cobra.Command{
}

var overrideBranchListCmd = &cobra.Command{
Use: "list <instance-id>",
Use: "list <name|id>",
Short: "List branch overrides for a stack instance",
Long: `List all branch overrides for a stack instance.

Examples:
stackctl override branch list 42
stackctl override branch list 42 -o json`,
stackctl override branch list my-stack
stackctl override branch list my-stack -o json`,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
instanceID, err := parseID(args[0])
c, err := newClient()
if err != nil {
return err
}

c, err := newClient()
instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}
Expand Down Expand Up @@ -261,20 +260,16 @@ Examples:
}

var overrideBranchSetCmd = &cobra.Command{
Use: "set <instance-id> <chart-id> <branch>",
Use: "set <name|id> <chart-id> <branch>",
Short: "Set a branch override for a chart",
Long: `Set a branch override for a specific chart in a stack instance.

Examples:
stackctl override branch set 42 1 feature/my-branch
stackctl override branch set 42 1 main -o json`,
stackctl override branch set my-stack 1 feature/my-branch
stackctl override branch set my-stack 1 main -o json`,
Args: cobra.ExactArgs(3),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
instanceID, err := parseID(args[0])
if err != nil {
return err
}
chartID, err := parseID(args[1])
if err != nil {
return err
Expand All @@ -286,6 +281,11 @@ Examples:
return err
}

instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}

override, err := c.SetBranchOverride(instanceID, chartID, &types.SetBranchOverrideRequest{
Branch: branch,
})
Expand All @@ -311,16 +311,16 @@ Examples:
}

var overrideBranchDeleteCmd = &cobra.Command{
Use: "delete <instance-id> <chart-id>",
Use: "delete <name|id> <chart-id>",
Short: "Delete a branch override",
Long: `Delete a branch override for a specific chart in a stack instance.

This is a destructive operation. You will be prompted for confirmation
unless --yes is specified.

Examples:
stackctl override branch delete 42 1
stackctl override branch delete 42 1 --yes`,
stackctl override branch delete my-stack 1
stackctl override branch delete my-stack 1 --yes`,
Args: cobra.ExactArgs(2),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -339,22 +339,22 @@ var overrideQuotaCmd = &cobra.Command{
}

var overrideQuotaGetCmd = &cobra.Command{
Use: "get <instance-id>",
Use: "get <name|id>",
Short: "Get quota override for a stack instance",
Long: `Get the resource quota override for a stack instance.

Examples:
stackctl override quota get 42
stackctl override quota get 42 -o json`,
stackctl override quota get my-stack
stackctl override quota get my-stack -o json`,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
instanceID, err := parseID(args[0])
c, err := newClient()
if err != nil {
return err
}

c, err := newClient()
instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}
Expand Down Expand Up @@ -388,24 +388,19 @@ Examples:
}

var overrideQuotaSetCmd = &cobra.Command{
Use: "set <instance-id>",
Use: "set <name|id>",
Short: "Set quota override for a stack instance",
Long: `Set resource quota overrides for a stack instance.

At least one of the quota flags must be specified.

Examples:
stackctl override quota set 42 --cpu-request 100m --cpu-limit 500m
stackctl override quota set 42 --memory-request 128Mi --memory-limit 512Mi
stackctl override quota set 42 --cpu-request 200m --memory-limit 1Gi`,
stackctl override quota set my-stack --cpu-request 100m --cpu-limit 500m
stackctl override quota set my-stack --memory-request 128Mi --memory-limit 512Mi
stackctl override quota set my-stack --cpu-request 200m --memory-limit 1Gi`,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
instanceID, err := parseID(args[0])
if err != nil {
return err
}

cpuReq, _ := cmd.Flags().GetString("cpu-request")
cpuLim, _ := cmd.Flags().GetString("cpu-limit")
memReq, _ := cmd.Flags().GetString("memory-request")
Expand All @@ -420,6 +415,11 @@ Examples:
return err
}

instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}

quota, err := c.SetQuotaOverride(instanceID, &types.SetQuotaOverrideRequest{
CPURequest: cpuReq,
CPULimit: cpuLim,
Expand Down Expand Up @@ -448,20 +448,25 @@ Examples:
}

var overrideQuotaDeleteCmd = &cobra.Command{
Use: "delete <instance-id>",
Use: "delete <name|id>",
Short: "Delete quota override for a stack instance",
Long: `Delete the resource quota override for a stack instance.

This is a destructive operation. You will be prompted for confirmation
unless --yes is specified.

Examples:
stackctl override quota delete 42
stackctl override quota delete 42 --yes`,
stackctl override quota delete my-stack
stackctl override quota delete my-stack --yes`,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
instanceID, err := parseID(args[0])
c, err := newClient()
if err != nil {
return err
}

instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}
Expand All @@ -475,11 +480,6 @@ Examples:
return nil
}

c, err := newClient()
if err != nil {
return err
}

if err := c.DeleteQuotaOverride(instanceID); err != nil {
return err
}
Expand All @@ -495,11 +495,17 @@ Examples:
}

func deleteChartOverride(cmd *cobra.Command, args []string, kind string, deleteFn func(*client.Client, string, string) error) error {
instanceID, err := parseID(args[0])
chartID, err := parseID(args[1])
if err != nil {
return err
}
chartID, err := parseID(args[1])

c, err := newClient()
if err != nil {
return err
}

instanceID, err := resolveStackID(c, args[0])
if err != nil {
return err
}
Expand All @@ -513,11 +519,6 @@ func deleteChartOverride(cmd *cobra.Command, args []string, kind string, deleteF
return nil
}

c, err := newClient()
if err != nil {
return err
}

if err := deleteFn(c, instanceID, chartID); err != nil {
return err
}
Expand Down
54 changes: 54 additions & 0 deletions cli/cmd/resolve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cmd

import (
"fmt"
"regexp"
"strconv"
"strings"

"github.com/omattsson/stackctl/cli/pkg/client"
)

var uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)

func looksLikeID(s string) bool {
if uuidRegex.MatchString(s) {
return true
}
if _, err := strconv.Atoi(s); err == nil {
return true
}
return false
}

func resolveStackID(c *client.Client, nameOrID string) (string, error) {
nameOrID = strings.TrimSpace(nameOrID)
if nameOrID == "" {
return "", fmt.Errorf("stack name or ID must not be empty")
}

if looksLikeID(nameOrID) {
return nameOrID, nil
}

resp, err := c.ListStacks(map[string]string{"name": nameOrID})
if err != nil {
return "", fmt.Errorf("resolving stack name %q: %w", nameOrID, err)
}

switch len(resp.Data) {
case 0:
return "", fmt.Errorf("no stack found with name %q", nameOrID)
case 1:
if !strings.EqualFold(resp.Data[0].Name, nameOrID) {
return "", fmt.Errorf("no stack found with name %q", nameOrID)
}
return resp.Data[0].ID, nil
default:
msg := fmt.Sprintf("multiple stacks match name %q — use the ID instead:\n", nameOrID)
for _, s := range resp.Data {
msg += fmt.Sprintf(" %s (owner: %s, status: %s)\n", s.ID, s.Owner, s.Status)
}
return "", fmt.Errorf("%s", msg)
}
}
Loading
Loading