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
55 changes: 47 additions & 8 deletions cmd/dbaas/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (

"github.com/spf13/cobra"

"github.com/thalassa-cloud/cli/internal/completion"
"github.com/thalassa-cloud/cli/internal/formattime"
iaasutil "github.com/thalassa-cloud/cli/internal/iaas"
"github.com/thalassa-cloud/cli/internal/table"
"github.com/thalassa-cloud/cli/internal/thalassaclient"
"github.com/thalassa-cloud/client-go/dbaas"
"github.com/thalassa-cloud/client-go/iaas"
)

var (
Expand All @@ -19,8 +22,10 @@ var (
createClusterEngine string
createClusterEngineVersion string
createClusterInstanceType string
createClusterVpc string
createClusterSubnet string
createClusterStorage int
createclusterVolumeType string
createClusterReplicas int
createClusterLabels []string
createClusterAnnotations []string
Expand Down Expand Up @@ -59,14 +64,38 @@ var createCmd = &cobra.Command{
return fmt.Errorf("replicas must be 0 or greater")
}

// Resolve subnet if provided
var subnetIdentity string
if createClusterSubnet != "" {
subnet, err := client.IaaS().GetSubnet(cmd.Context(), createClusterSubnet)
// Resolve volume type
volumeTypes, err := client.IaaS().ListVolumeTypes(cmd.Context(), &iaas.ListVolumeTypesRequest{})
if err != nil {
return fmt.Errorf("failed to get volume type: %w", err)
}
volumeType, err := iaasutil.FindVolumeTypeByIdentitySlugOrNameWithError(volumeTypes, createclusterVolumeType)
if err != nil {
return err
}

createclusterVolumeType = volumeType.Identity

// Resolve subnet (required)
if createClusterSubnet == "" {
return fmt.Errorf("subnet is required")
}
subnet, err := iaasutil.GetSubnetByIdentitySlugOrName(cmd.Context(), client.IaaS(), createClusterSubnet)
if err != nil {
return fmt.Errorf("failed to get subnet: %w", err)
}
subnetIdentity := subnet.Identity

// Resolve and validate VPC if provided
if createClusterVpc != "" {
vpc, err := iaasutil.GetVPCByIdentitySlugOrName(cmd.Context(), client.IaaS(), createClusterVpc)
if err != nil {
return fmt.Errorf("failed to get subnet: %w", err)
return fmt.Errorf("failed to get vpc: %w", err)
}
// Validate that the subnet belongs to the specified VPC
if subnet.Vpc != nil && subnet.Vpc.Identity != vpc.Identity {
return fmt.Errorf("subnet %s does not belong to VPC %s", subnetIdentity, vpc.Identity)
}
subnetIdentity = subnet.Identity
}

// Parse labels from key=value format
Expand All @@ -93,6 +122,7 @@ var createCmd = &cobra.Command{
Engine: dbaas.DbClusterDatabaseEngine(createClusterEngine),
EngineVersion: createClusterEngineVersion,
DatabaseInstanceTypeIdentity: createClusterInstanceType,
VolumeTypeClassIdentity: createclusterVolumeType,
SubnetIdentity: subnetIdentity,
AllocatedStorage: uint64(createClusterStorage),
Labels: labels,
Expand Down Expand Up @@ -182,18 +212,27 @@ func init() {
createCmd.Flags().StringVar(&createClusterEngine, "engine", "", "Database engine (e.g., postgres) (required)")
createCmd.Flags().StringVar(&createClusterEngineVersion, "engine-version", "", "Engine version (required)")
createCmd.Flags().StringVar(&createClusterInstanceType, "instance-type", "", "Instance type (required)")
createCmd.Flags().StringVar(&createClusterSubnet, "subnet", "", "Subnet identity")

createCmd.Flags().StringVar(&createClusterVpc, "vpc", "", "VPC identity, slug, or name")
createCmd.Flags().StringVar(&createClusterSubnet, "subnet", "", "Subnet identity, slug, or name (required)")
createCmd.Flags().StringVar(&createclusterVolumeType, "volume-type", "block", "Volume type")
createCmd.Flags().IntVar(&createClusterStorage, "storage", 0, "Storage size in GB (required)")
createCmd.Flags().IntVar(&createClusterReplicas, "replicas", 0, "Number of replicas (default: 0)")
createCmd.Flags().StringSliceVar(&createClusterLabels, "labels", []string{}, "Labels in key=value format (can be specified multiple times)")
createCmd.Flags().StringSliceVar(&createClusterAnnotations, "annotations", []string{}, "Annotations in key=value format (can be specified multiple times)")
createCmd.Flags().BoolVar(&createClusterDeleteProtection, "delete-protection", false, "Enable delete protection")
createCmd.Flags().BoolVar(&createClusterWait, "wait", false, "Wait for the database cluster to be available before returning")

// Register completions
createCmd.RegisterFlagCompletionFunc("vpc", completion.CompleteVPCID)
createCmd.RegisterFlagCompletionFunc("subnet", completion.CompleteSubnetEnhanced)
createCmd.RegisterFlagCompletionFunc("engine-version", completion.CompleteDbEngineVersion)

_ = createCmd.MarkFlagRequired("name")
_ = createCmd.MarkFlagRequired("engine")
_ = createCmd.MarkFlagRequired("engine-version")
_ = createCmd.MarkFlagRequired("instance-type")
_ = createCmd.MarkFlagRequired("vpc")
_ = createCmd.MarkFlagRequired("subnet")
_ = createCmd.MarkFlagRequired("volume-type")
_ = createCmd.MarkFlagRequired("storage")
}
4 changes: 2 additions & 2 deletions cmd/dbaas/instancetypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ var instanceTypesCmd = &cobra.Command{
body = append(body, []string{
instanceType.Identity,
instanceType.Name,
instanceType.CategorySlug,
fmt.Sprintf("%d vCPU", instanceType.Cpus),
fmt.Sprintf("%d GB", instanceType.Memory),
instanceType.Description,
instanceType.Architecture,
})
}
Expand All @@ -56,7 +56,7 @@ var instanceTypesCmd = &cobra.Command{
if noHeader {
table.Print(nil, body)
} else {
table.Print([]string{"ID", "Name", "vCPU", "Memory", "Description", "Architecture"}, body)
table.Print([]string{"ID", "Name", "Category", "vCPU", "Memory", "Architecture"}, body)
}
return nil
},
Expand Down
54 changes: 53 additions & 1 deletion cmd/dbaas/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (

"github.com/spf13/cobra"

"github.com/thalassa-cloud/cli/internal/completion"
"github.com/thalassa-cloud/cli/internal/formattime"
iaasutil "github.com/thalassa-cloud/cli/internal/iaas"
"github.com/thalassa-cloud/cli/internal/labels"
"github.com/thalassa-cloud/cli/internal/table"
"github.com/thalassa-cloud/cli/internal/thalassaclient"
Expand All @@ -23,6 +25,9 @@ var (
showExactTime bool
showLabels bool
listLabelSelector string
listEngineFilter string
listVpcFilter string
listSubnetFilter string
)

// listCmd represents the list command
Expand All @@ -40,6 +45,40 @@ var listCmd = &cobra.Command{
}

f := []filters.Filter{}

// Resolve VPC filter if provided
if listVpcFilter != "" {
vpc, err := iaasutil.GetVPCByIdentitySlugOrName(cmd.Context(), client.IaaS(), listVpcFilter)
if err != nil {
return fmt.Errorf("failed to get vpc: %w", err)
}
f = append(f, &filters.FilterKeyValue{
Key: "vpc",
Value: vpc.Identity,
})
}

// Resolve subnet filter if provided
if listSubnetFilter != "" {
subnet, err := iaasutil.GetSubnetByIdentitySlugOrName(cmd.Context(), client.IaaS(), listSubnetFilter)
if err != nil {
return fmt.Errorf("failed to get subnet: %w", err)
}
f = append(f, &filters.FilterKeyValue{
Key: "subnet",
Value: subnet.Identity,
})
}

// Add engine filter if provided
if listEngineFilter != "" {
f = append(f, &filters.FilterKeyValue{
Key: "engine",
Value: listEngineFilter,
})
}

// Add label selector filter if provided
if listLabelSelector != "" {
f = append(f, &filters.LabelFilter{
MatchLabels: labels.ParseLabelSelector(listLabelSelector),
Expand All @@ -60,6 +99,11 @@ var listCmd = &cobra.Command{
vpcName = cluster.Vpc.Name
}

subnetName := ""
if cluster.Subnet != nil {
subnetName = cluster.Subnet.Name
}

engineVersion := cluster.EngineVersion
if cluster.DatabaseEngineVersion != nil {
engineVersion = cluster.DatabaseEngineVersion.EngineVersion
Expand All @@ -74,6 +118,7 @@ var listCmd = &cobra.Command{
cluster.Identity,
cluster.Name,
vpcName,
subnetName,
string(cluster.Engine),
engineVersion,
instanceType,
Expand Down Expand Up @@ -105,7 +150,7 @@ var listCmd = &cobra.Command{
if noHeader {
table.Print(nil, body)
} else {
headers := []string{"ID", "Name", "VPC", "Engine", "Version", "Instance Type", "Replicas", "Storage", "Status", "Age"}
headers := []string{"ID", "Name", "VPC", "Subnet", "Engine", "Version", "Instance Type", "Replicas", "Storage", "Status", "Age"}
if showLabels {
headers = append(headers, "Labels")
}
Expand All @@ -121,4 +166,11 @@ func init() {
listCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show exact time instead of relative time")
listCmd.Flags().BoolVar(&showLabels, "show-labels", false, "Show labels")
listCmd.Flags().StringVarP(&listLabelSelector, "selector", "l", "", "Label selector to filter clusters (format: key1=value1,key2=value2)")
listCmd.Flags().StringVar(&listEngineFilter, "engine", "", "Filter by database engine (e.g., postgres)")
listCmd.Flags().StringVar(&listVpcFilter, "vpc", "", "Filter by VPC identity, slug, or name")
listCmd.Flags().StringVar(&listSubnetFilter, "subnet", "", "Filter by subnet identity, slug, or name")

// Register completions
listCmd.RegisterFlagCompletionFunc("vpc", completion.CompleteVPCID)
listCmd.RegisterFlagCompletionFunc("subnet", completion.CompleteSubnetEnhanced)
}
3 changes: 3 additions & 0 deletions cmd/dbaas/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,7 @@ func init() {
updateCmd.Flags().StringSliceVar(&updateClusterLabels, "labels", []string{}, "Labels in key=value format (can be specified multiple times)")
updateCmd.Flags().StringSliceVar(&updateClusterAnnotations, "annotations", []string{}, "Annotations in key=value format (can be specified multiple times)")
updateCmd.Flags().BoolVar(&updateClusterDeleteProtection, "delete-protection", false, "Enable or disable delete protection")

// Register completions
updateCmd.RegisterFlagCompletionFunc("instance-type", completion.CompleteDbInstanceType)
}
2 changes: 1 addition & 1 deletion cmd/iaas/compute/machines/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var deleteCmd = &cobra.Command{
Long: "Delete machine(s) by identity or label selector. This command will delete the machine(s) and all the services associated with it.",
Example: "tcloud compute machines delete vm-123\ntcloud compute machines delete vm-123 vm-456 --wait\ntcloud compute machines delete --selector environment=test --force",
Aliases: []string{"d", "del", "remove"},
Args: cobra.MinimumNArgs(0),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 && labelSelector == "" {
// Try interactive selection if no args and no selector
Expand Down
35 changes: 32 additions & 3 deletions cmd/iaas/compute/machines/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/spf13/cobra"

"github.com/thalassa-cloud/cli/internal/completion"
"github.com/thalassa-cloud/cli/internal/formattime"
"github.com/thalassa-cloud/cli/internal/labels"
"github.com/thalassa-cloud/cli/internal/table"
Expand All @@ -22,10 +23,13 @@ const NoHeaderKey = "no-header"
var noHeader bool

var (
showExactTime bool
showLabels bool
showExactTime bool
showLabels bool
listLabelSelector string
outputFormat string
outputFormat string
listRegionFilter string
listVpcFilter string
listStatusFilter string
)

// getCmd represents the get command
Expand All @@ -47,6 +51,24 @@ var getCmd = &cobra.Command{
MatchLabels: labels.ParseLabelSelector(listLabelSelector),
})
}
if listRegionFilter != "" {
f = append(f, &filters.FilterKeyValue{
Key: "region",
Value: listRegionFilter,
})
}
if listVpcFilter != "" {
f = append(f, &filters.FilterKeyValue{
Key: "vpc",
Value: listVpcFilter,
})
}
if listStatusFilter != "" {
f = append(f, &filters.FilterKeyValue{
Key: "status",
Value: listStatusFilter,
})
}

machines, err := client.IaaS().ListMachines(cmd.Context(), &iaas.ListMachinesRequest{
Filters: f,
Expand Down Expand Up @@ -149,4 +171,11 @@ func init() {
getCmd.Flags().BoolVar(&showLabels, "show-labels", false, "Show labels associated with machines")
getCmd.Flags().StringVarP(&listLabelSelector, "selector", "l", "", "Label selector to filter machines (format: key1=value1,key2=value2)")
getCmd.Flags().StringVarP(&outputFormat, "output", "o", "", "Output format. One of: wide")
getCmd.Flags().StringVar(&listRegionFilter, "region", "", "Region of the machine")
getCmd.Flags().StringVar(&listVpcFilter, "vpc", "", "VPC of the machine")
getCmd.Flags().StringVar(&listStatusFilter, "status", "", "Status of the machine")

// Register completions
getCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion)
getCmd.RegisterFlagCompletionFunc("vpc", completion.CompleteVPCID)
}
5 changes: 0 additions & 5 deletions cmd/iaas/compute/machines/machines.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/spf13/cobra"

"github.com/thalassa-cloud/cli/internal/config/contextstate"
"github.com/thalassa-cloud/cli/internal/fzf"
)

Expand All @@ -23,10 +22,6 @@ func init() {
}

func getSelectedMachine(args []string) (string, error) {
if contextstate.OrganisationFlag != "" {
return contextstate.OrganisationFlag, nil
}

if len(args) == 0 && fzf.IsInteractiveMode(os.Stdout) {
command := fmt.Sprintf("%s compute machines ls --no-header", os.Args[0])
return fzf.InteractiveChoice(command)
Expand Down
2 changes: 1 addition & 1 deletion cmd/iaas/compute/machines/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var startCmd = &cobra.Command{
Short: "Start a machine",
Long: "Start a machine to start it from stopped state. This command will start the machine and all the services associated with it.",
Aliases: []string{"s", "start"},
Args: cobra.NoArgs,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

client, err := thalassaclient.GetThalassaClient()
Expand Down
2 changes: 1 addition & 1 deletion cmd/iaas/compute/machines/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var stopCmd = &cobra.Command{
Short: "Stop a machine",
Long: "Stop a machine to stop it from running. This command will stop the machine and all the services associated with it.",
Aliases: []string{"s", "stop"},
Args: cobra.NoArgs,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

client, err := thalassaclient.GetThalassaClient()
Expand Down
Loading
Loading