From 49e6afa47f039110e5b332702ab065af09f90dd3 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:07:47 +0200 Subject: [PATCH 01/19] feat(completion): add completers for DNS, KMS, and networking resources Support shell completion for DNS zones, KMS keys, reserved IPs, route tables, and snapshot policies used by the new command groups. --- internal/completion/completion.go | 123 ++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/internal/completion/completion.go b/internal/completion/completion.go index bfc035f..117f4f3 100644 --- a/internal/completion/completion.go +++ b/internal/completion/completion.go @@ -10,7 +10,9 @@ import ( "github.com/thalassa-cloud/cli/internal/thalassaclient" "github.com/thalassa-cloud/client-go/containerregistry" "github.com/thalassa-cloud/client-go/dbaas" + "github.com/thalassa-cloud/client-go/dns" "github.com/thalassa-cloud/client-go/iaas" + "github.com/thalassa-cloud/client-go/kms" "github.com/thalassa-cloud/client-go/kubernetes" "github.com/thalassa-cloud/client-go/observability" "github.com/thalassa-cloud/client-go/tfs" @@ -125,6 +127,79 @@ func CompleteNatGatewayID(cmd *cobra.Command, args []string, toComplete string) return completions, cobra.ShellCompDirectiveNoFileComp } +// CompleteReservedIPID provides completion for reserved IP IDs. +func CompleteReservedIPID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + ips, err := client.IaaS().ListReservedIPs(cmd.Context(), &iaas.ListReservedIPsRequest{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(ips)) + for _, ip := range ips { + desc := ip.Name + if ip.IPv4Address != "" { + desc = fmt.Sprintf("%s (%s)", ip.Name, ip.IPv4Address) + } + completions = append(completions, ip.Identity+"\t"+desc) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} + +// CompleteRouteTableID provides completion for route table IDs. +func CompleteRouteTableID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + tables, err := client.IaaS().ListRouteTables(cmd.Context(), &iaas.ListRouteTablesRequest{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(tables)) + for _, table := range tables { + completions = append(completions, table.Identity+"\t"+table.Name) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} + +// CompleteSnapshotPolicyID provides completion for snapshot policy IDs. +func CompleteSnapshotPolicyID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + policies, err := client.IaaS().ListSnapshotPolicies(cmd.Context(), &iaas.ListSnapshotPoliciesRequest{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(policies)) + for _, policy := range policies { + completions = append(completions, policy.Identity+"\t"+policy.Name) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} + // CompleteMachineID provides completion for machine IDs func CompleteMachineID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) > 0 { @@ -777,3 +852,51 @@ func CompleteObservabilityWorkspaceID(cmd *cobra.Command, args []string, toCompl } return completions, cobra.ShellCompDirectiveNoFileComp } + +// CompleteDnsZoneIdentity provides completion for DNS zone identities. +func CompleteDnsZoneIdentity(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + zones, err := client.DNS().ListZones(cmd.Context(), &dns.ListZonesRequest{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(zones)) + for _, zone := range zones { + completions = append(completions, zone.Identity+"\t"+zone.Name) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} + +// CompleteKmsKeyIdentity provides completion for KMS key identities. +// Requires the --region flag to be set. +func CompleteKmsKeyIdentity(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + regionFlag, err := cmd.Flags().GetString("region") + if err != nil || regionFlag == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + keys, err := client.KMS().ListKeys(cmd.Context(), regionFlag, &kms.ListKeysRequest{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(keys)) + for _, key := range keys { + completions = append(completions, key.Identity+"\t"+key.Name) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} From ca0799b348f8bf510a74519c6126be12a3173246 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:08:01 +0200 Subject: [PATCH 02/19] feat(dns): add zones and records management commands Operators can manage DNS zones and records, including zonefile import/export and DNSSEC enablement. --- cmd/cmd.go | 2 + cmd/dns/dns.go | 19 +++++ cmd/dns/records/create.go | 96 +++++++++++++++++++++++ cmd/dns/records/delete.go | 57 ++++++++++++++ cmd/dns/records/list.go | 61 ++++++++++++++ cmd/dns/records/records.go | 18 +++++ cmd/dns/records/update.go | 75 ++++++++++++++++++ cmd/dns/records/view.go | 56 +++++++++++++ cmd/dns/zones/create.go | 66 ++++++++++++++++ cmd/dns/zones/delete.go | 55 +++++++++++++ cmd/dns/zones/dnssec.go | 157 +++++++++++++++++++++++++++++++++++++ cmd/dns/zones/export.go | 38 +++++++++ cmd/dns/zones/import.go | 66 ++++++++++++++++ cmd/dns/zones/list.go | 55 +++++++++++++ cmd/dns/zones/update.go | 71 +++++++++++++++++ cmd/dns/zones/view.go | 68 ++++++++++++++++ cmd/dns/zones/zones.go | 17 ++++ 17 files changed, 977 insertions(+) create mode 100644 cmd/dns/dns.go create mode 100644 cmd/dns/records/create.go create mode 100644 cmd/dns/records/delete.go create mode 100644 cmd/dns/records/list.go create mode 100644 cmd/dns/records/records.go create mode 100644 cmd/dns/records/update.go create mode 100644 cmd/dns/records/view.go create mode 100644 cmd/dns/zones/create.go create mode 100644 cmd/dns/zones/delete.go create mode 100644 cmd/dns/zones/dnssec.go create mode 100644 cmd/dns/zones/export.go create mode 100644 cmd/dns/zones/import.go create mode 100644 cmd/dns/zones/list.go create mode 100644 cmd/dns/zones/update.go create mode 100644 cmd/dns/zones/view.go create mode 100644 cmd/dns/zones/zones.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 0210474..4ca7bea 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -10,6 +10,7 @@ import ( "github.com/thalassa-cloud/cli/cmd/audit" "github.com/thalassa-cloud/cli/cmd/context" "github.com/thalassa-cloud/cli/cmd/dbaas" + "github.com/thalassa-cloud/cli/cmd/dns" "github.com/thalassa-cloud/cli/cmd/iaas/compute" "github.com/thalassa-cloud/cli/cmd/iaas/networking" "github.com/thalassa-cloud/cli/cmd/iaas/regions" @@ -71,6 +72,7 @@ func init() { RootCmd.AddCommand(storage.StorageCmd) RootCmd.AddCommand(compute.ComputeCmd) RootCmd.AddCommand(objectstorage.ObjectStorageCmd) + RootCmd.AddCommand(dns.DnsCmd) RootCmd.AddCommand(kubernetes.KubernetesCmd) RootCmd.AddCommand(dbaas.DbaasCmd) diff --git a/cmd/dns/dns.go b/cmd/dns/dns.go new file mode 100644 index 0000000..d0ffef2 --- /dev/null +++ b/cmd/dns/dns.go @@ -0,0 +1,19 @@ +package dns + +import ( + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/cmd/dns/records" + "github.com/thalassa-cloud/cli/cmd/dns/zones" +) + +// DnsCmd manages DNS zones and records. +var DnsCmd = &cobra.Command{ + Use: "dns", + Short: "Manage DNS zones and records", +} + +func init() { + DnsCmd.AddCommand(zones.ZonesCmd) + DnsCmd.AddCommand(records.RecordsCmd) +} diff --git a/cmd/dns/records/create.go b/cmd/dns/records/create.go new file mode 100644 index 0000000..b1510d9 --- /dev/null +++ b/cmd/dns/records/create.go @@ -0,0 +1,96 @@ +package records + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var ( + createName string + createType string + createTTL int + createValues []string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a DNS record", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + recordType := clientdns.DnsRecordType(strings.ToUpper(createType)) + switch recordType { + case clientdns.DnsRecordTypeTXT, + clientdns.DnsRecordTypeA, + clientdns.DnsRecordTypeCNAME, + clientdns.DnsRecordTypeCAA, + clientdns.DnsRecordTypeAAAA, + clientdns.DnsRecordTypeMX, + clientdns.DnsRecordTypeNS, + clientdns.DnsRecordTypeSRV: + default: + return fmt.Errorf("unsupported record type %q (supported: TXT, A, CNAME, CAA, AAAA, MX, NS, SRV)", createType) + } + + if len(createValues) == 0 { + return fmt.Errorf("--value is required") + } + + record, err := client.DNS().CreateRecord(cmd.Context(), zoneIdentity, clientdns.CreateDnsRecordRequest{ + Name: createName, + Type: recordType, + TTL: createTTL, + Values: createValues, + }) + if err != nil { + return fmt.Errorf("failed to create DNS record: %w", err) + } + + body := [][]string{{ + record.Identity, + record.Name, + string(record.Type), + fmt.Sprintf("%d", record.TTL), + strings.Join(record.Values, ","), + formattime.FormatTime(record.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Type", "TTL", "Values", "Created"}, body) + } + return nil + }, +} + +func init() { + RecordsCmd.AddCommand(createCmd) + createCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + createCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + createCmd.Flags().StringVar(&zoneIdentity, "zone", "", "DNS zone identity") + createCmd.Flags().StringVar(&createName, "name", "", "Record name") + createCmd.Flags().StringVar(&createType, "type", "", "Record type (TXT, A, CNAME, CAA, AAAA, MX, NS, SRV)") + createCmd.Flags().IntVar(&createTTL, "ttl", 300, "Record TTL in seconds") + createCmd.Flags().StringSliceVar(&createValues, "value", nil, "Record value (repeatable)") + _ = createCmd.MarkFlagRequired("zone") + _ = createCmd.MarkFlagRequired("name") + _ = createCmd.MarkFlagRequired("type") + _ = createCmd.MarkFlagRequired("value") + _ = createCmd.RegisterFlagCompletionFunc("zone", completion.CompleteDnsZoneIdentity) + _ = createCmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"TXT", "A", "CNAME", "CAA", "AAAA", "MX", "NS", "SRV"}, cobra.ShellCompDirectiveNoFileComp + }) +} diff --git a/cmd/dns/records/delete.go b/cmd/dns/records/delete.go new file mode 100644 index 0000000..8d8226e --- /dev/null +++ b/cmd/dns/records/delete.go @@ -0,0 +1,57 @@ +package records + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var deleteForce bool + +var deleteCmd = &cobra.Command{ + Use: "delete ", + Aliases: []string{"rm", "remove"}, + Short: "Delete a DNS record", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + recordIdentity := args[0] + record, err := client.DNS().GetRecord(cmd.Context(), zoneIdentity, recordIdentity) + if err != nil { + return fmt.Errorf("failed to get DNS record: %w", err) + } + + ok, err := shared.PromptDestructiveUnlessForce(deleteForce, fmt.Sprintf( + "Are you sure you want to delete this DNS record?\n ID: %s\n Name: %s\n Type: %s\n", + record.Identity, record.Name, record.Type, + )) + if err != nil { + return err + } + if !ok { + return nil + } + + if err := client.DNS().DeleteRecord(cmd.Context(), zoneIdentity, record.Identity); err != nil { + return fmt.Errorf("failed to delete DNS record: %w", err) + } + fmt.Printf("Deleted DNS record %s\n", record.Name) + return nil + }, +} + +func init() { + RecordsCmd.AddCommand(deleteCmd) + deleteCmd.Flags().BoolVar(&deleteForce, shared.ForceKey, false, "Skip the confirmation prompt and delete") + deleteCmd.Flags().StringVar(&zoneIdentity, "zone", "", "DNS zone identity") + _ = deleteCmd.MarkFlagRequired("zone") + _ = deleteCmd.RegisterFlagCompletionFunc("zone", completion.CompleteDnsZoneIdentity) +} diff --git a/cmd/dns/records/list.go b/cmd/dns/records/list.go new file mode 100644 index 0000000..335c30e --- /dev/null +++ b/cmd/dns/records/list.go @@ -0,0 +1,61 @@ +package records + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List DNS records in a zone", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + records, err := client.DNS().ListRecords(cmd.Context(), zoneIdentity, &clientdns.ListRecordsRequest{}) + if err != nil { + return fmt.Errorf("failed to list DNS records: %w", err) + } + + body := make([][]string, 0, len(records)) + for _, r := range records { + body = append(body, []string{ + r.Identity, + r.Name, + string(r.Type), + fmt.Sprintf("%d", r.TTL), + strings.Join(r.Values, ","), + formattime.FormatTime(r.CreatedAt.Local(), showExactTime), + }) + } + + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Type", "TTL", "Values", "Created"}, body) + } + return nil + }, +} + +func init() { + RecordsCmd.AddCommand(listCmd) + listCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + listCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + listCmd.Flags().StringVar(&zoneIdentity, "zone", "", "DNS zone identity") + _ = listCmd.MarkFlagRequired("zone") + _ = listCmd.RegisterFlagCompletionFunc("zone", completion.CompleteDnsZoneIdentity) +} diff --git a/cmd/dns/records/records.go b/cmd/dns/records/records.go new file mode 100644 index 0000000..28367ec --- /dev/null +++ b/cmd/dns/records/records.go @@ -0,0 +1,18 @@ +package records + +import ( + "github.com/spf13/cobra" +) + +// RecordsCmd manages DNS records. +var RecordsCmd = &cobra.Command{ + Use: "records", + Aliases: []string{"record", "rr"}, + Short: "Manage DNS records", +} + +var ( + noHeader bool + showExactTime bool + zoneIdentity string +) diff --git a/cmd/dns/records/update.go b/cmd/dns/records/update.go new file mode 100644 index 0000000..73f29b7 --- /dev/null +++ b/cmd/dns/records/update.go @@ -0,0 +1,75 @@ +package records + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var ( + updateTTL int + updateValues []string +) + +var updateCmd = &cobra.Command{ + Use: "update ", + Short: "Update a DNS record", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + if !cmd.Flags().Changed("value") { + return fmt.Errorf("--value is required") + } + + req := clientdns.UpdateDnsRecordRequest{ + Values: updateValues, + } + if cmd.Flags().Changed("ttl") { + req.TTL = updateTTL + } + + record, err := client.DNS().UpdateRecord(cmd.Context(), zoneIdentity, args[0], req) + if err != nil { + return fmt.Errorf("failed to update DNS record: %w", err) + } + + body := [][]string{{ + record.Identity, + record.Name, + string(record.Type), + fmt.Sprintf("%d", record.TTL), + strings.Join(record.Values, ","), + formattime.FormatTime(record.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Type", "TTL", "Values", "Created"}, body) + } + return nil + }, +} + +func init() { + RecordsCmd.AddCommand(updateCmd) + updateCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + updateCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + updateCmd.Flags().StringVar(&zoneIdentity, "zone", "", "DNS zone identity") + updateCmd.Flags().IntVar(&updateTTL, "ttl", 0, "Record TTL in seconds") + updateCmd.Flags().StringSliceVar(&updateValues, "value", nil, "Record value (repeatable)") + _ = updateCmd.MarkFlagRequired("zone") + _ = updateCmd.MarkFlagRequired("value") + _ = updateCmd.RegisterFlagCompletionFunc("zone", completion.CompleteDnsZoneIdentity) +} diff --git a/cmd/dns/records/view.go b/cmd/dns/records/view.go new file mode 100644 index 0000000..6be2d17 --- /dev/null +++ b/cmd/dns/records/view.go @@ -0,0 +1,56 @@ +package records + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var viewCmd = &cobra.Command{ + Use: "view ", + Aliases: []string{"get", "show"}, + Short: "View a DNS record", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + record, err := client.DNS().GetRecord(cmd.Context(), zoneIdentity, args[0]) + if err != nil { + return fmt.Errorf("failed to get DNS record: %w", err) + } + + body := [][]string{{ + record.Identity, + record.Name, + string(record.Type), + fmt.Sprintf("%d", record.TTL), + strings.Join(record.Values, ","), + formattime.FormatTime(record.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Type", "TTL", "Values", "Created"}, body) + } + return nil + }, +} + +func init() { + RecordsCmd.AddCommand(viewCmd) + viewCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + viewCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + viewCmd.Flags().StringVar(&zoneIdentity, "zone", "", "DNS zone identity") + _ = viewCmd.MarkFlagRequired("zone") + _ = viewCmd.RegisterFlagCompletionFunc("zone", completion.CompleteDnsZoneIdentity) +} diff --git a/cmd/dns/zones/create.go b/cmd/dns/zones/create.go new file mode 100644 index 0000000..3a8454c --- /dev/null +++ b/cmd/dns/zones/create.go @@ -0,0 +1,66 @@ +package zones + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var ( + createName string + createDescription string + createLabels []string + createAnnotations []string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a DNS zone", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + zone, err := client.DNS().CreateZone(cmd.Context(), clientdns.CreateDnsZoneRequest{ + ZoneName: createName, + Description: createDescription, + Labels: shared.KeyValuePairsToMap(createLabels), + Annotations: shared.KeyValuePairsToMap(createAnnotations), + }) + if err != nil { + return fmt.Errorf("failed to create DNS zone: %w", err) + } + + body := [][]string{{ + zone.Identity, + zone.Name, + zone.Slug, + formattime.FormatTime(zone.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Slug", "Created"}, body) + } + return nil + }, +} + +func init() { + ZonesCmd.AddCommand(createCmd) + createCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + createCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + createCmd.Flags().StringVar(&createName, "name", "", "Zone name (e.g. example.com)") + createCmd.Flags().StringVar(&createDescription, "description", "", "Zone description") + createCmd.Flags().StringSliceVar(&createLabels, "labels", nil, "Labels as key=value (repeatable)") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") + _ = createCmd.MarkFlagRequired("name") +} diff --git a/cmd/dns/zones/delete.go b/cmd/dns/zones/delete.go new file mode 100644 index 0000000..333db0a --- /dev/null +++ b/cmd/dns/zones/delete.go @@ -0,0 +1,55 @@ +package zones + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var deleteForce bool + +var deleteCmd = &cobra.Command{ + Use: "delete ", + Aliases: []string{"rm", "remove"}, + Short: "Delete a DNS zone and all of its records", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + zoneIdentity := args[0] + zone, err := client.DNS().GetZone(cmd.Context(), zoneIdentity) + if err != nil { + return fmt.Errorf("failed to get DNS zone: %w", err) + } + + ok, err := shared.PromptDestructiveUnlessForce(deleteForce, fmt.Sprintf( + "Are you sure you want to delete this DNS zone and all of its records?\n ID: %s\n Name: %s\n", + zone.Identity, zone.Name, + )) + if err != nil { + return err + } + if !ok { + return nil + } + + if err := client.DNS().DeleteZone(cmd.Context(), zone.Identity); err != nil { + return fmt.Errorf("failed to delete DNS zone: %w", err) + } + fmt.Printf("Deleted DNS zone %s\n", zone.Name) + return nil + }, +} + +func init() { + ZonesCmd.AddCommand(deleteCmd) + deleteCmd.Flags().BoolVar(&deleteForce, shared.ForceKey, false, "Skip the confirmation prompt and delete") +} diff --git a/cmd/dns/zones/dnssec.go b/cmd/dns/zones/dnssec.go new file mode 100644 index 0000000..132bf7d --- /dev/null +++ b/cmd/dns/zones/dnssec.go @@ -0,0 +1,157 @@ +package zones + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var dnssecCmd = &cobra.Command{ + Use: "dnssec", + Short: "Manage DNSSEC for a DNS zone", +} + +var ( + dnssecRegion string + dnssecKmsKeyIdentity string + dnssecDisableForce bool +) + +var dnssecGetCmd = &cobra.Command{ + Use: "get ", + Aliases: []string{"view", "show", "status"}, + Short: "Get DNSSEC status for a zone", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + status, err := client.DNS().GetDnssec(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to get DNSSEC status: %w", err) + } + + dsCount := fmt.Sprintf("%d", len(status.DsRecords)) + body := [][]string{{ + fmt.Sprintf("%v", status.Enabled), + fmt.Sprintf("%v", status.DsDelegated), + dsCount, + status.Region, + status.KmsKeyIdentity, + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Enabled", "DS Delegated", "DS Records", "Region", "KMS Key"}, body) + } + + if len(status.DsRecords) > 0 { + fmt.Println() + dsBody := make([][]string, 0, len(status.DsRecords)) + for _, ds := range status.DsRecords { + dsBody = append(dsBody, []string{ + fmt.Sprintf("%d", ds.KeyTag), + fmt.Sprintf("%d", ds.Algorithm), + ds.DigestTypeName, + ds.KeyRole, + ds.Record, + }) + } + if noHeader { + table.Print(nil, dsBody) + } else { + table.Print([]string{"Key Tag", "Algorithm", "Digest Type", "Key Role", "Record"}, dsBody) + } + } + return nil + }, +} + +var dnssecEnableCmd = &cobra.Command{ + Use: "enable ", + Short: "Enable DNSSEC signing for a zone", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + status, err := client.DNS().SetDnssec(cmd.Context(), args[0], clientdns.SetDnssecRequest{ + Region: dnssecRegion, + KmsKeyIdentity: dnssecKmsKeyIdentity, + }) + if err != nil { + return fmt.Errorf("failed to enable DNSSEC: %w", err) + } + + body := [][]string{{ + fmt.Sprintf("%v", status.Enabled), + status.Region, + status.KmsKeyIdentity, + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Enabled", "Region", "KMS Key"}, body) + } + return nil + }, +} + +var dnssecDisableCmd = &cobra.Command{ + Use: "disable ", + Short: "Disable DNSSEC signing for a zone", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + ok, err := shared.PromptDestructiveUnlessForce(dnssecDisableForce, fmt.Sprintf( + "Are you sure you want to disable DNSSEC for zone %s?\n", args[0], + )) + if err != nil { + return err + } + if !ok { + return nil + } + + if err := client.DNS().DeleteDnssec(cmd.Context(), args[0]); err != nil { + return fmt.Errorf("failed to disable DNSSEC: %w", err) + } + fmt.Printf("Disabled DNSSEC for zone %s\n", args[0]) + return nil + }, +} + +func init() { + ZonesCmd.AddCommand(dnssecCmd) + dnssecCmd.AddCommand(dnssecGetCmd) + dnssecCmd.AddCommand(dnssecEnableCmd) + dnssecCmd.AddCommand(dnssecDisableCmd) + + dnssecGetCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + + dnssecEnableCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + dnssecEnableCmd.Flags().StringVar(&dnssecRegion, "region", "", "Region for DNSSEC KMS key") + dnssecEnableCmd.Flags().StringVar(&dnssecKmsKeyIdentity, "kms-key", "", "KMS key identity used for DNSSEC signing") + _ = dnssecEnableCmd.MarkFlagRequired("region") + _ = dnssecEnableCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = dnssecEnableCmd.RegisterFlagCompletionFunc("kms-key", completion.CompleteKmsKeyIdentity) + + dnssecDisableCmd.Flags().BoolVar(&dnssecDisableForce, shared.ForceKey, false, "Skip the confirmation prompt") +} diff --git a/cmd/dns/zones/export.go b/cmd/dns/zones/export.go new file mode 100644 index 0000000..edfa74f --- /dev/null +++ b/cmd/dns/zones/export.go @@ -0,0 +1,38 @@ +package zones + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var exportCmd = &cobra.Command{ + Use: "export ", + Short: "Export a DNS zone as a BIND zone file to stdout", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + exported, err := client.DNS().ExportZoneFile(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to export DNS zone: %w", err) + } + + fmt.Print(exported.ZoneFile) + if exported.ZoneFile != "" && exported.ZoneFile[len(exported.ZoneFile)-1] != '\n' { + fmt.Println() + } + return nil + }, +} + +func init() { + ZonesCmd.AddCommand(exportCmd) +} diff --git a/cmd/dns/zones/import.go b/cmd/dns/zones/import.go new file mode 100644 index 0000000..d83ffbf --- /dev/null +++ b/cmd/dns/zones/import.go @@ -0,0 +1,66 @@ +package zones + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var ( + importFile string + importReplace bool +) + +var importCmd = &cobra.Command{ + Use: "import ", + Short: "Import DNS records from a BIND zone file", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + data, err := os.ReadFile(importFile) + if err != nil { + return fmt.Errorf("failed to read zone file: %w", err) + } + + result, err := client.DNS().ImportZoneFile(cmd.Context(), args[0], clientdns.ImportDnsZoneFileRequest{ + ZoneFile: string(data), + ReplaceExisting: importReplace, + }) + if err != nil { + return fmt.Errorf("failed to import DNS zone: %w", err) + } + + body := [][]string{{ + fmt.Sprintf("%d", result.Created), + fmt.Sprintf("%d", result.Updated), + fmt.Sprintf("%d", result.Deleted), + fmt.Sprintf("%d", result.Skipped), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Created", "Updated", "Deleted", "Skipped"}, body) + } + return nil + }, +} + +func init() { + ZonesCmd.AddCommand(importCmd) + importCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + importCmd.Flags().StringVar(&importFile, "file", "", "Path to BIND zone file") + importCmd.Flags().BoolVar(&importReplace, "replace", false, "Replace existing records that conflict") + _ = importCmd.MarkFlagRequired("file") +} diff --git a/cmd/dns/zones/list.go b/cmd/dns/zones/list.go new file mode 100644 index 0000000..8f7d1ca --- /dev/null +++ b/cmd/dns/zones/list.go @@ -0,0 +1,55 @@ +package zones + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List DNS zones", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + zones, err := client.DNS().ListZones(cmd.Context(), &clientdns.ListZonesRequest{}) + if err != nil { + return fmt.Errorf("failed to list DNS zones: %w", err) + } + + body := make([][]string, 0, len(zones)) + for _, z := range zones { + body = append(body, []string{ + z.Identity, + z.Name, + z.Slug, + z.Description, + formattime.FormatTime(z.CreatedAt.Local(), showExactTime), + }) + } + + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Slug", "Description", "Created"}, body) + } + return nil + }, +} + +func init() { + ZonesCmd.AddCommand(listCmd) + listCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + listCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") +} diff --git a/cmd/dns/zones/update.go b/cmd/dns/zones/update.go new file mode 100644 index 0000000..70ade1a --- /dev/null +++ b/cmd/dns/zones/update.go @@ -0,0 +1,71 @@ +package zones + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientdns "github.com/thalassa-cloud/client-go/dns" +) + +var ( + updateDescription string + updateLabels []string + updateAnnotations []string +) + +var updateCmd = &cobra.Command{ + Use: "update ", + Short: "Update a DNS zone", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req := clientdns.UpdateDnsZoneRequest{ + Description: updateDescription, + } + if cmd.Flags().Changed("labels") { + req.Labels = shared.KeyValuePairsToMap(updateLabels) + } + if cmd.Flags().Changed("annotations") { + req.Annotations = shared.KeyValuePairsToMap(updateAnnotations) + } + + zone, err := client.DNS().UpdateZone(cmd.Context(), args[0], req) + if err != nil { + return fmt.Errorf("failed to update DNS zone: %w", err) + } + + body := [][]string{{ + zone.Identity, + zone.Name, + zone.Slug, + zone.Description, + formattime.FormatTime(zone.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Slug", "Description", "Created"}, body) + } + return nil + }, +} + +func init() { + ZonesCmd.AddCommand(updateCmd) + updateCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + updateCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + updateCmd.Flags().StringVar(&updateDescription, "description", "", "Zone description") + updateCmd.Flags().StringSliceVar(&updateLabels, "labels", nil, "Labels as key=value (repeatable)") + updateCmd.Flags().StringSliceVar(&updateAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") +} diff --git a/cmd/dns/zones/view.go b/cmd/dns/zones/view.go new file mode 100644 index 0000000..44782ae --- /dev/null +++ b/cmd/dns/zones/view.go @@ -0,0 +1,68 @@ +package zones + +import ( + "fmt" + "sort" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var viewCmd = &cobra.Command{ + Use: "view ", + Aliases: []string{"get", "show"}, + Short: "View a DNS zone", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteDnsZoneIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + zone, err := client.DNS().GetZone(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to get DNS zone: %w", err) + } + + body := [][]string{{ + zone.Identity, + zone.Name, + zone.Slug, + zone.Description, + formatMap(zone.Labels), + formatMap(zone.Annotations), + formattime.FormatTime(zone.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Slug", "Description", "Labels", "Annotations", "Created"}, body) + } + return nil + }, +} + +func formatMap(m map[string]string) string { + if len(m) == 0 { + return "-" + } + parts := make([]string, 0, len(m)) + for k, v := range m { + parts = append(parts, k+"="+v) + } + sort.Strings(parts) + return strings.Join(parts, ",") +} + +func init() { + ZonesCmd.AddCommand(viewCmd) + viewCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + viewCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") +} diff --git a/cmd/dns/zones/zones.go b/cmd/dns/zones/zones.go new file mode 100644 index 0000000..9d13fb4 --- /dev/null +++ b/cmd/dns/zones/zones.go @@ -0,0 +1,17 @@ +package zones + +import ( + "github.com/spf13/cobra" +) + +// ZonesCmd manages DNS zones. +var ZonesCmd = &cobra.Command{ + Use: "zones", + Aliases: []string{"zone", "z"}, + Short: "Manage DNS zones", +} + +var ( + noHeader bool + showExactTime bool +) From 585f0514b5c19b095545322ca64452cb2ad02567 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:14:32 +0200 Subject: [PATCH 03/19] feat(kms): add key management and crypto commands Expose KMS key lifecycle, rotation, and encrypt/decrypt/sign/HMAC operations through the CLI. --- cmd/cmd.go | 2 + cmd/kms/decrypt.go | 51 +++++++++++++++ cmd/kms/encrypt.go | 54 +++++++++++++++ cmd/kms/export.go | 66 +++++++++++++++++++ cmd/kms/hmac.go | 57 ++++++++++++++++ cmd/kms/keys/cancel_deletion.go | 36 ++++++++++ cmd/kms/keys/create.go | 110 +++++++++++++++++++++++++++++++ cmd/kms/keys/delete.go | 57 ++++++++++++++++ cmd/kms/keys/enable_disable.go | 79 ++++++++++++++++++++++ cmd/kms/keys/keys.go | 18 +++++ cmd/kms/keys/list.go | 60 +++++++++++++++++ cmd/kms/keys/rotate.go | 54 +++++++++++++++ cmd/kms/keys/rotation.go | 79 ++++++++++++++++++++++ cmd/kms/keys/view.go | 79 ++++++++++++++++++++++ cmd/kms/kms.go | 21 ++++++ cmd/kms/public_key.go | 71 ++++++++++++++++++++ cmd/kms/sign.go | 63 ++++++++++++++++++ cmd/kms/summary.go | 54 +++++++++++++++ cmd/kms/verify.go | 112 ++++++++++++++++++++++++++++++++ cmd/kms/wrapping_key.go | 47 ++++++++++++++ 20 files changed, 1170 insertions(+) create mode 100644 cmd/kms/decrypt.go create mode 100644 cmd/kms/encrypt.go create mode 100644 cmd/kms/export.go create mode 100644 cmd/kms/hmac.go create mode 100644 cmd/kms/keys/cancel_deletion.go create mode 100644 cmd/kms/keys/create.go create mode 100644 cmd/kms/keys/delete.go create mode 100644 cmd/kms/keys/enable_disable.go create mode 100644 cmd/kms/keys/keys.go create mode 100644 cmd/kms/keys/list.go create mode 100644 cmd/kms/keys/rotate.go create mode 100644 cmd/kms/keys/rotation.go create mode 100644 cmd/kms/keys/view.go create mode 100644 cmd/kms/kms.go create mode 100644 cmd/kms/public_key.go create mode 100644 cmd/kms/sign.go create mode 100644 cmd/kms/summary.go create mode 100644 cmd/kms/verify.go create mode 100644 cmd/kms/wrapping_key.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 4ca7bea..05d6a7b 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -16,6 +16,7 @@ import ( "github.com/thalassa-cloud/cli/cmd/iaas/regions" "github.com/thalassa-cloud/cli/cmd/iaas/storage" "github.com/thalassa-cloud/cli/cmd/iam" + "github.com/thalassa-cloud/cli/cmd/kms" "github.com/thalassa-cloud/cli/cmd/kubernetes" "github.com/thalassa-cloud/cli/cmd/me" "github.com/thalassa-cloud/cli/cmd/objectstorage" @@ -73,6 +74,7 @@ func init() { RootCmd.AddCommand(compute.ComputeCmd) RootCmd.AddCommand(objectstorage.ObjectStorageCmd) RootCmd.AddCommand(dns.DnsCmd) + RootCmd.AddCommand(kms.KmsCmd) RootCmd.AddCommand(kubernetes.KubernetesCmd) RootCmd.AddCommand(dbaas.DbaasCmd) diff --git a/cmd/kms/decrypt.go b/cmd/kms/decrypt.go new file mode 100644 index 0000000..f0885bf --- /dev/null +++ b/cmd/kms/decrypt.go @@ -0,0 +1,51 @@ +package kms + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + decryptRegion string + decryptKey string + decryptCiphertext string +) + +var decryptCmd = &cobra.Command{ + Use: "decrypt", + Short: "Decrypt ciphertext with a KMS key (prints base64 plaintext to stdout)", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().Decrypt(cmd.Context(), decryptRegion, decryptKey, clientkms.DecryptRequest{ + Ciphertext: decryptCiphertext, + }) + if err != nil { + return fmt.Errorf("failed to decrypt: %w", err) + } + + fmt.Println(result.Plaintext) + return nil + }, +} + +func init() { + KmsCmd.AddCommand(decryptCmd) + decryptCmd.Flags().StringVar(&decryptRegion, "region", "", "Region") + decryptCmd.Flags().StringVar(&decryptKey, "key", "", "KMS key identity") + decryptCmd.Flags().StringVar(&decryptCiphertext, "ciphertext", "", "Ciphertext from encrypt") + _ = decryptCmd.MarkFlagRequired("region") + _ = decryptCmd.MarkFlagRequired("key") + _ = decryptCmd.MarkFlagRequired("ciphertext") + _ = decryptCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = decryptCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/kms/encrypt.go b/cmd/kms/encrypt.go new file mode 100644 index 0000000..31637f1 --- /dev/null +++ b/cmd/kms/encrypt.go @@ -0,0 +1,54 @@ +package kms + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + encryptRegion string + encryptKey string + encryptPlaintext string + encryptKeyVersion string +) + +var encryptCmd = &cobra.Command{ + Use: "encrypt", + Short: "Encrypt plaintext with a KMS key (plaintext must be base64-encoded)", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().Encrypt(cmd.Context(), encryptRegion, encryptKey, clientkms.EncryptRequest{ + Plaintext: encryptPlaintext, + KeyVersion: encryptKeyVersion, + }) + if err != nil { + return fmt.Errorf("failed to encrypt: %w", err) + } + + fmt.Println(result.Ciphertext) + return nil + }, +} + +func init() { + KmsCmd.AddCommand(encryptCmd) + encryptCmd.Flags().StringVar(&encryptRegion, "region", "", "Region") + encryptCmd.Flags().StringVar(&encryptKey, "key", "", "KMS key identity") + encryptCmd.Flags().StringVar(&encryptPlaintext, "plaintext", "", "Base64-encoded plaintext") + encryptCmd.Flags().StringVar(&encryptKeyVersion, "key-version", "", "Key version") + _ = encryptCmd.MarkFlagRequired("region") + _ = encryptCmd.MarkFlagRequired("key") + _ = encryptCmd.MarkFlagRequired("plaintext") + _ = encryptCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = encryptCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/kms/export.go b/cmd/kms/export.go new file mode 100644 index 0000000..3b2fbc4 --- /dev/null +++ b/cmd/kms/export.go @@ -0,0 +1,66 @@ +package kms + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + exportRegion string + exportKey string + exportKeyVersion string + exportForce bool +) + +var exportCmd = &cobra.Command{ + Use: "export", + Short: "Export key material for a KMS key (when export is allowed)", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + ok, err := shared.PromptDestructiveUnlessForce(exportForce, fmt.Sprintf( + "Exporting key material for %s. Ensure the output is handled securely.\n", exportKey, + )) + if err != nil { + return err + } + if !ok { + return nil + } + + result, err := client.KMS().ExportKey(cmd.Context(), exportRegion, exportKey, clientkms.ExportKeyRequest{ + KeyVersion: exportKeyVersion, + }) + if err != nil { + return fmt.Errorf("failed to export KMS key: %w", err) + } + + fmt.Print(result.KeyMaterial) + if result.KeyMaterial != "" && result.KeyMaterial[len(result.KeyMaterial)-1] != '\n' { + fmt.Println() + } + return nil + }, +} + +func init() { + KmsCmd.AddCommand(exportCmd) + exportCmd.Flags().StringVar(&exportRegion, "region", "", "Region") + exportCmd.Flags().StringVar(&exportKey, "key", "", "KMS key identity") + exportCmd.Flags().StringVar(&exportKeyVersion, "key-version", "", "Key version to export") + exportCmd.Flags().BoolVar(&exportForce, shared.ForceKey, false, "Skip the confirmation prompt") + _ = exportCmd.MarkFlagRequired("region") + _ = exportCmd.MarkFlagRequired("key") + _ = exportCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = exportCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/kms/hmac.go b/cmd/kms/hmac.go new file mode 100644 index 0000000..af7d7d2 --- /dev/null +++ b/cmd/kms/hmac.go @@ -0,0 +1,57 @@ +package kms + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + hmacRegion string + hmacKey string + hmacInput string + hmacKeyVersion string + hmacAlgorithm string +) + +var hmacCmd = &cobra.Command{ + Use: "hmac", + Short: "Compute an HMAC with a KMS key", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().HMAC(cmd.Context(), hmacRegion, hmacKey, clientkms.HMACRequest{ + Input: hmacInput, + KeyVersion: hmacKeyVersion, + Algorithm: hmacAlgorithm, + }) + if err != nil { + return fmt.Errorf("failed to compute HMAC: %w", err) + } + + fmt.Println(result.HMAC) + return nil + }, +} + +func init() { + KmsCmd.AddCommand(hmacCmd) + hmacCmd.Flags().StringVar(&hmacRegion, "region", "", "Region") + hmacCmd.Flags().StringVar(&hmacKey, "key", "", "KMS key identity") + hmacCmd.Flags().StringVar(&hmacInput, "input", "", "Input for HMAC") + hmacCmd.Flags().StringVar(&hmacKeyVersion, "key-version", "", "Key version") + hmacCmd.Flags().StringVar(&hmacAlgorithm, "algorithm", "", "HMAC algorithm") + _ = hmacCmd.MarkFlagRequired("region") + _ = hmacCmd.MarkFlagRequired("key") + _ = hmacCmd.MarkFlagRequired("input") + _ = hmacCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = hmacCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/kms/keys/cancel_deletion.go b/cmd/kms/keys/cancel_deletion.go new file mode 100644 index 0000000..1bb5941 --- /dev/null +++ b/cmd/kms/keys/cancel_deletion.go @@ -0,0 +1,36 @@ +package keys + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var cancelDeletionCmd = &cobra.Command{ + Use: "cancel-deletion ", + Short: "Cancel a pending KMS key deletion", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteKmsKeyIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + if err := client.KMS().CancelDeletion(cmd.Context(), region, args[0]); err != nil { + return fmt.Errorf("failed to cancel KMS key deletion: %w", err) + } + fmt.Printf("Cancelled deletion for KMS key %s\n", args[0]) + return nil + }, +} + +func init() { + KeysCmd.AddCommand(cancelDeletionCmd) + cancelDeletionCmd.Flags().StringVar(®ion, "region", "", "Region") + _ = cancelDeletionCmd.MarkFlagRequired("region") + _ = cancelDeletionCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/kms/keys/create.go b/cmd/kms/keys/create.go new file mode 100644 index 0000000..2bbd1e2 --- /dev/null +++ b/cmd/kms/keys/create.go @@ -0,0 +1,110 @@ +package keys + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + createName string + createDescription string + createLabels []string + createAnnotations []string + createKeyType string + createExportAllowed bool + createKeyRotationEnabled bool + createRotationPeriodInDays int + createImportKeyMaterial string + createHashFunction string + createAllowRotation bool +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a KMS key", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + keyType := clientkms.KmsKeyType(createKeyType) + if createKeyType != "" && !keyType.IsValid() { + return fmt.Errorf("unsupported key type %q", createKeyType) + } + + req := clientkms.CreateKmsKeyRequest{ + Name: createName, + Description: createDescription, + Labels: shared.KeyValuePairsToMap(createLabels), + Annotations: shared.KeyValuePairsToMap(createAnnotations), + KeyType: keyType, + ExportAllowed: createExportAllowed, + KeyRotationEnabled: createKeyRotationEnabled, + ImportKeyMaterial: createImportKeyMaterial, + HashFunction: createHashFunction, + AllowRotation: createAllowRotation, + } + if cmd.Flags().Changed("rotation-period-days") { + period := createRotationPeriodInDays + req.RotationPeriodInDays = &period + } + + key, err := client.KMS().CreateKey(cmd.Context(), region, req) + if err != nil { + return fmt.Errorf("failed to create KMS key: %w", err) + } + + body := [][]string{{ + key.Identity, + key.Name, + string(key.KeyType), + string(key.Status), + formattime.FormatTime(key.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Type", "Status", "Created"}, body) + } + return nil + }, +} + +func init() { + KeysCmd.AddCommand(createCmd) + createCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + createCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + createCmd.Flags().StringVar(®ion, "region", "", "Region") + createCmd.Flags().StringVar(&createName, "name", "", "Key name") + createCmd.Flags().StringVar(&createDescription, "description", "", "Key description") + createCmd.Flags().StringSliceVar(&createLabels, "labels", nil, "Labels as key=value (repeatable)") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") + createCmd.Flags().StringVar(&createKeyType, "key-type", "", "Key type (aes128-gcm96, aes256-gcm96, chacha20-poly1305, ed25519, ecdsa-p256/384/521, rsa-2048/3072/4096, hmac, hmac-sha256, hmac-sha512)") + createCmd.Flags().BoolVar(&createExportAllowed, "export-allowed", false, "Allow exporting key material") + createCmd.Flags().BoolVar(&createKeyRotationEnabled, "rotation-enabled", false, "Enable automatic key rotation") + createCmd.Flags().IntVar(&createRotationPeriodInDays, "rotation-period-days", 0, "Automatic rotation period in days") + createCmd.Flags().StringVar(&createImportKeyMaterial, "import-key-material", "", "Wrapped key material for BYOK import") + createCmd.Flags().StringVar(&createHashFunction, "hash-function", "", "Hash function for imported or HMAC keys") + createCmd.Flags().BoolVar(&createAllowRotation, "allow-rotation", false, "Allow rotation for imported keys") + _ = createCmd.MarkFlagRequired("region") + _ = createCmd.MarkFlagRequired("name") + _ = createCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = createCmd.RegisterFlagCompletionFunc("key-type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{ + "aes128-gcm96", "aes256-gcm96", "chacha20-poly1305", + "ed25519", "ecdsa-p256", "ecdsa-p384", "ecdsa-p521", + "rsa-2048", "rsa-3072", "rsa-4096", + "hmac", "hmac-sha256", "hmac-sha512", + }, cobra.ShellCompDirectiveNoFileComp + }) +} diff --git a/cmd/kms/keys/delete.go b/cmd/kms/keys/delete.go new file mode 100644 index 0000000..0d15b17 --- /dev/null +++ b/cmd/kms/keys/delete.go @@ -0,0 +1,57 @@ +package keys + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var deleteForce bool + +var deleteCmd = &cobra.Command{ + Use: "delete ", + Aliases: []string{"rm", "remove"}, + Short: "Schedule a KMS key for deletion", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteKmsKeyIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + key, err := client.KMS().GetKey(cmd.Context(), region, args[0]) + if err != nil { + return fmt.Errorf("failed to get KMS key: %w", err) + } + + ok, err := shared.PromptDestructiveUnlessForce(deleteForce, fmt.Sprintf( + "Are you sure you want to schedule this KMS key for deletion?\n ID: %s\n Name: %s\n", + key.Identity, key.Name, + )) + if err != nil { + return err + } + if !ok { + return nil + } + + if err := client.KMS().DeleteKey(cmd.Context(), region, key.Identity); err != nil { + return fmt.Errorf("failed to delete KMS key: %w", err) + } + fmt.Printf("Scheduled KMS key %s for deletion\n", key.Name) + return nil + }, +} + +func init() { + KeysCmd.AddCommand(deleteCmd) + deleteCmd.Flags().BoolVar(&deleteForce, shared.ForceKey, false, "Skip the confirmation prompt and delete") + deleteCmd.Flags().StringVar(®ion, "region", "", "Region") + _ = deleteCmd.MarkFlagRequired("region") + _ = deleteCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/kms/keys/enable_disable.go b/cmd/kms/keys/enable_disable.go new file mode 100644 index 0000000..af0f740 --- /dev/null +++ b/cmd/kms/keys/enable_disable.go @@ -0,0 +1,79 @@ +package keys + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +func runKeyStatusMutation(ctx context.Context, keyIdentity string, mutate func(context.Context, string, string) (*clientkms.KmsKey, error), action string) error { + key, err := mutate(ctx, region, keyIdentity) + if err != nil { + return fmt.Errorf("failed to %s KMS key: %w", action, err) + } + + body := [][]string{{ + key.Identity, + key.Name, + string(key.Status), + formattime.FormatTime(key.UpdatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Status", "Updated"}, body) + } + return nil +} + +func registerKeyRegionFlags(cmd *cobra.Command) { + cmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + cmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + cmd.Flags().StringVar(®ion, "region", "", "Region") + _ = cmd.MarkFlagRequired("region") + _ = cmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} + +var enableCmd = &cobra.Command{ + Use: "enable ", + Short: "Enable a KMS key", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteKmsKeyIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + return runKeyStatusMutation(cmd.Context(), args[0], client.KMS().EnableKey, "enable") + }, +} + +var disableCmd = &cobra.Command{ + Use: "disable ", + Short: "Disable a KMS key", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteKmsKeyIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + return runKeyStatusMutation(cmd.Context(), args[0], client.KMS().DisableKey, "disable") + }, +} + +func init() { + KeysCmd.AddCommand(enableCmd) + registerKeyRegionFlags(enableCmd) + + KeysCmd.AddCommand(disableCmd) + registerKeyRegionFlags(disableCmd) +} diff --git a/cmd/kms/keys/keys.go b/cmd/kms/keys/keys.go new file mode 100644 index 0000000..84bfd3e --- /dev/null +++ b/cmd/kms/keys/keys.go @@ -0,0 +1,18 @@ +package keys + +import ( + "github.com/spf13/cobra" +) + +// KeysCmd manages KMS keys. +var KeysCmd = &cobra.Command{ + Use: "keys", + Aliases: []string{"key", "k"}, + Short: "Manage KMS keys", +} + +var ( + noHeader bool + showExactTime bool + region string +) diff --git a/cmd/kms/keys/list.go b/cmd/kms/keys/list.go new file mode 100644 index 0000000..259f92b --- /dev/null +++ b/cmd/kms/keys/list.go @@ -0,0 +1,60 @@ +package keys + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List KMS keys in a region", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + keys, err := client.KMS().ListKeys(cmd.Context(), region, &clientkms.ListKeysRequest{}) + if err != nil { + return fmt.Errorf("failed to list KMS keys: %w", err) + } + + body := make([][]string, 0, len(keys)) + for _, k := range keys { + body = append(body, []string{ + k.Identity, + k.Name, + string(k.KeyType), + string(k.Status), + fmt.Sprintf("%v", k.KeyRotationEnabled), + formattime.FormatTime(k.CreatedAt.Local(), showExactTime), + }) + } + + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Type", "Status", "Rotation", "Created"}, body) + } + return nil + }, +} + +func init() { + KeysCmd.AddCommand(listCmd) + listCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + listCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + listCmd.Flags().StringVar(®ion, "region", "", "Region") + _ = listCmd.MarkFlagRequired("region") + _ = listCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/kms/keys/rotate.go b/cmd/kms/keys/rotate.go new file mode 100644 index 0000000..29cf151 --- /dev/null +++ b/cmd/kms/keys/rotate.go @@ -0,0 +1,54 @@ +package keys + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var rotateCmd = &cobra.Command{ + Use: "rotate ", + Short: "Rotate a KMS key on demand", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteKmsKeyIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + key, err := client.KMS().RotateKey(cmd.Context(), region, args[0]) + if err != nil { + return fmt.Errorf("failed to rotate KMS key: %w", err) + } + + body := [][]string{{ + key.Identity, + key.Name, + string(key.Status), + fmt.Sprintf("%d", key.LatestVersion), + formattime.FormatTime(key.UpdatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Status", "Latest Version", "Updated"}, body) + } + return nil + }, +} + +func init() { + KeysCmd.AddCommand(rotateCmd) + rotateCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + rotateCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + rotateCmd.Flags().StringVar(®ion, "region", "", "Region") + _ = rotateCmd.MarkFlagRequired("region") + _ = rotateCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/kms/keys/rotation.go b/cmd/kms/keys/rotation.go new file mode 100644 index 0000000..9982a68 --- /dev/null +++ b/cmd/kms/keys/rotation.go @@ -0,0 +1,79 @@ +package keys + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + rotationEnabled bool + rotationPeriodDays int +) + +var rotationCmd = &cobra.Command{ + Use: "rotation ", + Short: "Update automatic rotation settings for a KMS key", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteKmsKeyIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req := clientkms.UpdateRotationRequest{} + if cmd.Flags().Changed("enabled") { + enabled := rotationEnabled + req.KeyRotationEnabled = &enabled + } + if cmd.Flags().Changed("period-days") { + period := rotationPeriodDays + req.RotationPeriodInDays = &period + } + if req.KeyRotationEnabled == nil && req.RotationPeriodInDays == nil { + return fmt.Errorf("at least one of --enabled or --period-days is required") + } + + key, err := client.KMS().UpdateRotation(cmd.Context(), region, args[0], req) + if err != nil { + return fmt.Errorf("failed to update KMS key rotation: %w", err) + } + + period := "-" + if key.RotationPeriodInDays != nil { + period = fmt.Sprintf("%d", *key.RotationPeriodInDays) + } + body := [][]string{{ + key.Identity, + key.Name, + fmt.Sprintf("%v", key.KeyRotationEnabled), + period, + formattime.FormatTime(key.UpdatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Rotation Enabled", "Period Days", "Updated"}, body) + } + return nil + }, +} + +func init() { + KeysCmd.AddCommand(rotationCmd) + rotationCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + rotationCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + rotationCmd.Flags().StringVar(®ion, "region", "", "Region") + rotationCmd.Flags().BoolVar(&rotationEnabled, "enabled", false, "Enable or disable automatic rotation") + rotationCmd.Flags().IntVar(&rotationPeriodDays, "period-days", 0, "Automatic rotation period in days") + _ = rotationCmd.MarkFlagRequired("region") + _ = rotationCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/kms/keys/view.go b/cmd/kms/keys/view.go new file mode 100644 index 0000000..e2550d1 --- /dev/null +++ b/cmd/kms/keys/view.go @@ -0,0 +1,79 @@ +package keys + +import ( + "fmt" + "sort" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var viewCmd = &cobra.Command{ + Use: "view ", + Aliases: []string{"get", "show"}, + Short: "View a KMS key", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteKmsKeyIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + key, err := client.KMS().GetKey(cmd.Context(), region, args[0]) + if err != nil { + return fmt.Errorf("failed to get KMS key: %w", err) + } + + rotationPeriod := "-" + if key.RotationPeriodInDays != nil { + rotationPeriod = fmt.Sprintf("%d", *key.RotationPeriodInDays) + } + + body := [][]string{{ + key.Identity, + key.Name, + string(key.KeyType), + string(key.Status), + fmt.Sprintf("%v", key.ExportAllowed), + fmt.Sprintf("%v", key.KeyRotationEnabled), + rotationPeriod, + fmt.Sprintf("%d", key.LatestVersion), + formatMap(key.Labels), + formattime.FormatTime(key.CreatedAt.Local(), showExactTime), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Type", "Status", "Exportable", "Rotation", "Period Days", "Latest Version", "Labels", "Created"}, body) + } + return nil + }, +} + +func formatMap(m map[string]string) string { + if len(m) == 0 { + return "-" + } + parts := make([]string, 0, len(m)) + for k, v := range m { + parts = append(parts, k+"="+v) + } + sort.Strings(parts) + return strings.Join(parts, ",") +} + +func init() { + KeysCmd.AddCommand(viewCmd) + viewCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + viewCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + viewCmd.Flags().StringVar(®ion, "region", "", "Region") + _ = viewCmd.MarkFlagRequired("region") + _ = viewCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/kms/kms.go b/cmd/kms/kms.go new file mode 100644 index 0000000..5ac4308 --- /dev/null +++ b/cmd/kms/kms.go @@ -0,0 +1,21 @@ +package kms + +import ( + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/cmd/kms/keys" +) + +// KmsCmd manages Key Management Service resources and crypto operations. +var KmsCmd = &cobra.Command{ + Use: "kms", + Short: "Manage KMS keys and cryptographic operations", +} + +var ( + noHeader bool +) + +func init() { + KmsCmd.AddCommand(keys.KeysCmd) +} diff --git a/cmd/kms/public_key.go b/cmd/kms/public_key.go new file mode 100644 index 0000000..b14df6c --- /dev/null +++ b/cmd/kms/public_key.go @@ -0,0 +1,71 @@ +package kms + +import ( + "fmt" + "sort" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + publicKeyRegion string + publicKeyKey string + publicKeyVersion int +) + +var publicKeyCmd = &cobra.Command{ + Use: "public-key", + Short: "Get the public key material for an asymmetric KMS key", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + var version *int + if cmd.Flags().Changed("version") { + v := publicKeyVersion + version = &v + } + + result, err := client.KMS().GetPublicKey(cmd.Context(), publicKeyRegion, publicKeyKey, version) + if err != nil { + return fmt.Errorf("failed to get public key: %w", err) + } + + keys := make([]string, 0, len(result.Keys)) + for k := range result.Keys { + keys = append(keys, k) + } + sort.Strings(keys) + + body := make([][]string, 0, len(keys)) + for _, k := range keys { + body = append(body, []string{k, result.Keys[k]}) + } + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Version", "Public Key"}, body) + } + return nil + }, +} + +func init() { + KmsCmd.AddCommand(publicKeyCmd) + publicKeyCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + publicKeyCmd.Flags().StringVar(&publicKeyRegion, "region", "", "Region") + publicKeyCmd.Flags().StringVar(&publicKeyKey, "key", "", "KMS key identity") + publicKeyCmd.Flags().IntVar(&publicKeyVersion, "version", 0, "Key version") + _ = publicKeyCmd.MarkFlagRequired("region") + _ = publicKeyCmd.MarkFlagRequired("key") + _ = publicKeyCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = publicKeyCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/kms/sign.go b/cmd/kms/sign.go new file mode 100644 index 0000000..128a20f --- /dev/null +++ b/cmd/kms/sign.go @@ -0,0 +1,63 @@ +package kms + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + signRegion string + signKey string + signInput string + signKeyVersion string + signHashAlgorithm string + signPrehashed bool + signContext string +) + +var signCmd = &cobra.Command{ + Use: "sign", + Short: "Sign input with an asymmetric KMS key", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().Sign(cmd.Context(), signRegion, signKey, clientkms.SignRequest{ + Input: signInput, + KeyVersion: signKeyVersion, + HashAlgorithm: signHashAlgorithm, + Prehashed: signPrehashed, + Context: signContext, + }) + if err != nil { + return fmt.Errorf("failed to sign: %w", err) + } + + fmt.Println(result.Signature) + return nil + }, +} + +func init() { + KmsCmd.AddCommand(signCmd) + signCmd.Flags().StringVar(&signRegion, "region", "", "Region") + signCmd.Flags().StringVar(&signKey, "key", "", "KMS key identity") + signCmd.Flags().StringVar(&signInput, "input", "", "Input to sign (as required by the API)") + signCmd.Flags().StringVar(&signKeyVersion, "key-version", "", "Key version") + signCmd.Flags().StringVar(&signHashAlgorithm, "hash-algorithm", "", "Hash algorithm") + signCmd.Flags().BoolVar(&signPrehashed, "prehashed", false, "Input is already hashed") + signCmd.Flags().StringVar(&signContext, "context", "", "Optional signing context") + _ = signCmd.MarkFlagRequired("region") + _ = signCmd.MarkFlagRequired("key") + _ = signCmd.MarkFlagRequired("input") + _ = signCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = signCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/kms/summary.go b/cmd/kms/summary.go new file mode 100644 index 0000000..7ca8f5c --- /dev/null +++ b/cmd/kms/summary.go @@ -0,0 +1,54 @@ +package kms + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var summaryCmd = &cobra.Command{ + Use: "summary", + Short: "Show KMS availability and regional key counts", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + summary, err := client.KMS().GetSummary(cmd.Context()) + if err != nil { + return fmt.Errorf("failed to get KMS summary: %w", err) + } + + fmt.Printf("Feature enabled: %v\n", summary.FeatureEnabled) + body := make([][]string, 0, len(summary.Regions)) + for _, r := range summary.Regions { + body = append(body, []string{ + r.Identity, + r.Name, + r.Slug, + fmt.Sprintf("%v", r.KmsAvailable), + fmt.Sprintf("%d", r.TotalKeys), + fmt.Sprintf("%d", r.ActiveKeys), + fmt.Sprintf("%d", r.DisabledKeys), + fmt.Sprintf("%d", r.PendingDeletionKeys), + }) + } + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Slug", "Available", "Total", "Active", "Disabled", "Pending Deletion"}, body) + } + return nil + }, +} + +func init() { + KmsCmd.AddCommand(summaryCmd) + summaryCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") +} diff --git a/cmd/kms/verify.go b/cmd/kms/verify.go new file mode 100644 index 0000000..8fd7f25 --- /dev/null +++ b/cmd/kms/verify.go @@ -0,0 +1,112 @@ +package kms + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +var ( + verifyRegion string + verifyKey string + verifyInput string + verifySignature string + verifyHashAlgorithm string + + verifyHmacRegion string + verifyHmacKey string + verifyHmacInput string + verifyHmacValue string + verifyHmacHashAlgorithm string +) + +func printValidity(valid bool) { + fmt.Printf("%v\n", valid) +} + +func runVerifySignature(ctx context.Context) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().VerifySignature(ctx, verifyRegion, verifyKey, clientkms.VerifySignatureRequest{ + Input: verifyInput, + Signature: verifySignature, + HashAlgorithm: verifyHashAlgorithm, + }) + if err != nil { + return fmt.Errorf("failed to verify signature: %w", err) + } + printValidity(result.Valid) + return nil +} + +func runVerifyHMAC(ctx context.Context) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().VerifyHMAC(ctx, verifyHmacRegion, verifyHmacKey, clientkms.VerifyHMACRequest{ + Input: verifyHmacInput, + HMAC: verifyHmacValue, + HashAlgorithm: verifyHmacHashAlgorithm, + }) + if err != nil { + return fmt.Errorf("failed to verify HMAC: %w", err) + } + printValidity(result.Valid) + return nil +} + +var verifyCmd = &cobra.Command{ + Use: "verify", + Short: "Verify a signature with an asymmetric KMS key", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + return runVerifySignature(cmd.Context()) + }, +} + +var verifyHmacCmd = &cobra.Command{ + Use: "verify-hmac", + Short: "Verify an HMAC with a KMS key", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + return runVerifyHMAC(cmd.Context()) + }, +} + +func init() { + KmsCmd.AddCommand(verifyCmd) + verifyCmd.Flags().StringVar(&verifyRegion, "region", "", "Region") + verifyCmd.Flags().StringVar(&verifyKey, "key", "", "KMS key identity") + verifyCmd.Flags().StringVar(&verifyInput, "input", "", "Input that was signed") + verifyCmd.Flags().StringVar(&verifySignature, "signature", "", "Signature to verify") + verifyCmd.Flags().StringVar(&verifyHashAlgorithm, "hash-algorithm", "", "Hash algorithm") + _ = verifyCmd.MarkFlagRequired("region") + _ = verifyCmd.MarkFlagRequired("key") + _ = verifyCmd.MarkFlagRequired("input") + _ = verifyCmd.MarkFlagRequired("signature") + _ = verifyCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = verifyCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) + + KmsCmd.AddCommand(verifyHmacCmd) + verifyHmacCmd.Flags().StringVar(&verifyHmacRegion, "region", "", "Region") + verifyHmacCmd.Flags().StringVar(&verifyHmacKey, "key", "", "KMS key identity") + verifyHmacCmd.Flags().StringVar(&verifyHmacInput, "input", "", "Input that was HMACed") + verifyHmacCmd.Flags().StringVar(&verifyHmacValue, "hmac", "", "HMAC value to verify") + verifyHmacCmd.Flags().StringVar(&verifyHmacHashAlgorithm, "hash-algorithm", "", "Hash algorithm") + _ = verifyHmacCmd.MarkFlagRequired("region") + _ = verifyHmacCmd.MarkFlagRequired("key") + _ = verifyHmacCmd.MarkFlagRequired("input") + _ = verifyHmacCmd.MarkFlagRequired("hmac") + _ = verifyHmacCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = verifyHmacCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/kms/wrapping_key.go b/cmd/kms/wrapping_key.go new file mode 100644 index 0000000..94f55ed --- /dev/null +++ b/cmd/kms/wrapping_key.go @@ -0,0 +1,47 @@ +package kms + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var wrappingKeyRegion string + +var wrappingKeyCmd = &cobra.Command{ + Use: "wrapping-key", + Short: "Get the regional wrapping public key for BYOK import", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + wrappingKey, err := client.KMS().GetWrappingKey(cmd.Context(), wrappingKeyRegion) + if err != nil { + return fmt.Errorf("failed to get wrapping key: %w", err) + } + + body := [][]string{{wrappingKey.Algorithm, wrappingKey.PublicKey}} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Algorithm", "Public Key"}, body) + } + return nil + }, +} + +func init() { + KmsCmd.AddCommand(wrappingKeyCmd) + wrappingKeyCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + wrappingKeyCmd.Flags().StringVar(&wrappingKeyRegion, "region", "", "Region") + _ = wrappingKeyCmd.MarkFlagRequired("region") + _ = wrappingKeyCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} From a2d2662a49c16e52d425aac76dcd948b490dbed3 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:14:57 +0200 Subject: [PATCH 04/19] feat(secrets): add secrets manager commands Browse and manage secret metadata, versions, and access policies without printing secret material unless get-value is used. --- cmd/cmd.go | 2 + cmd/secrets/browse.go | 87 ++++++++++++++++++++++ cmd/secrets/create.go | 131 +++++++++++++++++++++++++++++++++ cmd/secrets/delete.go | 54 ++++++++++++++ cmd/secrets/destroy_version.go | 56 ++++++++++++++ cmd/secrets/get_value.go | 68 +++++++++++++++++ cmd/secrets/list.go | 71 ++++++++++++++++++ cmd/secrets/policy.go | 54 ++++++++++++++ cmd/secrets/put.go | 75 +++++++++++++++++++ cmd/secrets/secrets.go | 17 +++++ cmd/secrets/view.go | 93 +++++++++++++++++++++++ 11 files changed, 708 insertions(+) create mode 100644 cmd/secrets/browse.go create mode 100644 cmd/secrets/create.go create mode 100644 cmd/secrets/delete.go create mode 100644 cmd/secrets/destroy_version.go create mode 100644 cmd/secrets/get_value.go create mode 100644 cmd/secrets/list.go create mode 100644 cmd/secrets/policy.go create mode 100644 cmd/secrets/put.go create mode 100644 cmd/secrets/secrets.go create mode 100644 cmd/secrets/view.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 05d6a7b..6cb9286 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -24,6 +24,7 @@ import ( "github.com/thalassa-cloud/cli/cmd/projects" "github.com/thalassa-cloud/cli/cmd/quotas" "github.com/thalassa-cloud/cli/cmd/registry" + "github.com/thalassa-cloud/cli/cmd/secrets" "github.com/thalassa-cloud/cli/cmd/version" "github.com/thalassa-cloud/cli/internal/completion" "github.com/thalassa-cloud/cli/internal/config/contextstate" @@ -75,6 +76,7 @@ func init() { RootCmd.AddCommand(objectstorage.ObjectStorageCmd) RootCmd.AddCommand(dns.DnsCmd) RootCmd.AddCommand(kms.KmsCmd) + RootCmd.AddCommand(secrets.SecretsCmd) RootCmd.AddCommand(kubernetes.KubernetesCmd) RootCmd.AddCommand(dbaas.DbaasCmd) diff --git a/cmd/secrets/browse.go b/cmd/secrets/browse.go new file mode 100644 index 0000000..f68050e --- /dev/null +++ b/cmd/secrets/browse.go @@ -0,0 +1,87 @@ +package secrets + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + browseRegion string + browsePath string +) + +var browseCmd = &cobra.Command{ + Use: "browse", + Short: "Browse secret prefixes and secrets at a path", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.Secrets().BrowseSecrets(cmd.Context(), browseRegion, browsePath) + if err != nil { + return fmt.Errorf("failed to browse secrets: %w", err) + } + + if len(result.Prefixes) > 0 { + prefixBody := make([][]string, 0, len(result.Prefixes)) + for _, p := range result.Prefixes { + prefixBody = append(prefixBody, []string{p}) + } + if noHeader { + table.Print(nil, prefixBody) + } else { + fmt.Println("Prefixes:") + table.Print([]string{"Prefix"}, prefixBody) + } + } + + if len(result.Secrets) > 0 { + if len(result.Prefixes) > 0 { + fmt.Println() + } + secretBody := make([][]string, 0, len(result.Secrets)) + for _, s := range result.Secrets { + kmsKey := "-" + if s.KmsKey != nil { + kmsKey = s.KmsKey.Identity + if kmsKey == "" { + kmsKey = s.KmsKey.Name + } + } + secretBody = append(secretBody, []string{ + s.Path, + fmt.Sprintf("%d", s.CurrentVersion), + kmsKey, + formattime.FormatTime(s.UpdatedAt.Local(), showExactTime), + }) + } + if noHeader { + table.Print(nil, secretBody) + } else { + fmt.Println("Secrets:") + table.Print([]string{"Path", "Version", "KMS Key", "Updated"}, secretBody) + } + } + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(browseCmd) + browseCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + browseCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + browseCmd.Flags().StringVar(&browseRegion, "region", "", "Region") + browseCmd.Flags().StringVar(&browsePath, "path", "/", "Path to browse") + _ = browseCmd.MarkFlagRequired("region") + _ = browseCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/secrets/create.go b/cmd/secrets/create.go new file mode 100644 index 0000000..9845632 --- /dev/null +++ b/cmd/secrets/create.go @@ -0,0 +1,131 @@ +package secrets + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientsecrets "github.com/thalassa-cloud/client-go/secrets" +) + +var ( + createRegion string + createPath string + createDescription string + createKmsKey string + createString string + createFromFile string + createKV []string + createGenerateLen int + createLabels []string + createAnnotations []string + createPolicyFile string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a secret (metadata response only; use get-value to read material)", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + if createPath == "" { + return fmt.Errorf("--path is required") + } + if createKmsKey == "" { + return fmt.Errorf("--kms-key is required") + } + + secretString := createString + if createFromFile != "" { + data, err := os.ReadFile(createFromFile) + if err != nil { + return fmt.Errorf("read --from-file: %w", err) + } + secretString = string(data) + } + + req := clientsecrets.CreateSecretRequest{ + Path: createPath, + Description: createDescription, + Labels: shared.KeyValuePairsToMap(createLabels), + Annotations: shared.KeyValuePairsToMap(createAnnotations), + KmsKeyIdentity: createKmsKey, + SecretString: secretString, + SecretKeyValues: shared.KeyValuePairsToMap(createKV), + } + if createGenerateLen > 0 { + req.GenerateSecret = &clientsecrets.GenerateSecret{ByteLength: createGenerateLen} + } + if createPolicyFile != "" { + policy, err := readSecretPolicy(createPolicyFile) + if err != nil { + return err + } + req.AccessPolicy = policy + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + secret, err := client.Secrets().CreateSecret(cmd.Context(), createRegion, req) + if err != nil { + return fmt.Errorf("failed to create secret: %w", err) + } + + kmsKey := "-" + if secret.KmsKey != nil { + kmsKey = secret.KmsKey.Identity + } + body := [][]string{{ + secret.Path, + fmt.Sprintf("%d", secret.CurrentVersion), + kmsKey, + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Path", "Version", "KMS Key"}, body) + } + return nil + }, +} + +func readSecretPolicy(path string) (*clientsecrets.SecretPolicy, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read policy file: %w", err) + } + var policy clientsecrets.SecretPolicy + if err := json.Unmarshal(data, &policy); err != nil { + return nil, fmt.Errorf("parse policy JSON: %w", err) + } + return &policy, nil +} + +func init() { + SecretsCmd.AddCommand(createCmd) + createCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + createCmd.Flags().StringVar(&createRegion, "region", "", "Region") + createCmd.Flags().StringVar(&createPath, "path", "", "Secret path") + createCmd.Flags().StringVar(&createDescription, "description", "", "Description") + createCmd.Flags().StringVar(&createKmsKey, "kms-key", "", "KMS key identity used to encrypt the secret") + createCmd.Flags().StringVar(&createString, "string", "", "Secret string value") + createCmd.Flags().StringVar(&createFromFile, "from-file", "", "Read secret string from a file") + createCmd.Flags().StringSliceVar(&createKV, "kv", nil, "Secret key/value pairs as key=value (repeatable)") + createCmd.Flags().IntVar(&createGenerateLen, "generate-bytes", 0, "Generate a random secret of this many bytes") + createCmd.Flags().StringSliceVar(&createLabels, "labels", nil, "Labels as key=value (repeatable)") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") + createCmd.Flags().StringVar(&createPolicyFile, "policy-file", "", "JSON file with an access policy") + _ = createCmd.MarkFlagRequired("region") + _ = createCmd.MarkFlagRequired("path") + _ = createCmd.MarkFlagRequired("kms-key") + _ = createCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = createCmd.RegisterFlagCompletionFunc("kms-key", completion.CompleteKmsKeyIdentity) +} diff --git a/cmd/secrets/delete.go b/cmd/secrets/delete.go new file mode 100644 index 0000000..21bffd0 --- /dev/null +++ b/cmd/secrets/delete.go @@ -0,0 +1,54 @@ +package secrets + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + deleteRegion string + deletePath string + deleteForce bool +) + +var deleteCmd = &cobra.Command{ + Use: "delete", + Aliases: []string{"rm", "del"}, + Short: "Delete a secret", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + proceed, err := shared.PromptDestructiveUnlessForce(deleteForce, fmt.Sprintf("Delete secret %q in region %s?\n", deletePath, deleteRegion)) + if err != nil { + return err + } + if !proceed { + return nil + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + if err := client.Secrets().DeleteSecret(cmd.Context(), deleteRegion, deletePath); err != nil { + return fmt.Errorf("failed to delete secret: %w", err) + } + fmt.Printf("Secret %s deleted\n", deletePath) + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(deleteCmd) + deleteCmd.Flags().StringVar(&deleteRegion, "region", "", "Region") + deleteCmd.Flags().StringVar(&deletePath, "path", "", "Secret path") + deleteCmd.Flags().BoolVar(&deleteForce, shared.ForceKey, false, "Skip confirmation") + _ = deleteCmd.MarkFlagRequired("region") + _ = deleteCmd.MarkFlagRequired("path") + _ = deleteCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/secrets/destroy_version.go b/cmd/secrets/destroy_version.go new file mode 100644 index 0000000..558b4a0 --- /dev/null +++ b/cmd/secrets/destroy_version.go @@ -0,0 +1,56 @@ +package secrets + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + destroyRegion string + destroyPath string + destroyVersion int + destroyForce bool +) + +var destroyVersionCmd = &cobra.Command{ + Use: "destroy-version", + Short: "Permanently destroy a secret version", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + proceed, err := shared.PromptDestructiveUnlessForce(destroyForce, fmt.Sprintf("Destroy version %d of secret %q?\n", destroyVersion, destroyPath)) + if err != nil { + return err + } + if !proceed { + return nil + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + if err := client.Secrets().DestroySecretVersion(cmd.Context(), destroyRegion, destroyPath, destroyVersion); err != nil { + return fmt.Errorf("failed to destroy secret version: %w", err) + } + fmt.Printf("Destroyed version %d of %s\n", destroyVersion, destroyPath) + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(destroyVersionCmd) + destroyVersionCmd.Flags().StringVar(&destroyRegion, "region", "", "Region") + destroyVersionCmd.Flags().StringVar(&destroyPath, "path", "", "Secret path") + destroyVersionCmd.Flags().IntVar(&destroyVersion, "version", 0, "Version to destroy") + destroyVersionCmd.Flags().BoolVar(&destroyForce, shared.ForceKey, false, "Skip confirmation") + _ = destroyVersionCmd.MarkFlagRequired("region") + _ = destroyVersionCmd.MarkFlagRequired("path") + _ = destroyVersionCmd.MarkFlagRequired("version") + _ = destroyVersionCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/secrets/get_value.go b/cmd/secrets/get_value.go new file mode 100644 index 0000000..10eeba6 --- /dev/null +++ b/cmd/secrets/get_value.go @@ -0,0 +1,68 @@ +package secrets + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + getValueRegion string + getValuePath string + getValueVersion int +) + +var getValueCmd = &cobra.Command{ + Use: "get-value", + Short: "Print secret material to stdout", + Long: "Fetches and prints the secret value. Prefer piping to a file and avoid logging the output.", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + var version *int + if cmd.Flags().Changed("version") { + v := getValueVersion + version = &v + } + + result, err := client.Secrets().GetSecretValue(cmd.Context(), getValueRegion, getValuePath, version) + if err != nil { + return fmt.Errorf("failed to get secret value: %w", err) + } + + switch { + case result.SecretString != "": + fmt.Print(result.SecretString) + if len(result.SecretString) == 0 || result.SecretString[len(result.SecretString)-1] != '\n' { + fmt.Println() + } + case len(result.SecretKeyValues) > 0: + enc := json.NewEncoder(cmd.OutOrStdout()) + enc.SetIndent("", " ") + if err := enc.Encode(result.SecretKeyValues); err != nil { + return err + } + default: + fmt.Println("{}") + } + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(getValueCmd) + getValueCmd.Flags().StringVar(&getValueRegion, "region", "", "Region") + getValueCmd.Flags().StringVar(&getValuePath, "path", "", "Secret path") + getValueCmd.Flags().IntVar(&getValueVersion, "version", 0, "Specific version (defaults to current)") + _ = getValueCmd.MarkFlagRequired("region") + _ = getValueCmd.MarkFlagRequired("path") + _ = getValueCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/secrets/list.go b/cmd/secrets/list.go new file mode 100644 index 0000000..ee84a07 --- /dev/null +++ b/cmd/secrets/list.go @@ -0,0 +1,71 @@ +package secrets + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + listRegion string + listPrefix string +) + +var listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List secrets under a path prefix (metadata only)", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + secrets, err := client.Secrets().ListSecrets(cmd.Context(), listRegion, listPrefix) + if err != nil { + return fmt.Errorf("failed to list secrets: %w", err) + } + + body := make([][]string, 0, len(secrets)) + for _, s := range secrets { + kmsKey := "-" + if s.KmsKey != nil { + kmsKey = s.KmsKey.Identity + if kmsKey == "" { + kmsKey = s.KmsKey.Name + } + } + body = append(body, []string{ + s.Path, + s.Description, + fmt.Sprintf("%d", s.CurrentVersion), + kmsKey, + formattime.FormatTime(s.CreatedAt.Local(), showExactTime), + }) + } + + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Path", "Description", "Version", "KMS Key", "Created"}, body) + } + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(listCmd) + listCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + listCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + listCmd.Flags().StringVar(&listRegion, "region", "", "Region") + listCmd.Flags().StringVar(&listPrefix, "prefix", "/", "Path prefix") + _ = listCmd.MarkFlagRequired("region") + _ = listCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/secrets/policy.go b/cmd/secrets/policy.go new file mode 100644 index 0000000..3859606 --- /dev/null +++ b/cmd/secrets/policy.go @@ -0,0 +1,54 @@ +package secrets + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientsecrets "github.com/thalassa-cloud/client-go/secrets" +) + +var ( + policyRegion string + policyPath string + policyFile string +) + +var policyCmd = &cobra.Command{ + Use: "policy", + Short: "Replace a secret access policy from a JSON file", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + policy, err := readSecretPolicy(policyFile) + if err != nil { + return err + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + secret, err := client.Secrets().UpdateAccessPolicy(cmd.Context(), policyRegion, policyPath, clientsecrets.UpdateAccessPolicyRequest{ + AccessPolicy: *policy, + }) + if err != nil { + return fmt.Errorf("failed to update access policy: %w", err) + } + fmt.Printf("Updated access policy for %s\n", secret.Path) + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(policyCmd) + policyCmd.Flags().StringVar(&policyRegion, "region", "", "Region") + policyCmd.Flags().StringVar(&policyPath, "path", "", "Secret path") + policyCmd.Flags().StringVar(&policyFile, "file", "", "JSON file containing the access policy") + _ = policyCmd.MarkFlagRequired("region") + _ = policyCmd.MarkFlagRequired("path") + _ = policyCmd.MarkFlagRequired("file") + _ = policyCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/secrets/put.go b/cmd/secrets/put.go new file mode 100644 index 0000000..0414ce4 --- /dev/null +++ b/cmd/secrets/put.go @@ -0,0 +1,75 @@ +package secrets + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientsecrets "github.com/thalassa-cloud/client-go/secrets" +) + +var ( + putRegion string + putPath string + putString string + putFromFile string + putKV []string + putGenerateLen int +) + +var putCmd = &cobra.Command{ + Use: "put", + Short: "Put a new secret version", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + secretString := putString + if putFromFile != "" { + data, err := os.ReadFile(putFromFile) + if err != nil { + return fmt.Errorf("read --from-file: %w", err) + } + secretString = string(data) + } + + req := clientsecrets.PutSecretValueRequest{ + Path: putPath, + SecretString: secretString, + SecretKeyValues: shared.KeyValuePairsToMap(putKV), + } + if putGenerateLen > 0 { + req.GenerateSecret = &clientsecrets.GenerateSecret{ByteLength: putGenerateLen} + } + if req.SecretString == "" && len(req.SecretKeyValues) == 0 && req.GenerateSecret == nil { + return fmt.Errorf("provide --string, --from-file, --kv, or --generate-bytes") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.Secrets().PutSecretValue(cmd.Context(), putRegion, putPath, req) + if err != nil { + return fmt.Errorf("failed to put secret value: %w", err) + } + fmt.Printf("Secret %s updated to version %d\n", result.Path, result.Version) + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(putCmd) + putCmd.Flags().StringVar(&putRegion, "region", "", "Region") + putCmd.Flags().StringVar(&putPath, "path", "", "Secret path") + putCmd.Flags().StringVar(&putString, "string", "", "Secret string value") + putCmd.Flags().StringVar(&putFromFile, "from-file", "", "Read secret string from a file") + putCmd.Flags().StringSliceVar(&putKV, "kv", nil, "Secret key/value pairs as key=value (repeatable)") + putCmd.Flags().IntVar(&putGenerateLen, "generate-bytes", 0, "Generate a random secret of this many bytes") + _ = putCmd.MarkFlagRequired("region") + _ = putCmd.MarkFlagRequired("path") + _ = putCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/secrets/secrets.go b/cmd/secrets/secrets.go new file mode 100644 index 0000000..3caefb7 --- /dev/null +++ b/cmd/secrets/secrets.go @@ -0,0 +1,17 @@ +package secrets + +import ( + "github.com/spf13/cobra" +) + +// SecretsCmd manages Secrets Manager resources. +var SecretsCmd = &cobra.Command{ + Use: "secrets", + Aliases: []string{"secret"}, + Short: "Manage secrets", +} + +var ( + noHeader bool + showExactTime bool +) diff --git a/cmd/secrets/view.go b/cmd/secrets/view.go new file mode 100644 index 0000000..a481a11 --- /dev/null +++ b/cmd/secrets/view.go @@ -0,0 +1,93 @@ +package secrets + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + viewRegion string + viewPath string + viewVersions bool +) + +var viewCmd = &cobra.Command{ + Use: "view", + Short: "View secret metadata (does not print secret material)", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + secret, err := client.Secrets().GetSecret(cmd.Context(), viewRegion, viewPath, viewVersions) + if err != nil { + return fmt.Errorf("failed to get secret: %w", err) + } + + kmsKey := "-" + if secret.KmsKey != nil { + kmsKey = secret.KmsKey.Identity + if kmsKey == "" { + kmsKey = secret.KmsKey.Name + } + } + + body := [][]string{ + {"Path", secret.Path}, + {"Description", secret.Description}, + {"Current Version", fmt.Sprintf("%d", secret.CurrentVersion)}, + {"KMS Key", kmsKey}, + {"Created", formattime.FormatTime(secret.CreatedAt.Local(), showExactTime)}, + {"Updated", formattime.FormatTime(secret.UpdatedAt.Local(), showExactTime)}, + } + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Field", "Value"}, body) + } + + if viewVersions && len(secret.Versions) > 0 { + fmt.Println() + versionBody := make([][]string, 0, len(secret.Versions)) + for _, v := range secret.Versions { + destroyed := "-" + if v.DestroyedAt != nil { + destroyed = formattime.FormatTime(v.DestroyedAt.Local(), showExactTime) + } + versionBody = append(versionBody, []string{ + fmt.Sprintf("%d", v.Version), + v.Status, + formattime.FormatTime(v.CreatedAt.Local(), showExactTime), + destroyed, + }) + } + if noHeader { + table.Print(nil, versionBody) + } else { + table.Print([]string{"Version", "Status", "Created", "Destroyed"}, versionBody) + } + } + return nil + }, +} + +func init() { + SecretsCmd.AddCommand(viewCmd) + viewCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + viewCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + viewCmd.Flags().StringVar(&viewRegion, "region", "", "Region") + viewCmd.Flags().StringVar(&viewPath, "path", "", "Secret path") + viewCmd.Flags().BoolVar(&viewVersions, "versions", false, "Include version history") + _ = viewCmd.MarkFlagRequired("region") + _ = viewCmd.MarkFlagRequired("path") + _ = viewCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} From 21124ca2e658d09d2507d7e167e22677fdf08f86 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:14:58 +0200 Subject: [PATCH 05/19] feat(networking): add reserved IP management Create, update, and associate reserved public IPs with load balancers or NAT gateways. --- cmd/iaas/networking/networking.go | 2 + cmd/iaas/networking/reservedips/associate.go | 77 ++++++++++++ cmd/iaas/networking/reservedips/completion.go | 13 ++ cmd/iaas/networking/reservedips/create.go | 94 +++++++++++++++ cmd/iaas/networking/reservedips/delete.go | 103 ++++++++++++++++ .../networking/reservedips/disassociate.go | 52 ++++++++ cmd/iaas/networking/reservedips/helpers.go | 48 ++++++++ cmd/iaas/networking/reservedips/list.go | 112 ++++++++++++++++++ .../networking/reservedips/reservedips.go | 21 ++++ cmd/iaas/networking/reservedips/update.go | 93 +++++++++++++++ cmd/iaas/networking/reservedips/view.go | 69 +++++++++++ 11 files changed, 684 insertions(+) create mode 100644 cmd/iaas/networking/reservedips/associate.go create mode 100644 cmd/iaas/networking/reservedips/completion.go create mode 100644 cmd/iaas/networking/reservedips/create.go create mode 100644 cmd/iaas/networking/reservedips/delete.go create mode 100644 cmd/iaas/networking/reservedips/disassociate.go create mode 100644 cmd/iaas/networking/reservedips/helpers.go create mode 100644 cmd/iaas/networking/reservedips/list.go create mode 100644 cmd/iaas/networking/reservedips/reservedips.go create mode 100644 cmd/iaas/networking/reservedips/update.go create mode 100644 cmd/iaas/networking/reservedips/view.go diff --git a/cmd/iaas/networking/networking.go b/cmd/iaas/networking/networking.go index ef08215..7192b83 100644 --- a/cmd/iaas/networking/networking.go +++ b/cmd/iaas/networking/networking.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" "github.com/thalassa-cloud/cli/cmd/iaas/networking/loadbalancers" "github.com/thalassa-cloud/cli/cmd/iaas/networking/natgateways" + "github.com/thalassa-cloud/cli/cmd/iaas/networking/reservedips" "github.com/thalassa-cloud/cli/cmd/iaas/networking/routetables" "github.com/thalassa-cloud/cli/cmd/iaas/networking/securitygroups" "github.com/thalassa-cloud/cli/cmd/iaas/networking/subnets" @@ -25,6 +26,7 @@ func init() { NetworkingCmd.AddCommand(subnets.SubnetsCmd) NetworkingCmd.AddCommand(routetables.RouteTablesCmd) NetworkingCmd.AddCommand(natgateways.NatGatewaysCmd) + NetworkingCmd.AddCommand(reservedips.ReservedIPsCmd) NetworkingCmd.AddCommand(loadbalancers.LoadbalancersCmd) NetworkingCmd.AddCommand(targetgroups.TargetGroupsCmd) NetworkingCmd.AddCommand(securitygroups.SecurityGroupsCmd) diff --git a/cmd/iaas/networking/reservedips/associate.go b/cmd/iaas/networking/reservedips/associate.go new file mode 100644 index 0000000..d3f97cc --- /dev/null +++ b/cmd/iaas/networking/reservedips/associate.go @@ -0,0 +1,77 @@ +package reservedips + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + associateLoadbalancer string + associateNatGateway string +) + +var associateCmd = &cobra.Command{ + Use: "associate", + Short: "Associate a reserved IP with a resource", + Long: "Associate a reserved IP with exactly one of a load balancer or NAT gateway.", + Example: "tcloud networking reserved-ips associate rip-123 --loadbalancer lb-456\ntcloud networking reserved-ips associate rip-123 --nat-gateway ngw-789", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + hasLB := associateLoadbalancer != "" + hasNGW := associateNatGateway != "" + if hasLB == hasNGW { + return fmt.Errorf("exactly one of --loadbalancer or --nat-gateway must be provided") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req := iaas.AssociateReservedIpRequest{} + if hasLB { + req.LoadbalancerIdentity = &associateLoadbalancer + } + if hasNGW { + req.NatGatewayIdentity = &associateNatGateway + } + + rip, err := client.IaaS().AssociateReservedIP(cmd.Context(), args[0], req) + if err != nil { + return err + } + + body := [][]string{{ + rip.Identity, + rip.Name, + regionName(*rip), + string(rip.Status), + dashIfEmpty(rip.IPv4Address), + dashIfEmpty(rip.IPv6Address), + attachedTo(*rip), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Region", "Status", "IPv4", "IPv6", "AttachedTo"}, body) + } + return nil + }, +} + +func init() { + ReservedIPsCmd.AddCommand(associateCmd) + + associateCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + associateCmd.Flags().StringVar(&associateLoadbalancer, "loadbalancer", "", "Load balancer identity to associate with") + associateCmd.Flags().StringVar(&associateNatGateway, "nat-gateway", "", "NAT gateway identity to associate with") + + associateCmd.ValidArgsFunction = completeReservedIPID + _ = associateCmd.RegisterFlagCompletionFunc("loadbalancer", completeLoadbalancerID) + _ = associateCmd.RegisterFlagCompletionFunc("nat-gateway", completeNatGatewayID) +} diff --git a/cmd/iaas/networking/reservedips/completion.go b/cmd/iaas/networking/reservedips/completion.go new file mode 100644 index 0000000..c248695 --- /dev/null +++ b/cmd/iaas/networking/reservedips/completion.go @@ -0,0 +1,13 @@ +package reservedips + +import ( + "github.com/thalassa-cloud/cli/internal/completion" +) + +var ( + completeReservedIPID = completion.CompleteReservedIPID + completeRegion = completion.CompleteRegion + completeLoadbalancerID = completion.CompleteLoadbalancerID + completeNatGatewayID = completion.CompleteNatGatewayID + completeOutputFormat = completion.CompleteOutputFormat +) diff --git a/cmd/iaas/networking/reservedips/create.go b/cmd/iaas/networking/reservedips/create.go new file mode 100644 index 0000000..f5e0e9e --- /dev/null +++ b/cmd/iaas/networking/reservedips/create.go @@ -0,0 +1,94 @@ +package reservedips + +import ( + "fmt" + + "github.com/spf13/cobra" + + 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/iaas" +) + +var ( + createName string + createDescription string + createRegion string + createLabels []string + createAnnotations []string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a reserved IP address", + Long: "Create a new reserved public IP address in the specified region.", + Example: "tcloud networking reserved-ips create --name my-ip --region nl-ams\ntcloud networking reserved-ips create --name lb-ip --region nl-ams --description 'for production LB'", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if createName == "" { + return fmt.Errorf("name is required") + } + if createRegion == "" { + return fmt.Errorf("region is required") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + regions, err := client.IaaS().ListRegions(cmd.Context(), &iaas.ListRegionsRequest{}) + if err != nil { + return fmt.Errorf("failed to list regions: %w", err) + } + region, err := iaasutil.FindRegionByIdentitySlugOrNameWithError(regions, createRegion) + if err != nil { + return err + } + + req := iaas.CreateReservedIpRequest{ + Name: createName, + Description: createDescription, + Labels: parseKeyValueSlice(createLabels), + Annotations: parseKeyValueSlice(createAnnotations), + Region: region.Identity, + } + + rip, err := client.IaaS().CreateReservedIP(cmd.Context(), req) + if err != nil { + return err + } + + body := [][]string{{ + rip.Identity, + rip.Name, + regionName(*rip), + string(rip.Status), + dashIfEmpty(rip.IPv4Address), + dashIfEmpty(rip.IPv6Address), + attachedTo(*rip), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Region", "Status", "IPv4", "IPv6", "AttachedTo"}, body) + } + return nil + }, +} + +func init() { + ReservedIPsCmd.AddCommand(createCmd) + + createCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + createCmd.Flags().StringVar(&createName, "name", "", "Name of the reserved IP") + createCmd.Flags().StringVar(&createDescription, "description", "", "Description of the reserved IP") + createCmd.Flags().StringVar(&createRegion, "region", "", "Region for the reserved IP") + createCmd.Flags().StringSliceVar(&createLabels, "labels", []string{}, "Labels in key=value format") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", []string{}, "Annotations in key=value format") + + _ = createCmd.MarkFlagRequired("name") + _ = createCmd.MarkFlagRequired("region") + _ = createCmd.RegisterFlagCompletionFunc("region", completeRegion) +} diff --git a/cmd/iaas/networking/reservedips/delete.go b/cmd/iaas/networking/reservedips/delete.go new file mode 100644 index 0000000..8f77ca2 --- /dev/null +++ b/cmd/iaas/networking/reservedips/delete.go @@ -0,0 +1,103 @@ +package reservedips + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/labels" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/filters" + "github.com/thalassa-cloud/client-go/iaas" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var ( + deleteForce bool + deleteLabelSelector string +) + +var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete reserved IP address(es)", + Long: "Delete reserved IP address(es) by identity or label selector. Attached reserved IPs are disassociated before deletion.", + Example: "tcloud networking reserved-ips delete rip-123 --force\ntcloud networking reserved-ips delete --selector env=test --force", + Aliases: []string{"d", "del", "remove", "rm"}, + Args: cobra.MinimumNArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 && deleteLabelSelector == "" { + return fmt.Errorf("either reserved IP identity(ies) or --selector must be provided") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + toDelete := []iaas.ReservedIP{} + if deleteLabelSelector != "" { + all, err := client.IaaS().ListReservedIPs(cmd.Context(), &iaas.ListReservedIPsRequest{ + Filters: []filters.Filter{ + &filters.LabelFilter{MatchLabels: labels.ParseLabelSelector(deleteLabelSelector)}, + }, + }) + if err != nil { + return fmt.Errorf("failed to list reserved IPs: %w", err) + } + if len(all) == 0 { + fmt.Println("No reserved IPs found matching the label selector") + return nil + } + toDelete = append(toDelete, all...) + } else { + for _, identity := range args { + rip, err := client.IaaS().GetReservedIP(cmd.Context(), identity) + if err != nil { + if tcclient.IsNotFound(err) { + fmt.Printf("Reserved IP %s not found\n", identity) + continue + } + return fmt.Errorf("failed to get reserved IP: %w", err) + } + toDelete = append(toDelete, *rip) + } + } + + if len(toDelete) == 0 { + fmt.Println("No reserved IPs to delete") + return nil + } + + var summary strings.Builder + fmt.Fprintf(&summary, "Are you sure you want to delete the following reserved IP(s)?\n") + for _, rip := range toDelete { + fmt.Fprintf(&summary, " %s (%s)\n", rip.Name, rip.Identity) + } + proceed, err := shared.PromptDestructiveUnlessForce(deleteForce, summary.String()) + if err != nil { + return err + } + if !proceed { + return nil + } + + for _, rip := range toDelete { + fmt.Printf("Deleting reserved IP: %s (%s)\n", rip.Name, rip.Identity) + if err := client.IaaS().DeleteReservedIP(cmd.Context(), rip.Identity); err != nil { + return fmt.Errorf("failed to delete reserved IP: %w", err) + } + fmt.Printf("Reserved IP %s deleted successfully\n", rip.Identity) + } + return nil + }, +} + +func init() { + ReservedIPsCmd.AddCommand(deleteCmd) + + deleteCmd.Flags().BoolVar(&deleteForce, "force", false, "Force the deletion and skip the confirmation") + deleteCmd.Flags().StringVarP(&deleteLabelSelector, "selector", "l", "", "Label selector to filter reserved IPs (format: key1=value1,key2=value2)") + deleteCmd.ValidArgsFunction = completeReservedIPID +} diff --git a/cmd/iaas/networking/reservedips/disassociate.go b/cmd/iaas/networking/reservedips/disassociate.go new file mode 100644 index 0000000..c7f0ee1 --- /dev/null +++ b/cmd/iaas/networking/reservedips/disassociate.go @@ -0,0 +1,52 @@ +package reservedips + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var disassociateCmd = &cobra.Command{ + Use: "disassociate", + Short: "Disassociate a reserved IP from its resource", + Long: "Detach a reserved IP from its currently associated load balancer or NAT gateway.", + Example: "tcloud networking reserved-ips disassociate rip-123", + Aliases: []string{"detach"}, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + rip, err := client.IaaS().DisassociateReservedIP(cmd.Context(), args[0]) + if err != nil { + return err + } + + body := [][]string{{ + rip.Identity, + rip.Name, + regionName(*rip), + string(rip.Status), + dashIfEmpty(rip.IPv4Address), + dashIfEmpty(rip.IPv6Address), + attachedTo(*rip), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Region", "Status", "IPv4", "IPv6", "AttachedTo"}, body) + } + return nil + }, +} + +func init() { + ReservedIPsCmd.AddCommand(disassociateCmd) + disassociateCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + disassociateCmd.ValidArgsFunction = completeReservedIPID +} diff --git a/cmd/iaas/networking/reservedips/helpers.go b/cmd/iaas/networking/reservedips/helpers.go new file mode 100644 index 0000000..5433b8f --- /dev/null +++ b/cmd/iaas/networking/reservedips/helpers.go @@ -0,0 +1,48 @@ +package reservedips + +import ( + "strings" + + "github.com/thalassa-cloud/client-go/iaas" +) + +func parseKeyValueSlice(items []string) map[string]string { + result := make(map[string]string) + for _, item := range items { + parts := strings.SplitN(item, "=", 2) + if len(parts) == 2 { + result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + return result +} + +func regionName(rip iaas.ReservedIP) string { + if rip.Region == nil { + return "" + } + if rip.Region.Name != "" { + return rip.Region.Name + } + if rip.Region.Slug != "" { + return rip.Region.Slug + } + return rip.Region.Identity +} + +func attachedTo(rip iaas.ReservedIP) string { + if rip.AttachedToResourceIdentity == "" { + return "-" + } + if rip.AttachedToResourceType != "" { + return string(rip.AttachedToResourceType) + ":" + rip.AttachedToResourceIdentity + } + return rip.AttachedToResourceIdentity +} + +func dashIfEmpty(s string) string { + if s == "" { + return "-" + } + return s +} diff --git a/cmd/iaas/networking/reservedips/list.go b/cmd/iaas/networking/reservedips/list.go new file mode 100644 index 0000000..867b405 --- /dev/null +++ b/cmd/iaas/networking/reservedips/list.go @@ -0,0 +1,112 @@ +package reservedips + +import ( + "fmt" + "sort" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/labels" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/filters" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + showExactTime bool + showLabels bool + listLabelSelector string + listRegion string +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "List reserved IP addresses", + Long: "List reserved IP addresses within your organisation.", + Example: "tcloud networking reserved-ips list\ntcloud networking reserved-ips list --region nl-ams\ntcloud networking reserved-ips list --selector env=prod", + Aliases: []string{"g", "get", "ls"}, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + f := filters.Filters{} + if listRegion != "" { + f = append(f, &filters.FilterKeyValue{ + Key: "region", + Value: listRegion, + }) + } + if listLabelSelector != "" { + f = append(f, &filters.LabelFilter{ + MatchLabels: labels.ParseLabelSelector(listLabelSelector), + }) + } + + reservedIPs, err := client.IaaS().ListReservedIPs(cmd.Context(), &iaas.ListReservedIPsRequest{ + Filters: f, + }) + if err != nil { + return err + } + + body := make([][]string, 0, len(reservedIPs)) + for _, rip := range reservedIPs { + row := []string{ + rip.Identity, + rip.Name, + regionName(rip), + string(rip.Status), + dashIfEmpty(rip.IPv4Address), + dashIfEmpty(rip.IPv6Address), + attachedTo(rip), + } + if showLabels { + labelPairs := make([]string, 0, len(rip.Labels)) + for k, v := range rip.Labels { + labelPairs = append(labelPairs, k+"="+v) + } + sort.Strings(labelPairs) + if len(labelPairs) == 0 { + labelPairs = []string{"-"} + } + row = append(row, strings.Join(labelPairs, ",")) + } + if showExactTime { + row = append(row, formattime.FormatTime(rip.CreatedAt.Local(), true)) + } + body = append(body, row) + } + + if noHeader { + table.Print(nil, body) + } else { + headers := []string{"ID", "Name", "Region", "Status", "IPv4", "IPv6", "AttachedTo"} + if showLabels { + headers = append(headers, "Labels") + } + if showExactTime { + headers = append(headers, "Created") + } + table.Print(headers, body) + } + return nil + }, +} + +func init() { + ReservedIPsCmd.AddCommand(listCmd) + + listCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + listCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show exact creation time") + listCmd.Flags().BoolVar(&showLabels, "show-labels", false, "Show labels") + listCmd.Flags().StringVar(&listRegion, "region", "", "Filter by region") + listCmd.Flags().StringVarP(&listLabelSelector, "selector", "l", "", "Label selector to filter reserved IPs (format: key1=value1,key2=value2)") + + _ = listCmd.RegisterFlagCompletionFunc("region", completeRegion) +} diff --git a/cmd/iaas/networking/reservedips/reservedips.go b/cmd/iaas/networking/reservedips/reservedips.go new file mode 100644 index 0000000..67d0a0b --- /dev/null +++ b/cmd/iaas/networking/reservedips/reservedips.go @@ -0,0 +1,21 @@ +package reservedips + +import ( + "github.com/spf13/cobra" +) + +const NoHeaderKey = "no-header" + +var noHeader bool + +// ReservedIPsCmd manages reserved IP addresses. +var ReservedIPsCmd = &cobra.Command{ + Use: "reserved-ips", + Aliases: []string{"reserved-ip", "rip"}, + Short: "Manage reserved IP addresses", + Long: "Manage reserved public IP addresses that can be associated with load balancers or NAT gateways.", + Example: "tcloud networking reserved-ips list\ntcloud networking reserved-ips create --name my-ip --region nl-ams\ntcloud networking reserved-ips associate rip-123 --nat-gateway ngw-456", +} + +func init() { +} diff --git a/cmd/iaas/networking/reservedips/update.go b/cmd/iaas/networking/reservedips/update.go new file mode 100644 index 0000000..e493e70 --- /dev/null +++ b/cmd/iaas/networking/reservedips/update.go @@ -0,0 +1,93 @@ +package reservedips + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var ( + updateName string + updateDescription string + updateLabels []string + updateAnnotations []string +) + +var updateCmd = &cobra.Command{ + Use: "update", + Short: "Update a reserved IP address", + Long: "Update metadata of an existing reserved IP address. Unspecified fields are preserved from the current resource.", + Example: "tcloud networking reserved-ips update rip-123 --name new-name\ntcloud networking reserved-ips update rip-123 --description 'updated'", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + current, err := client.IaaS().GetReservedIP(cmd.Context(), args[0]) + if err != nil { + if tcclient.IsNotFound(err) { + return fmt.Errorf("reserved IP not found: %s", args[0]) + } + return fmt.Errorf("failed to get reserved IP: %w", err) + } + + req := iaas.UpdateReservedIpRequest{ + Name: current.Name, + Description: current.Description, + Labels: current.Labels, + Annotations: current.Annotations, + } + if cmd.Flags().Changed("name") { + req.Name = updateName + } + if cmd.Flags().Changed("description") { + req.Description = updateDescription + } + if cmd.Flags().Changed("labels") { + req.Labels = parseKeyValueSlice(updateLabels) + } + if cmd.Flags().Changed("annotations") { + req.Annotations = parseKeyValueSlice(updateAnnotations) + } + + rip, err := client.IaaS().UpdateReservedIP(cmd.Context(), current.Identity, req) + if err != nil { + return err + } + + body := [][]string{{ + rip.Identity, + rip.Name, + regionName(*rip), + string(rip.Status), + dashIfEmpty(rip.IPv4Address), + dashIfEmpty(rip.IPv6Address), + attachedTo(*rip), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Region", "Status", "IPv4", "IPv6", "AttachedTo"}, body) + } + return nil + }, +} + +func init() { + ReservedIPsCmd.AddCommand(updateCmd) + + updateCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + updateCmd.Flags().StringVar(&updateName, "name", "", "Name of the reserved IP") + updateCmd.Flags().StringVar(&updateDescription, "description", "", "Description of the reserved IP") + updateCmd.Flags().StringSliceVar(&updateLabels, "labels", []string{}, "Labels in key=value format") + updateCmd.Flags().StringSliceVar(&updateAnnotations, "annotations", []string{}, "Annotations in key=value format") + + updateCmd.ValidArgsFunction = completeReservedIPID +} diff --git a/cmd/iaas/networking/reservedips/view.go b/cmd/iaas/networking/reservedips/view.go new file mode 100644 index 0000000..5d63909 --- /dev/null +++ b/cmd/iaas/networking/reservedips/view.go @@ -0,0 +1,69 @@ +package reservedips + +import ( + "fmt" + + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var viewOutputFormat string + +var viewCmd = &cobra.Command{ + Use: "view", + Short: "View reserved IP details", + Long: "View detailed information about a specific reserved IP address.", + Example: "tcloud networking reserved-ips view rip-123\ntcloud networking reserved-ips view rip-123 --output yaml", + Aliases: []string{"show", "get", "describe"}, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + rip, err := client.IaaS().GetReservedIP(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to get reserved IP: %w", err) + } + + if viewOutputFormat == "yaml" { + return outputYAML(*rip) + } + + fmt.Printf("Reserved IP Details:\n") + fmt.Printf(" ID: %s\n", rip.Identity) + fmt.Printf(" Name: %s\n", rip.Name) + fmt.Printf(" Description: %s\n", rip.Description) + fmt.Printf(" Status: %s\n", rip.Status) + fmt.Printf(" Region: %s\n", regionName(*rip)) + fmt.Printf(" IPv4: %s\n", dashIfEmpty(rip.IPv4Address)) + fmt.Printf(" IPv6: %s\n", dashIfEmpty(rip.IPv6Address)) + fmt.Printf(" Attached To: %s\n", attachedTo(*rip)) + fmt.Printf(" Created: %s\n", formattime.FormatTime(rip.CreatedAt.Local(), false)) + if rip.UpdatedAt != nil { + fmt.Printf(" Updated: %s\n", formattime.FormatTime(rip.UpdatedAt.Local(), false)) + } + return nil + }, +} + +func outputYAML(rip iaas.ReservedIP) error { + yamlData, err := yaml.Marshal(&rip) + if err != nil { + return fmt.Errorf("failed to marshal to YAML: %w", err) + } + fmt.Print(string(yamlData)) + return nil +} + +func init() { + ReservedIPsCmd.AddCommand(viewCmd) + viewCmd.Flags().StringVarP(&viewOutputFormat, "output", "o", "", "Output format (yaml)") + _ = viewCmd.RegisterFlagCompletionFunc("output", completeOutputFormat) + viewCmd.ValidArgsFunction = completeReservedIPID +} From 17c82ca46c8b2ca3daec2e2b2caef7574ad542bc Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:14:58 +0200 Subject: [PATCH 06/19] feat(storage): add snapshot policy commands Automate volume snapshots with scheduled policies, retention, and selector or explicit volume targets. --- cmd/iaas/storage/snapshotpolicies/create.go | 142 +++++++++++++++ cmd/iaas/storage/snapshotpolicies/delete.go | 102 +++++++++++ cmd/iaas/storage/snapshotpolicies/helpers.go | 102 +++++++++++ cmd/iaas/storage/snapshotpolicies/list.go | 107 ++++++++++++ .../snapshotpolicies/snapshotpolicies.go | 21 +++ cmd/iaas/storage/snapshotpolicies/update.go | 165 ++++++++++++++++++ cmd/iaas/storage/snapshotpolicies/view.go | 75 ++++++++ cmd/iaas/storage/storage.go | 2 + 8 files changed, 716 insertions(+) create mode 100644 cmd/iaas/storage/snapshotpolicies/create.go create mode 100644 cmd/iaas/storage/snapshotpolicies/delete.go create mode 100644 cmd/iaas/storage/snapshotpolicies/helpers.go create mode 100644 cmd/iaas/storage/snapshotpolicies/list.go create mode 100644 cmd/iaas/storage/snapshotpolicies/snapshotpolicies.go create mode 100644 cmd/iaas/storage/snapshotpolicies/update.go create mode 100644 cmd/iaas/storage/snapshotpolicies/view.go diff --git a/cmd/iaas/storage/snapshotpolicies/create.go b/cmd/iaas/storage/snapshotpolicies/create.go new file mode 100644 index 0000000..80baa41 --- /dev/null +++ b/cmd/iaas/storage/snapshotpolicies/create.go @@ -0,0 +1,142 @@ +package snapshotpolicies + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + 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/iaas" +) + +var ( + createName string + createDescription string + createRegion string + createTTL string + createKeepCount int + createEnabled bool + createSchedule string + createTimezone string + createTargetType string + createSelectors []string + createVolumes []string + createLabels []string + createAnnotations []string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a snapshot policy", + Long: "Create a new automated snapshot policy for volumes in a region.", + Example: `tcloud storage snapshot-policies create --name daily --region nl-ams --schedule "0 2 * * *" --ttl 168h --timezone UTC --target-type selector --selector backup=true +tcloud storage snapshot-policies create --name weekly --region nl-ams --schedule "0 3 * * 0" --ttl 720h --target-type explicit --volumes vol-1,vol-2`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if createName == "" { + return fmt.Errorf("name is required") + } + if createRegion == "" { + return fmt.Errorf("region is required") + } + if createSchedule == "" { + return fmt.Errorf("schedule is required") + } + + ttl, err := parseTTL(createTTL) + if err != nil { + return err + } + target, err := buildTarget(createTargetType, createSelectors, createVolumes) + if err != nil { + return err + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + regions, err := client.IaaS().ListRegions(cmd.Context(), &iaas.ListRegionsRequest{}) + if err != nil { + return fmt.Errorf("failed to list regions: %w", err) + } + region, err := iaasutil.FindRegionByIdentitySlugOrNameWithError(regions, createRegion) + if err != nil { + return err + } + + req := iaas.CreateSnapshotPolicyRequest{ + Name: createName, + Description: createDescription, + Labels: parseKeyValueSlice(createLabels), + Annotations: parseKeyValueSlice(createAnnotations), + Region: region.Identity, + Ttl: ttl, + Enabled: createEnabled, + Schedule: createSchedule, + Timezone: createTimezone, + Target: target, + } + if cmd.Flags().Changed("keep-count") { + keep := createKeepCount + req.KeepCount = &keep + } + + policy, err := client.IaaS().CreateSnapshotPolicy(cmd.Context(), req) + if err != nil { + return err + } + + body := [][]string{{ + policy.Identity, + policy.Name, + regionName(*policy), + fmt.Sprintf("%t", policy.Enabled), + policy.Schedule, + policy.Ttl.String(), + keepCountString(policy.KeepCount), + targetSummary(policy.Target), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Region", "Enabled", "Schedule", "TTL", "Keep", "Target"}, body) + } + return nil + }, +} + +func init() { + SnapshotPoliciesCmd.AddCommand(createCmd) + + createCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + createCmd.Flags().StringVar(&createName, "name", "", "Name of the snapshot policy") + createCmd.Flags().StringVar(&createDescription, "description", "", "Description of the snapshot policy") + createCmd.Flags().StringVar(&createRegion, "region", "", "Region of the snapshot policy") + createCmd.Flags().StringVar(&createTTL, "ttl", "", "Snapshot retention duration (e.g. 24h, 168h)") + createCmd.Flags().IntVar(&createKeepCount, "keep-count", 0, "Maximum number of snapshots to retain") + createCmd.Flags().BoolVar(&createEnabled, "enabled", true, "Enable the snapshot policy") + createCmd.Flags().StringVar(&createSchedule, "schedule", "", "Cron schedule for snapshot creation") + createCmd.Flags().StringVar(&createTimezone, "timezone", "UTC", "Timezone for the schedule") + createCmd.Flags().StringVar(&createTargetType, "target-type", "", "Target type: selector or explicit") + createCmd.Flags().StringSliceVar(&createSelectors, "selector", []string{}, "Label selectors for target volumes (key=value)") + createCmd.Flags().StringSliceVar(&createVolumes, "volumes", []string{}, "Volume identities when target-type is explicit") + createCmd.Flags().StringSliceVar(&createLabels, "labels", []string{}, "Labels in key=value format") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", []string{}, "Annotations in key=value format") + + _ = createCmd.MarkFlagRequired("name") + _ = createCmd.MarkFlagRequired("region") + _ = createCmd.MarkFlagRequired("ttl") + _ = createCmd.MarkFlagRequired("schedule") + _ = createCmd.MarkFlagRequired("target-type") + + _ = createCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = createCmd.RegisterFlagCompletionFunc("target-type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"selector", "explicit"}, cobra.ShellCompDirectiveNoFileComp + }) + _ = createCmd.RegisterFlagCompletionFunc("volumes", completion.CompleteVolumeID) +} diff --git a/cmd/iaas/storage/snapshotpolicies/delete.go b/cmd/iaas/storage/snapshotpolicies/delete.go new file mode 100644 index 0000000..d6ae606 --- /dev/null +++ b/cmd/iaas/storage/snapshotpolicies/delete.go @@ -0,0 +1,102 @@ +package snapshotpolicies + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/labels" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/filters" + "github.com/thalassa-cloud/client-go/iaas" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var ( + deleteForce bool + deleteLabelSelector string +) + +var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete snapshot policy(ies)", + Aliases: []string{"d", "del", "remove", "rm"}, + Args: cobra.MinimumNArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 && deleteLabelSelector == "" { + return fmt.Errorf("either snapshot policy identity(ies) or --selector must be provided") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + toDelete := []iaas.SnapshotPolicy{} + if deleteLabelSelector != "" { + all, err := client.IaaS().ListSnapshotPolicies(cmd.Context(), &iaas.ListSnapshotPoliciesRequest{ + Filters: []filters.Filter{ + &filters.LabelFilter{MatchLabels: labels.ParseLabelSelector(deleteLabelSelector)}, + }, + }) + if err != nil { + return fmt.Errorf("failed to list snapshot policies: %w", err) + } + if len(all) == 0 { + fmt.Println("No snapshot policies found matching the label selector") + return nil + } + toDelete = append(toDelete, all...) + } else { + for _, identity := range args { + policy, err := client.IaaS().GetSnapshotPolicy(cmd.Context(), identity) + if err != nil { + if tcclient.IsNotFound(err) { + fmt.Printf("Snapshot policy %s not found\n", identity) + continue + } + return fmt.Errorf("failed to get snapshot policy: %w", err) + } + toDelete = append(toDelete, *policy) + } + } + + if len(toDelete) == 0 { + fmt.Println("No snapshot policies to delete") + return nil + } + + var summary strings.Builder + fmt.Fprintf(&summary, "Are you sure you want to delete the following snapshot policy(ies)?\n") + for _, policy := range toDelete { + fmt.Fprintf(&summary, " %s (%s)\n", policy.Name, policy.Identity) + } + proceed, err := shared.PromptDestructiveUnlessForce(deleteForce, summary.String()) + if err != nil { + return err + } + if !proceed { + return nil + } + + for _, policy := range toDelete { + fmt.Printf("Deleting snapshot policy: %s (%s)\n", policy.Name, policy.Identity) + if err := client.IaaS().DeleteSnapshotPolicy(cmd.Context(), policy.Identity); err != nil { + return fmt.Errorf("failed to delete snapshot policy: %w", err) + } + fmt.Printf("Snapshot policy %s deleted successfully\n", policy.Identity) + } + return nil + }, +} + +func init() { + SnapshotPoliciesCmd.AddCommand(deleteCmd) + + deleteCmd.Flags().BoolVar(&deleteForce, "force", false, "Force the deletion and skip the confirmation") + deleteCmd.Flags().StringVarP(&deleteLabelSelector, "selector", "l", "", "Label selector to filter snapshot policies") + deleteCmd.ValidArgsFunction = completion.CompleteSnapshotPolicyID +} diff --git a/cmd/iaas/storage/snapshotpolicies/helpers.go b/cmd/iaas/storage/snapshotpolicies/helpers.go new file mode 100644 index 0000000..3c3ca2f --- /dev/null +++ b/cmd/iaas/storage/snapshotpolicies/helpers.go @@ -0,0 +1,102 @@ +package snapshotpolicies + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/thalassa-cloud/client-go/iaas" +) + +func parseKeyValueSlice(items []string) map[string]string { + result := make(map[string]string) + for _, item := range items { + parts := strings.SplitN(item, "=", 2) + if len(parts) == 2 { + result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + return result +} + +func regionName(policy iaas.SnapshotPolicy) string { + if policy.Region == nil { + return "" + } + if policy.Region.Name != "" { + return policy.Region.Name + } + if policy.Region.Slug != "" { + return policy.Region.Slug + } + return policy.Region.Identity +} + +func keepCountString(keepCount *int) string { + if keepCount == nil { + return "-" + } + return strconv.Itoa(*keepCount) +} + +func targetSummary(target iaas.SnapshotPolicyTarget) string { + switch target.Type { + case iaas.SnapshotPolicyTargetTypeSelector: + if len(target.Selector) == 0 { + return "selector" + } + pairs := make([]string, 0, len(target.Selector)) + for k, v := range target.Selector { + pairs = append(pairs, k+"="+v) + } + return "selector:" + strings.Join(pairs, ",") + case iaas.SnapshotPolicyTargetTypeExplicit: + if len(target.VolumeIdentities) == 0 { + return "explicit" + } + return fmt.Sprintf("explicit:%d volumes", len(target.VolumeIdentities)) + default: + if target.Type == "" { + return "-" + } + return string(target.Type) + } +} + +func parseTTL(value string) (time.Duration, error) { + if value == "" { + return 0, fmt.Errorf("ttl is required") + } + ttl, err := time.ParseDuration(value) + if err != nil { + return 0, fmt.Errorf("invalid ttl %q: %w", value, err) + } + if ttl <= 0 { + return 0, fmt.Errorf("ttl must be greater than 0") + } + return ttl, nil +} + +func buildTarget(targetType string, selectors []string, volumes []string) (iaas.SnapshotPolicyTarget, error) { + target := iaas.SnapshotPolicyTarget{ + Type: iaas.SnapshotPolicyTargetType(targetType), + Selector: parseKeyValueSlice(selectors), + VolumeIdentities: volumes, + } + switch target.Type { + case iaas.SnapshotPolicyTargetTypeSelector: + if len(target.Selector) == 0 { + return target, fmt.Errorf("--selector is required when --target-type is selector") + } + case iaas.SnapshotPolicyTargetTypeExplicit: + if len(target.VolumeIdentities) == 0 { + return target, fmt.Errorf("--volumes is required when --target-type is explicit") + } + case "": + return target, fmt.Errorf("--target-type is required (selector or explicit)") + default: + return target, fmt.Errorf("invalid --target-type %q (must be selector or explicit)", targetType) + } + return target, nil +} diff --git a/cmd/iaas/storage/snapshotpolicies/list.go b/cmd/iaas/storage/snapshotpolicies/list.go new file mode 100644 index 0000000..1cd6a0b --- /dev/null +++ b/cmd/iaas/storage/snapshotpolicies/list.go @@ -0,0 +1,107 @@ +package snapshotpolicies + +import ( + "fmt" + "sort" + "strings" + + "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" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/filters" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + showExactTime bool + showLabels bool + listLabelSelector string + listRegion string +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "List snapshot policies", + Aliases: []string{"g", "get", "ls"}, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + f := filters.Filters{} + if listRegion != "" { + f = append(f, &filters.FilterKeyValue{ + Key: "region", + Value: listRegion, + }) + } + if listLabelSelector != "" { + f = append(f, &filters.LabelFilter{ + MatchLabels: labels.ParseLabelSelector(listLabelSelector), + }) + } + + policies, err := client.IaaS().ListSnapshotPolicies(cmd.Context(), &iaas.ListSnapshotPoliciesRequest{ + Filters: f, + }) + if err != nil { + return err + } + + body := make([][]string, 0, len(policies)) + for _, policy := range policies { + row := []string{ + policy.Identity, + policy.Name, + regionName(policy), + fmt.Sprintf("%t", policy.Enabled), + policy.Schedule, + policy.Ttl.String(), + keepCountString(policy.KeepCount), + targetSummary(policy.Target), + formattime.FormatTime(policy.CreatedAt.Local(), showExactTime), + } + if showLabels { + labelPairs := make([]string, 0, len(policy.Labels)) + for k, v := range policy.Labels { + labelPairs = append(labelPairs, k+"="+v) + } + sort.Strings(labelPairs) + if len(labelPairs) == 0 { + labelPairs = []string{"-"} + } + row = append(row, strings.Join(labelPairs, ",")) + } + body = append(body, row) + } + + if noHeader { + table.Print(nil, body) + } else { + headers := []string{"ID", "Name", "Region", "Enabled", "Schedule", "TTL", "Keep", "Target", "Age"} + if showLabels { + headers = append(headers, "Labels") + } + table.Print(headers, body) + } + return nil + }, +} + +func init() { + SnapshotPoliciesCmd.AddCommand(listCmd) + + listCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + 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().StringVar(&listRegion, "region", "", "Filter by region") + listCmd.Flags().StringVarP(&listLabelSelector, "selector", "l", "", "Label selector to filter snapshot policies") + + _ = listCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) +} diff --git a/cmd/iaas/storage/snapshotpolicies/snapshotpolicies.go b/cmd/iaas/storage/snapshotpolicies/snapshotpolicies.go new file mode 100644 index 0000000..f6e6ae0 --- /dev/null +++ b/cmd/iaas/storage/snapshotpolicies/snapshotpolicies.go @@ -0,0 +1,21 @@ +package snapshotpolicies + +import ( + "github.com/spf13/cobra" +) + +const NoHeaderKey = "no-header" + +var noHeader bool + +// SnapshotPoliciesCmd manages snapshot policies. +var SnapshotPoliciesCmd = &cobra.Command{ + Use: "snapshot-policies", + Aliases: []string{"snapshot-policy", "sp"}, + Short: "Manage snapshot policies", + Long: "Manage automated volume snapshot policies within the Thalassa Cloud Platform.", + Example: "tcloud storage snapshot-policies list\ntcloud storage snapshot-policies create --name daily --region nl-ams --schedule '0 2 * * *' --ttl 168h --target-type selector --selector backup=true", +} + +func init() { +} diff --git a/cmd/iaas/storage/snapshotpolicies/update.go b/cmd/iaas/storage/snapshotpolicies/update.go new file mode 100644 index 0000000..71ee8e2 --- /dev/null +++ b/cmd/iaas/storage/snapshotpolicies/update.go @@ -0,0 +1,165 @@ +package snapshotpolicies + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var ( + updateName string + updateDescription string + updateTTL string + updateKeepCount int + updateEnabled bool + updateSchedule string + updateTimezone string + updateTargetType string + updateSelectors []string + updateVolumes []string + updateLabels []string + updateAnnotations []string +) + +var updateCmd = &cobra.Command{ + Use: "update", + Short: "Update a snapshot policy", + Long: "Update an existing snapshot policy. Unspecified fields are preserved from the current resource.", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteSnapshotPolicyID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + current, err := client.IaaS().GetSnapshotPolicy(cmd.Context(), args[0]) + if err != nil { + if tcclient.IsNotFound(err) { + return fmt.Errorf("snapshot policy not found: %s", args[0]) + } + return fmt.Errorf("failed to get snapshot policy: %w", err) + } + + req := iaas.UpdateSnapshotPolicyRequest{ + Name: current.Name, + Description: current.Description, + Labels: current.Labels, + Annotations: current.Annotations, + Ttl: current.Ttl, + KeepCount: current.KeepCount, + Enabled: current.Enabled, + Schedule: current.Schedule, + Timezone: current.Timezone, + Target: current.Target, + } + + if cmd.Flags().Changed("name") { + req.Name = updateName + } + if cmd.Flags().Changed("description") { + req.Description = updateDescription + } + if cmd.Flags().Changed("labels") { + req.Labels = parseKeyValueSlice(updateLabels) + } + if cmd.Flags().Changed("annotations") { + req.Annotations = parseKeyValueSlice(updateAnnotations) + } + if cmd.Flags().Changed("ttl") { + ttl, err := parseTTL(updateTTL) + if err != nil { + return err + } + req.Ttl = ttl + } + if cmd.Flags().Changed("keep-count") { + keep := updateKeepCount + req.KeepCount = &keep + } + if cmd.Flags().Changed("enabled") { + req.Enabled = updateEnabled + } + if cmd.Flags().Changed("schedule") { + req.Schedule = updateSchedule + } + if cmd.Flags().Changed("timezone") { + req.Timezone = updateTimezone + } + + targetChanged := cmd.Flags().Changed("target-type") || cmd.Flags().Changed("selector") || cmd.Flags().Changed("volumes") + if targetChanged { + targetType := string(current.Target.Type) + selectors := updateSelectors + volumes := updateVolumes + if cmd.Flags().Changed("target-type") { + targetType = updateTargetType + } + if !cmd.Flags().Changed("selector") { + selectors = make([]string, 0, len(current.Target.Selector)) + for k, v := range current.Target.Selector { + selectors = append(selectors, k+"="+v) + } + } + if !cmd.Flags().Changed("volumes") { + volumes = current.Target.VolumeIdentities + } + target, err := buildTarget(targetType, selectors, volumes) + if err != nil { + return err + } + req.Target = target + } + + policy, err := client.IaaS().UpdateSnapshotPolicy(cmd.Context(), current.Identity, req) + if err != nil { + return err + } + + body := [][]string{{ + policy.Identity, + policy.Name, + regionName(*policy), + fmt.Sprintf("%t", policy.Enabled), + policy.Schedule, + policy.Ttl.String(), + keepCountString(policy.KeepCount), + targetSummary(policy.Target), + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Region", "Enabled", "Schedule", "TTL", "Keep", "Target"}, body) + } + return nil + }, +} + +func init() { + SnapshotPoliciesCmd.AddCommand(updateCmd) + + updateCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + updateCmd.Flags().StringVar(&updateName, "name", "", "Name of the snapshot policy") + updateCmd.Flags().StringVar(&updateDescription, "description", "", "Description of the snapshot policy") + updateCmd.Flags().StringVar(&updateTTL, "ttl", "", "Snapshot retention duration (e.g. 24h, 168h)") + updateCmd.Flags().IntVar(&updateKeepCount, "keep-count", 0, "Maximum number of snapshots to retain") + updateCmd.Flags().BoolVar(&updateEnabled, "enabled", true, "Enable or disable the snapshot policy") + updateCmd.Flags().StringVar(&updateSchedule, "schedule", "", "Cron schedule for snapshot creation") + updateCmd.Flags().StringVar(&updateTimezone, "timezone", "", "Timezone for the schedule") + updateCmd.Flags().StringVar(&updateTargetType, "target-type", "", "Target type: selector or explicit") + updateCmd.Flags().StringSliceVar(&updateSelectors, "selector", []string{}, "Label selectors for target volumes (key=value)") + updateCmd.Flags().StringSliceVar(&updateVolumes, "volumes", []string{}, "Volume identities when target-type is explicit") + updateCmd.Flags().StringSliceVar(&updateLabels, "labels", []string{}, "Labels in key=value format") + updateCmd.Flags().StringSliceVar(&updateAnnotations, "annotations", []string{}, "Annotations in key=value format") + + _ = updateCmd.RegisterFlagCompletionFunc("target-type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"selector", "explicit"}, cobra.ShellCompDirectiveNoFileComp + }) + _ = updateCmd.RegisterFlagCompletionFunc("volumes", completion.CompleteVolumeID) +} diff --git a/cmd/iaas/storage/snapshotpolicies/view.go b/cmd/iaas/storage/snapshotpolicies/view.go new file mode 100644 index 0000000..21bb43b --- /dev/null +++ b/cmd/iaas/storage/snapshotpolicies/view.go @@ -0,0 +1,75 @@ +package snapshotpolicies + +import ( + "fmt" + + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var viewOutputFormat string + +var viewCmd = &cobra.Command{ + Use: "view", + Short: "View snapshot policy details", + Aliases: []string{"show", "get", "describe"}, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteSnapshotPolicyID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + policy, err := client.IaaS().GetSnapshotPolicy(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to get snapshot policy: %w", err) + } + + if viewOutputFormat == "yaml" { + return outputYAML(*policy) + } + + fmt.Printf("Snapshot Policy Details:\n") + fmt.Printf(" ID: %s\n", policy.Identity) + fmt.Printf(" Name: %s\n", policy.Name) + fmt.Printf(" Description: %s\n", policy.Description) + fmt.Printf(" Region: %s\n", regionName(*policy)) + fmt.Printf(" Enabled: %t\n", policy.Enabled) + fmt.Printf(" Schedule: %s\n", policy.Schedule) + fmt.Printf(" Timezone: %s\n", policy.Timezone) + fmt.Printf(" TTL: %s\n", policy.Ttl.String()) + fmt.Printf(" Keep Count: %s\n", keepCountString(policy.KeepCount)) + fmt.Printf(" Target: %s\n", targetSummary(policy.Target)) + fmt.Printf(" Created: %s\n", formattime.FormatTime(policy.CreatedAt.Local(), false)) + fmt.Printf(" Updated: %s\n", formattime.FormatTime(policy.UpdatedAt.Local(), false)) + if policy.NextSnapshotAt != nil { + fmt.Printf(" Next Snapshot: %s\n", formattime.FormatTime(policy.NextSnapshotAt.Local(), true)) + } + if policy.LastSnapshotAt != nil { + fmt.Printf(" Last Snapshot: %s\n", formattime.FormatTime(policy.LastSnapshotAt.Local(), true)) + } + return nil + }, +} + +func outputYAML(policy iaas.SnapshotPolicy) error { + policy.Organisation = nil + yamlData, err := yaml.Marshal(&policy) + if err != nil { + return fmt.Errorf("failed to marshal to YAML: %w", err) + } + fmt.Print(string(yamlData)) + return nil +} + +func init() { + SnapshotPoliciesCmd.AddCommand(viewCmd) + viewCmd.Flags().StringVarP(&viewOutputFormat, "output", "o", "", "Output format (yaml)") + _ = viewCmd.RegisterFlagCompletionFunc("output", completion.CompleteOutputFormat) +} diff --git a/cmd/iaas/storage/storage.go b/cmd/iaas/storage/storage.go index a75f225..64a1a34 100644 --- a/cmd/iaas/storage/storage.go +++ b/cmd/iaas/storage/storage.go @@ -2,6 +2,7 @@ package storage import ( "github.com/spf13/cobra" + "github.com/thalassa-cloud/cli/cmd/iaas/storage/snapshotpolicies" "github.com/thalassa-cloud/cli/cmd/iaas/storage/snapshots" "github.com/thalassa-cloud/cli/cmd/iaas/storage/tfs" "github.com/thalassa-cloud/cli/cmd/iaas/storage/volumes" @@ -17,5 +18,6 @@ var StorageCmd = &cobra.Command{ func init() { StorageCmd.AddCommand(volumes.VolumesCmd) StorageCmd.AddCommand(snapshots.SnapshotsCmd) + StorageCmd.AddCommand(snapshotpolicies.SnapshotPoliciesCmd) StorageCmd.AddCommand(tfs.TfsCmd) } From f684e145a098294baaaa90828022940ec590f3e7 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:14:59 +0200 Subject: [PATCH 07/19] feat(networking): add NAT gateway create and update Support provisioning NAT gateways with security groups, reserved IPs, and optional wait for an endpoint. --- cmd/iaas/networking/natgateways/completion.go | 11 +- cmd/iaas/networking/natgateways/create.go | 127 ++++++++++++++++++ cmd/iaas/networking/natgateways/update.go | 108 +++++++++++++++ 3 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 cmd/iaas/networking/natgateways/create.go create mode 100644 cmd/iaas/networking/natgateways/update.go diff --git a/cmd/iaas/networking/natgateways/completion.go b/cmd/iaas/networking/natgateways/completion.go index d8aba76..091b010 100644 --- a/cmd/iaas/networking/natgateways/completion.go +++ b/cmd/iaas/networking/natgateways/completion.go @@ -6,8 +6,11 @@ import ( // Re-export completion functions for convenience var ( - completeNatGatewayID = completion.CompleteNatGatewayID - completeVPCID = completion.CompleteVPCID - completeRegion = completion.CompleteRegion - completeOutputFormat = completion.CompleteOutputFormat + completeNatGatewayID = completion.CompleteNatGatewayID + completeVPCID = completion.CompleteVPCID + completeRegion = completion.CompleteRegion + completeOutputFormat = completion.CompleteOutputFormat + completeSubnetID = completion.CompleteSubnetID + completeSecurityGroupID = completion.CompleteSecurityGroupID + completeReservedIPID = completion.CompleteReservedIPID ) diff --git a/cmd/iaas/networking/natgateways/create.go b/cmd/iaas/networking/natgateways/create.go new file mode 100644 index 0000000..dc5db4e --- /dev/null +++ b/cmd/iaas/networking/natgateways/create.go @@ -0,0 +1,127 @@ +package natgateways + +import ( + "fmt" + "strings" + "time" + + "github.com/spf13/cobra" + + iaasutil "github.com/thalassa-cloud/cli/internal/iaas" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + createName string + createDescription string + createSubnet string + createLabels []string + createAnnotations []string + createSecurityGroups []string + createConfigureDefaultRoute bool + createReservedIP string + createWait bool + createWaitTimeout time.Duration +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a NAT gateway", + Long: "Create a new NAT gateway in the specified subnet.", + Example: "tcloud networking natgateways create --name egress --subnet subnet-123\ntcloud networking natgateways create --name egress --subnet subnet-123 --configure-default-route --wait", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if createName == "" { + return fmt.Errorf("name is required") + } + if createSubnet == "" { + return fmt.Errorf("subnet is required") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + subnet, err := iaasutil.GetSubnetByIdentitySlugOrName(cmd.Context(), client.IaaS(), createSubnet) + if err != nil { + return fmt.Errorf("failed to get subnet: %w", err) + } + + req := iaas.CreateVpcNatGateway{ + Name: createName, + Description: createDescription, + Labels: parseKeyValueSlice(createLabels), + Annotations: parseKeyValueSlice(createAnnotations), + SubnetIdentity: subnet.Identity, + SecurityGroupAttachments: createSecurityGroups, + ConfigureDefaultRoute: createConfigureDefaultRoute, + } + if createReservedIP != "" { + req.ReservedIpID = &createReservedIP + } + + ngw, err := client.IaaS().CreateNatGateway(cmd.Context(), req) + if err != nil { + return err + } + + if createWait { + ctxWithTimeout, cancel, err := shared.WaitContext(cmd.Context(), createWaitTimeout) + if err != nil { + return err + } + defer cancel() + fmt.Println("Waiting for NAT gateway to have an endpoint...") + ngw, err = client.IaaS().WaitUntilNatGatewayHasEndpoint(ctxWithTimeout, ngw.Identity) + if err != nil { + return fmt.Errorf("failed waiting for NAT gateway endpoint: %w", err) + } + fmt.Println("NAT gateway endpoint is ready") + } + + fmt.Printf("NAT gateway created successfully\n") + fmt.Printf("ID: %s\n", ngw.Identity) + fmt.Printf("Name: %s\n", ngw.Name) + fmt.Printf("Status: %s\n", ngw.Status) + if ngw.EndpointIP != "" { + fmt.Printf("Endpoint IP: %s\n", ngw.EndpointIP) + } + return nil + }, +} + +func parseKeyValueSlice(items []string) map[string]string { + result := make(map[string]string) + for _, item := range items { + parts := strings.SplitN(item, "=", 2) + if len(parts) == 2 { + result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + return result +} + +func init() { + NatGatewaysCmd.AddCommand(createCmd) + + createCmd.Flags().StringVar(&createName, "name", "", "Name of the NAT gateway") + createCmd.Flags().StringVar(&createDescription, "description", "", "Description of the NAT gateway") + createCmd.Flags().StringVar(&createSubnet, "subnet", "", "Subnet identity, slug, or name") + createCmd.Flags().StringSliceVar(&createLabels, "labels", []string{}, "Labels in key=value format") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", []string{}, "Annotations in key=value format") + createCmd.Flags().StringSliceVar(&createSecurityGroups, "security-groups", []string{}, "Security group identities to attach") + createCmd.Flags().BoolVar(&createConfigureDefaultRoute, "configure-default-route", false, "Configure the default route for the subnet route table") + createCmd.Flags().StringVar(&createReservedIP, "reserved-ip", "", "Reserved IP identity to attach") + createCmd.Flags().BoolVar(&createWait, "wait", false, "Wait for the NAT gateway to have an endpoint") + createCmd.Flags().DurationVar(&createWaitTimeout, "wait-timeout", 20*time.Minute, "Maximum time to wait for the NAT gateway endpoint") + + _ = createCmd.MarkFlagRequired("name") + _ = createCmd.MarkFlagRequired("subnet") + + _ = createCmd.RegisterFlagCompletionFunc("subnet", completeSubnetID) + _ = createCmd.RegisterFlagCompletionFunc("security-groups", completeSecurityGroupID) + _ = createCmd.RegisterFlagCompletionFunc("reserved-ip", completeReservedIPID) +} diff --git a/cmd/iaas/networking/natgateways/update.go b/cmd/iaas/networking/natgateways/update.go new file mode 100644 index 0000000..5a55b50 --- /dev/null +++ b/cmd/iaas/networking/natgateways/update.go @@ -0,0 +1,108 @@ +package natgateways + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var ( + updateName string + updateDescription string + updateLabels []string + updateAnnotations []string + updateSecurityGroups []string + updateReservedIP string + updateDetachReserved bool +) + +var updateCmd = &cobra.Command{ + Use: "update", + Short: "Update a NAT gateway", + Long: "Update properties of an existing NAT gateway. Unspecified fields are preserved.", + Example: "tcloud networking natgateways update ngw-123 --name egress-prod\ntcloud networking natgateways update ngw-123 --reserved-ip rip-456\ntcloud networking natgateways update ngw-123 --detach-reserved-ip", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if updateDetachReserved && cmd.Flags().Changed("reserved-ip") { + return fmt.Errorf("--detach-reserved-ip and --reserved-ip are mutually exclusive") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + current, err := client.IaaS().GetNatGateway(cmd.Context(), args[0]) + if err != nil { + if tcclient.IsNotFound(err) { + return fmt.Errorf("NAT gateway not found: %s", args[0]) + } + return fmt.Errorf("failed to get NAT gateway: %w", err) + } + + req := iaas.UpdateVpcNatGateway{ + Name: current.Name, + Description: current.Description, + Labels: current.Labels, + Annotations: current.Annotations, + } + if len(current.SecurityGroups) > 0 { + for _, sg := range current.SecurityGroups { + req.SecurityGroupAttachments = append(req.SecurityGroupAttachments, sg.Identity) + } + } + + if cmd.Flags().Changed("name") { + req.Name = updateName + } + if cmd.Flags().Changed("description") { + req.Description = updateDescription + } + if cmd.Flags().Changed("labels") { + req.Labels = parseKeyValueSlice(updateLabels) + } + if cmd.Flags().Changed("annotations") { + req.Annotations = parseKeyValueSlice(updateAnnotations) + } + if cmd.Flags().Changed("security-groups") { + req.SecurityGroupAttachments = updateSecurityGroups + } + if updateDetachReserved { + empty := "" + req.ReservedIpID = &empty + } else if cmd.Flags().Changed("reserved-ip") { + req.ReservedIpID = &updateReservedIP + } + + ngw, err := client.IaaS().UpdateNatGateway(cmd.Context(), current.Identity, req) + if err != nil { + return err + } + + fmt.Printf("NAT gateway updated successfully\n") + fmt.Printf("ID: %s\n", ngw.Identity) + fmt.Printf("Name: %s\n", ngw.Name) + fmt.Printf("Status: %s\n", ngw.Status) + return nil + }, +} + +func init() { + NatGatewaysCmd.AddCommand(updateCmd) + + updateCmd.Flags().StringVar(&updateName, "name", "", "Name of the NAT gateway") + updateCmd.Flags().StringVar(&updateDescription, "description", "", "Description of the NAT gateway") + updateCmd.Flags().StringSliceVar(&updateLabels, "labels", []string{}, "Labels in key=value format") + updateCmd.Flags().StringSliceVar(&updateAnnotations, "annotations", []string{}, "Annotations in key=value format") + updateCmd.Flags().StringSliceVar(&updateSecurityGroups, "security-groups", []string{}, "Security group identities to attach") + updateCmd.Flags().StringVar(&updateReservedIP, "reserved-ip", "", "Reserved IP identity to attach or replace") + updateCmd.Flags().BoolVar(&updateDetachReserved, "detach-reserved-ip", false, "Detach the currently associated reserved IP") + + updateCmd.ValidArgsFunction = completeNatGatewayID + _ = updateCmd.RegisterFlagCompletionFunc("security-groups", completeSecurityGroupID) + _ = updateCmd.RegisterFlagCompletionFunc("reserved-ip", completeReservedIPID) +} From 66d8e9dc37b182cea261bc61b398b5cecaca1824 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:15:12 +0200 Subject: [PATCH 08/19] feat(networking): expand route tables with routes management Add route table CRUD and nested route commands for create, update, delete, and full-table replace. --- cmd/iaas/networking/routetables/create.go | 71 +++++++++++ cmd/iaas/networking/routetables/delete.go | 63 ++++++++++ cmd/iaas/networking/routetables/helpers.go | 42 +++++++ cmd/iaas/networking/routetables/list.go | 2 +- cmd/iaas/networking/routetables/routes_cmd.go | 16 +++ .../networking/routetables/routes_create.go | 95 +++++++++++++++ .../networking/routetables/routes_delete.go | 53 +++++++++ .../networking/routetables/routes_list.go | 51 ++++++++ cmd/iaas/networking/routetables/routes_set.go | 75 ++++++++++++ .../networking/routetables/routes_update.go | 112 ++++++++++++++++++ .../networking/routetables/routes_view.go | 56 +++++++++ .../networking/routetables/routetables.go | 8 +- cmd/iaas/networking/routetables/update.go | 72 +++++++++++ cmd/iaas/networking/routetables/view.go | 75 ++++++++++++ 14 files changed, 787 insertions(+), 4 deletions(-) create mode 100644 cmd/iaas/networking/routetables/create.go create mode 100644 cmd/iaas/networking/routetables/delete.go create mode 100644 cmd/iaas/networking/routetables/helpers.go create mode 100644 cmd/iaas/networking/routetables/routes_cmd.go create mode 100644 cmd/iaas/networking/routetables/routes_create.go create mode 100644 cmd/iaas/networking/routetables/routes_delete.go create mode 100644 cmd/iaas/networking/routetables/routes_list.go create mode 100644 cmd/iaas/networking/routetables/routes_set.go create mode 100644 cmd/iaas/networking/routetables/routes_update.go create mode 100644 cmd/iaas/networking/routetables/routes_view.go create mode 100644 cmd/iaas/networking/routetables/update.go create mode 100644 cmd/iaas/networking/routetables/view.go diff --git a/cmd/iaas/networking/routetables/create.go b/cmd/iaas/networking/routetables/create.go new file mode 100644 index 0000000..1dcb145 --- /dev/null +++ b/cmd/iaas/networking/routetables/create.go @@ -0,0 +1,71 @@ +package routetables + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + createName string + createDescription string + createVPC string + createLabels []string + createAnnotations []string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a route table", + Example: "tcloud networking routetables create --name private --vpc vpc-123", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + if createName == "" { + return fmt.Errorf("name is required") + } + if createVPC == "" { + return fmt.Errorf("vpc is required") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req := iaas.CreateRouteTable{ + Name: createName, + Labels: shared.KeyValuePairsToMap(createLabels), + Annotations: shared.KeyValuePairsToMap(createAnnotations), + VpcIdentity: createVPC, + } + if createDescription != "" { + req.Description = &createDescription + } + + rt, err := client.IaaS().CreateRouteTable(cmd.Context(), req) + if err != nil { + return fmt.Errorf("failed to create route table: %w", err) + } + + fmt.Printf("Route table created successfully\n") + fmt.Printf("ID: %s\n", rt.Identity) + fmt.Printf("Name: %s\n", rt.Name) + return nil + }, +} + +func init() { + RouteTablesCmd.AddCommand(createCmd) + createCmd.Flags().StringVar(&createName, "name", "", "Name of the route table") + createCmd.Flags().StringVar(&createDescription, "description", "", "Description") + createCmd.Flags().StringVar(&createVPC, "vpc", "", "VPC identity") + createCmd.Flags().StringSliceVar(&createLabels, "labels", nil, "Labels as key=value (repeatable)") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") + _ = createCmd.MarkFlagRequired("name") + _ = createCmd.MarkFlagRequired("vpc") + _ = createCmd.RegisterFlagCompletionFunc("vpc", completeVPCID) +} diff --git a/cmd/iaas/networking/routetables/delete.go b/cmd/iaas/networking/routetables/delete.go new file mode 100644 index 0000000..0858f28 --- /dev/null +++ b/cmd/iaas/networking/routetables/delete.go @@ -0,0 +1,63 @@ +package routetables + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var deleteForce bool + +var deleteCmd = &cobra.Command{ + Use: "delete ROUTE_TABLE [ROUTE_TABLE...]", + Aliases: []string{"rm", "del"}, + Short: "Delete route table(s)", + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + var summary strings.Builder + fmt.Fprintf(&summary, "Delete the following route table(s)?\n") + for _, id := range args { + rt, err := client.IaaS().GetRouteTable(cmd.Context(), id) + if err != nil { + if tcclient.IsNotFound(err) { + fmt.Printf("Route table %s not found\n", id) + continue + } + return err + } + fmt.Fprintf(&summary, " %s (%s)\n", rt.Name, rt.Identity) + } + + proceed, err := shared.PromptDestructiveUnlessForce(deleteForce, summary.String()) + if err != nil { + return err + } + if !proceed { + return nil + } + + for _, id := range args { + if err := client.IaaS().DeleteRouteTable(cmd.Context(), id); err != nil { + return fmt.Errorf("failed to delete route table %s: %w", id, err) + } + fmt.Printf("Route table %s deleted\n", id) + } + return nil + }, +} + +func init() { + RouteTablesCmd.AddCommand(deleteCmd) + deleteCmd.Flags().BoolVar(&deleteForce, shared.ForceKey, false, "Skip confirmation") +} diff --git a/cmd/iaas/networking/routetables/helpers.go b/cmd/iaas/networking/routetables/helpers.go new file mode 100644 index 0000000..830eddf --- /dev/null +++ b/cmd/iaas/networking/routetables/helpers.go @@ -0,0 +1,42 @@ +package routetables + +import ( + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + completeRouteTableID = completion.CompleteRouteTableID + completeVPCID = completion.CompleteVPCID + completeOutputFormat = completion.CompleteOutputFormat + completeNatGatewayID = completion.CompleteNatGatewayID +) + +func descriptionString(desc *string) string { + if desc == nil || *desc == "" { + return "-" + } + return *desc +} + +func vpcName(rt iaas.RouteTable) string { + if rt.Vpc == nil { + return "" + } + return rt.Vpc.Name +} + +func routeTarget(entry iaas.RouteEntry) string { + switch { + case entry.TargetNatGatewayIdentity != nil && *entry.TargetNatGatewayIdentity != "": + return "nat:" + *entry.TargetNatGatewayIdentity + case entry.TargetGatewayIdentity != nil && *entry.TargetGatewayIdentity != "": + return "gateway:" + *entry.TargetGatewayIdentity + case entry.TargetVpcPeeringConnectionId != nil && *entry.TargetVpcPeeringConnectionId != "": + return "peering:" + *entry.TargetVpcPeeringConnectionId + case entry.GatewayAddress != nil && *entry.GatewayAddress != "": + return "address:" + *entry.GatewayAddress + default: + return "-" + } +} diff --git a/cmd/iaas/networking/routetables/list.go b/cmd/iaas/networking/routetables/list.go index 3b5bb58..0c88746 100644 --- a/cmd/iaas/networking/routetables/list.go +++ b/cmd/iaas/networking/routetables/list.go @@ -56,7 +56,7 @@ var getCmd = &cobra.Command{ row := []string{ rt.Identity, rt.Name, - rt.Vpc.Name, + vpcName(rt), formattime.FormatTime(rt.CreatedAt.Local(), showExactTime), } diff --git a/cmd/iaas/networking/routetables/routes_cmd.go b/cmd/iaas/networking/routetables/routes_cmd.go new file mode 100644 index 0000000..0ee5228 --- /dev/null +++ b/cmd/iaas/networking/routetables/routes_cmd.go @@ -0,0 +1,16 @@ +package routetables + +import ( + "github.com/spf13/cobra" +) + +// RoutesCmd manages routes within a route table. +var RoutesCmd = &cobra.Command{ + Use: "routes", + Aliases: []string{"route"}, + Short: "Manage routes in a route table", +} + +func init() { + RouteTablesCmd.AddCommand(RoutesCmd) +} diff --git a/cmd/iaas/networking/routetables/routes_create.go b/cmd/iaas/networking/routetables/routes_create.go new file mode 100644 index 0000000..80b1f89 --- /dev/null +++ b/cmd/iaas/networking/routetables/routes_create.go @@ -0,0 +1,95 @@ +package routetables + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + routeCreateDestination string + routeCreateGateway string + routeCreateNatGateway string + routeCreatePeering string + routeCreateAddress string +) + +var routesCreateCmd = &cobra.Command{ + Use: "create ROUTE_TABLE", + Short: "Create a route in a route table", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + if routeCreateDestination == "" { + return fmt.Errorf("destination is required") + } + + targets := 0 + if routeCreateGateway != "" { + targets++ + } + if routeCreateNatGateway != "" { + targets++ + } + if routeCreatePeering != "" { + targets++ + } + if routeCreateAddress != "" { + targets++ + } + if targets != 1 { + return fmt.Errorf("exactly one of --gateway, --nat-gateway, --vpc-peering, or --gateway-address must be provided") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req := iaas.CreateRouteTableRoute{ + DestinationCidrBlock: routeCreateDestination, + TargetGatewayIdentity: routeCreateGateway, + TargetNatGatewayIdentity: routeCreateNatGateway, + GatewayAddress: routeCreateAddress, + } + if routeCreatePeering != "" { + req.TargetVpcPeeringConnectionId = &routeCreatePeering + } + + route, err := client.IaaS().CreateRouteTableRoute(cmd.Context(), args[0], req) + if err != nil { + return err + } + + body := [][]string{{ + route.Identity, + route.DestinationCidrBlock, + routeTarget(*route), + route.Type, + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Destination", "Target", "Type"}, body) + } + return nil + }, +} + +func init() { + RoutesCmd.AddCommand(routesCreateCmd) + + routesCreateCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + routesCreateCmd.Flags().StringVar(&routeCreateDestination, "destination", "", "Destination CIDR block") + routesCreateCmd.Flags().StringVar(&routeCreateGateway, "gateway", "", "Target gateway identity") + routesCreateCmd.Flags().StringVar(&routeCreateNatGateway, "nat-gateway", "", "Target NAT gateway identity") + routesCreateCmd.Flags().StringVar(&routeCreatePeering, "vpc-peering", "", "Target VPC peering connection identity") + routesCreateCmd.Flags().StringVar(&routeCreateAddress, "gateway-address", "", "Gateway address") + + _ = routesCreateCmd.MarkFlagRequired("destination") + _ = routesCreateCmd.RegisterFlagCompletionFunc("nat-gateway", completeNatGatewayID) +} diff --git a/cmd/iaas/networking/routetables/routes_delete.go b/cmd/iaas/networking/routetables/routes_delete.go new file mode 100644 index 0000000..7f53700 --- /dev/null +++ b/cmd/iaas/networking/routetables/routes_delete.go @@ -0,0 +1,53 @@ +package routetables + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var routesDeleteForce bool + +var routesDeleteCmd = &cobra.Command{ + Use: "delete ROUTE_TABLE ROUTE", + Short: "Delete a route from a route table", + Aliases: []string{"d", "del", "remove", "rm"}, + Args: cobra.ExactArgs(2), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + route, err := client.IaaS().GetRouteTableRoute(cmd.Context(), args[0], args[1]) + if err != nil { + return fmt.Errorf("failed to get route: %w", err) + } + + var summary strings.Builder + fmt.Fprintf(&summary, "Are you sure you want to delete route %s (%s)?\n", route.Identity, route.DestinationCidrBlock) + proceed, err := shared.PromptDestructiveUnlessForce(routesDeleteForce, summary.String()) + if err != nil { + return err + } + if !proceed { + return nil + } + + if err := client.IaaS().DeleteRouteTableRoute(cmd.Context(), args[0], args[1]); err != nil { + return fmt.Errorf("failed to delete route: %w", err) + } + fmt.Printf("Route %s deleted successfully\n", args[1]) + return nil + }, +} + +func init() { + RoutesCmd.AddCommand(routesDeleteCmd) + routesDeleteCmd.Flags().BoolVar(&routesDeleteForce, "force", false, "Force the deletion and skip the confirmation") +} diff --git a/cmd/iaas/networking/routetables/routes_list.go b/cmd/iaas/networking/routetables/routes_list.go new file mode 100644 index 0000000..57584d9 --- /dev/null +++ b/cmd/iaas/networking/routetables/routes_list.go @@ -0,0 +1,51 @@ +package routetables + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var routesListCmd = &cobra.Command{ + Use: "list ROUTE_TABLE", + Short: "List routes in a route table", + Aliases: []string{"ls", "get"}, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + rt, err := client.IaaS().GetRouteTable(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to get route table: %w", err) + } + + body := make([][]string, 0, len(rt.Routes)) + for _, route := range rt.Routes { + body = append(body, []string{ + route.Identity, + route.DestinationCidrBlock, + routeTarget(route), + route.Type, + }) + } + + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Destination", "Target", "Type"}, body) + } + return nil + }, +} + +func init() { + RoutesCmd.AddCommand(routesListCmd) + routesListCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") +} diff --git a/cmd/iaas/networking/routetables/routes_set.go b/cmd/iaas/networking/routetables/routes_set.go new file mode 100644 index 0000000..812b3e0 --- /dev/null +++ b/cmd/iaas/networking/routetables/routes_set.go @@ -0,0 +1,75 @@ +package routetables + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var routesSetFile string + +var routesSetCmd = &cobra.Command{ + Use: "set ROUTE_TABLE", + Short: "Replace all routes in a route table from a JSON file", + Long: "Batch-update routes for a route table. The file must contain a JSON array of UpdateRouteTableRoute objects.", + Example: `tcloud networking routetables routes set rt-123 --file routes.json`, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + if routesSetFile == "" { + return fmt.Errorf("--file is required") + } + + data, err := os.ReadFile(routesSetFile) + if err != nil { + return fmt.Errorf("failed to read routes file: %w", err) + } + + var routes []iaas.UpdateRouteTableRoute + if err := json.Unmarshal(data, &routes); err != nil { + return fmt.Errorf("failed to parse routes file: %w", err) + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + updated, err := client.IaaS().UpdateRouteTableRoutes(cmd.Context(), args[0], iaas.UpdateRouteTableRoutes{ + Routes: routes, + }) + if err != nil { + return err + } + + body := make([][]string, 0, len(updated)) + for _, route := range updated { + body = append(body, []string{ + route.Identity, + route.DestinationCidrBlock, + routeTarget(route), + route.Type, + }) + } + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Destination", "Target", "Type"}, body) + } + return nil + }, +} + +func init() { + RoutesCmd.AddCommand(routesSetCmd) + + routesSetCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + routesSetCmd.Flags().StringVar(&routesSetFile, "file", "", "JSON file containing an array of routes") + _ = routesSetCmd.MarkFlagRequired("file") +} diff --git a/cmd/iaas/networking/routetables/routes_update.go b/cmd/iaas/networking/routetables/routes_update.go new file mode 100644 index 0000000..86b19a6 --- /dev/null +++ b/cmd/iaas/networking/routetables/routes_update.go @@ -0,0 +1,112 @@ +package routetables + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var ( + routeUpdateDestination string + routeUpdateGateway string + routeUpdateNatGateway string + routeUpdatePeering string + routeUpdateAddress string +) + +var routesUpdateCmd = &cobra.Command{ + Use: "update ROUTE_TABLE ROUTE", + Short: "Update a route in a route table", + Args: cobra.ExactArgs(2), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + current, err := client.IaaS().GetRouteTableRoute(cmd.Context(), args[0], args[1]) + if err != nil { + return fmt.Errorf("failed to get route: %w", err) + } + + req := iaas.UpdateRouteTableRoute{ + DestinationCidrBlock: current.DestinationCidrBlock, + } + if current.TargetGatewayIdentity != nil { + req.TargetGatewayIdentity = *current.TargetGatewayIdentity + } + if current.TargetNatGatewayIdentity != nil { + req.TargetNatGatewayIdentity = *current.TargetNatGatewayIdentity + } + if current.TargetVpcPeeringConnectionId != nil { + req.TargetVpcPeeringConnectionId = current.TargetVpcPeeringConnectionId + } + if current.GatewayAddress != nil { + req.GatewayAddress = *current.GatewayAddress + } + + if cmd.Flags().Changed("destination") { + req.DestinationCidrBlock = routeUpdateDestination + } + if cmd.Flags().Changed("gateway") { + req.TargetGatewayIdentity = routeUpdateGateway + req.TargetNatGatewayIdentity = "" + req.TargetVpcPeeringConnectionId = nil + req.GatewayAddress = "" + } + if cmd.Flags().Changed("nat-gateway") { + req.TargetNatGatewayIdentity = routeUpdateNatGateway + req.TargetGatewayIdentity = "" + req.TargetVpcPeeringConnectionId = nil + req.GatewayAddress = "" + } + if cmd.Flags().Changed("vpc-peering") { + req.TargetVpcPeeringConnectionId = &routeUpdatePeering + req.TargetGatewayIdentity = "" + req.TargetNatGatewayIdentity = "" + req.GatewayAddress = "" + } + if cmd.Flags().Changed("gateway-address") { + req.GatewayAddress = routeUpdateAddress + req.TargetGatewayIdentity = "" + req.TargetNatGatewayIdentity = "" + req.TargetVpcPeeringConnectionId = nil + } + + route, err := client.IaaS().UpdateRouteTableRoute(cmd.Context(), args[0], args[1], req) + if err != nil { + return err + } + + body := [][]string{{ + route.Identity, + route.DestinationCidrBlock, + routeTarget(*route), + route.Type, + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Destination", "Target", "Type"}, body) + } + return nil + }, +} + +func init() { + RoutesCmd.AddCommand(routesUpdateCmd) + + routesUpdateCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + routesUpdateCmd.Flags().StringVar(&routeUpdateDestination, "destination", "", "Destination CIDR block") + routesUpdateCmd.Flags().StringVar(&routeUpdateGateway, "gateway", "", "Target gateway identity") + routesUpdateCmd.Flags().StringVar(&routeUpdateNatGateway, "nat-gateway", "", "Target NAT gateway identity") + routesUpdateCmd.Flags().StringVar(&routeUpdatePeering, "vpc-peering", "", "Target VPC peering connection identity") + routesUpdateCmd.Flags().StringVar(&routeUpdateAddress, "gateway-address", "", "Gateway address") + + _ = routesUpdateCmd.RegisterFlagCompletionFunc("nat-gateway", completeNatGatewayID) +} diff --git a/cmd/iaas/networking/routetables/routes_view.go b/cmd/iaas/networking/routetables/routes_view.go new file mode 100644 index 0000000..318a8aa --- /dev/null +++ b/cmd/iaas/networking/routetables/routes_view.go @@ -0,0 +1,56 @@ +package routetables + +import ( + "fmt" + + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var routesViewOutputFormat string + +var routesViewCmd = &cobra.Command{ + Use: "view ROUTE_TABLE ROUTE", + Short: "View a route in a route table", + Aliases: []string{"show", "describe"}, + Args: cobra.ExactArgs(2), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + route, err := client.IaaS().GetRouteTableRoute(cmd.Context(), args[0], args[1]) + if err != nil { + return fmt.Errorf("failed to get route: %w", err) + } + + if routesViewOutputFormat == "yaml" { + yamlData, err := yaml.Marshal(route) + if err != nil { + return fmt.Errorf("failed to marshal to YAML: %w", err) + } + fmt.Print(string(yamlData)) + return nil + } + + fmt.Printf("Route Details:\n") + fmt.Printf(" ID: %s\n", route.Identity) + fmt.Printf(" Destination: %s\n", route.DestinationCidrBlock) + fmt.Printf(" Target: %s\n", routeTarget(*route)) + fmt.Printf(" Type: %s\n", route.Type) + if route.Note != nil { + fmt.Printf(" Note: %s\n", *route.Note) + } + return nil + }, +} + +func init() { + RoutesCmd.AddCommand(routesViewCmd) + routesViewCmd.Flags().StringVarP(&routesViewOutputFormat, "output", "o", "", "Output format (yaml)") + _ = routesViewCmd.RegisterFlagCompletionFunc("output", completeOutputFormat) +} diff --git a/cmd/iaas/networking/routetables/routetables.go b/cmd/iaas/networking/routetables/routetables.go index d334541..4eb3905 100644 --- a/cmd/iaas/networking/routetables/routetables.go +++ b/cmd/iaas/networking/routetables/routetables.go @@ -4,11 +4,13 @@ import ( "github.com/spf13/cobra" ) -// RouteTablesCmd represents the incidents command +// RouteTablesCmd represents the route tables command var RouteTablesCmd = &cobra.Command{ Use: "routetables", - Aliases: []string{"routetables"}, - Short: "Manage routetables", + Aliases: []string{"route-tables", "rt"}, + Short: "Manage route tables", + Long: "Manage VPC route tables and their routes within the Thalassa Cloud Platform.", + Example: "tcloud networking routetables list\ntcloud networking routetables create --name custom --vpc vpc-123\ntcloud networking routetables routes list rt-123", } func init() { diff --git a/cmd/iaas/networking/routetables/update.go b/cmd/iaas/networking/routetables/update.go new file mode 100644 index 0000000..8708ad9 --- /dev/null +++ b/cmd/iaas/networking/routetables/update.go @@ -0,0 +1,72 @@ +package routetables + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var ( + updateName string + updateDescription string + updateLabels []string + updateAnnotations []string +) + +var updateCmd = &cobra.Command{ + Use: "update ROUTE_TABLE", + Short: "Update a route table", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + current, err := client.IaaS().GetRouteTable(cmd.Context(), args[0]) + if err != nil { + if tcclient.IsNotFound(err) { + return fmt.Errorf("route table not found: %s", args[0]) + } + return fmt.Errorf("failed to get route table: %w", err) + } + + req := iaas.UpdateRouteTable{ + Labels: current.Labels, + Annotations: current.Annotations, + } + if cmd.Flags().Changed("name") { + req.Name = &updateName + } + if cmd.Flags().Changed("description") { + req.Description = &updateDescription + } + if cmd.Flags().Changed("labels") { + req.Labels = shared.KeyValuePairsToMap(updateLabels) + } + if cmd.Flags().Changed("annotations") { + req.Annotations = shared.KeyValuePairsToMap(updateAnnotations) + } + + rt, err := client.IaaS().UpdateRouteTable(cmd.Context(), current.Identity, req) + if err != nil { + return fmt.Errorf("failed to update route table: %w", err) + } + fmt.Printf("Route table %s updated\n", rt.Identity) + return nil + }, +} + +func init() { + RouteTablesCmd.AddCommand(updateCmd) + updateCmd.Flags().StringVar(&updateName, "name", "", "Name") + updateCmd.Flags().StringVar(&updateDescription, "description", "", "Description") + updateCmd.Flags().StringSliceVar(&updateLabels, "labels", nil, "Labels as key=value (repeatable)") + updateCmd.Flags().StringSliceVar(&updateAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") +} diff --git a/cmd/iaas/networking/routetables/view.go b/cmd/iaas/networking/routetables/view.go new file mode 100644 index 0000000..c8d6bbf --- /dev/null +++ b/cmd/iaas/networking/routetables/view.go @@ -0,0 +1,75 @@ +package routetables + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var viewExactTime bool + +var viewCmd = &cobra.Command{ + Use: "view ROUTE_TABLE", + Short: "View a route table and its routes", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeRouteTableID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + rt, err := client.IaaS().GetRouteTable(cmd.Context(), args[0]) + if err != nil { + if tcclient.IsNotFound(err) { + return fmt.Errorf("route table not found: %s", args[0]) + } + return fmt.Errorf("failed to get route table: %w", err) + } + + body := [][]string{ + {"ID", rt.Identity}, + {"Name", rt.Name}, + {"Slug", rt.Slug}, + {"Description", descriptionString(rt.Description)}, + {"VPC", vpcName(*rt)}, + {"Default", fmt.Sprintf("%t", rt.IsDefault)}, + {"Created", formattime.FormatTime(rt.CreatedAt.Local(), viewExactTime)}, + } + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"Field", "Value"}, body) + } + + if len(rt.Routes) > 0 { + fmt.Println() + routeBody := make([][]string, 0, len(rt.Routes)) + for _, route := range rt.Routes { + routeBody = append(routeBody, []string{ + route.Identity, + route.DestinationCidrBlock, + routeTarget(route), + route.Type, + }) + } + if noHeader { + table.Print(nil, routeBody) + } else { + table.Print([]string{"ID", "Destination", "Target", "Type"}, routeBody) + } + } + return nil + }, +} + +func init() { + RouteTablesCmd.AddCommand(viewCmd) + viewCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + viewCmd.Flags().BoolVar(&viewExactTime, "exact-time", false, "Show full timestamps instead of relative time") +} From 011e08b591b41692d77eacbaf7646e694f5b71fc Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:15:13 +0200 Subject: [PATCH 09/19] feat(networking): add security group update and batch rules Update security group metadata without touching rules, and replace ingress or egress rules from a JSON file. --- cmd/iaas/networking/securitygroups/rules.go | 93 ++++++++++++++++++++ cmd/iaas/networking/securitygroups/update.go | 83 +++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 cmd/iaas/networking/securitygroups/rules.go create mode 100644 cmd/iaas/networking/securitygroups/update.go diff --git a/cmd/iaas/networking/securitygroups/rules.go b/cmd/iaas/networking/securitygroups/rules.go new file mode 100644 index 0000000..2c65c72 --- /dev/null +++ b/cmd/iaas/networking/securitygroups/rules.go @@ -0,0 +1,93 @@ +package securitygroups + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" +) + +var RulesCmd = &cobra.Command{ + Use: "rules", + Aliases: []string{"rule"}, + Short: "Manage security group rules", +} + +var ( + setIngressFile string + setEgressFile string +) + +func loadRules(path string) ([]iaas.SecurityGroupRule, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read rules file: %w", err) + } + var rules []iaas.SecurityGroupRule + if err := json.Unmarshal(data, &rules); err != nil { + return nil, fmt.Errorf("parse rules JSON: %w", err) + } + return rules, nil +} + +var setIngressCmd = &cobra.Command{ + Use: "set-ingress SECURITY_GROUP", + Short: "Replace all ingress rules from a JSON file", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeSecurityGroupID, + RunE: func(cmd *cobra.Command, args []string) error { + rules, err := loadRules(setIngressFile) + if err != nil { + return err + } + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + result, err := client.IaaS().BatchUpdateSecurityGroupIngressRules(cmd.Context(), args[0], iaas.BatchUpdateSecurityGroupRulesRequest{Rules: rules}) + if err != nil { + return fmt.Errorf("failed to set ingress rules: %w", err) + } + fmt.Printf("Set %d ingress rules on %s\n", len(result), args[0]) + return nil + }, +} + +var setEgressCmd = &cobra.Command{ + Use: "set-egress SECURITY_GROUP", + Short: "Replace all egress rules from a JSON file", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeSecurityGroupID, + RunE: func(cmd *cobra.Command, args []string) error { + rules, err := loadRules(setEgressFile) + if err != nil { + return err + } + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + result, err := client.IaaS().BatchUpdateSecurityGroupEgressRules(cmd.Context(), args[0], iaas.BatchUpdateSecurityGroupRulesRequest{Rules: rules}) + if err != nil { + return fmt.Errorf("failed to set egress rules: %w", err) + } + fmt.Printf("Set %d egress rules on %s\n", len(result), args[0]) + return nil + }, +} + +func init() { + SecurityGroupsCmd.AddCommand(RulesCmd) + + RulesCmd.AddCommand(setIngressCmd) + setIngressCmd.Flags().StringVar(&setIngressFile, "file", "", "JSON array of SecurityGroupRule objects") + _ = setIngressCmd.MarkFlagRequired("file") + + RulesCmd.AddCommand(setEgressCmd) + setEgressCmd.Flags().StringVar(&setEgressFile, "file", "", "JSON array of SecurityGroupRule objects") + _ = setEgressCmd.MarkFlagRequired("file") +} diff --git a/cmd/iaas/networking/securitygroups/update.go b/cmd/iaas/networking/securitygroups/update.go new file mode 100644 index 0000000..e7ceb5d --- /dev/null +++ b/cmd/iaas/networking/securitygroups/update.go @@ -0,0 +1,83 @@ +package securitygroups + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/iaas" + tcclient "github.com/thalassa-cloud/client-go/pkg/client" +) + +var ( + updateName string + updateDescription string + updateLabels []string + updateAnnotations []string + updateAllowSameGroup bool +) + +var updateCmd = &cobra.Command{ + Use: "update SECURITY_GROUP", + Short: "Update security group metadata", + Long: "Update name, description, labels, annotations, or allow-same-group. Rules are left unchanged.", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completeSecurityGroupID, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + current, err := client.IaaS().GetSecurityGroup(cmd.Context(), args[0]) + if err != nil { + if tcclient.IsNotFound(err) { + return fmt.Errorf("security group not found: %s", args[0]) + } + return fmt.Errorf("failed to get security group: %w", err) + } + + req := iaas.UpdateSecurityGroupRequest{ + Name: current.Name, + Description: current.Description, + Labels: current.Labels, + Annotations: current.Annotations, + ObjectVersion: current.ObjectVersion, + AllowSameGroupTraffic: current.AllowSameGroupTraffic, + SkipRulesUpdate: true, + } + if cmd.Flags().Changed("name") { + req.Name = updateName + } + if cmd.Flags().Changed("description") { + req.Description = updateDescription + } + if cmd.Flags().Changed("labels") { + req.Labels = shared.KeyValuePairsToMap(updateLabels) + } + if cmd.Flags().Changed("annotations") { + req.Annotations = shared.KeyValuePairsToMap(updateAnnotations) + } + if cmd.Flags().Changed("allow-same-group") { + req.AllowSameGroupTraffic = updateAllowSameGroup + } + + sg, err := client.IaaS().UpdateSecurityGroup(cmd.Context(), current.Identity, req) + if err != nil { + return fmt.Errorf("failed to update security group: %w", err) + } + fmt.Printf("Security group %s updated\n", sg.Identity) + return nil + }, +} + +func init() { + SecurityGroupsCmd.AddCommand(updateCmd) + updateCmd.Flags().StringVar(&updateName, "name", "", "Name") + updateCmd.Flags().StringVar(&updateDescription, "description", "", "Description") + updateCmd.Flags().StringSliceVar(&updateLabels, "labels", nil, "Labels as key=value (repeatable)") + updateCmd.Flags().StringSliceVar(&updateAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") + updateCmd.Flags().BoolVar(&updateAllowSameGroup, "allow-same-group", false, "Allow traffic between instances in the same security group") +} From 8ba1a40d6b65a57357325e15623bc285bff18745 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:15:13 +0200 Subject: [PATCH 10/19] feat(projects): add create, view, update, and delete commands Complete project management beyond list for organisations with the project feature gate enabled. --- cmd/projects/create.go | 77 ++++++++++++++++++++++++++++++++++ cmd/projects/delete.go | 46 ++++++++++++++++++++ cmd/projects/update.go | 95 ++++++++++++++++++++++++++++++++++++++++++ cmd/projects/view.go | 53 +++++++++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 cmd/projects/create.go create mode 100644 cmd/projects/delete.go create mode 100644 cmd/projects/update.go create mode 100644 cmd/projects/view.go diff --git a/cmd/projects/create.go b/cmd/projects/create.go new file mode 100644 index 0000000..a181df8 --- /dev/null +++ b/cmd/projects/create.go @@ -0,0 +1,77 @@ +package projects + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientprojects "github.com/thalassa-cloud/client-go/projects" +) + +var ( + createName string + createDescription string + createLabels []string + createAnnotations []string + createParent string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a project in the current organisation", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + if createName == "" { + return fmt.Errorf("--name is required") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req := clientprojects.CreateProjectRequest{ + Name: createName, + Description: createDescription, + Labels: shared.KeyValuePairsToMap(createLabels), + Annotations: shared.KeyValuePairsToMap(createAnnotations), + } + if cmd.Flags().Changed("parent") { + parent := createParent + req.ParentProjectIdentity = &parent + } + + project, err := client.Projects().CreateProject(cmd.Context(), req) + if err != nil { + return fmt.Errorf("failed to create project: %w", err) + } + + body := [][]string{{ + project.Identity, + project.Name, + project.Slug, + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Slug"}, body) + } + return nil + }, +} + +func init() { + ProjectsCmd.AddCommand(createCmd) + createCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "do not print headers") + createCmd.Flags().StringVar(&createName, "name", "", "Project display name") + createCmd.Flags().StringVar(&createDescription, "description", "", "Project description") + createCmd.Flags().StringSliceVar(&createLabels, "labels", nil, "Labels as key=value (repeatable)") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") + createCmd.Flags().StringVar(&createParent, "parent", "", "Parent project identity or slug") + _ = createCmd.MarkFlagRequired("name") + _ = createCmd.RegisterFlagCompletionFunc("parent", completion.CompleteProject) +} diff --git a/cmd/projects/delete.go b/cmd/projects/delete.go new file mode 100644 index 0000000..2616210 --- /dev/null +++ b/cmd/projects/delete.go @@ -0,0 +1,46 @@ +package projects + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var deleteForce bool + +var deleteCmd = &cobra.Command{ + Use: "delete ", + Short: "Delete a project", + Aliases: []string{"rm", "remove"}, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteProject, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + ok, err := shared.PromptDestructiveUnlessForce(deleteForce, fmt.Sprintf("Are you sure you want to delete this project?\n Project: %s\n", args[0])) + if err != nil { + return err + } + if !ok { + return nil + } + + if err := client.Projects().DeleteProject(cmd.Context(), args[0]); err != nil { + return fmt.Errorf("failed to delete project: %w", err) + } + fmt.Printf("Deleted project %s\n", args[0]) + return nil + }, +} + +func init() { + ProjectsCmd.AddCommand(deleteCmd) + deleteCmd.Flags().BoolVar(&deleteForce, shared.ForceKey, false, "Skip the confirmation prompt and delete") +} diff --git a/cmd/projects/update.go b/cmd/projects/update.go new file mode 100644 index 0000000..bfe24d2 --- /dev/null +++ b/cmd/projects/update.go @@ -0,0 +1,95 @@ +package projects + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientprojects "github.com/thalassa-cloud/client-go/projects" +) + +var ( + updateName string + updateDescription string + updateLabels []string + updateAnnotations []string + updateParent string +) + +var updateCmd = &cobra.Command{ + Use: "update ", + Short: "Update a project (only flags you set are changed)", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteProject, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + project, err := client.Projects().GetProject(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to get project: %w", err) + } + + req := clientprojects.UpdateProjectRequest{ + Name: project.Name, + Description: project.Description, + Labels: project.Labels, + Annotations: project.Annotations, + } + if project.ParentProject != nil { + parentIdentity := project.ParentProject.Identity + req.ParentProjectIdentity = &parentIdentity + } + + if cmd.Flags().Changed("name") { + req.Name = updateName + } + if cmd.Flags().Changed("description") { + req.Description = updateDescription + } + if cmd.Flags().Changed("labels") { + req.Labels = shared.KeyValuePairsToMap(updateLabels) + } + if cmd.Flags().Changed("annotations") { + req.Annotations = shared.KeyValuePairsToMap(updateAnnotations) + } + if cmd.Flags().Changed("parent") { + parent := updateParent + req.ParentProjectIdentity = &parent + } + + out, err := client.Projects().UpdateProject(cmd.Context(), project.Identity, req) + if err != nil { + return fmt.Errorf("failed to update project: %w", err) + } + + body := [][]string{{ + out.Identity, + out.Name, + out.Slug, + }} + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Name", "Slug"}, body) + } + return nil + }, +} + +func init() { + ProjectsCmd.AddCommand(updateCmd) + updateCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "do not print headers") + updateCmd.Flags().StringVar(&updateName, "name", "", "Project display name") + updateCmd.Flags().StringVar(&updateDescription, "description", "", "Project description") + updateCmd.Flags().StringSliceVar(&updateLabels, "labels", nil, "Replace labels (key=value, repeatable)") + updateCmd.Flags().StringSliceVar(&updateAnnotations, "annotations", nil, "Replace annotations (key=value, repeatable)") + updateCmd.Flags().StringVar(&updateParent, "parent", "", "Parent project identity or slug (empty clears parent)") + _ = updateCmd.RegisterFlagCompletionFunc("parent", completion.CompleteProject) +} diff --git a/cmd/projects/view.go b/cmd/projects/view.go new file mode 100644 index 0000000..de765f7 --- /dev/null +++ b/cmd/projects/view.go @@ -0,0 +1,53 @@ +package projects + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var viewExactTime bool + +var viewCmd = &cobra.Command{ + Use: "view ", + Short: "View a project", + Aliases: []string{"show", "get", "describe"}, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteProject, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + project, err := client.Projects().GetProject(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to get project: %w", err) + } + + parent := "-" + if project.ParentProject != nil { + parent = project.ParentProject.Name + if project.ParentProject.Slug != "" { + parent = fmt.Sprintf("%s (%s)", project.ParentProject.Name, project.ParentProject.Slug) + } + } + + fmt.Printf("ID: %s\n", project.Identity) + fmt.Printf("Name: %s\n", project.Name) + fmt.Printf("Slug: %s\n", project.Slug) + fmt.Printf("Description: %s\n", project.Description) + fmt.Printf("Parent: %s\n", parent) + fmt.Printf("CreatedAt: %s\n", formattime.FormatTime(project.CreatedAt.Local(), viewExactTime)) + return nil + }, +} + +func init() { + ProjectsCmd.AddCommand(viewCmd) + viewCmd.Flags().BoolVar(&viewExactTime, "exact-time", false, "Show full timestamps instead of relative time") +} From a731796d8d13752613a0fb1f8160c593d3971449 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 16:15:14 +0200 Subject: [PATCH 11/19] feat(kubernetes): add kubeconfig session list and delete Inspect and revoke active kubeconfig sessions for a cluster. --- cmd/kubernetes/kube.go | 2 + cmd/kubernetes/kubeconfigsessions/delete.go | 96 ++++++++++++++ .../kubeconfigsessions/kubeconfigsessions.go | 18 +++ cmd/kubernetes/kubeconfigsessions/list.go | 117 ++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 cmd/kubernetes/kubeconfigsessions/delete.go create mode 100644 cmd/kubernetes/kubeconfigsessions/kubeconfigsessions.go create mode 100644 cmd/kubernetes/kubeconfigsessions/list.go diff --git a/cmd/kubernetes/kube.go b/cmd/kubernetes/kube.go index eb54d2c..c891a26 100644 --- a/cmd/kubernetes/kube.go +++ b/cmd/kubernetes/kube.go @@ -5,6 +5,7 @@ import ( "github.com/thalassa-cloud/cli/cmd/kubernetes/connect" "github.com/thalassa-cloud/cli/cmd/kubernetes/credential" "github.com/thalassa-cloud/cli/cmd/kubernetes/iam" + "github.com/thalassa-cloud/cli/cmd/kubernetes/kubeconfigsessions" "github.com/thalassa-cloud/cli/cmd/kubernetes/kubernetesversions" "github.com/thalassa-cloud/cli/cmd/kubernetes/machines" "github.com/thalassa-cloud/cli/cmd/kubernetes/nodepools" @@ -24,4 +25,5 @@ func init() { KubernetesCmd.AddCommand(iam.IamCmd) KubernetesCmd.AddCommand(connect.KubernetesConnectCmd) KubernetesCmd.AddCommand(credential.CredentialCmd) + KubernetesCmd.AddCommand(kubeconfigsessions.KubeconfigSessionsCmd) } diff --git a/cmd/kubernetes/kubeconfigsessions/delete.go b/cmd/kubernetes/kubeconfigsessions/delete.go new file mode 100644 index 0000000..5dc077b --- /dev/null +++ b/cmd/kubernetes/kubeconfigsessions/delete.go @@ -0,0 +1,96 @@ +package kubeconfigsessions + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/kuberesolve" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var ( + deleteCluster string + deleteForce bool +) + +var deleteCmd = &cobra.Command{ + Use: "delete [cluster] ", + Short: "Delete a kubeconfig session", + Long: `Revoke a kubeconfig session for a Kubernetes cluster. + +Provide the cluster as the first argument or with --cluster, and the session identity as the final argument.`, + Aliases: []string{"rm", "remove"}, + Args: cobra.RangeArgs(1, 2), + ValidArgsFunction: completeDeleteArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + clusterRef := deleteCluster + sessionIdentity := "" + switch len(args) { + case 1: + sessionIdentity = args[0] + case 2: + if clusterRef != "" && clusterRef != args[0] { + return fmt.Errorf("provide the cluster as an argument or --cluster, not both with different values") + } + clusterRef = args[0] + sessionIdentity = args[1] + } + if clusterRef == "" { + return fmt.Errorf("cluster is required (argument or --cluster)") + } + if sessionIdentity == "" { + return fmt.Errorf("session identity is required") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + cluster, err := kuberesolve.ResolveKubernetesClusterRef(ctx, client.Kubernetes(), clusterRef) + if err != nil { + return err + } + + ok, err := shared.PromptDestructiveUnlessForce(deleteForce, fmt.Sprintf( + "Are you sure you want to delete this kubeconfig session?\n Cluster: %s\n Session: %s\n", + cluster.Name, sessionIdentity, + )) + if err != nil { + return err + } + if !ok { + return nil + } + + if err := client.Kubernetes().DeleteKubeconfigSession(ctx, cluster.Identity, sessionIdentity); err != nil { + return fmt.Errorf("failed to delete kubeconfig session: %w", err) + } + fmt.Printf("Deleted kubeconfig session %s\n", sessionIdentity) + return nil + }, +} + +func completeDeleteArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + switch len(args) { + case 0: + if deleteCluster != "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return completion.CompleteKubernetesCluster(cmd, args, toComplete) + default: + return nil, cobra.ShellCompDirectiveNoFileComp + } +} + +func init() { + KubeconfigSessionsCmd.AddCommand(deleteCmd) + deleteCmd.Flags().StringVar(&deleteCluster, ClusterFlag, "", "Cluster identity, name, or slug") + deleteCmd.Flags().BoolVar(&deleteForce, shared.ForceKey, false, "Skip the confirmation prompt and delete") + _ = deleteCmd.RegisterFlagCompletionFunc(ClusterFlag, completion.CompleteKubernetesCluster) +} diff --git a/cmd/kubernetes/kubeconfigsessions/kubeconfigsessions.go b/cmd/kubernetes/kubeconfigsessions/kubeconfigsessions.go new file mode 100644 index 0000000..a508ad3 --- /dev/null +++ b/cmd/kubernetes/kubeconfigsessions/kubeconfigsessions.go @@ -0,0 +1,18 @@ +package kubeconfigsessions + +import ( + "github.com/spf13/cobra" +) + +// KubeconfigSessionsCmd manages Kubernetes kubeconfig sessions. +var KubeconfigSessionsCmd = &cobra.Command{ + Use: "kubeconfig-sessions", + Aliases: []string{"kubeconfigsessions", "kcs"}, + Short: "Manage Kubernetes kubeconfig sessions", + Long: "List and revoke active kubeconfig sessions for a Kubernetes cluster.", + Example: ` # List kubeconfig sessions for a cluster + tcloud kubernetes kubeconfig-sessions list my-cluster + + # Delete a kubeconfig session + tcloud kubernetes kubeconfig-sessions delete my-cluster sess-abc123 --force`, +} diff --git a/cmd/kubernetes/kubeconfigsessions/list.go b/cmd/kubernetes/kubeconfigsessions/list.go new file mode 100644 index 0000000..258ff50 --- /dev/null +++ b/cmd/kubernetes/kubeconfigsessions/list.go @@ -0,0 +1,117 @@ +package kubeconfigsessions + +import ( + "fmt" + "time" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/kuberesolve" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + "github.com/thalassa-cloud/client-go/kubernetes" +) + +const ( + NoHeaderKey = "no-header" + ClusterFlag = "cluster" +) + +var ( + noHeader bool + showExactTime bool + listCluster string +) + +var listCmd = &cobra.Command{ + Use: "list [cluster]", + Aliases: []string{"ls", "l"}, + Short: "List kubeconfig sessions for a Kubernetes cluster", + Args: cobra.MaximumNArgs(1), + ValidArgsFunction: completion.CompleteKubernetesCluster, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + clusterRef := listCluster + if len(args) > 0 { + if clusterRef != "" && clusterRef != args[0] { + return fmt.Errorf("provide the cluster as an argument or --cluster, not both with different values") + } + clusterRef = args[0] + } + if clusterRef == "" { + return fmt.Errorf("cluster is required (argument or --cluster)") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + cluster, err := kuberesolve.ResolveKubernetesClusterRef(ctx, client.Kubernetes(), clusterRef) + if err != nil { + return err + } + + sessions, err := client.Kubernetes().ListKubeconfigSessions(ctx, cluster.Identity) + if err != nil { + return fmt.Errorf("failed to list kubeconfig sessions: %w", err) + } + + if len(sessions) == 0 { + fmt.Println("No kubeconfig sessions found") + return nil + } + + body := make([][]string, 0, len(sessions)) + for _, session := range sessions { + body = append(body, []string{ + session.Identity, + sessionSubject(session), + formattime.FormatTime(session.CreatedAt.Local(), showExactTime), + formatOptionalTime(session.LastUsedAt, showExactTime), + formattime.FormatTime(session.ExpiresAt.Local(), showExactTime), + }) + } + + headers := []string{"Identity", "User/ServiceAccount", "CreatedAt", "LastUsedAt", "ExpiresAt"} + if noHeader { + table.Print(nil, body) + } else { + table.Print(headers, body) + } + return nil + }, +} + +func sessionSubject(session kubernetes.KubernetesClusterSession) string { + if session.User != nil { + return "user:" + shared.UserPtrDisplay(session.User) + } + if session.ServiceAccount != nil { + name := session.ServiceAccount.Name + if name == "" { + name = session.ServiceAccount.Identity + } + return "serviceaccount:" + name + } + return "-" +} + +func formatOptionalTime(t *time.Time, exact bool) string { + if t == nil { + return "-" + } + return formattime.FormatTime(t.Local(), exact) +} + +func init() { + KubeconfigSessionsCmd.AddCommand(listCmd) + listCmd.Flags().BoolVar(&noHeader, NoHeaderKey, false, "Do not print the header") + listCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") + listCmd.Flags().StringVar(&listCluster, ClusterFlag, "", "Cluster identity, name, or slug") + _ = listCmd.RegisterFlagCompletionFunc(ClusterFlag, completion.CompleteKubernetesCluster) +} From adf7d87d006a22dbf481a9a143a32e1416381e8d Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 18:43:16 +0200 Subject: [PATCH 12/19] chore(cli): mark dns, kms, secrets, and projects as beta Surface beta status in short and long help so operators know these command groups are still evolving. --- cmd/dns/dns.go | 5 ++++- cmd/kms/kms.go | 5 ++++- cmd/projects/projects.go | 4 ++-- cmd/secrets/secrets.go | 6 +++++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cmd/dns/dns.go b/cmd/dns/dns.go index d0ffef2..abe2594 100644 --- a/cmd/dns/dns.go +++ b/cmd/dns/dns.go @@ -10,7 +10,10 @@ import ( // DnsCmd manages DNS zones and records. var DnsCmd = &cobra.Command{ Use: "dns", - Short: "Manage DNS zones and records", + Short: "Manage DNS zones and records (beta)", + Long: `Manage DNS zones and records within the Thalassa Cloud Platform. + +Note: This command is in beta.`, } func init() { diff --git a/cmd/kms/kms.go b/cmd/kms/kms.go index 5ac4308..0d573fd 100644 --- a/cmd/kms/kms.go +++ b/cmd/kms/kms.go @@ -9,7 +9,10 @@ import ( // KmsCmd manages Key Management Service resources and crypto operations. var KmsCmd = &cobra.Command{ Use: "kms", - Short: "Manage KMS keys and cryptographic operations", + Short: "Manage KMS keys and cryptographic operations (beta)", + Long: `Manage Key Management Service keys and cryptographic operations. + +Note: This command is in beta.`, } var ( diff --git a/cmd/projects/projects.go b/cmd/projects/projects.go index a4db188..5fe640b 100644 --- a/cmd/projects/projects.go +++ b/cmd/projects/projects.go @@ -6,8 +6,8 @@ import ( var ProjectsCmd = &cobra.Command{ Use: "projects", - Short: "Manage projects (private beta)", + Short: "Manage projects (beta)", Long: `Manage projects within the organisation selected in your context. -Note: This command is in private beta and requires the project feature gate to be enabled on your organisation.`, +Note: This command is in beta and requires the project feature gate to be enabled on your organisation.`, } diff --git a/cmd/secrets/secrets.go b/cmd/secrets/secrets.go index 3caefb7..a1cdc21 100644 --- a/cmd/secrets/secrets.go +++ b/cmd/secrets/secrets.go @@ -8,7 +8,11 @@ import ( var SecretsCmd = &cobra.Command{ Use: "secrets", Aliases: []string{"secret"}, - Short: "Manage secrets", + Short: "Manage secrets (beta)", + Long: `Manage Secrets Manager paths, versions, and access policies. + +Note: This command is in beta. Commands that list or view secrets show +metadata only; use get-value when you intentionally need secret material.`, } var ( From 046a7af649f042d923c0ba1a7ea0dcd23c3c2a6f Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 18:43:23 +0200 Subject: [PATCH 13/19] feat(kms): add file input and output for crypto commands Support --from-file/--to-file on encrypt, decrypt, sign, and verify so operators can work with files without pasting base64 on the command line. --- cmd/kms/decrypt.go | 32 +++++++--- cmd/kms/encrypt.go | 28 +++++++-- cmd/kms/io.go | 61 +++++++++++++++++++ cmd/kms/sign.go | 28 +++++++-- cmd/kms/verify.go | 144 +++++++++++++++++++++++++++++++-------------- 5 files changed, 228 insertions(+), 65 deletions(-) create mode 100644 cmd/kms/io.go diff --git a/cmd/kms/decrypt.go b/cmd/kms/decrypt.go index f0885bf..dcd0b5a 100644 --- a/cmd/kms/decrypt.go +++ b/cmd/kms/decrypt.go @@ -7,34 +7,44 @@ import ( "github.com/thalassa-cloud/cli/internal/completion" "github.com/thalassa-cloud/cli/internal/thalassaclient" - clientkms "github.com/thalassa-cloud/client-go/kms" ) var ( decryptRegion string decryptKey string decryptCiphertext string + decryptFromFile string + decryptToFile string ) var decryptCmd = &cobra.Command{ Use: "decrypt", - Short: "Decrypt ciphertext with a KMS key (prints base64 plaintext to stdout)", - Args: cobra.NoArgs, + Short: "Decrypt ciphertext with a KMS key", + Long: `Decrypt data with a KMS key. + +Provide exactly one of --ciphertext or --from-file (file containing ciphertext). +When --to-file is set, decoded plaintext bytes are written with mode 0600. +Otherwise base64-encoded plaintext is printed to stdout.`, + Example: ` tcloud kms decrypt --region nl-ams --key kms-123 --ciphertext 'thalassa:v1:...' + tcloud kms decrypt --region nl-ams --key kms-123 --from-file secret.enc --to-file secret.txt`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { + ciphertext, err := resolveTextFileInput(decryptCiphertext, decryptFromFile, "ciphertext") + if err != nil { + return err + } + client, err := thalassaclient.GetThalassaClient() if err != nil { return fmt.Errorf("failed to create client: %w", err) } - result, err := client.KMS().Decrypt(cmd.Context(), decryptRegion, decryptKey, clientkms.DecryptRequest{ - Ciphertext: decryptCiphertext, - }) + plaintext, err := client.KMS().DecryptBytes(cmd.Context(), decryptRegion, decryptKey, ciphertext) if err != nil { return fmt.Errorf("failed to decrypt: %w", err) } - fmt.Println(result.Plaintext) - return nil + return writeBinaryResult(decryptToFile, plaintext) }, } @@ -43,9 +53,13 @@ func init() { decryptCmd.Flags().StringVar(&decryptRegion, "region", "", "Region") decryptCmd.Flags().StringVar(&decryptKey, "key", "", "KMS key identity") decryptCmd.Flags().StringVar(&decryptCiphertext, "ciphertext", "", "Ciphertext from encrypt") + decryptCmd.Flags().StringVar(&decryptFromFile, "from-file", "", "Read ciphertext from a file") + decryptCmd.Flags().StringVar(&decryptToFile, "to-file", "", "Write decoded plaintext to a file (mode 0600) instead of stdout") _ = decryptCmd.MarkFlagRequired("region") _ = decryptCmd.MarkFlagRequired("key") - _ = decryptCmd.MarkFlagRequired("ciphertext") + decryptCmd.MarkFlagsMutuallyExclusive("ciphertext", "from-file") + _ = decryptCmd.MarkFlagFilename("from-file") + _ = decryptCmd.MarkFlagFilename("to-file") _ = decryptCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = decryptCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) } diff --git a/cmd/kms/encrypt.go b/cmd/kms/encrypt.go index 31637f1..8cdf802 100644 --- a/cmd/kms/encrypt.go +++ b/cmd/kms/encrypt.go @@ -14,29 +14,41 @@ var ( encryptRegion string encryptKey string encryptPlaintext string + encryptFromFile string + encryptToFile string encryptKeyVersion string ) var encryptCmd = &cobra.Command{ Use: "encrypt", - Short: "Encrypt plaintext with a KMS key (plaintext must be base64-encoded)", - Args: cobra.NoArgs, + Short: "Encrypt plaintext with a KMS key", + Long: `Encrypt data with a KMS key. + +Provide exactly one of --plaintext (base64-encoded) or --from-file (raw file +bytes). Ciphertext is written to --to-file when set, otherwise to stdout.`, + Example: ` tcloud kms encrypt --region nl-ams --key kms-123 --plaintext "$(echo -n hello | base64)" + tcloud kms encrypt --region nl-ams --key kms-123 --from-file secret.txt --to-file secret.enc`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { + plaintext, err := resolveWireInput(encryptPlaintext, encryptFromFile, "plaintext") + if err != nil { + return err + } + client, err := thalassaclient.GetThalassaClient() if err != nil { return fmt.Errorf("failed to create client: %w", err) } result, err := client.KMS().Encrypt(cmd.Context(), encryptRegion, encryptKey, clientkms.EncryptRequest{ - Plaintext: encryptPlaintext, + Plaintext: plaintext, KeyVersion: encryptKeyVersion, }) if err != nil { return fmt.Errorf("failed to encrypt: %w", err) } - fmt.Println(result.Ciphertext) - return nil + return writeTextResult(encryptToFile, result.Ciphertext) }, } @@ -45,10 +57,14 @@ func init() { encryptCmd.Flags().StringVar(&encryptRegion, "region", "", "Region") encryptCmd.Flags().StringVar(&encryptKey, "key", "", "KMS key identity") encryptCmd.Flags().StringVar(&encryptPlaintext, "plaintext", "", "Base64-encoded plaintext") + encryptCmd.Flags().StringVar(&encryptFromFile, "from-file", "", "Read raw plaintext bytes from a file") + encryptCmd.Flags().StringVar(&encryptToFile, "to-file", "", "Write ciphertext to a file (mode 0600) instead of stdout") encryptCmd.Flags().StringVar(&encryptKeyVersion, "key-version", "", "Key version") _ = encryptCmd.MarkFlagRequired("region") _ = encryptCmd.MarkFlagRequired("key") - _ = encryptCmd.MarkFlagRequired("plaintext") + encryptCmd.MarkFlagsMutuallyExclusive("plaintext", "from-file") + _ = encryptCmd.MarkFlagFilename("from-file") + _ = encryptCmd.MarkFlagFilename("to-file") _ = encryptCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = encryptCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) } diff --git a/cmd/kms/io.go b/cmd/kms/io.go new file mode 100644 index 0000000..9283c9d --- /dev/null +++ b/cmd/kms/io.go @@ -0,0 +1,61 @@ +package kms + +import ( + "fmt" + "os" + "strings" + + "github.com/thalassa-cloud/cli/internal/config/securefile" + clientkms "github.com/thalassa-cloud/client-go/kms" +) + +func resolveWireInput(flagValue, fromFile, flagName string) (string, error) { + if flagValue == "" && fromFile == "" { + return "", fmt.Errorf("provide --%s or --from-file", flagName) + } + if fromFile == "" { + return flagValue, nil + } + data, err := os.ReadFile(fromFile) + if err != nil { + return "", fmt.Errorf("read --from-file: %w", err) + } + return clientkms.EncodeBytes(data), nil +} + +func resolveTextFileInput(flagValue, fromFile, flagName string) (string, error) { + if flagValue == "" && fromFile == "" { + return "", fmt.Errorf("provide --%s or --from-file", flagName) + } + if fromFile == "" { + return flagValue, nil + } + data, err := os.ReadFile(fromFile) + if err != nil { + return "", fmt.Errorf("read --from-file: %w", err) + } + return strings.TrimSpace(string(data)), nil +} + +func writeTextResult(toFile, value string) error { + value = strings.TrimSpace(value) + if toFile != "" { + if err := securefile.Write(toFile, []byte(value+"\n")); err != nil { + return fmt.Errorf("write --to-file: %w", err) + } + return nil + } + fmt.Println(value) + return nil +} + +func writeBinaryResult(toFile string, data []byte) error { + if toFile != "" { + if err := securefile.Write(toFile, data); err != nil { + return fmt.Errorf("write --to-file: %w", err) + } + return nil + } + fmt.Println(clientkms.EncodeBytes(data)) + return nil +} diff --git a/cmd/kms/sign.go b/cmd/kms/sign.go index 128a20f..6c5cfca 100644 --- a/cmd/kms/sign.go +++ b/cmd/kms/sign.go @@ -14,6 +14,8 @@ var ( signRegion string signKey string signInput string + signFromFile string + signToFile string signKeyVersion string signHashAlgorithm string signPrehashed bool @@ -23,15 +25,26 @@ var ( var signCmd = &cobra.Command{ Use: "sign", Short: "Sign input with an asymmetric KMS key", - Args: cobra.NoArgs, + Long: `Sign data with an asymmetric KMS key. + +Provide exactly one of --input (base64-encoded) or --from-file (raw file bytes). +The signature is written to --to-file when set, otherwise to stdout.`, + Example: ` tcloud kms sign --region nl-ams --key kms-123 --input "$(echo -n hello | base64)" + tcloud kms sign --region nl-ams --key kms-123 --from-file message.txt --to-file message.sig`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { + input, err := resolveWireInput(signInput, signFromFile, "input") + if err != nil { + return err + } + client, err := thalassaclient.GetThalassaClient() if err != nil { return fmt.Errorf("failed to create client: %w", err) } result, err := client.KMS().Sign(cmd.Context(), signRegion, signKey, clientkms.SignRequest{ - Input: signInput, + Input: input, KeyVersion: signKeyVersion, HashAlgorithm: signHashAlgorithm, Prehashed: signPrehashed, @@ -41,8 +54,7 @@ var signCmd = &cobra.Command{ return fmt.Errorf("failed to sign: %w", err) } - fmt.Println(result.Signature) - return nil + return writeTextResult(signToFile, result.Signature) }, } @@ -50,14 +62,18 @@ func init() { KmsCmd.AddCommand(signCmd) signCmd.Flags().StringVar(&signRegion, "region", "", "Region") signCmd.Flags().StringVar(&signKey, "key", "", "KMS key identity") - signCmd.Flags().StringVar(&signInput, "input", "", "Input to sign (as required by the API)") + signCmd.Flags().StringVar(&signInput, "input", "", "Base64-encoded input to sign") + signCmd.Flags().StringVar(&signFromFile, "from-file", "", "Read raw input bytes from a file") + signCmd.Flags().StringVar(&signToFile, "to-file", "", "Write signature to a file (mode 0600) instead of stdout") signCmd.Flags().StringVar(&signKeyVersion, "key-version", "", "Key version") signCmd.Flags().StringVar(&signHashAlgorithm, "hash-algorithm", "", "Hash algorithm") signCmd.Flags().BoolVar(&signPrehashed, "prehashed", false, "Input is already hashed") signCmd.Flags().StringVar(&signContext, "context", "", "Optional signing context") _ = signCmd.MarkFlagRequired("region") _ = signCmd.MarkFlagRequired("key") - _ = signCmd.MarkFlagRequired("input") + signCmd.MarkFlagsMutuallyExclusive("input", "from-file") + _ = signCmd.MarkFlagFilename("from-file") + _ = signCmd.MarkFlagFilename("to-file") _ = signCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = signCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) } diff --git a/cmd/kms/verify.go b/cmd/kms/verify.go index 8fd7f25..2bc1ce3 100644 --- a/cmd/kms/verify.go +++ b/cmd/kms/verify.go @@ -1,8 +1,9 @@ package kms import ( - "context" "fmt" + "os" + "strings" "github.com/spf13/cobra" @@ -15,71 +16,114 @@ var ( verifyRegion string verifyKey string verifyInput string + verifyFromFile string verifySignature string + verifySignatureFile string + verifyToFile string verifyHashAlgorithm string verifyHmacRegion string verifyHmacKey string verifyHmacInput string + verifyHmacFromFile string verifyHmacValue string + verifyHmacFromHMACFile string + verifyHmacToFile string verifyHmacHashAlgorithm string ) -func printValidity(valid bool) { - fmt.Printf("%v\n", valid) -} - -func runVerifySignature(ctx context.Context) error { - client, err := thalassaclient.GetThalassaClient() - if err != nil { - return fmt.Errorf("failed to create client: %w", err) +func resolveSignatureInput(flagValue, fromFile, flagName string) (string, error) { + if flagValue == "" && fromFile == "" { + return "", fmt.Errorf("provide --%s or --%s-file", flagName, flagName) } - - result, err := client.KMS().VerifySignature(ctx, verifyRegion, verifyKey, clientkms.VerifySignatureRequest{ - Input: verifyInput, - Signature: verifySignature, - HashAlgorithm: verifyHashAlgorithm, - }) - if err != nil { - return fmt.Errorf("failed to verify signature: %w", err) + if fromFile == "" { + return flagValue, nil } - printValidity(result.Valid) - return nil -} - -func runVerifyHMAC(ctx context.Context) error { - client, err := thalassaclient.GetThalassaClient() + data, err := os.ReadFile(fromFile) if err != nil { - return fmt.Errorf("failed to create client: %w", err) + return "", fmt.Errorf("read --%s-file: %w", flagName, err) } + return strings.TrimSpace(string(data)), nil +} - result, err := client.KMS().VerifyHMAC(ctx, verifyHmacRegion, verifyHmacKey, clientkms.VerifyHMACRequest{ - Input: verifyHmacInput, - HMAC: verifyHmacValue, - HashAlgorithm: verifyHmacHashAlgorithm, - }) - if err != nil { - return fmt.Errorf("failed to verify HMAC: %w", err) - } - printValidity(result.Valid) - return nil +func writeValidity(toFile string, valid bool) error { + return writeTextResult(toFile, fmt.Sprintf("%v", valid)) } var verifyCmd = &cobra.Command{ Use: "verify", Short: "Verify a signature with an asymmetric KMS key", - Args: cobra.NoArgs, + Long: `Verify a signature with an asymmetric KMS key. + +Provide exactly one of --input (base64-encoded) or --from-file (raw file bytes), +and exactly one of --signature or --signature-file. Validity is written to +--to-file when set, otherwise to stdout.`, + Example: ` tcloud kms verify --region nl-ams --key kms-123 --input "$(echo -n hello | base64)" --signature '...' + tcloud kms verify --region nl-ams --key kms-123 --from-file message.txt --signature-file message.sig`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - return runVerifySignature(cmd.Context()) + input, err := resolveWireInput(verifyInput, verifyFromFile, "input") + if err != nil { + return err + } + signature, err := resolveSignatureInput(verifySignature, verifySignatureFile, "signature") + if err != nil { + return err + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().VerifySignature(cmd.Context(), verifyRegion, verifyKey, clientkms.VerifySignatureRequest{ + Input: input, + Signature: signature, + HashAlgorithm: verifyHashAlgorithm, + }) + if err != nil { + return fmt.Errorf("failed to verify signature: %w", err) + } + + return writeValidity(verifyToFile, result.Valid) }, } var verifyHmacCmd = &cobra.Command{ Use: "verify-hmac", Short: "Verify an HMAC with a KMS key", - Args: cobra.NoArgs, + Long: `Verify an HMAC with a KMS key. + +Provide exactly one of --input (base64-encoded) or --from-file (raw file bytes), +and exactly one of --hmac or --hmac-file. Validity is written to --to-file when +set, otherwise to stdout.`, + Example: ` tcloud kms verify-hmac --region nl-ams --key kms-123 --from-file message.txt --hmac-file message.hmac`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - return runVerifyHMAC(cmd.Context()) + input, err := resolveWireInput(verifyHmacInput, verifyHmacFromFile, "input") + if err != nil { + return err + } + hmacValue, err := resolveSignatureInput(verifyHmacValue, verifyHmacFromHMACFile, "hmac") + if err != nil { + return err + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + result, err := client.KMS().VerifyHMAC(cmd.Context(), verifyHmacRegion, verifyHmacKey, clientkms.VerifyHMACRequest{ + Input: input, + HMAC: hmacValue, + HashAlgorithm: verifyHmacHashAlgorithm, + }) + if err != nil { + return fmt.Errorf("failed to verify HMAC: %w", err) + } + + return writeValidity(verifyHmacToFile, result.Valid) }, } @@ -87,26 +131,38 @@ func init() { KmsCmd.AddCommand(verifyCmd) verifyCmd.Flags().StringVar(&verifyRegion, "region", "", "Region") verifyCmd.Flags().StringVar(&verifyKey, "key", "", "KMS key identity") - verifyCmd.Flags().StringVar(&verifyInput, "input", "", "Input that was signed") + verifyCmd.Flags().StringVar(&verifyInput, "input", "", "Base64-encoded input that was signed") + verifyCmd.Flags().StringVar(&verifyFromFile, "from-file", "", "Read raw input bytes from a file") verifyCmd.Flags().StringVar(&verifySignature, "signature", "", "Signature to verify") + verifyCmd.Flags().StringVar(&verifySignatureFile, "signature-file", "", "Read signature from a file") + verifyCmd.Flags().StringVar(&verifyToFile, "to-file", "", "Write validity (true/false) to a file instead of stdout") verifyCmd.Flags().StringVar(&verifyHashAlgorithm, "hash-algorithm", "", "Hash algorithm") _ = verifyCmd.MarkFlagRequired("region") _ = verifyCmd.MarkFlagRequired("key") - _ = verifyCmd.MarkFlagRequired("input") - _ = verifyCmd.MarkFlagRequired("signature") + verifyCmd.MarkFlagsMutuallyExclusive("input", "from-file") + verifyCmd.MarkFlagsMutuallyExclusive("signature", "signature-file") + _ = verifyCmd.MarkFlagFilename("from-file") + _ = verifyCmd.MarkFlagFilename("signature-file") + _ = verifyCmd.MarkFlagFilename("to-file") _ = verifyCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = verifyCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) KmsCmd.AddCommand(verifyHmacCmd) verifyHmacCmd.Flags().StringVar(&verifyHmacRegion, "region", "", "Region") verifyHmacCmd.Flags().StringVar(&verifyHmacKey, "key", "", "KMS key identity") - verifyHmacCmd.Flags().StringVar(&verifyHmacInput, "input", "", "Input that was HMACed") + verifyHmacCmd.Flags().StringVar(&verifyHmacInput, "input", "", "Base64-encoded input that was HMACed") + verifyHmacCmd.Flags().StringVar(&verifyHmacFromFile, "from-file", "", "Read raw input bytes from a file") verifyHmacCmd.Flags().StringVar(&verifyHmacValue, "hmac", "", "HMAC value to verify") + verifyHmacCmd.Flags().StringVar(&verifyHmacFromHMACFile, "hmac-file", "", "Read HMAC value from a file") + verifyHmacCmd.Flags().StringVar(&verifyHmacToFile, "to-file", "", "Write validity (true/false) to a file instead of stdout") verifyHmacCmd.Flags().StringVar(&verifyHmacHashAlgorithm, "hash-algorithm", "", "Hash algorithm") _ = verifyHmacCmd.MarkFlagRequired("region") _ = verifyHmacCmd.MarkFlagRequired("key") - _ = verifyHmacCmd.MarkFlagRequired("input") - _ = verifyHmacCmd.MarkFlagRequired("hmac") + verifyHmacCmd.MarkFlagsMutuallyExclusive("input", "from-file") + verifyHmacCmd.MarkFlagsMutuallyExclusive("hmac", "hmac-file") + _ = verifyHmacCmd.MarkFlagFilename("from-file") + _ = verifyHmacCmd.MarkFlagFilename("hmac-file") + _ = verifyHmacCmd.MarkFlagFilename("to-file") _ = verifyHmacCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = verifyHmacCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) } From f12ab26b6185b823cf6902d6c8bc63d6933ca386 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 18:44:18 +0200 Subject: [PATCH 14/19] feat(kms): complete --key and --key-version from the API Improve region resolution during shell completion and suggest key versions from GetKey for encrypt, sign, hmac, export, and public-key. --- cmd/kms/encrypt.go | 1 + cmd/kms/export.go | 1 + cmd/kms/hmac.go | 1 + cmd/kms/public_key.go | 1 + cmd/kms/sign.go | 1 + internal/completion/completion.go | 60 +++++++++++++++++++++++++++++-- 6 files changed, 63 insertions(+), 2 deletions(-) diff --git a/cmd/kms/encrypt.go b/cmd/kms/encrypt.go index 8cdf802..8d41004 100644 --- a/cmd/kms/encrypt.go +++ b/cmd/kms/encrypt.go @@ -67,4 +67,5 @@ func init() { _ = encryptCmd.MarkFlagFilename("to-file") _ = encryptCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = encryptCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) + _ = encryptCmd.RegisterFlagCompletionFunc("key-version", completion.CompleteKmsKeyVersion) } diff --git a/cmd/kms/export.go b/cmd/kms/export.go index 3b2fbc4..13ab71e 100644 --- a/cmd/kms/export.go +++ b/cmd/kms/export.go @@ -63,4 +63,5 @@ func init() { _ = exportCmd.MarkFlagRequired("key") _ = exportCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = exportCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) + _ = exportCmd.RegisterFlagCompletionFunc("key-version", completion.CompleteKmsKeyVersion) } diff --git a/cmd/kms/hmac.go b/cmd/kms/hmac.go index af7d7d2..a7e5e18 100644 --- a/cmd/kms/hmac.go +++ b/cmd/kms/hmac.go @@ -54,4 +54,5 @@ func init() { _ = hmacCmd.MarkFlagRequired("input") _ = hmacCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = hmacCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) + _ = hmacCmd.RegisterFlagCompletionFunc("key-version", completion.CompleteKmsKeyVersion) } diff --git a/cmd/kms/public_key.go b/cmd/kms/public_key.go index b14df6c..1a1dce0 100644 --- a/cmd/kms/public_key.go +++ b/cmd/kms/public_key.go @@ -68,4 +68,5 @@ func init() { _ = publicKeyCmd.MarkFlagRequired("key") _ = publicKeyCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = publicKeyCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) + _ = publicKeyCmd.RegisterFlagCompletionFunc("version", completion.CompleteKmsKeyVersion) } diff --git a/cmd/kms/sign.go b/cmd/kms/sign.go index 6c5cfca..d45dbff 100644 --- a/cmd/kms/sign.go +++ b/cmd/kms/sign.go @@ -76,4 +76,5 @@ func init() { _ = signCmd.MarkFlagFilename("to-file") _ = signCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) _ = signCmd.RegisterFlagCompletionFunc("key", completion.CompleteKmsKeyIdentity) + _ = signCmd.RegisterFlagCompletionFunc("key-version", completion.CompleteKmsKeyVersion) } diff --git a/internal/completion/completion.go b/internal/completion/completion.go index 117f4f3..0971c5e 100644 --- a/internal/completion/completion.go +++ b/internal/completion/completion.go @@ -876,11 +876,34 @@ func CompleteDnsZoneIdentity(cmd *cobra.Command, args []string, toComplete strin return completions, cobra.ShellCompDirectiveNoFileComp } +// completionFlagString returns a flag value from local or inherited flags. +// Prefer Value.String so shell completion sees values typed earlier on the line. +func completionFlagString(cmd *cobra.Command, name string) string { + if cmd == nil { + return "" + } + if f := cmd.Flags().Lookup(name); f != nil { + if v := strings.TrimSpace(f.Value.String()); v != "" { + return v + } + } + if f := cmd.InheritedFlags().Lookup(name); f != nil { + if v := strings.TrimSpace(f.Value.String()); v != "" { + return v + } + } + return "" +} + // CompleteKmsKeyIdentity provides completion for KMS key identities. // Requires the --region flag to be set. func CompleteKmsKeyIdentity(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - regionFlag, err := cmd.Flags().GetString("region") - if err != nil || regionFlag == "" { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + regionFlag := completionFlagString(cmd, "region") + if regionFlag == "" { return nil, cobra.ShellCompDirectiveNoFileComp } @@ -900,3 +923,36 @@ func CompleteKmsKeyIdentity(cmd *cobra.Command, args []string, toComplete string } return completions, cobra.ShellCompDirectiveNoFileComp } + +// CompleteKmsKeyVersion provides completion for KMS key versions. +// Requires --region and --key (or a positional key identity). +func CompleteKmsKeyVersion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + regionFlag := completionFlagString(cmd, "region") + keyFlag := completionFlagString(cmd, "key") + if keyFlag == "" && len(args) > 0 { + keyFlag = strings.TrimSpace(args[0]) + } + if regionFlag == "" || keyFlag == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + key, err := client.KMS().GetKey(cmd.Context(), regionFlag, keyFlag) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(key.Versions)) + for _, version := range key.Versions { + desc := version.Status + if desc == "" { + desc = "version" + } + completions = append(completions, fmt.Sprintf("%d\t%s", version.Version, desc)) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} From 25e60e5dcb2609051a84ee550e7e69da8bea917c Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 19:01:20 +0200 Subject: [PATCH 15/19] feat(secrets): add interactive fzf browse navigation Let operators descend prefixes, go up, and open secrets in a TTY, pausing after metadata or reveal so fzf does not hide the output. --- cmd/secrets/browse.go | 17 +- cmd/secrets/browse_interactive.go | 324 +++++++++++++++++++++++++ cmd/secrets/browse_interactive_test.go | 71 ++++++ internal/fzf/fzf.go | 33 ++- 4 files changed, 431 insertions(+), 14 deletions(-) create mode 100644 cmd/secrets/browse_interactive.go create mode 100644 cmd/secrets/browse_interactive_test.go diff --git a/cmd/secrets/browse.go b/cmd/secrets/browse.go index f68050e..4e04933 100644 --- a/cmd/secrets/browse.go +++ b/cmd/secrets/browse.go @@ -2,11 +2,13 @@ package secrets import ( "fmt" + "os" "github.com/spf13/cobra" "github.com/thalassa-cloud/cli/internal/completion" "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/fzf" "github.com/thalassa-cloud/cli/internal/shared" "github.com/thalassa-cloud/cli/internal/table" "github.com/thalassa-cloud/cli/internal/thalassaclient" @@ -20,13 +22,25 @@ var ( var browseCmd = &cobra.Command{ Use: "browse", Short: "Browse secret prefixes and secrets at a path", - Args: cobra.NoArgs, + Long: `Browse Secrets Manager prefixes and secrets. + +In a terminal with fzf available, browse is interactive: select prefixes to +descend, ".." to go up, and secrets to view metadata (optionally reveal values +after confirmation). Press Esc to quit. + +When stdout is not a terminal, or TC_IGNORE_FZF is set, prints a non-interactive +table for the given --path.`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { client, err := thalassaclient.GetThalassaClient() if err != nil { return fmt.Errorf("failed to create client: %w", err) } + if fzf.IsInteractiveMode(os.Stdout) { + return runInteractiveBrowse(cmd.Context(), cmd.OutOrStdout(), client.Secrets(), browseRegion, browsePath) + } + result, err := client.Secrets().BrowseSecrets(cmd.Context(), browseRegion, browsePath) if err != nil { return fmt.Errorf("failed to browse secrets: %w", err) @@ -84,4 +98,5 @@ func init() { browseCmd.Flags().StringVar(&browsePath, "path", "/", "Path to browse") _ = browseCmd.MarkFlagRequired("region") _ = browseCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = browseCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) } diff --git a/cmd/secrets/browse_interactive.go b/cmd/secrets/browse_interactive.go new file mode 100644 index 0000000..433fdc4 --- /dev/null +++ b/cmd/secrets/browse_interactive.go @@ -0,0 +1,324 @@ +package secrets + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path" + "strings" + + clientsecrets "github.com/thalassa-cloud/client-go/secrets" + + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/fzf" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" +) + +const ( + browseSelectUp = ".." + + secretActionBack = "back" + secretActionVersions = "versions" + secretActionReveal = "reveal" + secretActionQuit = "quit" +) + +type secretsBrowser interface { + BrowseSecrets(ctx context.Context, region, path string) (*clientsecrets.BrowseSecretsResponse, error) + GetSecret(ctx context.Context, region, path string, includeVersions bool) (*clientsecrets.Secret, error) + GetSecretValue(ctx context.Context, region, path string, version *int) (*clientsecrets.GetSecretValueResponse, error) +} + +func runInteractiveBrowse(ctx context.Context, out io.Writer, api secretsBrowser, region, startPath string) error { + current, err := clientsecrets.NormalizePath(startPath) + if err != nil { + return err + } + + for { + result, err := api.BrowseSecrets(ctx, region, current) + if err != nil { + return fmt.Errorf("failed to browse secrets: %w", err) + } + if result.Path != "" { + current = result.Path + } + + lines := buildBrowseSelectionLines(current, result) + if len(lines) == 0 { + _, _ = fmt.Fprintf(out, "No prefixes or secrets at %s\n", current) + return nil + } + + choice, err := fzf.InteractiveChoiceFromLinesWithOptions(lines, fzf.InteractiveChoiceOptions{ + FzfArgs: []string{ + "--ansi", + "--header=region " + region + " path " + current + " (Esc to quit)", + "--prompt=secrets> ", + }, + }) + if err != nil { + if errors.Is(err, fzf.ErrSelectionCancelled) { + return nil + } + return err + } + + switch { + case choice == browseSelectUp: + current = parentBrowsePath(current) + case isBrowsePrefix(choice, result.Prefixes): + current = choice + default: + quit, openErr := openSecretInteractive(ctx, out, api, region, choice) + if openErr != nil { + return openErr + } + if quit { + return nil + } + } + } +} + +func buildBrowseSelectionLines(current string, result *clientsecrets.BrowseSecretsResponse) []string { + if result == nil { + return nil + } + + lines := make([]string, 0, 1+len(result.Prefixes)+len(result.Secrets)) + if current != "/" { + lines = append(lines, browseSelectUp+"\t..\tgo up") + } + + for _, prefix := range result.Prefixes { + label := path.Base(strings.TrimSuffix(prefix, "/")) + if label == "" || label == "." { + label = prefix + } + lines = append(lines, prefix+"\t"+label+"/\tprefix") + } + + for _, secret := range result.Secrets { + label := path.Base(secret.Path) + if label == "" || label == "/" || label == "." { + label = secret.Path + } + lines = append(lines, fmt.Sprintf("%s\t%s\tsecret · v%d · %s", + secret.Path, + label, + secret.CurrentVersion, + formattime.FormatTime(secret.UpdatedAt.Local(), showExactTime), + )) + } + return lines +} + +func isBrowsePrefix(choice string, prefixes []string) bool { + if strings.HasSuffix(choice, "/") { + return true + } + for _, prefix := range prefixes { + if prefix == choice { + return true + } + } + return false +} + +func parentBrowsePath(p string) string { + normalized := strings.TrimSpace(p) + if normalized == "" || normalized == "/" { + return "/" + } + normalized = strings.TrimSuffix(normalized, "/") + idx := strings.LastIndex(normalized, "/") + if idx <= 0 { + return "/" + } + return normalized[:idx+1] +} + +func openSecretInteractive(ctx context.Context, out io.Writer, api secretsBrowser, region, secretPath string) (quit bool, err error) { + secret, err := api.GetSecret(ctx, region, secretPath, false) + if err != nil { + return false, fmt.Errorf("failed to get secret: %w", err) + } + printSecretMetadata(out, secret) + if err := pauseForInteractiveView(out); err != nil { + return false, err + } + + for { + action, err := fzf.InteractiveChoiceFromLinesWithOptions([]string{ + secretActionBack + "\tBack to browse", + secretActionVersions + "\tShow version history", + secretActionReveal + "\tReveal secret value (sensitive)", + secretActionQuit + "\tQuit", + }, fzf.InteractiveChoiceOptions{ + FzfArgs: []string{ + "--ansi", + "--header=secret " + secretPath, + "--prompt=secret> ", + }, + }) + if err != nil { + if errors.Is(err, fzf.ErrSelectionCancelled) { + return false, nil + } + return false, err + } + + switch action { + case secretActionBack: + return false, nil + case secretActionQuit: + return true, nil + case secretActionVersions: + if err := printSecretVersions(ctx, out, api, region, secretPath); err != nil { + return false, err + } + if err := pauseForInteractiveView(out); err != nil { + return false, err + } + case secretActionReveal: + if err := revealSecretValue(ctx, out, api, region, secretPath); err != nil { + return false, err + } + if err := pauseForInteractiveView(out); err != nil { + return false, err + } + default: + return false, nil + } + } +} + +// pauseForInteractiveView keeps printed output visible until the user continues, +// because the next fzf screen would otherwise hide it. +func pauseForInteractiveView(out io.Writer) error { + _, _ = fmt.Fprint(out, "\nPress Enter to continue...") + _, err := bufio.NewReader(os.Stdin).ReadBytes('\n') + _, _ = fmt.Fprintln(out) + if err != nil && !errors.Is(err, io.EOF) { + return fmt.Errorf("wait for continue: %w", err) + } + return nil +} + +func printSecretMetadata(out io.Writer, secret *clientsecrets.Secret) { + if secret == nil { + return + } + + kmsKey := "-" + if secret.KmsKey != nil { + kmsKey = secret.KmsKey.Identity + if kmsKey == "" { + kmsKey = secret.KmsKey.Name + } + } + + body := [][]string{ + {"Path", secret.Path}, + {"Description", secret.Description}, + {"Current Version", fmt.Sprintf("%d", secret.CurrentVersion)}, + {"KMS Key", kmsKey}, + {"Created", formattime.FormatTime(secret.CreatedAt.Local(), showExactTime)}, + {"Updated", formattime.FormatTime(secret.UpdatedAt.Local(), showExactTime)}, + } + _, _ = fmt.Fprintln(out) + if noHeader { + table.PrintWithWriter(out, nil, body) + } else { + table.PrintWithWriter(out, []string{"Field", "Value"}, body) + } +} + +func printSecretVersions(ctx context.Context, out io.Writer, api secretsBrowser, region, secretPath string) error { + secret, err := api.GetSecret(ctx, region, secretPath, true) + if err != nil { + return fmt.Errorf("failed to get secret versions: %w", err) + } + if len(secret.Versions) == 0 { + _, _ = fmt.Fprintln(out, "No versions returned.") + return nil + } + printVersionTable(out, secret.Versions) + return nil +} + +func printVersionTable(out io.Writer, versions []clientsecrets.SecretVersion) { + _, _ = fmt.Fprintln(out) + versionBody := make([][]string, 0, len(versions)) + for _, v := range versions { + destroyed := "-" + if v.DestroyedAt != nil { + destroyed = formattime.FormatTime(v.DestroyedAt.Local(), showExactTime) + } + versionBody = append(versionBody, []string{ + fmt.Sprintf("%d", v.Version), + v.Status, + formattime.FormatTime(v.CreatedAt.Local(), showExactTime), + destroyed, + }) + } + if noHeader { + table.PrintWithWriter(out, nil, versionBody) + } else { + table.PrintWithWriter(out, []string{"Version", "Status", "Created", "Destroyed"}, versionBody) + } +} + +func revealSecretValue(ctx context.Context, out io.Writer, api secretsBrowser, region, secretPath string) error { + proceed, err := shared.PromptDestructiveUnlessForce(false, fmt.Sprintf( + "Reveal secret material for %q to this terminal?\nThis prints sensitive data. Prefer piping get-value to a file when possible.", + secretPath, + )) + if err != nil { + return err + } + if !proceed { + return nil + } + + result, err := api.GetSecretValue(ctx, region, secretPath, nil) + if err != nil { + return fmt.Errorf("failed to get secret value: %w", err) + } + + _, _ = fmt.Fprintln(out) + switch { + case result.SecretString != "": + plaintext, decodeErr := clientsecrets.DecodeBytes("secretString", result.SecretString) + if decodeErr != nil { + if _, err := fmt.Fprint(out, result.SecretString); err != nil { + return err + } + if !strings.HasSuffix(result.SecretString, "\n") { + _, _ = fmt.Fprintln(out) + } + return nil + } + if _, err := fmt.Fprint(out, string(plaintext)); err != nil { + return err + } + if len(plaintext) == 0 || plaintext[len(plaintext)-1] != '\n' { + _, _ = fmt.Fprintln(out) + } + case len(result.SecretKeyValues) > 0: + enc := json.NewEncoder(out) + enc.SetIndent("", " ") + if err := enc.Encode(result.SecretKeyValues); err != nil { + return err + } + default: + _, _ = fmt.Fprintln(out, "{}") + } + return nil +} diff --git a/cmd/secrets/browse_interactive_test.go b/cmd/secrets/browse_interactive_test.go new file mode 100644 index 0000000..56b9ff1 --- /dev/null +++ b/cmd/secrets/browse_interactive_test.go @@ -0,0 +1,71 @@ +package secrets + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + clientsecrets "github.com/thalassa-cloud/client-go/secrets" +) + +func TestParentBrowsePath(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + path string + want string + }{ + {name: "root", path: "/", want: "/"}, + {name: "empty", path: "", want: "/"}, + {name: "one level with slash", path: "/app/", want: "/"}, + {name: "one level without slash", path: "/app", want: "/"}, + {name: "nested", path: "/app/prod/", want: "/app/"}, + {name: "nested without slash", path: "/app/prod/db", want: "/app/prod/"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, parentBrowsePath(tt.path)) + }) + } +} + +func TestBuildBrowseSelectionLines(t *testing.T) { + t.Parallel() + + updated := time.Date(2026, 9, 10, 12, 0, 0, 0, time.UTC) + lines := buildBrowseSelectionLines("/app/", &clientsecrets.BrowseSecretsResponse{ + Path: "/app/", + Prefixes: []string{"/app/prod/"}, + Secrets: []clientsecrets.Secret{ + {Path: "/app/config", CurrentVersion: 3, UpdatedAt: updated}, + }, + }) + + require.Len(t, lines, 3) + assert.Equal(t, "..\t..\tgo up", lines[0]) + assert.Equal(t, "/app/prod/\tprod/\tprefix", lines[1]) + assert.Contains(t, lines[2], "/app/config\tconfig\tsecret · v3 ·") +} + +func TestBuildBrowseSelectionLinesRootOmitsUp(t *testing.T) { + t.Parallel() + + lines := buildBrowseSelectionLines("/", &clientsecrets.BrowseSecretsResponse{ + Path: "/", + Prefixes: []string{"/app/"}, + }) + require.Len(t, lines, 1) + assert.Equal(t, "/app/\tapp/\tprefix", lines[0]) +} + +func TestIsBrowsePrefix(t *testing.T) { + t.Parallel() + + assert.True(t, isBrowsePrefix("/app/prod/", nil)) + assert.True(t, isBrowsePrefix("/app/prod", []string{"/app/prod"})) + assert.False(t, isBrowsePrefix("/app/config", []string{"/app/prod/"})) +} diff --git a/internal/fzf/fzf.go b/internal/fzf/fzf.go index 49b97de..41676ad 100644 --- a/internal/fzf/fzf.go +++ b/internal/fzf/fzf.go @@ -11,6 +11,9 @@ import ( "github.com/mattn/go-isatty" ) +// ErrSelectionCancelled is returned when the user aborts fzf (e.g. Esc / Ctrl-C). +var ErrSelectionCancelled = errors.New("selection cancelled") + // InteractiveChoiceOptions provides configuration for the interactive choice functionality. type InteractiveChoiceOptions struct { // FzfArgs are additional arguments to pass to the fzf command @@ -53,19 +56,15 @@ func InteractiveChoiceWithOptions(command string, opts InteractiveChoiceOptions) cmd.Env = append(os.Environ(), fmt.Sprintf("FZF_DEFAULT_COMMAND=%s", command)) if err := cmd.Run(); err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - // Exit code 130 typically means user interrupted (Ctrl-C), which is a normal operation - if exitErr.ExitCode() == 130 { - return "", errors.New("selection cancelled") - } - return "", fmt.Errorf("fzf exited with error: %w", exitErr) + if isFzfCancel(err) { + return "", ErrSelectionCancelled } return "", fmt.Errorf("error running fzf: %w", err) } choice := strings.TrimSpace(out.String()) if choice == "" { - return "", errors.New("no option selected") + return "", ErrSelectionCancelled } return firstColumn(choice), nil @@ -99,23 +98,31 @@ func InteractiveChoiceFromLinesWithOptions(lines []string, opts InteractiveChoic cmd.Env = withoutEnv(os.Environ(), "FZF_DEFAULT_COMMAND") if err := cmd.Run(); err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - if exitErr.ExitCode() == 130 { - return "", errors.New("selection cancelled") - } - return "", fmt.Errorf("fzf exited with error: %w", exitErr) + if isFzfCancel(err) { + return "", ErrSelectionCancelled } return "", fmt.Errorf("error running fzf: %w", err) } choice := strings.TrimSpace(out.String()) if choice == "" { - return "", errors.New("no option selected") + return "", ErrSelectionCancelled } return firstColumn(choice), nil } +// isFzfCancel reports whether fzf exited because the user aborted selection. +// Exit 1 is abort/Esc; 130 is Ctrl-C. +func isFzfCancel(err error) bool { + var exitErr *exec.ExitError + if !errors.As(err, &exitErr) { + return false + } + code := exitErr.ExitCode() + return code == 1 || code == 130 +} + func firstColumn(choice string) string { parts := strings.SplitN(choice, "\t", 2) return strings.TrimSpace(parts[0]) From e9033ff3111a2a4a581daf3b032c6e6b9d11dfe5 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 19:01:24 +0200 Subject: [PATCH 16/19] feat(secrets): complete --path and --version from the API Suggest secret paths via BrowseSecrets and versions via GetSecret so common secrets flags can be completed after --region is set. --- cmd/secrets/delete.go | 1 + cmd/secrets/destroy_version.go | 2 + cmd/secrets/get_value.go | 2 + cmd/secrets/list.go | 1 + cmd/secrets/policy.go | 2 + cmd/secrets/view.go | 1 + internal/completion/completion.go | 80 +++++++++++++++++++ internal/completion/completion_secret_test.go | 31 +++++++ 8 files changed, 120 insertions(+) create mode 100644 internal/completion/completion_secret_test.go diff --git a/cmd/secrets/delete.go b/cmd/secrets/delete.go index 21bffd0..03eed57 100644 --- a/cmd/secrets/delete.go +++ b/cmd/secrets/delete.go @@ -51,4 +51,5 @@ func init() { _ = deleteCmd.MarkFlagRequired("region") _ = deleteCmd.MarkFlagRequired("path") _ = deleteCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = deleteCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) } diff --git a/cmd/secrets/destroy_version.go b/cmd/secrets/destroy_version.go index 558b4a0..eb30151 100644 --- a/cmd/secrets/destroy_version.go +++ b/cmd/secrets/destroy_version.go @@ -53,4 +53,6 @@ func init() { _ = destroyVersionCmd.MarkFlagRequired("path") _ = destroyVersionCmd.MarkFlagRequired("version") _ = destroyVersionCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = destroyVersionCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) + _ = destroyVersionCmd.RegisterFlagCompletionFunc("version", completion.CompleteSecretVersion) } diff --git a/cmd/secrets/get_value.go b/cmd/secrets/get_value.go index 10eeba6..7a7381a 100644 --- a/cmd/secrets/get_value.go +++ b/cmd/secrets/get_value.go @@ -65,4 +65,6 @@ func init() { _ = getValueCmd.MarkFlagRequired("region") _ = getValueCmd.MarkFlagRequired("path") _ = getValueCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = getValueCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) + _ = getValueCmd.RegisterFlagCompletionFunc("version", completion.CompleteSecretVersion) } diff --git a/cmd/secrets/list.go b/cmd/secrets/list.go index ee84a07..25385eb 100644 --- a/cmd/secrets/list.go +++ b/cmd/secrets/list.go @@ -68,4 +68,5 @@ func init() { listCmd.Flags().StringVar(&listPrefix, "prefix", "/", "Path prefix") _ = listCmd.MarkFlagRequired("region") _ = listCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = listCmd.RegisterFlagCompletionFunc("prefix", completion.CompleteSecretPath) } diff --git a/cmd/secrets/policy.go b/cmd/secrets/policy.go index 3859606..ad4de78 100644 --- a/cmd/secrets/policy.go +++ b/cmd/secrets/policy.go @@ -50,5 +50,7 @@ func init() { _ = policyCmd.MarkFlagRequired("region") _ = policyCmd.MarkFlagRequired("path") _ = policyCmd.MarkFlagRequired("file") + _ = policyCmd.MarkFlagFilename("file") _ = policyCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = policyCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) } diff --git a/cmd/secrets/view.go b/cmd/secrets/view.go index a481a11..9a56648 100644 --- a/cmd/secrets/view.go +++ b/cmd/secrets/view.go @@ -90,4 +90,5 @@ func init() { _ = viewCmd.MarkFlagRequired("region") _ = viewCmd.MarkFlagRequired("path") _ = viewCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = viewCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) } diff --git a/internal/completion/completion.go b/internal/completion/completion.go index 0971c5e..91e0b5a 100644 --- a/internal/completion/completion.go +++ b/internal/completion/completion.go @@ -956,3 +956,83 @@ func CompleteKmsKeyVersion(cmd *cobra.Command, args []string, toComplete string) } return completions, cobra.ShellCompDirectiveNoFileComp } + +// CompleteSecretPath provides completion for secret paths and prefixes. +// Requires the --region flag. Uses BrowseSecrets at the parent of toComplete. +func CompleteSecretPath(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + regionFlag := completionFlagString(cmd, "region") + if regionFlag == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + browsePath := secretBrowseParent(toComplete) + result, err := client.Secrets().BrowseSecrets(cmd.Context(), regionFlag, browsePath) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(result.Prefixes)+len(result.Secrets)) + for _, prefix := range result.Prefixes { + completions = append(completions, prefix+"\tprefix") + } + for _, secret := range result.Secrets { + desc := "secret" + if secret.Description != "" { + desc = secret.Description + } + completions = append(completions, secret.Path+"\t"+desc) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} + +// CompleteSecretVersion provides completion for secret versions. +// Requires --region and --path. +func CompleteSecretVersion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + regionFlag := completionFlagString(cmd, "region") + pathFlag := completionFlagString(cmd, "path") + if regionFlag == "" || pathFlag == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + secret, err := client.Secrets().GetSecret(cmd.Context(), regionFlag, pathFlag, true) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + completions := make([]string, 0, len(secret.Versions)) + for _, version := range secret.Versions { + desc := version.Status + if desc == "" { + desc = "version" + } + completions = append(completions, fmt.Sprintf("%d\t%s", version.Version, desc)) + } + return completions, cobra.ShellCompDirectiveNoFileComp +} +func secretBrowseParent(toComplete string) string { + trimmed := strings.TrimSpace(toComplete) + if trimmed == "" || trimmed == "/" { + return "/" + } + if !strings.HasPrefix(trimmed, "/") { + trimmed = "/" + trimmed + } + if strings.HasSuffix(trimmed, "/") { + return trimmed + } + idx := strings.LastIndex(trimmed, "/") + if idx <= 0 { + return "/" + } + return trimmed[:idx+1] +} diff --git a/internal/completion/completion_secret_test.go b/internal/completion/completion_secret_test.go new file mode 100644 index 0000000..504d2ca --- /dev/null +++ b/internal/completion/completion_secret_test.go @@ -0,0 +1,31 @@ +package completion + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSecretBrowseParent(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + toComplete string + want string + }{ + {name: "empty", toComplete: "", want: "/"}, + {name: "root", toComplete: "/", want: "/"}, + {name: "partial root segment", toComplete: "/ap", want: "/"}, + {name: "prefix with slash", toComplete: "/app/", want: "/app/"}, + {name: "nested partial", toComplete: "/app/prod/db", want: "/app/prod/"}, + {name: "missing leading slash", toComplete: "app/prod", want: "/app/"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, secretBrowseParent(tt.toComplete)) + }) + } +} From 2d108c555434cf2b826bbf56c5b557c5327c6272 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 19:01:25 +0200 Subject: [PATCH 17/19] fix(secrets): validate generate-bytes and tighten create UX Accept bare --generate-bytes as 32, reject sizes outside 16-4096 before the API call, and silence usage dumps on runtime errors. --- cmd/secrets/create.go | 19 +++++++++++++-- cmd/secrets/generate_bytes.go | 21 +++++++++++++++++ cmd/secrets/generate_bytes_test.go | 38 ++++++++++++++++++++++++++++++ cmd/secrets/put.go | 10 ++++++-- cmd/secrets/secrets.go | 11 ++++++--- 5 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 cmd/secrets/generate_bytes.go create mode 100644 cmd/secrets/generate_bytes_test.go diff --git a/cmd/secrets/create.go b/cmd/secrets/create.go index 9845632..1029acf 100644 --- a/cmd/secrets/create.go +++ b/cmd/secrets/create.go @@ -31,7 +31,10 @@ var ( var createCmd = &cobra.Command{ Use: "create", Short: "Create a secret (metadata response only; use get-value to read material)", - Args: cobra.NoArgs, + Example: ` tcloud secrets create --region nl-01 --path /app/prod/db --kms-key kms-123 --generate-bytes + tcloud secrets create --region nl-01 --path /app/prod/db --kms-key kms-123 --generate-bytes=64 + tcloud secrets create --region nl-01 --path /app/prod/token --kms-key kms-123 --from-file ./token.txt`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { if createPath == "" { return fmt.Errorf("--path is required") @@ -39,6 +42,14 @@ var createCmd = &cobra.Command{ if createKmsKey == "" { return fmt.Errorf("--kms-key is required") } + if createString == "" && createFromFile == "" && len(createKV) == 0 && createGenerateLen <= 0 { + return fmt.Errorf("provide --string, --from-file, --kv, or --generate-bytes[=N] (bare --generate-bytes defaults to %d)", generateBytesDefault) + } + if createGenerateLen > 0 { + if err := validateGenerateBytes(createGenerateLen); err != nil { + return err + } + } secretString := createString if createFromFile != "" { @@ -119,13 +130,17 @@ func init() { createCmd.Flags().StringVar(&createString, "string", "", "Secret string value") createCmd.Flags().StringVar(&createFromFile, "from-file", "", "Read secret string from a file") createCmd.Flags().StringSliceVar(&createKV, "kv", nil, "Secret key/value pairs as key=value (repeatable)") - createCmd.Flags().IntVar(&createGenerateLen, "generate-bytes", 0, "Generate a random secret of this many bytes") + createCmd.Flags().IntVar(&createGenerateLen, "generate-bytes", 0, generateBytesFlagUsage()) + createCmd.Flags().Lookup("generate-bytes").NoOptDefVal = fmt.Sprintf("%d", generateBytesDefault) createCmd.Flags().StringSliceVar(&createLabels, "labels", nil, "Labels as key=value (repeatable)") createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") createCmd.Flags().StringVar(&createPolicyFile, "policy-file", "", "JSON file with an access policy") _ = createCmd.MarkFlagRequired("region") _ = createCmd.MarkFlagRequired("path") _ = createCmd.MarkFlagRequired("kms-key") + _ = createCmd.MarkFlagFilename("from-file") + _ = createCmd.MarkFlagFilename("policy-file") _ = createCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = createCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) _ = createCmd.RegisterFlagCompletionFunc("kms-key", completion.CompleteKmsKeyIdentity) } diff --git a/cmd/secrets/generate_bytes.go b/cmd/secrets/generate_bytes.go new file mode 100644 index 0000000..c74f621 --- /dev/null +++ b/cmd/secrets/generate_bytes.go @@ -0,0 +1,21 @@ +package secrets + +import "fmt" + +const ( + generateBytesMin = 16 + generateBytesMax = 4096 + generateBytesDefault = 32 +) + +func generateBytesFlagUsage() string { + return fmt.Sprintf("Generate a random secret of this many bytes (%d-%d; bare --generate-bytes uses %d)", + generateBytesMin, generateBytesMax, generateBytesDefault) +} + +func validateGenerateBytes(n int) error { + if n < generateBytesMin || n > generateBytesMax { + return fmt.Errorf("--generate-bytes must be between %d and %d (got %d)", generateBytesMin, generateBytesMax, n) + } + return nil +} diff --git a/cmd/secrets/generate_bytes_test.go b/cmd/secrets/generate_bytes_test.go new file mode 100644 index 0000000..f21082b --- /dev/null +++ b/cmd/secrets/generate_bytes_test.go @@ -0,0 +1,38 @@ +package secrets + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValidateGenerateBytes(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + n int + wantErr string + }{ + {name: "min", n: 16}, + {name: "default", n: 32}, + {name: "max", n: 4096}, + {name: "too small", n: 10, wantErr: "between 16 and 4096"}, + {name: "too large", n: 5000, wantErr: "between 16 and 4096"}, + {name: "zero", n: 0, wantErr: "between 16 and 4096"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + err := validateGenerateBytes(tt.n) + if tt.wantErr == "" { + require.NoError(t, err) + return + } + require.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + }) + } +} diff --git a/cmd/secrets/put.go b/cmd/secrets/put.go index 0414ce4..8ddfab9 100644 --- a/cmd/secrets/put.go +++ b/cmd/secrets/put.go @@ -41,10 +41,13 @@ var putCmd = &cobra.Command{ SecretKeyValues: shared.KeyValuePairsToMap(putKV), } if putGenerateLen > 0 { + if err := validateGenerateBytes(putGenerateLen); err != nil { + return err + } req.GenerateSecret = &clientsecrets.GenerateSecret{ByteLength: putGenerateLen} } if req.SecretString == "" && len(req.SecretKeyValues) == 0 && req.GenerateSecret == nil { - return fmt.Errorf("provide --string, --from-file, --kv, or --generate-bytes") + return fmt.Errorf("provide --string, --from-file, --kv, or --generate-bytes[=N] (bare --generate-bytes defaults to %d)", generateBytesDefault) } client, err := thalassaclient.GetThalassaClient() @@ -68,8 +71,11 @@ func init() { putCmd.Flags().StringVar(&putString, "string", "", "Secret string value") putCmd.Flags().StringVar(&putFromFile, "from-file", "", "Read secret string from a file") putCmd.Flags().StringSliceVar(&putKV, "kv", nil, "Secret key/value pairs as key=value (repeatable)") - putCmd.Flags().IntVar(&putGenerateLen, "generate-bytes", 0, "Generate a random secret of this many bytes") + putCmd.Flags().IntVar(&putGenerateLen, "generate-bytes", 0, generateBytesFlagUsage()) + putCmd.Flags().Lookup("generate-bytes").NoOptDefVal = fmt.Sprintf("%d", generateBytesDefault) _ = putCmd.MarkFlagRequired("region") _ = putCmd.MarkFlagRequired("path") + _ = putCmd.MarkFlagFilename("from-file") _ = putCmd.RegisterFlagCompletionFunc("region", completion.CompleteRegion) + _ = putCmd.RegisterFlagCompletionFunc("path", completion.CompleteSecretPath) } diff --git a/cmd/secrets/secrets.go b/cmd/secrets/secrets.go index a1cdc21..3e2440c 100644 --- a/cmd/secrets/secrets.go +++ b/cmd/secrets/secrets.go @@ -6,13 +6,18 @@ import ( // SecretsCmd manages Secrets Manager resources. var SecretsCmd = &cobra.Command{ - Use: "secrets", - Aliases: []string{"secret"}, - Short: "Manage secrets (beta)", + Use: "secrets", + Aliases: []string{"secret"}, + Short: "Manage secrets (beta)", + SilenceUsage: true, Long: `Manage Secrets Manager paths, versions, and access policies. Note: This command is in beta. Commands that list or view secrets show metadata only; use get-value when you intentionally need secret material.`, + PersistentPreRun: func(cmd *cobra.Command, _ []string) { + // Avoid dumping full flag help on runtime / API validation errors. + cmd.SilenceUsage = true + }, } var ( From 305448a7d6a00243f39722fe7ff366563b20cace Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 19:02:23 +0200 Subject: [PATCH 18/19] chore(docs): generate docs Signed-off-by: Thomas Kooi --- docs/tcloud/compute/machines_delete/_index.md | 9 +-- docs/tcloud/context/fix/_index.md | 6 +- docs/tcloud/dns/_index.md | 45 +++++++++++++ docs/tcloud/dns/records/_index.md | 42 ++++++++++++ docs/tcloud/dns/records_create/_index.md | 48 ++++++++++++++ docs/tcloud/dns/records_delete/_index.md | 43 ++++++++++++ docs/tcloud/dns/records_list/_index.md | 44 +++++++++++++ docs/tcloud/dns/records_update/_index.md | 46 +++++++++++++ docs/tcloud/dns/records_view/_index.md | 44 +++++++++++++ docs/tcloud/dns/zones/_index.md | 45 +++++++++++++ docs/tcloud/dns/zones_create/_index.md | 47 +++++++++++++ docs/tcloud/dns/zones_delete/_index.md | 42 ++++++++++++ docs/tcloud/dns/zones_dnssec/_index.md | 40 +++++++++++ .../tcloud/dns/zones_dnssec_disable/_index.md | 42 ++++++++++++ docs/tcloud/dns/zones_dnssec_enable/_index.md | 44 +++++++++++++ docs/tcloud/dns/zones_dnssec_get/_index.md | 42 ++++++++++++ docs/tcloud/dns/zones_export/_index.md | 41 ++++++++++++ docs/tcloud/dns/zones_import/_index.md | 44 +++++++++++++ docs/tcloud/dns/zones_list/_index.md | 43 ++++++++++++ docs/tcloud/dns/zones_update/_index.md | 46 +++++++++++++ docs/tcloud/dns/zones_view/_index.md | 43 ++++++++++++ docs/tcloud/iam/_index.md | 2 +- .../tcloud/iam/federated-identities/_index.md | 2 +- .../iam/federated-identities_create/_index.md | 2 +- .../iam/federated-identities_delete/_index.md | 2 +- .../iam/federated-identities_get/_index.md | 2 +- .../iam/federated-identities_list/_index.md | 2 +- .../iam/federated-identities_update/_index.md | 2 +- .../federated-identity-providers/_index.md | 2 +- .../_index.md | 2 +- .../_index.md | 2 +- .../_index.md | 2 +- .../_index.md | 2 +- .../_index.md | 2 +- docs/tcloud/iam/invites/_index.md | 2 +- docs/tcloud/iam/invites_list/_index.md | 2 +- docs/tcloud/iam/members/_index.md | 2 +- docs/tcloud/iam/members_delete/_index.md | 2 +- docs/tcloud/iam/members_list/_index.md | 2 +- docs/tcloud/iam/members_update/_index.md | 2 +- docs/tcloud/iam/roles/_index.md | 2 +- docs/tcloud/iam/roles_bindings/_index.md | 2 +- .../iam/roles_bindings_create/_index.md | 2 +- .../iam/roles_bindings_delete/_index.md | 2 +- docs/tcloud/iam/roles_bindings_list/_index.md | 2 +- docs/tcloud/iam/roles_create/_index.md | 2 +- docs/tcloud/iam/roles_delete/_index.md | 2 +- docs/tcloud/iam/roles_get/_index.md | 2 +- docs/tcloud/iam/roles_list/_index.md | 2 +- docs/tcloud/iam/roles_rules/_index.md | 2 +- docs/tcloud/iam/roles_rules_add/_index.md | 2 +- docs/tcloud/iam/roles_rules_delete/_index.md | 2 +- docs/tcloud/iam/service-accounts/_index.md | 2 +- .../iam/service-accounts_create/_index.md | 2 +- .../iam/service-accounts_delete/_index.md | 2 +- .../tcloud/iam/service-accounts_get/_index.md | 2 +- .../iam/service-accounts_list/_index.md | 2 +- .../iam/service-accounts_update/_index.md | 2 +- docs/tcloud/iam/teams/_index.md | 2 +- docs/tcloud/iam/teams_create/_index.md | 2 +- docs/tcloud/iam/teams_delete/_index.md | 2 +- docs/tcloud/iam/teams_get/_index.md | 2 +- docs/tcloud/iam/teams_list/_index.md | 2 +- docs/tcloud/iam/teams_members/_index.md | 2 +- docs/tcloud/iam/teams_members_add/_index.md | 2 +- docs/tcloud/iam/teams_members_list/_index.md | 2 +- .../tcloud/iam/teams_members_remove/_index.md | 2 +- docs/tcloud/iam/teams_update/_index.md | 2 +- .../workload-identity-federation/_index.md | 2 +- .../_index.md | 2 +- .../_index.md | 2 +- .../_index.md | 2 +- .../_index.md | 2 +- docs/tcloud/kms/_index.md | 54 +++++++++++++++ docs/tcloud/kms/decrypt/_index.md | 61 +++++++++++++++++ docs/tcloud/kms/encrypt/_index.md | 61 +++++++++++++++++ docs/tcloud/kms/export/_index.md | 45 +++++++++++++ docs/tcloud/kms/hmac/_index.md | 46 +++++++++++++ docs/tcloud/kms/keys/_index.md | 46 +++++++++++++ .../tcloud/kms/keys_cancel-deletion/_index.md | 42 ++++++++++++ docs/tcloud/kms/keys_create/_index.md | 55 ++++++++++++++++ docs/tcloud/kms/keys_delete/_index.md | 43 ++++++++++++ docs/tcloud/kms/keys_disable/_index.md | 44 +++++++++++++ docs/tcloud/kms/keys_enable/_index.md | 44 +++++++++++++ docs/tcloud/kms/keys_list/_index.md | 44 +++++++++++++ docs/tcloud/kms/keys_rotate/_index.md | 44 +++++++++++++ docs/tcloud/kms/keys_rotation/_index.md | 46 +++++++++++++ docs/tcloud/kms/keys_view/_index.md | 44 +++++++++++++ docs/tcloud/kms/public-key/_index.md | 45 +++++++++++++ docs/tcloud/kms/sign/_index.md | 63 ++++++++++++++++++ docs/tcloud/kms/summary/_index.md | 42 ++++++++++++ docs/tcloud/kms/verify-hmac/_index.md | 63 ++++++++++++++++++ docs/tcloud/kms/verify/_index.md | 64 ++++++++++++++++++ docs/tcloud/kms/wrapping-key/_index.md | 43 ++++++++++++ docs/tcloud/kubernetes/_index.md | 3 +- docs/tcloud/kubernetes/connect/_index.md | 2 +- docs/tcloud/kubernetes/create/_index.md | 3 +- docs/tcloud/kubernetes/delete/_index.md | 9 +-- docs/tcloud/kubernetes/iam/_index.md | 2 +- docs/tcloud/kubernetes/iam_roles/_index.md | 2 +- .../kubernetes/iam_roles_bindings/_index.md | 2 +- .../iam_roles_bindings_create/_index.md | 2 +- .../iam_roles_bindings_delete/_index.md | 2 +- .../iam_roles_bindings_list/_index.md | 2 +- .../kubernetes/iam_roles_create/_index.md | 2 +- .../kubernetes/iam_roles_delete/_index.md | 2 +- .../tcloud/kubernetes/iam_roles_get/_index.md | 2 +- .../kubernetes/iam_roles_list/_index.md | 2 +- .../kubernetes/iam_roles_rules/_index.md | 2 +- .../kubernetes/iam_roles_rules_add/_index.md | 2 +- .../iam_roles_rules_delete/_index.md | 2 +- .../kubernetes/kubeconfig-sessions/_index.md | 53 +++++++++++++++ .../kubeconfig-sessions_delete/_index.md | 49 ++++++++++++++ .../kubeconfig-sessions_list/_index.md | 44 +++++++++++++ docs/tcloud/kubernetes/kubeconfig/_index.md | 7 +- docs/tcloud/kubernetes/list/_index.md | 2 +- docs/tcloud/kubernetes/machines/_index.md | 2 +- .../tcloud/kubernetes/machines_list/_index.md | 2 +- docs/tcloud/kubernetes/nodepools/_index.md | 2 +- .../kubernetes/nodepools_create/_index.md | 3 +- .../kubernetes/nodepools_delete/_index.md | 16 +++-- .../kubernetes/nodepools_list/_index.md | 2 +- .../kubernetes/nodepools_update/_index.md | 3 +- docs/tcloud/kubernetes/update/_index.md | 3 +- docs/tcloud/kubernetes/upgrade/_index.md | 2 +- docs/tcloud/kubernetes/versions/_index.md | 2 +- docs/tcloud/me/_index.md | 2 +- docs/tcloud/me/organisations/_index.md | 2 +- docs/tcloud/networking/_index.md | 5 +- .../tcloud/networking/loadbalancers/_index.md | 2 +- .../networking/loadbalancers_create/_index.md | 3 +- .../networking/loadbalancers_delete/_index.md | 11 ++-- .../networking/loadbalancers_list/_index.md | 2 +- .../loadbalancers_listeners/_index.md | 2 +- .../loadbalancers_listeners_create/_index.md | 2 +- .../loadbalancers_listeners_delete/_index.md | 2 +- .../loadbalancers_listeners_list/_index.md | 2 +- .../loadbalancers_listeners_update/_index.md | 2 +- .../loadbalancers_listeners_view/_index.md | 2 +- .../networking/loadbalancers_update/_index.md | 2 +- .../networking/loadbalancers_view/_index.md | 2 +- docs/tcloud/networking/natgateways/_index.md | 4 +- .../networking/natgateways_create/_index.md | 62 +++++++++++++++++ .../networking/natgateways_delete/_index.md | 11 ++-- .../networking/natgateways_list/_index.md | 2 +- .../networking/natgateways_update/_index.md | 60 +++++++++++++++++ .../networking/natgateways_view/_index.md | 2 +- docs/tcloud/networking/reserved-ips/_index.md | 56 ++++++++++++++++ .../reserved-ips_associate/_index.md | 55 ++++++++++++++++ .../networking/reserved-ips_create/_index.md | 58 ++++++++++++++++ .../networking/reserved-ips_delete/_index.md | 54 +++++++++++++++ .../reserved-ips_disassociate/_index.md | 52 +++++++++++++++ .../networking/reserved-ips_list/_index.md | 58 ++++++++++++++++ .../networking/reserved-ips_update/_index.md | 57 ++++++++++++++++ .../networking/reserved-ips_view/_index.md | 53 +++++++++++++++ docs/tcloud/networking/routetables/_index.md | 21 +++++- .../networking/routetables_create/_index.md | 52 +++++++++++++++ .../networking/routetables_delete/_index.md | 42 ++++++++++++ .../networking/routetables_list/_index.md | 4 +- .../networking/routetables_routes/_index.md | 43 ++++++++++++ .../routetables_routes_create/_index.md | 47 +++++++++++++ .../routetables_routes_delete/_index.md | 42 ++++++++++++ .../routetables_routes_list/_index.md | 42 ++++++++++++ .../routetables_routes_set/_index.md | 53 +++++++++++++++ .../routetables_routes_update/_index.md | 47 +++++++++++++ .../routetables_routes_view/_index.md | 42 ++++++++++++ .../networking/routetables_update/_index.md | 45 +++++++++++++ .../networking/routetables_view/_index.md | 43 ++++++++++++ .../networking/security-groups/_index.md | 4 +- .../security-groups_create/_index.md | 2 +- .../security-groups_delete/_index.md | 2 +- .../networking/security-groups_list/_index.md | 2 +- .../security-groups_rules/_index.md | 39 +++++++++++ .../_index.md | 42 ++++++++++++ .../_index.md | 42 ++++++++++++ .../security-groups_update/_index.md | 50 ++++++++++++++ .../networking/security-groups_view/_index.md | 2 +- docs/tcloud/networking/subnets/_index.md | 2 +- .../networking/subnets_create/_index.md | 18 ++--- .../networking/subnets_delete/_index.md | 11 ++-- docs/tcloud/networking/subnets_list/_index.md | 2 +- .../tcloud/networking/target-groups/_index.md | 2 +- .../networking/target-groups_attach/_index.md | 2 +- .../networking/target-groups_create/_index.md | 2 +- .../networking/target-groups_delete/_index.md | 2 +- .../networking/target-groups_detach/_index.md | 2 +- .../networking/target-groups_list/_index.md | 2 +- .../target-groups_set-attachments/_index.md | 2 +- .../networking/target-groups_update/_index.md | 2 +- .../networking/target-groups_view/_index.md | 2 +- docs/tcloud/networking/vpc-peering/_index.md | 2 +- .../networking/vpc-peering_accept/_index.md | 2 +- .../networking/vpc-peering_create/_index.md | 2 +- .../networking/vpc-peering_delete/_index.md | 2 +- .../networking/vpc-peering_list/_index.md | 2 +- .../networking/vpc-peering_reject/_index.md | 2 +- .../networking/vpc-peering_update/_index.md | 2 +- docs/tcloud/networking/vpcs/_index.md | 2 +- docs/tcloud/networking/vpcs_create/_index.md | 18 ++--- docs/tcloud/networking/vpcs_delete/_index.md | 11 ++-- docs/tcloud/networking/vpcs_list/_index.md | 2 +- docs/tcloud/object-storage/_index.md | 2 +- docs/tcloud/object-storage/create/_index.md | 2 +- docs/tcloud/object-storage/delete/_index.md | 2 +- docs/tcloud/object-storage/list/_index.md | 2 +- docs/tcloud/object-storage/update/_index.md | 2 +- docs/tcloud/oidc/_index.md | 2 +- docs/tcloud/oidc/token-exchange/_index.md | 2 +- docs/tcloud/projects/_index.md | 10 ++- docs/tcloud/projects/create/_index.md | 47 +++++++++++++ docs/tcloud/projects/delete/_index.md | 42 ++++++++++++ docs/tcloud/projects/list/_index.md | 4 +- docs/tcloud/projects/update/_index.md | 47 +++++++++++++ docs/tcloud/projects/view/_index.md | 42 ++++++++++++ docs/tcloud/quotas/_index.md | 2 +- docs/tcloud/quotas/get/_index.md | 2 +- docs/tcloud/quotas/list/_index.md | 2 +- docs/tcloud/quotas/request-increase/_index.md | 2 +- docs/tcloud/regions/_index.md | 2 +- docs/tcloud/regions/list/_index.md | 2 +- docs/tcloud/registry/_index.md | 2 +- docs/tcloud/registry/namespaces/_index.md | 2 +- .../namespaces_configuration/_index.md | 2 +- .../namespaces_configuration_create/_index.md | 2 +- .../namespaces_configuration_delete/_index.md | 2 +- .../namespaces_configuration_update/_index.md | 2 +- .../namespaces_configuration_view/_index.md | 2 +- .../registry/namespaces_create/_index.md | 2 +- .../registry/namespaces_delete/_index.md | 2 +- .../tcloud/registry/namespaces_list/_index.md | 2 +- .../registry/namespaces_retention/_index.md | 2 +- .../namespaces_retention_run/_index.md | 2 +- .../registry/namespaces_update/_index.md | 2 +- .../tcloud/registry/namespaces_view/_index.md | 2 +- docs/tcloud/registry/repositories/_index.md | 2 +- .../repositories_delete-artifacts/_index.md | 2 +- .../registry/repositories_delete/_index.md | 2 +- .../registry/repositories_list/_index.md | 2 +- .../registry/repositories_view/_index.md | 2 +- docs/tcloud/secrets/_index.md | 53 +++++++++++++++ docs/tcloud/secrets/browse/_index.md | 56 ++++++++++++++++ docs/tcloud/secrets/create/_index.md | 61 +++++++++++++++++ docs/tcloud/secrets/delete/_index.md | 44 +++++++++++++ docs/tcloud/secrets/destroy-version/_index.md | 45 +++++++++++++ docs/tcloud/secrets/get-value/_index.md | 48 ++++++++++++++ docs/tcloud/secrets/list/_index.md | 45 +++++++++++++ docs/tcloud/secrets/policy/_index.md | 44 +++++++++++++ docs/tcloud/secrets/put/_index.md | 47 +++++++++++++ docs/tcloud/secrets/view/_index.md | 46 +++++++++++++ docs/tcloud/storage/_index.md | 3 +- .../storage/snapshot-policies/_index.md | 53 +++++++++++++++ .../snapshot-policies_create/_index.md | 66 +++++++++++++++++++ .../snapshot-policies_delete/_index.md | 43 ++++++++++++ .../storage/snapshot-policies_list/_index.md | 46 +++++++++++++ .../snapshot-policies_update/_index.md | 58 ++++++++++++++++ .../storage/snapshot-policies_view/_index.md | 42 ++++++++++++ docs/tcloud/storage/snapshots/_index.md | 2 +- .../tcloud/storage/snapshots_create/_index.md | 17 ++--- .../tcloud/storage/snapshots_delete/_index.md | 11 ++-- docs/tcloud/storage/snapshots_list/_index.md | 2 +- docs/tcloud/storage/tfs/_index.md | 2 +- docs/tcloud/storage/tfs_create/_index.md | 27 ++++---- docs/tcloud/storage/tfs_delete/_index.md | 11 ++-- docs/tcloud/storage/tfs_list/_index.md | 2 +- docs/tcloud/storage/tfs_update/_index.md | 2 +- docs/tcloud/storage/tfs_view/_index.md | 2 +- docs/tcloud/storage/volumes/_index.md | 2 +- docs/tcloud/storage/volumes_attach/_index.md | 2 +- docs/tcloud/storage/volumes_create/_index.md | 25 +++---- docs/tcloud/storage/volumes_delete/_index.md | 11 ++-- docs/tcloud/storage/volumes_detach/_index.md | 2 +- docs/tcloud/storage/volumes_list/_index.md | 2 +- docs/tcloud/storage/volumes_resize/_index.md | 13 ++-- docs/tcloud/tcloud.md | 7 +- docs/tcloud/tcloud_version.md | 2 +- 275 files changed, 4572 insertions(+), 285 deletions(-) create mode 100644 docs/tcloud/dns/_index.md create mode 100644 docs/tcloud/dns/records/_index.md create mode 100644 docs/tcloud/dns/records_create/_index.md create mode 100644 docs/tcloud/dns/records_delete/_index.md create mode 100644 docs/tcloud/dns/records_list/_index.md create mode 100644 docs/tcloud/dns/records_update/_index.md create mode 100644 docs/tcloud/dns/records_view/_index.md create mode 100644 docs/tcloud/dns/zones/_index.md create mode 100644 docs/tcloud/dns/zones_create/_index.md create mode 100644 docs/tcloud/dns/zones_delete/_index.md create mode 100644 docs/tcloud/dns/zones_dnssec/_index.md create mode 100644 docs/tcloud/dns/zones_dnssec_disable/_index.md create mode 100644 docs/tcloud/dns/zones_dnssec_enable/_index.md create mode 100644 docs/tcloud/dns/zones_dnssec_get/_index.md create mode 100644 docs/tcloud/dns/zones_export/_index.md create mode 100644 docs/tcloud/dns/zones_import/_index.md create mode 100644 docs/tcloud/dns/zones_list/_index.md create mode 100644 docs/tcloud/dns/zones_update/_index.md create mode 100644 docs/tcloud/dns/zones_view/_index.md create mode 100644 docs/tcloud/kms/_index.md create mode 100644 docs/tcloud/kms/decrypt/_index.md create mode 100644 docs/tcloud/kms/encrypt/_index.md create mode 100644 docs/tcloud/kms/export/_index.md create mode 100644 docs/tcloud/kms/hmac/_index.md create mode 100644 docs/tcloud/kms/keys/_index.md create mode 100644 docs/tcloud/kms/keys_cancel-deletion/_index.md create mode 100644 docs/tcloud/kms/keys_create/_index.md create mode 100644 docs/tcloud/kms/keys_delete/_index.md create mode 100644 docs/tcloud/kms/keys_disable/_index.md create mode 100644 docs/tcloud/kms/keys_enable/_index.md create mode 100644 docs/tcloud/kms/keys_list/_index.md create mode 100644 docs/tcloud/kms/keys_rotate/_index.md create mode 100644 docs/tcloud/kms/keys_rotation/_index.md create mode 100644 docs/tcloud/kms/keys_view/_index.md create mode 100644 docs/tcloud/kms/public-key/_index.md create mode 100644 docs/tcloud/kms/sign/_index.md create mode 100644 docs/tcloud/kms/summary/_index.md create mode 100644 docs/tcloud/kms/verify-hmac/_index.md create mode 100644 docs/tcloud/kms/verify/_index.md create mode 100644 docs/tcloud/kms/wrapping-key/_index.md create mode 100644 docs/tcloud/kubernetes/kubeconfig-sessions/_index.md create mode 100644 docs/tcloud/kubernetes/kubeconfig-sessions_delete/_index.md create mode 100644 docs/tcloud/kubernetes/kubeconfig-sessions_list/_index.md create mode 100644 docs/tcloud/networking/natgateways_create/_index.md create mode 100644 docs/tcloud/networking/natgateways_update/_index.md create mode 100644 docs/tcloud/networking/reserved-ips/_index.md create mode 100644 docs/tcloud/networking/reserved-ips_associate/_index.md create mode 100644 docs/tcloud/networking/reserved-ips_create/_index.md create mode 100644 docs/tcloud/networking/reserved-ips_delete/_index.md create mode 100644 docs/tcloud/networking/reserved-ips_disassociate/_index.md create mode 100644 docs/tcloud/networking/reserved-ips_list/_index.md create mode 100644 docs/tcloud/networking/reserved-ips_update/_index.md create mode 100644 docs/tcloud/networking/reserved-ips_view/_index.md create mode 100644 docs/tcloud/networking/routetables_create/_index.md create mode 100644 docs/tcloud/networking/routetables_delete/_index.md create mode 100644 docs/tcloud/networking/routetables_routes/_index.md create mode 100644 docs/tcloud/networking/routetables_routes_create/_index.md create mode 100644 docs/tcloud/networking/routetables_routes_delete/_index.md create mode 100644 docs/tcloud/networking/routetables_routes_list/_index.md create mode 100644 docs/tcloud/networking/routetables_routes_set/_index.md create mode 100644 docs/tcloud/networking/routetables_routes_update/_index.md create mode 100644 docs/tcloud/networking/routetables_routes_view/_index.md create mode 100644 docs/tcloud/networking/routetables_update/_index.md create mode 100644 docs/tcloud/networking/routetables_view/_index.md create mode 100644 docs/tcloud/networking/security-groups_rules/_index.md create mode 100644 docs/tcloud/networking/security-groups_rules_set-egress/_index.md create mode 100644 docs/tcloud/networking/security-groups_rules_set-ingress/_index.md create mode 100644 docs/tcloud/networking/security-groups_update/_index.md create mode 100644 docs/tcloud/projects/create/_index.md create mode 100644 docs/tcloud/projects/delete/_index.md create mode 100644 docs/tcloud/projects/update/_index.md create mode 100644 docs/tcloud/projects/view/_index.md create mode 100644 docs/tcloud/secrets/_index.md create mode 100644 docs/tcloud/secrets/browse/_index.md create mode 100644 docs/tcloud/secrets/create/_index.md create mode 100644 docs/tcloud/secrets/delete/_index.md create mode 100644 docs/tcloud/secrets/destroy-version/_index.md create mode 100644 docs/tcloud/secrets/get-value/_index.md create mode 100644 docs/tcloud/secrets/list/_index.md create mode 100644 docs/tcloud/secrets/policy/_index.md create mode 100644 docs/tcloud/secrets/put/_index.md create mode 100644 docs/tcloud/secrets/view/_index.md create mode 100644 docs/tcloud/storage/snapshot-policies/_index.md create mode 100644 docs/tcloud/storage/snapshot-policies_create/_index.md create mode 100644 docs/tcloud/storage/snapshot-policies_delete/_index.md create mode 100644 docs/tcloud/storage/snapshot-policies_list/_index.md create mode 100644 docs/tcloud/storage/snapshot-policies_update/_index.md create mode 100644 docs/tcloud/storage/snapshot-policies_view/_index.md diff --git a/docs/tcloud/compute/machines_delete/_index.md b/docs/tcloud/compute/machines_delete/_index.md index 29138af..14e69dd 100644 --- a/docs/tcloud/compute/machines_delete/_index.md +++ b/docs/tcloud/compute/machines_delete/_index.md @@ -30,10 +30,11 @@ tcloud compute machines delete --selector environment=test --force ### Options ``` - --force Force the deletion and skip the confirmation - -h, --help help for delete - -l, --selector string Label selector to filter machines (format: key1=value1,key2=value2) - -w, --wait Wait for the machine(s) to be deleted + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter machines (format: key1=value1,key2=value2) + -w, --wait Wait for the machine(s) to be deleted + --wait-timeout duration Maximum time to wait for the machine(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/context/fix/_index.md b/docs/tcloud/context/fix/_index.md index 42c7fb7..e12c4fd 100644 --- a/docs/tcloud/context/fix/_index.md +++ b/docs/tcloud/context/fix/_index.md @@ -13,7 +13,7 @@ Fix config file security issues ### Synopsis -Fix security issues in the CLI config file, such as overly permissive file permissions. +Fix security issues in the CLI config file, such as overly permissive file permissions or migrating credentials to the keychain. ``` tcloud context fix [flags] @@ -23,12 +23,14 @@ tcloud context fix [flags] ``` tcloud context fix + tcloud context fix --migrate-credentials ``` ### Options ``` - -h, --help help for fix + -h, --help help for fix + --migrate-credentials move plaintext credentials from the config file into the keychain ``` ### Options inherited from parent commands diff --git a/docs/tcloud/dns/_index.md b/docs/tcloud/dns/_index.md new file mode 100644 index 0000000..195d33a --- /dev/null +++ b/docs/tcloud/dns/_index.md @@ -0,0 +1,45 @@ +--- +linkTitle: "tcloud dns" +title: "dns" +slug: tcloud_dns +url: /docs/tcloud/tcloud_dns/ +weight: 9922 +cascade: + type: docs +--- +## tcloud dns + +Manage DNS zones and records (beta) + +### Synopsis + +Manage DNS zones and records within the Thalassa Cloud Platform. + +Note: This command is in beta. + +### Options + +``` + -h, --help help for dns +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud](/docs/tcloud/tcloud/) - A CLI for working with the Thalassa Cloud Platform +* [tcloud dns records](/docs/tcloud/dns/records/) - Manage DNS records +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/dns/records/_index.md b/docs/tcloud/dns/records/_index.md new file mode 100644 index 0000000..08de3bf --- /dev/null +++ b/docs/tcloud/dns/records/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud dns records" +title: "dns records" +slug: tcloud_dns_records +url: /docs/tcloud/dns/records/ +weight: 9935 +cascade: + type: docs +--- +## tcloud dns records + +Manage DNS records + +### Options + +``` + -h, --help help for records +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns](/docs/tcloud/tcloud_dns/) - Manage DNS zones and records (beta) +* [tcloud dns records create](/docs/tcloud/dns/records_create/) - Create a DNS record +* [tcloud dns records delete](/docs/tcloud/dns/records_delete/) - Delete a DNS record +* [tcloud dns records list](/docs/tcloud/dns/records_list/) - List DNS records in a zone +* [tcloud dns records update](/docs/tcloud/dns/records_update/) - Update a DNS record +* [tcloud dns records view](/docs/tcloud/dns/records_view/) - View a DNS record + diff --git a/docs/tcloud/dns/records_create/_index.md b/docs/tcloud/dns/records_create/_index.md new file mode 100644 index 0000000..2e684a1 --- /dev/null +++ b/docs/tcloud/dns/records_create/_index.md @@ -0,0 +1,48 @@ +--- +linkTitle: "tcloud dns records create" +title: "dns records create" +slug: tcloud_dns_records_create +url: /docs/tcloud/dns/records_create/ +weight: 9940 +cascade: + type: docs +--- +## tcloud dns records create + +Create a DNS record + +``` +tcloud dns records create [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for create + --name string Record name + --no-header Do not print table headers + --ttl int Record TTL in seconds (default 300) + --type string Record type (TXT, A, CNAME, CAA, AAAA, MX, NS, SRV) + --value strings Record value (repeatable) + --zone string DNS zone identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns records](/docs/tcloud/dns/records/) - Manage DNS records + diff --git a/docs/tcloud/dns/records_delete/_index.md b/docs/tcloud/dns/records_delete/_index.md new file mode 100644 index 0000000..e1ef102 --- /dev/null +++ b/docs/tcloud/dns/records_delete/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud dns records delete" +title: "dns records delete" +slug: tcloud_dns_records_delete +url: /docs/tcloud/dns/records_delete/ +weight: 9939 +cascade: + type: docs +--- +## tcloud dns records delete + +Delete a DNS record + +``` +tcloud dns records delete [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt and delete + -h, --help help for delete + --zone string DNS zone identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns records](/docs/tcloud/dns/records/) - Manage DNS records + diff --git a/docs/tcloud/dns/records_list/_index.md b/docs/tcloud/dns/records_list/_index.md new file mode 100644 index 0000000..19e4d1a --- /dev/null +++ b/docs/tcloud/dns/records_list/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud dns records list" +title: "dns records list" +slug: tcloud_dns_records_list +url: /docs/tcloud/dns/records_list/ +weight: 9938 +cascade: + type: docs +--- +## tcloud dns records list + +List DNS records in a zone + +``` +tcloud dns records list [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for list + --no-header Do not print table headers + --zone string DNS zone identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns records](/docs/tcloud/dns/records/) - Manage DNS records + diff --git a/docs/tcloud/dns/records_update/_index.md b/docs/tcloud/dns/records_update/_index.md new file mode 100644 index 0000000..d5ea3b3 --- /dev/null +++ b/docs/tcloud/dns/records_update/_index.md @@ -0,0 +1,46 @@ +--- +linkTitle: "tcloud dns records update" +title: "dns records update" +slug: tcloud_dns_records_update +url: /docs/tcloud/dns/records_update/ +weight: 9937 +cascade: + type: docs +--- +## tcloud dns records update + +Update a DNS record + +``` +tcloud dns records update [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for update + --no-header Do not print table headers + --ttl int Record TTL in seconds + --value strings Record value (repeatable) + --zone string DNS zone identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns records](/docs/tcloud/dns/records/) - Manage DNS records + diff --git a/docs/tcloud/dns/records_view/_index.md b/docs/tcloud/dns/records_view/_index.md new file mode 100644 index 0000000..f550bd9 --- /dev/null +++ b/docs/tcloud/dns/records_view/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud dns records view" +title: "dns records view" +slug: tcloud_dns_records_view +url: /docs/tcloud/dns/records_view/ +weight: 9936 +cascade: + type: docs +--- +## tcloud dns records view + +View a DNS record + +``` +tcloud dns records view [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for view + --no-header Do not print table headers + --zone string DNS zone identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns records](/docs/tcloud/dns/records/) - Manage DNS records + diff --git a/docs/tcloud/dns/zones/_index.md b/docs/tcloud/dns/zones/_index.md new file mode 100644 index 0000000..1e4b19c --- /dev/null +++ b/docs/tcloud/dns/zones/_index.md @@ -0,0 +1,45 @@ +--- +linkTitle: "tcloud dns zones" +title: "dns zones" +slug: tcloud_dns_zones +url: /docs/tcloud/dns/zones/ +weight: 9923 +cascade: + type: docs +--- +## tcloud dns zones + +Manage DNS zones + +### Options + +``` + -h, --help help for zones +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns](/docs/tcloud/tcloud_dns/) - Manage DNS zones and records (beta) +* [tcloud dns zones create](/docs/tcloud/dns/zones_create/) - Create a DNS zone +* [tcloud dns zones delete](/docs/tcloud/dns/zones_delete/) - Delete a DNS zone and all of its records +* [tcloud dns zones dnssec](/docs/tcloud/dns/zones_dnssec/) - Manage DNSSEC for a DNS zone +* [tcloud dns zones export](/docs/tcloud/dns/zones_export/) - Export a DNS zone as a BIND zone file to stdout +* [tcloud dns zones import](/docs/tcloud/dns/zones_import/) - Import DNS records from a BIND zone file +* [tcloud dns zones list](/docs/tcloud/dns/zones_list/) - List DNS zones +* [tcloud dns zones update](/docs/tcloud/dns/zones_update/) - Update a DNS zone +* [tcloud dns zones view](/docs/tcloud/dns/zones_view/) - View a DNS zone + diff --git a/docs/tcloud/dns/zones_create/_index.md b/docs/tcloud/dns/zones_create/_index.md new file mode 100644 index 0000000..cd7aad6 --- /dev/null +++ b/docs/tcloud/dns/zones_create/_index.md @@ -0,0 +1,47 @@ +--- +linkTitle: "tcloud dns zones create" +title: "dns zones create" +slug: tcloud_dns_zones_create +url: /docs/tcloud/dns/zones_create/ +weight: 9934 +cascade: + type: docs +--- +## tcloud dns zones create + +Create a DNS zone + +``` +tcloud dns zones create [flags] +``` + +### Options + +``` + --annotations strings Annotations as key=value (repeatable) + --description string Zone description + --exact-time Show full timestamps instead of relative time + -h, --help help for create + --labels strings Labels as key=value (repeatable) + --name string Zone name (e.g. example.com) + --no-header Do not print table headers +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/dns/zones_delete/_index.md b/docs/tcloud/dns/zones_delete/_index.md new file mode 100644 index 0000000..014bc01 --- /dev/null +++ b/docs/tcloud/dns/zones_delete/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud dns zones delete" +title: "dns zones delete" +slug: tcloud_dns_zones_delete +url: /docs/tcloud/dns/zones_delete/ +weight: 9933 +cascade: + type: docs +--- +## tcloud dns zones delete + +Delete a DNS zone and all of its records + +``` +tcloud dns zones delete [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt and delete + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/dns/zones_dnssec/_index.md b/docs/tcloud/dns/zones_dnssec/_index.md new file mode 100644 index 0000000..8cf0b56 --- /dev/null +++ b/docs/tcloud/dns/zones_dnssec/_index.md @@ -0,0 +1,40 @@ +--- +linkTitle: "tcloud dns zones dnssec" +title: "dns zones dnssec" +slug: tcloud_dns_zones_dnssec +url: /docs/tcloud/dns/zones_dnssec/ +weight: 9929 +cascade: + type: docs +--- +## tcloud dns zones dnssec + +Manage DNSSEC for a DNS zone + +### Options + +``` + -h, --help help for dnssec +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones +* [tcloud dns zones dnssec disable](/docs/tcloud/dns/zones_dnssec_disable/) - Disable DNSSEC signing for a zone +* [tcloud dns zones dnssec enable](/docs/tcloud/dns/zones_dnssec_enable/) - Enable DNSSEC signing for a zone +* [tcloud dns zones dnssec get](/docs/tcloud/dns/zones_dnssec_get/) - Get DNSSEC status for a zone + diff --git a/docs/tcloud/dns/zones_dnssec_disable/_index.md b/docs/tcloud/dns/zones_dnssec_disable/_index.md new file mode 100644 index 0000000..5d0439c --- /dev/null +++ b/docs/tcloud/dns/zones_dnssec_disable/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud dns zones dnssec disable" +title: "dns zones dnssec disable" +slug: tcloud_dns_zones_dnssec_disable +url: /docs/tcloud/dns/zones_dnssec_disable/ +weight: 9932 +cascade: + type: docs +--- +## tcloud dns zones dnssec disable + +Disable DNSSEC signing for a zone + +``` +tcloud dns zones dnssec disable [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt + -h, --help help for disable +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones dnssec](/docs/tcloud/dns/zones_dnssec/) - Manage DNSSEC for a DNS zone + diff --git a/docs/tcloud/dns/zones_dnssec_enable/_index.md b/docs/tcloud/dns/zones_dnssec_enable/_index.md new file mode 100644 index 0000000..3dffd5e --- /dev/null +++ b/docs/tcloud/dns/zones_dnssec_enable/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud dns zones dnssec enable" +title: "dns zones dnssec enable" +slug: tcloud_dns_zones_dnssec_enable +url: /docs/tcloud/dns/zones_dnssec_enable/ +weight: 9931 +cascade: + type: docs +--- +## tcloud dns zones dnssec enable + +Enable DNSSEC signing for a zone + +``` +tcloud dns zones dnssec enable [flags] +``` + +### Options + +``` + -h, --help help for enable + --kms-key string KMS key identity used for DNSSEC signing + --no-header Do not print table headers + --region string Region for DNSSEC KMS key +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones dnssec](/docs/tcloud/dns/zones_dnssec/) - Manage DNSSEC for a DNS zone + diff --git a/docs/tcloud/dns/zones_dnssec_get/_index.md b/docs/tcloud/dns/zones_dnssec_get/_index.md new file mode 100644 index 0000000..3b87dd9 --- /dev/null +++ b/docs/tcloud/dns/zones_dnssec_get/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud dns zones dnssec get" +title: "dns zones dnssec get" +slug: tcloud_dns_zones_dnssec_get +url: /docs/tcloud/dns/zones_dnssec_get/ +weight: 9930 +cascade: + type: docs +--- +## tcloud dns zones dnssec get + +Get DNSSEC status for a zone + +``` +tcloud dns zones dnssec get [flags] +``` + +### Options + +``` + -h, --help help for get + --no-header Do not print table headers +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones dnssec](/docs/tcloud/dns/zones_dnssec/) - Manage DNSSEC for a DNS zone + diff --git a/docs/tcloud/dns/zones_export/_index.md b/docs/tcloud/dns/zones_export/_index.md new file mode 100644 index 0000000..d3959c5 --- /dev/null +++ b/docs/tcloud/dns/zones_export/_index.md @@ -0,0 +1,41 @@ +--- +linkTitle: "tcloud dns zones export" +title: "dns zones export" +slug: tcloud_dns_zones_export +url: /docs/tcloud/dns/zones_export/ +weight: 9928 +cascade: + type: docs +--- +## tcloud dns zones export + +Export a DNS zone as a BIND zone file to stdout + +``` +tcloud dns zones export [flags] +``` + +### Options + +``` + -h, --help help for export +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/dns/zones_import/_index.md b/docs/tcloud/dns/zones_import/_index.md new file mode 100644 index 0000000..31b6acb --- /dev/null +++ b/docs/tcloud/dns/zones_import/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud dns zones import" +title: "dns zones import" +slug: tcloud_dns_zones_import +url: /docs/tcloud/dns/zones_import/ +weight: 9927 +cascade: + type: docs +--- +## tcloud dns zones import + +Import DNS records from a BIND zone file + +``` +tcloud dns zones import [flags] +``` + +### Options + +``` + --file string Path to BIND zone file + -h, --help help for import + --no-header Do not print table headers + --replace Replace existing records that conflict +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/dns/zones_list/_index.md b/docs/tcloud/dns/zones_list/_index.md new file mode 100644 index 0000000..2825530 --- /dev/null +++ b/docs/tcloud/dns/zones_list/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud dns zones list" +title: "dns zones list" +slug: tcloud_dns_zones_list +url: /docs/tcloud/dns/zones_list/ +weight: 9926 +cascade: + type: docs +--- +## tcloud dns zones list + +List DNS zones + +``` +tcloud dns zones list [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for list + --no-header Do not print table headers +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/dns/zones_update/_index.md b/docs/tcloud/dns/zones_update/_index.md new file mode 100644 index 0000000..b681e7a --- /dev/null +++ b/docs/tcloud/dns/zones_update/_index.md @@ -0,0 +1,46 @@ +--- +linkTitle: "tcloud dns zones update" +title: "dns zones update" +slug: tcloud_dns_zones_update +url: /docs/tcloud/dns/zones_update/ +weight: 9925 +cascade: + type: docs +--- +## tcloud dns zones update + +Update a DNS zone + +``` +tcloud dns zones update [flags] +``` + +### Options + +``` + --annotations strings Annotations as key=value (repeatable) + --description string Zone description + --exact-time Show full timestamps instead of relative time + -h, --help help for update + --labels strings Labels as key=value (repeatable) + --no-header Do not print table headers +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/dns/zones_view/_index.md b/docs/tcloud/dns/zones_view/_index.md new file mode 100644 index 0000000..9e853ac --- /dev/null +++ b/docs/tcloud/dns/zones_view/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud dns zones view" +title: "dns zones view" +slug: tcloud_dns_zones_view +url: /docs/tcloud/dns/zones_view/ +weight: 9924 +cascade: + type: docs +--- +## tcloud dns zones view + +View a DNS zone + +``` +tcloud dns zones view [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for view + --no-header Do not print table headers +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud dns zones](/docs/tcloud/dns/zones/) - Manage DNS zones + diff --git a/docs/tcloud/iam/_index.md b/docs/tcloud/iam/_index.md index 76eb73b..edff1a2 100644 --- a/docs/tcloud/iam/_index.md +++ b/docs/tcloud/iam/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam" title: "iam" slug: tcloud_iam url: /docs/tcloud/tcloud_iam/ -weight: 9903 +weight: 9870 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identities/_index.md b/docs/tcloud/iam/federated-identities/_index.md index 5aa4431..a64a886 100644 --- a/docs/tcloud/iam/federated-identities/_index.md +++ b/docs/tcloud/iam/federated-identities/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identities" title: "iam federated-identities" slug: tcloud_iam_federated-identities url: /docs/tcloud/iam/federated-identities/ -weight: 9949 +weight: 9916 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identities_create/_index.md b/docs/tcloud/iam/federated-identities_create/_index.md index dbacbd5..d64c6e4 100644 --- a/docs/tcloud/iam/federated-identities_create/_index.md +++ b/docs/tcloud/iam/federated-identities_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identities create" title: "iam federated-identities create" slug: tcloud_iam_federated-identities_create url: /docs/tcloud/iam/federated-identities_create/ -weight: 9954 +weight: 9921 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identities_delete/_index.md b/docs/tcloud/iam/federated-identities_delete/_index.md index 718c6dc..716fcf0 100644 --- a/docs/tcloud/iam/federated-identities_delete/_index.md +++ b/docs/tcloud/iam/federated-identities_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identities delete" title: "iam federated-identities delete" slug: tcloud_iam_federated-identities_delete url: /docs/tcloud/iam/federated-identities_delete/ -weight: 9953 +weight: 9920 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identities_get/_index.md b/docs/tcloud/iam/federated-identities_get/_index.md index fd3c2b5..f0c87f2 100644 --- a/docs/tcloud/iam/federated-identities_get/_index.md +++ b/docs/tcloud/iam/federated-identities_get/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identities get" title: "iam federated-identities get" slug: tcloud_iam_federated-identities_get url: /docs/tcloud/iam/federated-identities_get/ -weight: 9952 +weight: 9919 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identities_list/_index.md b/docs/tcloud/iam/federated-identities_list/_index.md index 90a4d54..2a25490 100644 --- a/docs/tcloud/iam/federated-identities_list/_index.md +++ b/docs/tcloud/iam/federated-identities_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identities list" title: "iam federated-identities list" slug: tcloud_iam_federated-identities_list url: /docs/tcloud/iam/federated-identities_list/ -weight: 9951 +weight: 9918 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identities_update/_index.md b/docs/tcloud/iam/federated-identities_update/_index.md index 04e4db6..842671d 100644 --- a/docs/tcloud/iam/federated-identities_update/_index.md +++ b/docs/tcloud/iam/federated-identities_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identities update" title: "iam federated-identities update" slug: tcloud_iam_federated-identities_update url: /docs/tcloud/iam/federated-identities_update/ -weight: 9950 +weight: 9917 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identity-providers/_index.md b/docs/tcloud/iam/federated-identity-providers/_index.md index 05dc763..c1a1798 100644 --- a/docs/tcloud/iam/federated-identity-providers/_index.md +++ b/docs/tcloud/iam/federated-identity-providers/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identity-providers" title: "iam federated-identity-providers" slug: tcloud_iam_federated-identity-providers url: /docs/tcloud/iam/federated-identity-providers/ -weight: 9943 +weight: 9910 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identity-providers_create/_index.md b/docs/tcloud/iam/federated-identity-providers_create/_index.md index be8eee1..0d47821 100644 --- a/docs/tcloud/iam/federated-identity-providers_create/_index.md +++ b/docs/tcloud/iam/federated-identity-providers_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identity-providers create" title: "iam federated-identity-providers create" slug: tcloud_iam_federated-identity-providers_create url: /docs/tcloud/iam/federated-identity-providers_create/ -weight: 9948 +weight: 9915 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identity-providers_delete/_index.md b/docs/tcloud/iam/federated-identity-providers_delete/_index.md index e7504e1..e525a91 100644 --- a/docs/tcloud/iam/federated-identity-providers_delete/_index.md +++ b/docs/tcloud/iam/federated-identity-providers_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identity-providers delete" title: "iam federated-identity-providers delete" slug: tcloud_iam_federated-identity-providers_delete url: /docs/tcloud/iam/federated-identity-providers_delete/ -weight: 9947 +weight: 9914 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identity-providers_get/_index.md b/docs/tcloud/iam/federated-identity-providers_get/_index.md index cd3bc71..3048256 100644 --- a/docs/tcloud/iam/federated-identity-providers_get/_index.md +++ b/docs/tcloud/iam/federated-identity-providers_get/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identity-providers get" title: "iam federated-identity-providers get" slug: tcloud_iam_federated-identity-providers_get url: /docs/tcloud/iam/federated-identity-providers_get/ -weight: 9946 +weight: 9913 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identity-providers_list/_index.md b/docs/tcloud/iam/federated-identity-providers_list/_index.md index 2e685f6..e98e83e 100644 --- a/docs/tcloud/iam/federated-identity-providers_list/_index.md +++ b/docs/tcloud/iam/federated-identity-providers_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identity-providers list" title: "iam federated-identity-providers list" slug: tcloud_iam_federated-identity-providers_list url: /docs/tcloud/iam/federated-identity-providers_list/ -weight: 9945 +weight: 9912 cascade: type: docs --- diff --git a/docs/tcloud/iam/federated-identity-providers_update/_index.md b/docs/tcloud/iam/federated-identity-providers_update/_index.md index ada2b64..3a8347d 100644 --- a/docs/tcloud/iam/federated-identity-providers_update/_index.md +++ b/docs/tcloud/iam/federated-identity-providers_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam federated-identity-providers update" title: "iam federated-identity-providers update" slug: tcloud_iam_federated-identity-providers_update url: /docs/tcloud/iam/federated-identity-providers_update/ -weight: 9944 +weight: 9911 cascade: type: docs --- diff --git a/docs/tcloud/iam/invites/_index.md b/docs/tcloud/iam/invites/_index.md index a5dea1f..f8aa6c0 100644 --- a/docs/tcloud/iam/invites/_index.md +++ b/docs/tcloud/iam/invites/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam invites" title: "iam invites" slug: tcloud_iam_invites url: /docs/tcloud/iam/invites/ -weight: 9941 +weight: 9908 cascade: type: docs --- diff --git a/docs/tcloud/iam/invites_list/_index.md b/docs/tcloud/iam/invites_list/_index.md index 2c09941..c647a98 100644 --- a/docs/tcloud/iam/invites_list/_index.md +++ b/docs/tcloud/iam/invites_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam invites list" title: "iam invites list" slug: tcloud_iam_invites_list url: /docs/tcloud/iam/invites_list/ -weight: 9942 +weight: 9909 cascade: type: docs --- diff --git a/docs/tcloud/iam/members/_index.md b/docs/tcloud/iam/members/_index.md index f923fb3..edec9ef 100644 --- a/docs/tcloud/iam/members/_index.md +++ b/docs/tcloud/iam/members/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam members" title: "iam members" slug: tcloud_iam_members url: /docs/tcloud/iam/members/ -weight: 9937 +weight: 9904 cascade: type: docs --- diff --git a/docs/tcloud/iam/members_delete/_index.md b/docs/tcloud/iam/members_delete/_index.md index d7f6d49..d0d3a79 100644 --- a/docs/tcloud/iam/members_delete/_index.md +++ b/docs/tcloud/iam/members_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam members delete" title: "iam members delete" slug: tcloud_iam_members_delete url: /docs/tcloud/iam/members_delete/ -weight: 9940 +weight: 9907 cascade: type: docs --- diff --git a/docs/tcloud/iam/members_list/_index.md b/docs/tcloud/iam/members_list/_index.md index 61cbb03..91029cc 100644 --- a/docs/tcloud/iam/members_list/_index.md +++ b/docs/tcloud/iam/members_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam members list" title: "iam members list" slug: tcloud_iam_members_list url: /docs/tcloud/iam/members_list/ -weight: 9939 +weight: 9906 cascade: type: docs --- diff --git a/docs/tcloud/iam/members_update/_index.md b/docs/tcloud/iam/members_update/_index.md index 07e8540..31b893c 100644 --- a/docs/tcloud/iam/members_update/_index.md +++ b/docs/tcloud/iam/members_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam members update" title: "iam members update" slug: tcloud_iam_members_update url: /docs/tcloud/iam/members_update/ -weight: 9938 +weight: 9905 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles/_index.md b/docs/tcloud/iam/roles/_index.md index 7dfb252..0b96774 100644 --- a/docs/tcloud/iam/roles/_index.md +++ b/docs/tcloud/iam/roles/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles" title: "iam roles" slug: tcloud_iam_roles url: /docs/tcloud/iam/roles/ -weight: 9925 +weight: 9892 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_bindings/_index.md b/docs/tcloud/iam/roles_bindings/_index.md index 47026d8..913e400 100644 --- a/docs/tcloud/iam/roles_bindings/_index.md +++ b/docs/tcloud/iam/roles_bindings/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles bindings" title: "iam roles bindings" slug: tcloud_iam_roles_bindings url: /docs/tcloud/iam/roles_bindings/ -weight: 9933 +weight: 9900 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_bindings_create/_index.md b/docs/tcloud/iam/roles_bindings_create/_index.md index ecd90c5..4ed8685 100644 --- a/docs/tcloud/iam/roles_bindings_create/_index.md +++ b/docs/tcloud/iam/roles_bindings_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles bindings create" title: "iam roles bindings create" slug: tcloud_iam_roles_bindings_create url: /docs/tcloud/iam/roles_bindings_create/ -weight: 9936 +weight: 9903 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_bindings_delete/_index.md b/docs/tcloud/iam/roles_bindings_delete/_index.md index fe3bb21..1b22175 100644 --- a/docs/tcloud/iam/roles_bindings_delete/_index.md +++ b/docs/tcloud/iam/roles_bindings_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles bindings delete" title: "iam roles bindings delete" slug: tcloud_iam_roles_bindings_delete url: /docs/tcloud/iam/roles_bindings_delete/ -weight: 9935 +weight: 9902 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_bindings_list/_index.md b/docs/tcloud/iam/roles_bindings_list/_index.md index 7d6759d..8443819 100644 --- a/docs/tcloud/iam/roles_bindings_list/_index.md +++ b/docs/tcloud/iam/roles_bindings_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles bindings list" title: "iam roles bindings list" slug: tcloud_iam_roles_bindings_list url: /docs/tcloud/iam/roles_bindings_list/ -weight: 9934 +weight: 9901 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_create/_index.md b/docs/tcloud/iam/roles_create/_index.md index fc75fbf..dffc18b 100644 --- a/docs/tcloud/iam/roles_create/_index.md +++ b/docs/tcloud/iam/roles_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles create" title: "iam roles create" slug: tcloud_iam_roles_create url: /docs/tcloud/iam/roles_create/ -weight: 9932 +weight: 9899 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_delete/_index.md b/docs/tcloud/iam/roles_delete/_index.md index 51bcf1d..acdbffd 100644 --- a/docs/tcloud/iam/roles_delete/_index.md +++ b/docs/tcloud/iam/roles_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles delete" title: "iam roles delete" slug: tcloud_iam_roles_delete url: /docs/tcloud/iam/roles_delete/ -weight: 9931 +weight: 9898 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_get/_index.md b/docs/tcloud/iam/roles_get/_index.md index 7ec7ece..ec4197c 100644 --- a/docs/tcloud/iam/roles_get/_index.md +++ b/docs/tcloud/iam/roles_get/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles get" title: "iam roles get" slug: tcloud_iam_roles_get url: /docs/tcloud/iam/roles_get/ -weight: 9930 +weight: 9897 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_list/_index.md b/docs/tcloud/iam/roles_list/_index.md index 72ea925..0f9d864 100644 --- a/docs/tcloud/iam/roles_list/_index.md +++ b/docs/tcloud/iam/roles_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles list" title: "iam roles list" slug: tcloud_iam_roles_list url: /docs/tcloud/iam/roles_list/ -weight: 9929 +weight: 9896 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_rules/_index.md b/docs/tcloud/iam/roles_rules/_index.md index 7582f9a..8a32a19 100644 --- a/docs/tcloud/iam/roles_rules/_index.md +++ b/docs/tcloud/iam/roles_rules/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles rules" title: "iam roles rules" slug: tcloud_iam_roles_rules url: /docs/tcloud/iam/roles_rules/ -weight: 9926 +weight: 9893 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_rules_add/_index.md b/docs/tcloud/iam/roles_rules_add/_index.md index 5b05d20..504ec81 100644 --- a/docs/tcloud/iam/roles_rules_add/_index.md +++ b/docs/tcloud/iam/roles_rules_add/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles rules add" title: "iam roles rules add" slug: tcloud_iam_roles_rules_add url: /docs/tcloud/iam/roles_rules_add/ -weight: 9928 +weight: 9895 cascade: type: docs --- diff --git a/docs/tcloud/iam/roles_rules_delete/_index.md b/docs/tcloud/iam/roles_rules_delete/_index.md index 87c6bf3..72ea5d4 100644 --- a/docs/tcloud/iam/roles_rules_delete/_index.md +++ b/docs/tcloud/iam/roles_rules_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam roles rules delete" title: "iam roles rules delete" slug: tcloud_iam_roles_rules_delete url: /docs/tcloud/iam/roles_rules_delete/ -weight: 9927 +weight: 9894 cascade: type: docs --- diff --git a/docs/tcloud/iam/service-accounts/_index.md b/docs/tcloud/iam/service-accounts/_index.md index 4a12eb1..574b534 100644 --- a/docs/tcloud/iam/service-accounts/_index.md +++ b/docs/tcloud/iam/service-accounts/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam service-accounts" title: "iam service-accounts" slug: tcloud_iam_service-accounts url: /docs/tcloud/iam/service-accounts/ -weight: 9919 +weight: 9886 cascade: type: docs --- diff --git a/docs/tcloud/iam/service-accounts_create/_index.md b/docs/tcloud/iam/service-accounts_create/_index.md index 115d5d4..b484e63 100644 --- a/docs/tcloud/iam/service-accounts_create/_index.md +++ b/docs/tcloud/iam/service-accounts_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam service-accounts create" title: "iam service-accounts create" slug: tcloud_iam_service-accounts_create url: /docs/tcloud/iam/service-accounts_create/ -weight: 9924 +weight: 9891 cascade: type: docs --- diff --git a/docs/tcloud/iam/service-accounts_delete/_index.md b/docs/tcloud/iam/service-accounts_delete/_index.md index 9289646..e87fc76 100644 --- a/docs/tcloud/iam/service-accounts_delete/_index.md +++ b/docs/tcloud/iam/service-accounts_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam service-accounts delete" title: "iam service-accounts delete" slug: tcloud_iam_service-accounts_delete url: /docs/tcloud/iam/service-accounts_delete/ -weight: 9923 +weight: 9890 cascade: type: docs --- diff --git a/docs/tcloud/iam/service-accounts_get/_index.md b/docs/tcloud/iam/service-accounts_get/_index.md index c837798..c10b3a4 100644 --- a/docs/tcloud/iam/service-accounts_get/_index.md +++ b/docs/tcloud/iam/service-accounts_get/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam service-accounts get" title: "iam service-accounts get" slug: tcloud_iam_service-accounts_get url: /docs/tcloud/iam/service-accounts_get/ -weight: 9922 +weight: 9889 cascade: type: docs --- diff --git a/docs/tcloud/iam/service-accounts_list/_index.md b/docs/tcloud/iam/service-accounts_list/_index.md index 9f03c90..07b227f 100644 --- a/docs/tcloud/iam/service-accounts_list/_index.md +++ b/docs/tcloud/iam/service-accounts_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam service-accounts list" title: "iam service-accounts list" slug: tcloud_iam_service-accounts_list url: /docs/tcloud/iam/service-accounts_list/ -weight: 9921 +weight: 9888 cascade: type: docs --- diff --git a/docs/tcloud/iam/service-accounts_update/_index.md b/docs/tcloud/iam/service-accounts_update/_index.md index cd65fbb..af380fc 100644 --- a/docs/tcloud/iam/service-accounts_update/_index.md +++ b/docs/tcloud/iam/service-accounts_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam service-accounts update" title: "iam service-accounts update" slug: tcloud_iam_service-accounts_update url: /docs/tcloud/iam/service-accounts_update/ -weight: 9920 +weight: 9887 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams/_index.md b/docs/tcloud/iam/teams/_index.md index 189fb59..cc13da9 100644 --- a/docs/tcloud/iam/teams/_index.md +++ b/docs/tcloud/iam/teams/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams" title: "iam teams" slug: tcloud_iam_teams url: /docs/tcloud/iam/teams/ -weight: 9909 +weight: 9876 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_create/_index.md b/docs/tcloud/iam/teams_create/_index.md index 4a1ff36..8907f0c 100644 --- a/docs/tcloud/iam/teams_create/_index.md +++ b/docs/tcloud/iam/teams_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams create" title: "iam teams create" slug: tcloud_iam_teams_create url: /docs/tcloud/iam/teams_create/ -weight: 9918 +weight: 9885 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_delete/_index.md b/docs/tcloud/iam/teams_delete/_index.md index 24f3b73..fa76cac 100644 --- a/docs/tcloud/iam/teams_delete/_index.md +++ b/docs/tcloud/iam/teams_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams delete" title: "iam teams delete" slug: tcloud_iam_teams_delete url: /docs/tcloud/iam/teams_delete/ -weight: 9917 +weight: 9884 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_get/_index.md b/docs/tcloud/iam/teams_get/_index.md index b756433..ccfe622 100644 --- a/docs/tcloud/iam/teams_get/_index.md +++ b/docs/tcloud/iam/teams_get/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams get" title: "iam teams get" slug: tcloud_iam_teams_get url: /docs/tcloud/iam/teams_get/ -weight: 9916 +weight: 9883 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_list/_index.md b/docs/tcloud/iam/teams_list/_index.md index d40f3a7..196d052 100644 --- a/docs/tcloud/iam/teams_list/_index.md +++ b/docs/tcloud/iam/teams_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams list" title: "iam teams list" slug: tcloud_iam_teams_list url: /docs/tcloud/iam/teams_list/ -weight: 9915 +weight: 9882 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_members/_index.md b/docs/tcloud/iam/teams_members/_index.md index 9a5b18f..0f66fdf 100644 --- a/docs/tcloud/iam/teams_members/_index.md +++ b/docs/tcloud/iam/teams_members/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams members" title: "iam teams members" slug: tcloud_iam_teams_members url: /docs/tcloud/iam/teams_members/ -weight: 9911 +weight: 9878 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_members_add/_index.md b/docs/tcloud/iam/teams_members_add/_index.md index 5e812a9..cf49670 100644 --- a/docs/tcloud/iam/teams_members_add/_index.md +++ b/docs/tcloud/iam/teams_members_add/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams members add" title: "iam teams members add" slug: tcloud_iam_teams_members_add url: /docs/tcloud/iam/teams_members_add/ -weight: 9914 +weight: 9881 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_members_list/_index.md b/docs/tcloud/iam/teams_members_list/_index.md index d37e3c5..316f3d0 100644 --- a/docs/tcloud/iam/teams_members_list/_index.md +++ b/docs/tcloud/iam/teams_members_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams members list" title: "iam teams members list" slug: tcloud_iam_teams_members_list url: /docs/tcloud/iam/teams_members_list/ -weight: 9913 +weight: 9880 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_members_remove/_index.md b/docs/tcloud/iam/teams_members_remove/_index.md index 81174f6..dd65f20 100644 --- a/docs/tcloud/iam/teams_members_remove/_index.md +++ b/docs/tcloud/iam/teams_members_remove/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams members remove" title: "iam teams members remove" slug: tcloud_iam_teams_members_remove url: /docs/tcloud/iam/teams_members_remove/ -weight: 9912 +weight: 9879 cascade: type: docs --- diff --git a/docs/tcloud/iam/teams_update/_index.md b/docs/tcloud/iam/teams_update/_index.md index 1056bdc..3d6b693 100644 --- a/docs/tcloud/iam/teams_update/_index.md +++ b/docs/tcloud/iam/teams_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam teams update" title: "iam teams update" slug: tcloud_iam_teams_update url: /docs/tcloud/iam/teams_update/ -weight: 9910 +weight: 9877 cascade: type: docs --- diff --git a/docs/tcloud/iam/workload-identity-federation/_index.md b/docs/tcloud/iam/workload-identity-federation/_index.md index bcf4199..555c3ff 100644 --- a/docs/tcloud/iam/workload-identity-federation/_index.md +++ b/docs/tcloud/iam/workload-identity-federation/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam workload-identity-federation" title: "iam workload-identity-federation" slug: tcloud_iam_workload-identity-federation url: /docs/tcloud/iam/workload-identity-federation/ -weight: 9904 +weight: 9871 cascade: type: docs --- diff --git a/docs/tcloud/iam/workload-identity-federation_bootstrap/_index.md b/docs/tcloud/iam/workload-identity-federation_bootstrap/_index.md index 47e848e..f29b5e8 100644 --- a/docs/tcloud/iam/workload-identity-federation_bootstrap/_index.md +++ b/docs/tcloud/iam/workload-identity-federation_bootstrap/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam workload-identity-federation bootstrap" title: "iam workload-identity-federation bootstrap" slug: tcloud_iam_workload-identity-federation_bootstrap url: /docs/tcloud/iam/workload-identity-federation_bootstrap/ -weight: 9905 +weight: 9872 cascade: type: docs --- diff --git a/docs/tcloud/iam/workload-identity-federation_bootstrap_github/_index.md b/docs/tcloud/iam/workload-identity-federation_bootstrap_github/_index.md index 248d420..342589d 100644 --- a/docs/tcloud/iam/workload-identity-federation_bootstrap_github/_index.md +++ b/docs/tcloud/iam/workload-identity-federation_bootstrap_github/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam workload-identity-federation bootstrap github" title: "iam workload-identity-federation bootstrap github" slug: tcloud_iam_workload-identity-federation_bootstrap_github url: /docs/tcloud/iam/workload-identity-federation_bootstrap_github/ -weight: 9908 +weight: 9875 cascade: type: docs --- diff --git a/docs/tcloud/iam/workload-identity-federation_bootstrap_gitlab/_index.md b/docs/tcloud/iam/workload-identity-federation_bootstrap_gitlab/_index.md index a617359..9410da4 100644 --- a/docs/tcloud/iam/workload-identity-federation_bootstrap_gitlab/_index.md +++ b/docs/tcloud/iam/workload-identity-federation_bootstrap_gitlab/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam workload-identity-federation bootstrap gitlab" title: "iam workload-identity-federation bootstrap gitlab" slug: tcloud_iam_workload-identity-federation_bootstrap_gitlab url: /docs/tcloud/iam/workload-identity-federation_bootstrap_gitlab/ -weight: 9907 +weight: 9874 cascade: type: docs --- diff --git a/docs/tcloud/iam/workload-identity-federation_bootstrap_kubernetes/_index.md b/docs/tcloud/iam/workload-identity-federation_bootstrap_kubernetes/_index.md index fbe58f1..d95f627 100644 --- a/docs/tcloud/iam/workload-identity-federation_bootstrap_kubernetes/_index.md +++ b/docs/tcloud/iam/workload-identity-federation_bootstrap_kubernetes/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud iam workload-identity-federation bootstrap kubernetes" title: "iam workload-identity-federation bootstrap kubernetes" slug: tcloud_iam_workload-identity-federation_bootstrap_kubernetes url: /docs/tcloud/iam/workload-identity-federation_bootstrap_kubernetes/ -weight: 9906 +weight: 9873 cascade: type: docs --- diff --git a/docs/tcloud/kms/_index.md b/docs/tcloud/kms/_index.md new file mode 100644 index 0000000..b12a795 --- /dev/null +++ b/docs/tcloud/kms/_index.md @@ -0,0 +1,54 @@ +--- +linkTitle: "tcloud kms" +title: "kms" +slug: tcloud_kms +url: /docs/tcloud/tcloud_kms/ +weight: 9849 +cascade: + type: docs +--- +## tcloud kms + +Manage KMS keys and cryptographic operations (beta) + +### Synopsis + +Manage Key Management Service keys and cryptographic operations. + +Note: This command is in beta. + +### Options + +``` + -h, --help help for kms +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud](/docs/tcloud/tcloud/) - A CLI for working with the Thalassa Cloud Platform +* [tcloud kms decrypt](/docs/tcloud/kms/decrypt/) - Decrypt ciphertext with a KMS key +* [tcloud kms encrypt](/docs/tcloud/kms/encrypt/) - Encrypt plaintext with a KMS key +* [tcloud kms export](/docs/tcloud/kms/export/) - Export key material for a KMS key (when export is allowed) +* [tcloud kms hmac](/docs/tcloud/kms/hmac/) - Compute an HMAC with a KMS key +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys +* [tcloud kms public-key](/docs/tcloud/kms/public-key/) - Get the public key material for an asymmetric KMS key +* [tcloud kms sign](/docs/tcloud/kms/sign/) - Sign input with an asymmetric KMS key +* [tcloud kms summary](/docs/tcloud/kms/summary/) - Show KMS availability and regional key counts +* [tcloud kms verify](/docs/tcloud/kms/verify/) - Verify a signature with an asymmetric KMS key +* [tcloud kms verify-hmac](/docs/tcloud/kms/verify-hmac/) - Verify an HMAC with a KMS key +* [tcloud kms wrapping-key](/docs/tcloud/kms/wrapping-key/) - Get the regional wrapping public key for BYOK import + diff --git a/docs/tcloud/kms/decrypt/_index.md b/docs/tcloud/kms/decrypt/_index.md new file mode 100644 index 0000000..9072a67 --- /dev/null +++ b/docs/tcloud/kms/decrypt/_index.md @@ -0,0 +1,61 @@ +--- +linkTitle: "tcloud kms decrypt" +title: "kms decrypt" +slug: tcloud_kms_decrypt +url: /docs/tcloud/kms/decrypt/ +weight: 9869 +cascade: + type: docs +--- +## tcloud kms decrypt + +Decrypt ciphertext with a KMS key + +### Synopsis + +Decrypt data with a KMS key. + +Provide exactly one of --ciphertext or --from-file (file containing ciphertext). +When --to-file is set, decoded plaintext bytes are written with mode 0600. +Otherwise base64-encoded plaintext is printed to stdout. + +``` +tcloud kms decrypt [flags] +``` + +### Examples + +``` + tcloud kms decrypt --region nl-ams --key kms-123 --ciphertext 'thalassa:v1:...' + tcloud kms decrypt --region nl-ams --key kms-123 --from-file secret.enc --to-file secret.txt +``` + +### Options + +``` + --ciphertext string Ciphertext from encrypt + --from-file string Read ciphertext from a file + -h, --help help for decrypt + --key string KMS key identity + --region string Region + --to-file string Write decoded plaintext to a file (mode 0600) instead of stdout +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/encrypt/_index.md b/docs/tcloud/kms/encrypt/_index.md new file mode 100644 index 0000000..b63a8eb --- /dev/null +++ b/docs/tcloud/kms/encrypt/_index.md @@ -0,0 +1,61 @@ +--- +linkTitle: "tcloud kms encrypt" +title: "kms encrypt" +slug: tcloud_kms_encrypt +url: /docs/tcloud/kms/encrypt/ +weight: 9868 +cascade: + type: docs +--- +## tcloud kms encrypt + +Encrypt plaintext with a KMS key + +### Synopsis + +Encrypt data with a KMS key. + +Provide exactly one of --plaintext (base64-encoded) or --from-file (raw file +bytes). Ciphertext is written to --to-file when set, otherwise to stdout. + +``` +tcloud kms encrypt [flags] +``` + +### Examples + +``` + tcloud kms encrypt --region nl-ams --key kms-123 --plaintext "$(echo -n hello | base64)" + tcloud kms encrypt --region nl-ams --key kms-123 --from-file secret.txt --to-file secret.enc +``` + +### Options + +``` + --from-file string Read raw plaintext bytes from a file + -h, --help help for encrypt + --key string KMS key identity + --key-version string Key version + --plaintext string Base64-encoded plaintext + --region string Region + --to-file string Write ciphertext to a file (mode 0600) instead of stdout +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/export/_index.md b/docs/tcloud/kms/export/_index.md new file mode 100644 index 0000000..556cd15 --- /dev/null +++ b/docs/tcloud/kms/export/_index.md @@ -0,0 +1,45 @@ +--- +linkTitle: "tcloud kms export" +title: "kms export" +slug: tcloud_kms_export +url: /docs/tcloud/kms/export/ +weight: 9867 +cascade: + type: docs +--- +## tcloud kms export + +Export key material for a KMS key (when export is allowed) + +``` +tcloud kms export [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt + -h, --help help for export + --key string KMS key identity + --key-version string Key version to export + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/hmac/_index.md b/docs/tcloud/kms/hmac/_index.md new file mode 100644 index 0000000..1183c6c --- /dev/null +++ b/docs/tcloud/kms/hmac/_index.md @@ -0,0 +1,46 @@ +--- +linkTitle: "tcloud kms hmac" +title: "kms hmac" +slug: tcloud_kms_hmac +url: /docs/tcloud/kms/hmac/ +weight: 9866 +cascade: + type: docs +--- +## tcloud kms hmac + +Compute an HMAC with a KMS key + +``` +tcloud kms hmac [flags] +``` + +### Options + +``` + --algorithm string HMAC algorithm + -h, --help help for hmac + --input string Input for HMAC + --key string KMS key identity + --key-version string Key version + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/keys/_index.md b/docs/tcloud/kms/keys/_index.md new file mode 100644 index 0000000..3d78b49 --- /dev/null +++ b/docs/tcloud/kms/keys/_index.md @@ -0,0 +1,46 @@ +--- +linkTitle: "tcloud kms keys" +title: "kms keys" +slug: tcloud_kms_keys +url: /docs/tcloud/kms/keys/ +weight: 9856 +cascade: + type: docs +--- +## tcloud kms keys + +Manage KMS keys + +### Options + +``` + -h, --help help for keys +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) +* [tcloud kms keys cancel-deletion](/docs/tcloud/kms/keys_cancel-deletion/) - Cancel a pending KMS key deletion +* [tcloud kms keys create](/docs/tcloud/kms/keys_create/) - Create a KMS key +* [tcloud kms keys delete](/docs/tcloud/kms/keys_delete/) - Schedule a KMS key for deletion +* [tcloud kms keys disable](/docs/tcloud/kms/keys_disable/) - Disable a KMS key +* [tcloud kms keys enable](/docs/tcloud/kms/keys_enable/) - Enable a KMS key +* [tcloud kms keys list](/docs/tcloud/kms/keys_list/) - List KMS keys in a region +* [tcloud kms keys rotate](/docs/tcloud/kms/keys_rotate/) - Rotate a KMS key on demand +* [tcloud kms keys rotation](/docs/tcloud/kms/keys_rotation/) - Update automatic rotation settings for a KMS key +* [tcloud kms keys view](/docs/tcloud/kms/keys_view/) - View a KMS key + diff --git a/docs/tcloud/kms/keys_cancel-deletion/_index.md b/docs/tcloud/kms/keys_cancel-deletion/_index.md new file mode 100644 index 0000000..55440e5 --- /dev/null +++ b/docs/tcloud/kms/keys_cancel-deletion/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud kms keys cancel-deletion" +title: "kms keys cancel-deletion" +slug: tcloud_kms_keys_cancel-deletion +url: /docs/tcloud/kms/keys_cancel-deletion/ +weight: 9865 +cascade: + type: docs +--- +## tcloud kms keys cancel-deletion + +Cancel a pending KMS key deletion + +``` +tcloud kms keys cancel-deletion [flags] +``` + +### Options + +``` + -h, --help help for cancel-deletion + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_create/_index.md b/docs/tcloud/kms/keys_create/_index.md new file mode 100644 index 0000000..515d726 --- /dev/null +++ b/docs/tcloud/kms/keys_create/_index.md @@ -0,0 +1,55 @@ +--- +linkTitle: "tcloud kms keys create" +title: "kms keys create" +slug: tcloud_kms_keys_create +url: /docs/tcloud/kms/keys_create/ +weight: 9864 +cascade: + type: docs +--- +## tcloud kms keys create + +Create a KMS key + +``` +tcloud kms keys create [flags] +``` + +### Options + +``` + --allow-rotation Allow rotation for imported keys + --annotations strings Annotations as key=value (repeatable) + --description string Key description + --exact-time Show full timestamps instead of relative time + --export-allowed Allow exporting key material + --hash-function string Hash function for imported or HMAC keys + -h, --help help for create + --import-key-material string Wrapped key material for BYOK import + --key-type string Key type (aes128-gcm96, aes256-gcm96, chacha20-poly1305, ed25519, ecdsa-p256/384/521, rsa-2048/3072/4096, hmac, hmac-sha256, hmac-sha512) + --labels strings Labels as key=value (repeatable) + --name string Key name + --no-header Do not print table headers + --region string Region + --rotation-enabled Enable automatic key rotation + --rotation-period-days int Automatic rotation period in days +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_delete/_index.md b/docs/tcloud/kms/keys_delete/_index.md new file mode 100644 index 0000000..9fa17eb --- /dev/null +++ b/docs/tcloud/kms/keys_delete/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud kms keys delete" +title: "kms keys delete" +slug: tcloud_kms_keys_delete +url: /docs/tcloud/kms/keys_delete/ +weight: 9863 +cascade: + type: docs +--- +## tcloud kms keys delete + +Schedule a KMS key for deletion + +``` +tcloud kms keys delete [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt and delete + -h, --help help for delete + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_disable/_index.md b/docs/tcloud/kms/keys_disable/_index.md new file mode 100644 index 0000000..2979b6f --- /dev/null +++ b/docs/tcloud/kms/keys_disable/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud kms keys disable" +title: "kms keys disable" +slug: tcloud_kms_keys_disable +url: /docs/tcloud/kms/keys_disable/ +weight: 9862 +cascade: + type: docs +--- +## tcloud kms keys disable + +Disable a KMS key + +``` +tcloud kms keys disable [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for disable + --no-header Do not print table headers + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_enable/_index.md b/docs/tcloud/kms/keys_enable/_index.md new file mode 100644 index 0000000..d1ec9da --- /dev/null +++ b/docs/tcloud/kms/keys_enable/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud kms keys enable" +title: "kms keys enable" +slug: tcloud_kms_keys_enable +url: /docs/tcloud/kms/keys_enable/ +weight: 9861 +cascade: + type: docs +--- +## tcloud kms keys enable + +Enable a KMS key + +``` +tcloud kms keys enable [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for enable + --no-header Do not print table headers + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_list/_index.md b/docs/tcloud/kms/keys_list/_index.md new file mode 100644 index 0000000..3f8dd20 --- /dev/null +++ b/docs/tcloud/kms/keys_list/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud kms keys list" +title: "kms keys list" +slug: tcloud_kms_keys_list +url: /docs/tcloud/kms/keys_list/ +weight: 9860 +cascade: + type: docs +--- +## tcloud kms keys list + +List KMS keys in a region + +``` +tcloud kms keys list [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for list + --no-header Do not print table headers + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_rotate/_index.md b/docs/tcloud/kms/keys_rotate/_index.md new file mode 100644 index 0000000..bf2f7c7 --- /dev/null +++ b/docs/tcloud/kms/keys_rotate/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud kms keys rotate" +title: "kms keys rotate" +slug: tcloud_kms_keys_rotate +url: /docs/tcloud/kms/keys_rotate/ +weight: 9859 +cascade: + type: docs +--- +## tcloud kms keys rotate + +Rotate a KMS key on demand + +``` +tcloud kms keys rotate [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for rotate + --no-header Do not print table headers + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_rotation/_index.md b/docs/tcloud/kms/keys_rotation/_index.md new file mode 100644 index 0000000..7ce4c51 --- /dev/null +++ b/docs/tcloud/kms/keys_rotation/_index.md @@ -0,0 +1,46 @@ +--- +linkTitle: "tcloud kms keys rotation" +title: "kms keys rotation" +slug: tcloud_kms_keys_rotation +url: /docs/tcloud/kms/keys_rotation/ +weight: 9858 +cascade: + type: docs +--- +## tcloud kms keys rotation + +Update automatic rotation settings for a KMS key + +``` +tcloud kms keys rotation [flags] +``` + +### Options + +``` + --enabled Enable or disable automatic rotation + --exact-time Show full timestamps instead of relative time + -h, --help help for rotation + --no-header Do not print table headers + --period-days int Automatic rotation period in days + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/keys_view/_index.md b/docs/tcloud/kms/keys_view/_index.md new file mode 100644 index 0000000..3902a0d --- /dev/null +++ b/docs/tcloud/kms/keys_view/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud kms keys view" +title: "kms keys view" +slug: tcloud_kms_keys_view +url: /docs/tcloud/kms/keys_view/ +weight: 9857 +cascade: + type: docs +--- +## tcloud kms keys view + +View a KMS key + +``` +tcloud kms keys view [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for view + --no-header Do not print table headers + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms keys](/docs/tcloud/kms/keys/) - Manage KMS keys + diff --git a/docs/tcloud/kms/public-key/_index.md b/docs/tcloud/kms/public-key/_index.md new file mode 100644 index 0000000..4cdb8e2 --- /dev/null +++ b/docs/tcloud/kms/public-key/_index.md @@ -0,0 +1,45 @@ +--- +linkTitle: "tcloud kms public-key" +title: "kms public-key" +slug: tcloud_kms_public-key +url: /docs/tcloud/kms/public-key/ +weight: 9855 +cascade: + type: docs +--- +## tcloud kms public-key + +Get the public key material for an asymmetric KMS key + +``` +tcloud kms public-key [flags] +``` + +### Options + +``` + -h, --help help for public-key + --key string KMS key identity + --no-header Do not print table headers + --region string Region + --version int Key version +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/sign/_index.md b/docs/tcloud/kms/sign/_index.md new file mode 100644 index 0000000..210bb1d --- /dev/null +++ b/docs/tcloud/kms/sign/_index.md @@ -0,0 +1,63 @@ +--- +linkTitle: "tcloud kms sign" +title: "kms sign" +slug: tcloud_kms_sign +url: /docs/tcloud/kms/sign/ +weight: 9854 +cascade: + type: docs +--- +## tcloud kms sign + +Sign input with an asymmetric KMS key + +### Synopsis + +Sign data with an asymmetric KMS key. + +Provide exactly one of --input (base64-encoded) or --from-file (raw file bytes). +The signature is written to --to-file when set, otherwise to stdout. + +``` +tcloud kms sign [flags] +``` + +### Examples + +``` + tcloud kms sign --region nl-ams --key kms-123 --input "$(echo -n hello | base64)" + tcloud kms sign --region nl-ams --key kms-123 --from-file message.txt --to-file message.sig +``` + +### Options + +``` + --context string Optional signing context + --from-file string Read raw input bytes from a file + --hash-algorithm string Hash algorithm + -h, --help help for sign + --input string Base64-encoded input to sign + --key string KMS key identity + --key-version string Key version + --prehashed Input is already hashed + --region string Region + --to-file string Write signature to a file (mode 0600) instead of stdout +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/summary/_index.md b/docs/tcloud/kms/summary/_index.md new file mode 100644 index 0000000..b1e68c2 --- /dev/null +++ b/docs/tcloud/kms/summary/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud kms summary" +title: "kms summary" +slug: tcloud_kms_summary +url: /docs/tcloud/kms/summary/ +weight: 9853 +cascade: + type: docs +--- +## tcloud kms summary + +Show KMS availability and regional key counts + +``` +tcloud kms summary [flags] +``` + +### Options + +``` + -h, --help help for summary + --no-header Do not print table headers +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/verify-hmac/_index.md b/docs/tcloud/kms/verify-hmac/_index.md new file mode 100644 index 0000000..e248438 --- /dev/null +++ b/docs/tcloud/kms/verify-hmac/_index.md @@ -0,0 +1,63 @@ +--- +linkTitle: "tcloud kms verify-hmac" +title: "kms verify-hmac" +slug: tcloud_kms_verify-hmac +url: /docs/tcloud/kms/verify-hmac/ +weight: 9851 +cascade: + type: docs +--- +## tcloud kms verify-hmac + +Verify an HMAC with a KMS key + +### Synopsis + +Verify an HMAC with a KMS key. + +Provide exactly one of --input (base64-encoded) or --from-file (raw file bytes), +and exactly one of --hmac or --hmac-file. Validity is written to --to-file when +set, otherwise to stdout. + +``` +tcloud kms verify-hmac [flags] +``` + +### Examples + +``` + tcloud kms verify-hmac --region nl-ams --key kms-123 --from-file message.txt --hmac-file message.hmac +``` + +### Options + +``` + --from-file string Read raw input bytes from a file + --hash-algorithm string Hash algorithm + -h, --help help for verify-hmac + --hmac string HMAC value to verify + --hmac-file string Read HMAC value from a file + --input string Base64-encoded input that was HMACed + --key string KMS key identity + --region string Region + --to-file string Write validity (true/false) to a file instead of stdout +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/verify/_index.md b/docs/tcloud/kms/verify/_index.md new file mode 100644 index 0000000..b002f0d --- /dev/null +++ b/docs/tcloud/kms/verify/_index.md @@ -0,0 +1,64 @@ +--- +linkTitle: "tcloud kms verify" +title: "kms verify" +slug: tcloud_kms_verify +url: /docs/tcloud/kms/verify/ +weight: 9852 +cascade: + type: docs +--- +## tcloud kms verify + +Verify a signature with an asymmetric KMS key + +### Synopsis + +Verify a signature with an asymmetric KMS key. + +Provide exactly one of --input (base64-encoded) or --from-file (raw file bytes), +and exactly one of --signature or --signature-file. Validity is written to +--to-file when set, otherwise to stdout. + +``` +tcloud kms verify [flags] +``` + +### Examples + +``` + tcloud kms verify --region nl-ams --key kms-123 --input "$(echo -n hello | base64)" --signature '...' + tcloud kms verify --region nl-ams --key kms-123 --from-file message.txt --signature-file message.sig +``` + +### Options + +``` + --from-file string Read raw input bytes from a file + --hash-algorithm string Hash algorithm + -h, --help help for verify + --input string Base64-encoded input that was signed + --key string KMS key identity + --region string Region + --signature string Signature to verify + --signature-file string Read signature from a file + --to-file string Write validity (true/false) to a file instead of stdout +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kms/wrapping-key/_index.md b/docs/tcloud/kms/wrapping-key/_index.md new file mode 100644 index 0000000..1726bcc --- /dev/null +++ b/docs/tcloud/kms/wrapping-key/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud kms wrapping-key" +title: "kms wrapping-key" +slug: tcloud_kms_wrapping-key +url: /docs/tcloud/kms/wrapping-key/ +weight: 9850 +cascade: + type: docs +--- +## tcloud kms wrapping-key + +Get the regional wrapping public key for BYOK import + +``` +tcloud kms wrapping-key [flags] +``` + +### Options + +``` + -h, --help help for wrapping-key + --no-header Do not print table headers + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) + diff --git a/docs/tcloud/kubernetes/_index.md b/docs/tcloud/kubernetes/_index.md index e182196..10f1f18 100644 --- a/docs/tcloud/kubernetes/_index.md +++ b/docs/tcloud/kubernetes/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes" title: "kubernetes" slug: tcloud_kubernetes url: /docs/tcloud/tcloud_kubernetes/ -weight: 9874 +weight: 9817 cascade: type: docs --- @@ -43,6 +43,7 @@ Kubernetes commands to manage your Kubernetes clusters and node pools within the * [tcloud kubernetes delete](/docs/tcloud/kubernetes/delete/) - Delete a Kubernetes cluster * [tcloud kubernetes iam](/docs/tcloud/kubernetes/iam/) - Kubernetes cluster IAM roles and bindings * [tcloud kubernetes kubeconfig](/docs/tcloud/kubernetes/kubeconfig/) - Print a kubeconfig for a Kubernetes cluster +* [tcloud kubernetes kubeconfig-sessions](/docs/tcloud/kubernetes/kubeconfig-sessions/) - Manage Kubernetes kubeconfig sessions * [tcloud kubernetes list](/docs/tcloud/kubernetes/list/) - Get a list of Kubernetes clusters * [tcloud kubernetes machines](/docs/tcloud/kubernetes/machines/) - List and manage Kubernetes cluster machines * [tcloud kubernetes nodepools](/docs/tcloud/kubernetes/nodepools/) - Manage Kubernetes NodePools diff --git a/docs/tcloud/kubernetes/connect/_index.md b/docs/tcloud/kubernetes/connect/_index.md index 92eab27..2506c45 100644 --- a/docs/tcloud/kubernetes/connect/_index.md +++ b/docs/tcloud/kubernetes/connect/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes connect" title: "kubernetes connect" slug: tcloud_kubernetes_connect url: /docs/tcloud/kubernetes/connect/ -weight: 9902 +weight: 9848 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/create/_index.md b/docs/tcloud/kubernetes/create/_index.md index 79d1d84..221de31 100644 --- a/docs/tcloud/kubernetes/create/_index.md +++ b/docs/tcloud/kubernetes/create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes create" title: "kubernetes create" slug: tcloud_kubernetes_create url: /docs/tcloud/kubernetes/create/ -weight: 9901 +weight: 9847 cascade: type: docs --- @@ -79,6 +79,7 @@ tcloud kubernetes create [flags] --subnet string Subnet for managed clusters --upgrade-strategy string Upgrade strategy: manual, auto, always, on-delete, inplace, or never (default "auto") --wait Wait for the cluster to be ready before returning + --wait-timeout duration Maximum time to wait for resources to be ready (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/kubernetes/delete/_index.md b/docs/tcloud/kubernetes/delete/_index.md index ee857bf..4ccd38e 100644 --- a/docs/tcloud/kubernetes/delete/_index.md +++ b/docs/tcloud/kubernetes/delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes delete" title: "kubernetes delete" slug: tcloud_kubernetes_delete url: /docs/tcloud/kubernetes/delete/ -weight: 9900 +weight: 9846 cascade: type: docs --- @@ -35,9 +35,10 @@ tcloud kubernetes delete [flags] ### Options ``` - --force Skip confirmation prompt - -h, --help help for delete - --wait Wait for the cluster to be deleted before returning + --force Skip confirmation prompt + -h, --help help for delete + --wait Wait for the cluster to be deleted before returning + --wait-timeout duration Maximum time to wait for the cluster to be deleted (default 30m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/kubernetes/iam/_index.md b/docs/tcloud/kubernetes/iam/_index.md index 5c80ac9..1d29d24 100644 --- a/docs/tcloud/kubernetes/iam/_index.md +++ b/docs/tcloud/kubernetes/iam/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam" title: "kubernetes iam" slug: tcloud_kubernetes_iam url: /docs/tcloud/kubernetes/iam/ -weight: 9887 +weight: 9833 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles/_index.md b/docs/tcloud/kubernetes/iam_roles/_index.md index 0424237..9bf404e 100644 --- a/docs/tcloud/kubernetes/iam_roles/_index.md +++ b/docs/tcloud/kubernetes/iam_roles/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles" title: "kubernetes iam roles" slug: tcloud_kubernetes_iam_roles url: /docs/tcloud/kubernetes/iam_roles/ -weight: 9888 +weight: 9834 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_bindings/_index.md b/docs/tcloud/kubernetes/iam_roles_bindings/_index.md index 0da0ceb..ab77dbe 100644 --- a/docs/tcloud/kubernetes/iam_roles_bindings/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_bindings/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles bindings" title: "kubernetes iam roles bindings" slug: tcloud_kubernetes_iam_roles_bindings url: /docs/tcloud/kubernetes/iam_roles_bindings/ -weight: 9896 +weight: 9842 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_bindings_create/_index.md b/docs/tcloud/kubernetes/iam_roles_bindings_create/_index.md index f914c64..4a9d09f 100644 --- a/docs/tcloud/kubernetes/iam_roles_bindings_create/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_bindings_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles bindings create" title: "kubernetes iam roles bindings create" slug: tcloud_kubernetes_iam_roles_bindings_create url: /docs/tcloud/kubernetes/iam_roles_bindings_create/ -weight: 9899 +weight: 9845 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_bindings_delete/_index.md b/docs/tcloud/kubernetes/iam_roles_bindings_delete/_index.md index 17ee691..a104f59 100644 --- a/docs/tcloud/kubernetes/iam_roles_bindings_delete/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_bindings_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles bindings delete" title: "kubernetes iam roles bindings delete" slug: tcloud_kubernetes_iam_roles_bindings_delete url: /docs/tcloud/kubernetes/iam_roles_bindings_delete/ -weight: 9898 +weight: 9844 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_bindings_list/_index.md b/docs/tcloud/kubernetes/iam_roles_bindings_list/_index.md index be16be2..16071b9 100644 --- a/docs/tcloud/kubernetes/iam_roles_bindings_list/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_bindings_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles bindings list" title: "kubernetes iam roles bindings list" slug: tcloud_kubernetes_iam_roles_bindings_list url: /docs/tcloud/kubernetes/iam_roles_bindings_list/ -weight: 9897 +weight: 9843 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_create/_index.md b/docs/tcloud/kubernetes/iam_roles_create/_index.md index 5bf1146..6c64111 100644 --- a/docs/tcloud/kubernetes/iam_roles_create/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles create" title: "kubernetes iam roles create" slug: tcloud_kubernetes_iam_roles_create url: /docs/tcloud/kubernetes/iam_roles_create/ -weight: 9895 +weight: 9841 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_delete/_index.md b/docs/tcloud/kubernetes/iam_roles_delete/_index.md index b1e987e..987585e 100644 --- a/docs/tcloud/kubernetes/iam_roles_delete/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles delete" title: "kubernetes iam roles delete" slug: tcloud_kubernetes_iam_roles_delete url: /docs/tcloud/kubernetes/iam_roles_delete/ -weight: 9894 +weight: 9840 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_get/_index.md b/docs/tcloud/kubernetes/iam_roles_get/_index.md index ed8bb97..606c65e 100644 --- a/docs/tcloud/kubernetes/iam_roles_get/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_get/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles get" title: "kubernetes iam roles get" slug: tcloud_kubernetes_iam_roles_get url: /docs/tcloud/kubernetes/iam_roles_get/ -weight: 9893 +weight: 9839 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_list/_index.md b/docs/tcloud/kubernetes/iam_roles_list/_index.md index 49451ae..70ae802 100644 --- a/docs/tcloud/kubernetes/iam_roles_list/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles list" title: "kubernetes iam roles list" slug: tcloud_kubernetes_iam_roles_list url: /docs/tcloud/kubernetes/iam_roles_list/ -weight: 9892 +weight: 9838 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_rules/_index.md b/docs/tcloud/kubernetes/iam_roles_rules/_index.md index dd87038..4ded544 100644 --- a/docs/tcloud/kubernetes/iam_roles_rules/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_rules/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles rules" title: "kubernetes iam roles rules" slug: tcloud_kubernetes_iam_roles_rules url: /docs/tcloud/kubernetes/iam_roles_rules/ -weight: 9889 +weight: 9835 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_rules_add/_index.md b/docs/tcloud/kubernetes/iam_roles_rules_add/_index.md index 00fb565..182a890 100644 --- a/docs/tcloud/kubernetes/iam_roles_rules_add/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_rules_add/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles rules add" title: "kubernetes iam roles rules add" slug: tcloud_kubernetes_iam_roles_rules_add url: /docs/tcloud/kubernetes/iam_roles_rules_add/ -weight: 9891 +weight: 9837 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/iam_roles_rules_delete/_index.md b/docs/tcloud/kubernetes/iam_roles_rules_delete/_index.md index f98c816..d6aa13a 100644 --- a/docs/tcloud/kubernetes/iam_roles_rules_delete/_index.md +++ b/docs/tcloud/kubernetes/iam_roles_rules_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes iam roles rules delete" title: "kubernetes iam roles rules delete" slug: tcloud_kubernetes_iam_roles_rules_delete url: /docs/tcloud/kubernetes/iam_roles_rules_delete/ -weight: 9890 +weight: 9836 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/kubeconfig-sessions/_index.md b/docs/tcloud/kubernetes/kubeconfig-sessions/_index.md new file mode 100644 index 0000000..b31cda6 --- /dev/null +++ b/docs/tcloud/kubernetes/kubeconfig-sessions/_index.md @@ -0,0 +1,53 @@ +--- +linkTitle: "tcloud kubernetes kubeconfig-sessions" +title: "kubernetes kubeconfig-sessions" +slug: tcloud_kubernetes_kubeconfig-sessions +url: /docs/tcloud/kubernetes/kubeconfig-sessions/ +weight: 9829 +cascade: + type: docs +--- +## tcloud kubernetes kubeconfig-sessions + +Manage Kubernetes kubeconfig sessions + +### Synopsis + +List and revoke active kubeconfig sessions for a Kubernetes cluster. + +### Examples + +``` + # List kubeconfig sessions for a cluster + tcloud kubernetes kubeconfig-sessions list my-cluster + + # Delete a kubeconfig session + tcloud kubernetes kubeconfig-sessions delete my-cluster sess-abc123 --force +``` + +### Options + +``` + -h, --help help for kubeconfig-sessions +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kubernetes](/docs/tcloud/tcloud_kubernetes/) - Manage Kubernetes clusters, node pools and more services related to Kubernetes +* [tcloud kubernetes kubeconfig-sessions delete](/docs/tcloud/kubernetes/kubeconfig-sessions_delete/) - Delete a kubeconfig session +* [tcloud kubernetes kubeconfig-sessions list](/docs/tcloud/kubernetes/kubeconfig-sessions_list/) - List kubeconfig sessions for a Kubernetes cluster + diff --git a/docs/tcloud/kubernetes/kubeconfig-sessions_delete/_index.md b/docs/tcloud/kubernetes/kubeconfig-sessions_delete/_index.md new file mode 100644 index 0000000..64dfa45 --- /dev/null +++ b/docs/tcloud/kubernetes/kubeconfig-sessions_delete/_index.md @@ -0,0 +1,49 @@ +--- +linkTitle: "tcloud kubernetes kubeconfig-sessions delete" +title: "kubernetes kubeconfig-sessions delete" +slug: tcloud_kubernetes_kubeconfig-sessions_delete +url: /docs/tcloud/kubernetes/kubeconfig-sessions_delete/ +weight: 9831 +cascade: + type: docs +--- +## tcloud kubernetes kubeconfig-sessions delete + +Delete a kubeconfig session + +### Synopsis + +Revoke a kubeconfig session for a Kubernetes cluster. + +Provide the cluster as the first argument or with --cluster, and the session identity as the final argument. + +``` +tcloud kubernetes kubeconfig-sessions delete [cluster] [flags] +``` + +### Options + +``` + --cluster string Cluster identity, name, or slug + --force Skip the confirmation prompt and delete + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kubernetes kubeconfig-sessions](/docs/tcloud/kubernetes/kubeconfig-sessions/) - Manage Kubernetes kubeconfig sessions + diff --git a/docs/tcloud/kubernetes/kubeconfig-sessions_list/_index.md b/docs/tcloud/kubernetes/kubeconfig-sessions_list/_index.md new file mode 100644 index 0000000..c640557 --- /dev/null +++ b/docs/tcloud/kubernetes/kubeconfig-sessions_list/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud kubernetes kubeconfig-sessions list" +title: "kubernetes kubeconfig-sessions list" +slug: tcloud_kubernetes_kubeconfig-sessions_list +url: /docs/tcloud/kubernetes/kubeconfig-sessions_list/ +weight: 9830 +cascade: + type: docs +--- +## tcloud kubernetes kubeconfig-sessions list + +List kubeconfig sessions for a Kubernetes cluster + +``` +tcloud kubernetes kubeconfig-sessions list [cluster] [flags] +``` + +### Options + +``` + --cluster string Cluster identity, name, or slug + --exact-time Show full timestamps instead of relative time + -h, --help help for list + --no-header Do not print the header +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud kubernetes kubeconfig-sessions](/docs/tcloud/kubernetes/kubeconfig-sessions/) - Manage Kubernetes kubeconfig sessions + diff --git a/docs/tcloud/kubernetes/kubeconfig/_index.md b/docs/tcloud/kubernetes/kubeconfig/_index.md index 7e7dc09..7413555 100644 --- a/docs/tcloud/kubernetes/kubeconfig/_index.md +++ b/docs/tcloud/kubernetes/kubeconfig/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes kubeconfig" title: "kubernetes kubeconfig" slug: tcloud_kubernetes_kubeconfig url: /docs/tcloud/kubernetes/kubeconfig/ -weight: 9886 +weight: 9832 cascade: type: docs --- @@ -18,8 +18,9 @@ tcloud kubernetes kubeconfig [flags] ### Options ``` - -h, --help help for kubeconfig - --inline-token embed the session token in the kubeconfig instead of using a kubectl exec credential plugin + -h, --help help for kubeconfig + --inline-token embed the session token in the kubeconfig instead of using a kubectl exec credential plugin + --session-lifetime duration Lifetime of the kubeconfig session token. Defaults to 4 weeks. (default 672h0m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/kubernetes/list/_index.md b/docs/tcloud/kubernetes/list/_index.md index c3c9053..f77ef07 100644 --- a/docs/tcloud/kubernetes/list/_index.md +++ b/docs/tcloud/kubernetes/list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes list" title: "kubernetes list" slug: tcloud_kubernetes_list url: /docs/tcloud/kubernetes/list/ -weight: 9885 +weight: 9828 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/machines/_index.md b/docs/tcloud/kubernetes/machines/_index.md index 6cf30e9..d32831e 100644 --- a/docs/tcloud/kubernetes/machines/_index.md +++ b/docs/tcloud/kubernetes/machines/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes machines" title: "kubernetes machines" slug: tcloud_kubernetes_machines url: /docs/tcloud/kubernetes/machines/ -weight: 9883 +weight: 9826 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/machines_list/_index.md b/docs/tcloud/kubernetes/machines_list/_index.md index 7beff9b..445f66a 100644 --- a/docs/tcloud/kubernetes/machines_list/_index.md +++ b/docs/tcloud/kubernetes/machines_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes machines list" title: "kubernetes machines list" slug: tcloud_kubernetes_machines_list url: /docs/tcloud/kubernetes/machines_list/ -weight: 9884 +weight: 9827 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/nodepools/_index.md b/docs/tcloud/kubernetes/nodepools/_index.md index e12bf00..7e60019 100644 --- a/docs/tcloud/kubernetes/nodepools/_index.md +++ b/docs/tcloud/kubernetes/nodepools/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes nodepools" title: "kubernetes nodepools" slug: tcloud_kubernetes_nodepools url: /docs/tcloud/kubernetes/nodepools/ -weight: 9878 +weight: 9821 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/nodepools_create/_index.md b/docs/tcloud/kubernetes/nodepools_create/_index.md index 5da11a7..7040a4f 100644 --- a/docs/tcloud/kubernetes/nodepools_create/_index.md +++ b/docs/tcloud/kubernetes/nodepools_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes nodepools create" title: "kubernetes nodepools create" slug: tcloud_kubernetes_nodepools_create url: /docs/tcloud/kubernetes/nodepools_create/ -weight: 9882 +weight: 9825 cascade: type: docs --- @@ -49,6 +49,7 @@ tcloud kubernetes nodepools create [flags] --subnet string Subnet for the node pool (defaults to cluster subnet) --upgrade-strategy string Upgrade strategy: manual, auto, always, on-delete, inplace, or never (default "auto") --wait Wait for the node pool to be ready before returning + --wait-timeout duration Maximum time to wait for the node pool to be ready (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/kubernetes/nodepools_delete/_index.md b/docs/tcloud/kubernetes/nodepools_delete/_index.md index 12ec9db..7116264 100644 --- a/docs/tcloud/kubernetes/nodepools_delete/_index.md +++ b/docs/tcloud/kubernetes/nodepools_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes nodepools delete" title: "kubernetes nodepools delete" slug: tcloud_kubernetes_nodepools_delete url: /docs/tcloud/kubernetes/nodepools_delete/ -weight: 9881 +weight: 9824 cascade: type: docs --- @@ -25,6 +25,9 @@ Examples: # Delete a node pool and wait for completion tcloud kubernetes nodepools delete --cluster my-cluster --nodepool worker-pool --wait + # Delete a node pool and wait up to 45 minutes + tcloud kubernetes nodepools delete --cluster my-cluster --nodepool worker-pool --wait --wait-timeout 45m + # Delete a node pool without confirmation tcloud kubernetes nodepools delete --cluster my-cluster --nodepool worker-pool --force @@ -35,11 +38,12 @@ tcloud kubernetes nodepools delete [flags] ### Options ``` - --cluster string Cluster identity, name, or slug (required) - --force Skip confirmation prompt - -h, --help help for delete - --nodepool string Node pool name, identity, or slug (required) - --wait Wait for the node pool to be deleted before returning + --cluster string Cluster identity, name, or slug (required) + --force Skip confirmation prompt + -h, --help help for delete + --nodepool string Node pool name, identity, or slug (required) + --wait Wait for the node pool to be deleted before returning + --wait-timeout duration Maximum time to wait for the node pool to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/kubernetes/nodepools_list/_index.md b/docs/tcloud/kubernetes/nodepools_list/_index.md index 5983f09..7aaa0c1 100644 --- a/docs/tcloud/kubernetes/nodepools_list/_index.md +++ b/docs/tcloud/kubernetes/nodepools_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes nodepools list" title: "kubernetes nodepools list" slug: tcloud_kubernetes_nodepools_list url: /docs/tcloud/kubernetes/nodepools_list/ -weight: 9880 +weight: 9823 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/nodepools_update/_index.md b/docs/tcloud/kubernetes/nodepools_update/_index.md index d07666d..5b54603 100644 --- a/docs/tcloud/kubernetes/nodepools_update/_index.md +++ b/docs/tcloud/kubernetes/nodepools_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes nodepools update" title: "kubernetes nodepools update" slug: tcloud_kubernetes_nodepools_update url: /docs/tcloud/kubernetes/nodepools_update/ -weight: 9879 +weight: 9822 cascade: type: docs --- @@ -53,6 +53,7 @@ tcloud kubernetes nodepools update [flags] --security-groups strings Security group identities to attach to node pool machines --upgrade-strategy string Upgrade strategy: manual, auto, always, on-delete, inplace, or never --wait Wait for the node pool update to complete + --wait-timeout duration Maximum time to wait for the node pool update to complete (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/kubernetes/update/_index.md b/docs/tcloud/kubernetes/update/_index.md index 7b2f0a4..4e13ffa 100644 --- a/docs/tcloud/kubernetes/update/_index.md +++ b/docs/tcloud/kubernetes/update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes update" title: "kubernetes update" slug: tcloud_kubernetes_update url: /docs/tcloud/kubernetes/update/ -weight: 9877 +weight: 9820 cascade: type: docs --- @@ -53,6 +53,7 @@ tcloud kubernetes update [flags] --name string Name of the cluster --pod-security-standards string Pod security standards profile: baseline, restricted, or privileged --wait Wait for the cluster update to complete + --wait-timeout duration Maximum time to wait for the cluster update to complete (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/kubernetes/upgrade/_index.md b/docs/tcloud/kubernetes/upgrade/_index.md index d32ec38..417965d 100644 --- a/docs/tcloud/kubernetes/upgrade/_index.md +++ b/docs/tcloud/kubernetes/upgrade/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes upgrade" title: "kubernetes upgrade" slug: tcloud_kubernetes_upgrade url: /docs/tcloud/kubernetes/upgrade/ -weight: 9876 +weight: 9819 cascade: type: docs --- diff --git a/docs/tcloud/kubernetes/versions/_index.md b/docs/tcloud/kubernetes/versions/_index.md index 86c90eb..acc7610 100644 --- a/docs/tcloud/kubernetes/versions/_index.md +++ b/docs/tcloud/kubernetes/versions/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud kubernetes versions" title: "kubernetes versions" slug: tcloud_kubernetes_versions url: /docs/tcloud/kubernetes/versions/ -weight: 9875 +weight: 9818 cascade: type: docs --- diff --git a/docs/tcloud/me/_index.md b/docs/tcloud/me/_index.md index b7f2536..507884a 100644 --- a/docs/tcloud/me/_index.md +++ b/docs/tcloud/me/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud me" title: "me" slug: tcloud_me url: /docs/tcloud/tcloud_me/ -weight: 9872 +weight: 9815 cascade: type: docs --- diff --git a/docs/tcloud/me/organisations/_index.md b/docs/tcloud/me/organisations/_index.md index 793d924..f3d83fa 100644 --- a/docs/tcloud/me/organisations/_index.md +++ b/docs/tcloud/me/organisations/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud me organisations" title: "me organisations" slug: tcloud_me_organisations url: /docs/tcloud/me/organisations/ -weight: 9873 +weight: 9816 cascade: type: docs --- diff --git a/docs/tcloud/networking/_index.md b/docs/tcloud/networking/_index.md index ced96b2..d830d2b 100644 --- a/docs/tcloud/networking/_index.md +++ b/docs/tcloud/networking/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking" title: "networking" slug: tcloud_networking url: /docs/tcloud/tcloud_networking/ -weight: 9824 +weight: 9742 cascade: type: docs --- @@ -40,7 +40,8 @@ Manage networking resources in the Thalassa Cloud Platform * [tcloud](/docs/tcloud/tcloud/) - A CLI for working with the Thalassa Cloud Platform * [tcloud networking loadbalancers](/docs/tcloud/networking/loadbalancers/) - Manage load balancers * [tcloud networking natgateways](/docs/tcloud/networking/natgateways/) - Manage NAT gateways -* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage routetables +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses +* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage route tables * [tcloud networking security-groups](/docs/tcloud/networking/security-groups/) - Manage security groups * [tcloud networking subnets](/docs/tcloud/networking/subnets/) - Manage subnets * [tcloud networking target-groups](/docs/tcloud/networking/target-groups/) - Manage load balancer target groups diff --git a/docs/tcloud/networking/loadbalancers/_index.md b/docs/tcloud/networking/loadbalancers/_index.md index 941bfb1..58fdaa9 100644 --- a/docs/tcloud/networking/loadbalancers/_index.md +++ b/docs/tcloud/networking/loadbalancers/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers" title: "networking loadbalancers" slug: tcloud_networking_loadbalancers url: /docs/tcloud/networking/loadbalancers/ -weight: 9860 +weight: 9803 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_create/_index.md b/docs/tcloud/networking/loadbalancers_create/_index.md index 9c6a113..0c509ee 100644 --- a/docs/tcloud/networking/loadbalancers_create/_index.md +++ b/docs/tcloud/networking/loadbalancers_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers create" title: "networking loadbalancers create" slug: tcloud_networking_loadbalancers_create url: /docs/tcloud/networking/loadbalancers_create/ -weight: 9871 +weight: 9814 cascade: type: docs --- @@ -39,6 +39,7 @@ tcloud networking loadbalancers create --name internal --subnet subnet-123 --int --security-groups strings Security group identities to attach --subnet string Subnet identity, slug, or name --wait Wait for the load balancer to be ready + --wait-timeout duration Maximum time to wait for the load balancer to be ready (default 10m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/networking/loadbalancers_delete/_index.md b/docs/tcloud/networking/loadbalancers_delete/_index.md index 75745df..f917483 100644 --- a/docs/tcloud/networking/loadbalancers_delete/_index.md +++ b/docs/tcloud/networking/loadbalancers_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers delete" title: "networking loadbalancers delete" slug: tcloud_networking_loadbalancers_delete url: /docs/tcloud/networking/loadbalancers_delete/ -weight: 9870 +weight: 9813 cascade: type: docs --- @@ -30,10 +30,11 @@ tcloud networking loadbalancers delete --selector env=test --force ### Options ``` - --force Force deletion and skip confirmation - -h, --help help for delete - -l, --selector string Label selector (format: key1=value1,key2=value2) - --wait Wait for the load balancer(s) to be deleted + --force Force deletion and skip confirmation + -h, --help help for delete + -l, --selector string Label selector (format: key1=value1,key2=value2) + --wait Wait for the load balancer(s) to be deleted + --wait-timeout duration Maximum time to wait for the load balancer(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/networking/loadbalancers_list/_index.md b/docs/tcloud/networking/loadbalancers_list/_index.md index b0aa716..7238b02 100644 --- a/docs/tcloud/networking/loadbalancers_list/_index.md +++ b/docs/tcloud/networking/loadbalancers_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers list" title: "networking loadbalancers list" slug: tcloud_networking_loadbalancers_list url: /docs/tcloud/networking/loadbalancers_list/ -weight: 9869 +weight: 9812 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_listeners/_index.md b/docs/tcloud/networking/loadbalancers_listeners/_index.md index 7d421a0..407d893 100644 --- a/docs/tcloud/networking/loadbalancers_listeners/_index.md +++ b/docs/tcloud/networking/loadbalancers_listeners/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers listeners" title: "networking loadbalancers listeners" slug: tcloud_networking_loadbalancers_listeners url: /docs/tcloud/networking/loadbalancers_listeners/ -weight: 9863 +weight: 9806 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_listeners_create/_index.md b/docs/tcloud/networking/loadbalancers_listeners_create/_index.md index c042ac3..6e3cfb2 100644 --- a/docs/tcloud/networking/loadbalancers_listeners_create/_index.md +++ b/docs/tcloud/networking/loadbalancers_listeners_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers listeners create" title: "networking loadbalancers listeners create" slug: tcloud_networking_loadbalancers_listeners_create url: /docs/tcloud/networking/loadbalancers_listeners_create/ -weight: 9868 +weight: 9811 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_listeners_delete/_index.md b/docs/tcloud/networking/loadbalancers_listeners_delete/_index.md index 2755e09..e34f4f8 100644 --- a/docs/tcloud/networking/loadbalancers_listeners_delete/_index.md +++ b/docs/tcloud/networking/loadbalancers_listeners_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers listeners delete" title: "networking loadbalancers listeners delete" slug: tcloud_networking_loadbalancers_listeners_delete url: /docs/tcloud/networking/loadbalancers_listeners_delete/ -weight: 9867 +weight: 9810 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_listeners_list/_index.md b/docs/tcloud/networking/loadbalancers_listeners_list/_index.md index 04952e7..94c59a2 100644 --- a/docs/tcloud/networking/loadbalancers_listeners_list/_index.md +++ b/docs/tcloud/networking/loadbalancers_listeners_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers listeners list" title: "networking loadbalancers listeners list" slug: tcloud_networking_loadbalancers_listeners_list url: /docs/tcloud/networking/loadbalancers_listeners_list/ -weight: 9866 +weight: 9809 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_listeners_update/_index.md b/docs/tcloud/networking/loadbalancers_listeners_update/_index.md index 82b8ca5..d635122 100644 --- a/docs/tcloud/networking/loadbalancers_listeners_update/_index.md +++ b/docs/tcloud/networking/loadbalancers_listeners_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers listeners update" title: "networking loadbalancers listeners update" slug: tcloud_networking_loadbalancers_listeners_update url: /docs/tcloud/networking/loadbalancers_listeners_update/ -weight: 9865 +weight: 9808 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_listeners_view/_index.md b/docs/tcloud/networking/loadbalancers_listeners_view/_index.md index d1beddf..fb2e91b 100644 --- a/docs/tcloud/networking/loadbalancers_listeners_view/_index.md +++ b/docs/tcloud/networking/loadbalancers_listeners_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers listeners view" title: "networking loadbalancers listeners view" slug: tcloud_networking_loadbalancers_listeners_view url: /docs/tcloud/networking/loadbalancers_listeners_view/ -weight: 9864 +weight: 9807 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_update/_index.md b/docs/tcloud/networking/loadbalancers_update/_index.md index 3bdeeb3..2f6490b 100644 --- a/docs/tcloud/networking/loadbalancers_update/_index.md +++ b/docs/tcloud/networking/loadbalancers_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers update" title: "networking loadbalancers update" slug: tcloud_networking_loadbalancers_update url: /docs/tcloud/networking/loadbalancers_update/ -weight: 9862 +weight: 9805 cascade: type: docs --- diff --git a/docs/tcloud/networking/loadbalancers_view/_index.md b/docs/tcloud/networking/loadbalancers_view/_index.md index 6785b80..df2061d 100644 --- a/docs/tcloud/networking/loadbalancers_view/_index.md +++ b/docs/tcloud/networking/loadbalancers_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking loadbalancers view" title: "networking loadbalancers view" slug: tcloud_networking_loadbalancers_view url: /docs/tcloud/networking/loadbalancers_view/ -weight: 9861 +weight: 9804 cascade: type: docs --- diff --git a/docs/tcloud/networking/natgateways/_index.md b/docs/tcloud/networking/natgateways/_index.md index f7f101d..c569416 100644 --- a/docs/tcloud/networking/natgateways/_index.md +++ b/docs/tcloud/networking/natgateways/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking natgateways" title: "networking natgateways" slug: tcloud_networking_natgateways url: /docs/tcloud/networking/natgateways/ -weight: 9856 +weight: 9797 cascade: type: docs --- @@ -46,7 +46,9 @@ tcloud networking natgateways view ngw-123 ### SEE ALSO * [tcloud networking](/docs/tcloud/tcloud_networking/) - Manage networking resources +* [tcloud networking natgateways create](/docs/tcloud/networking/natgateways_create/) - Create a NAT gateway * [tcloud networking natgateways delete](/docs/tcloud/networking/natgateways_delete/) - Delete NAT gateway(s) * [tcloud networking natgateways list](/docs/tcloud/networking/natgateways_list/) - Get a list of NAT gateways +* [tcloud networking natgateways update](/docs/tcloud/networking/natgateways_update/) - Update a NAT gateway * [tcloud networking natgateways view](/docs/tcloud/networking/natgateways_view/) - View NAT gateway details diff --git a/docs/tcloud/networking/natgateways_create/_index.md b/docs/tcloud/networking/natgateways_create/_index.md new file mode 100644 index 0000000..a22a343 --- /dev/null +++ b/docs/tcloud/networking/natgateways_create/_index.md @@ -0,0 +1,62 @@ +--- +linkTitle: "tcloud networking natgateways create" +title: "networking natgateways create" +slug: tcloud_networking_natgateways_create +url: /docs/tcloud/networking/natgateways_create/ +weight: 9802 +cascade: + type: docs +--- +## tcloud networking natgateways create + +Create a NAT gateway + +### Synopsis + +Create a new NAT gateway in the specified subnet. + +``` +tcloud networking natgateways create [flags] +``` + +### Examples + +``` +tcloud networking natgateways create --name egress --subnet subnet-123 +tcloud networking natgateways create --name egress --subnet subnet-123 --configure-default-route --wait +``` + +### Options + +``` + --annotations strings Annotations in key=value format + --configure-default-route Configure the default route for the subnet route table + --description string Description of the NAT gateway + -h, --help help for create + --labels strings Labels in key=value format + --name string Name of the NAT gateway + --reserved-ip string Reserved IP identity to attach + --security-groups strings Security group identities to attach + --subnet string Subnet identity, slug, or name + --wait Wait for the NAT gateway to have an endpoint + --wait-timeout duration Maximum time to wait for the NAT gateway endpoint (default 20m0s) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking natgateways](/docs/tcloud/networking/natgateways/) - Manage NAT gateways + diff --git a/docs/tcloud/networking/natgateways_delete/_index.md b/docs/tcloud/networking/natgateways_delete/_index.md index f98cb56..fe26480 100644 --- a/docs/tcloud/networking/natgateways_delete/_index.md +++ b/docs/tcloud/networking/natgateways_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking natgateways delete" title: "networking natgateways delete" slug: tcloud_networking_natgateways_delete url: /docs/tcloud/networking/natgateways_delete/ -weight: 9859 +weight: 9801 cascade: type: docs --- @@ -30,10 +30,11 @@ tcloud networking natgateways delete --selector environment=test --force ### Options ``` - --force Force the deletion and skip the confirmation - -h, --help help for delete - -l, --selector string Label selector to filter NAT gateways (format: key1=value1,key2=value2) - --wait Wait for the NAT gateway(s) to be deleted + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter NAT gateways (format: key1=value1,key2=value2) + --wait Wait for the NAT gateway(s) to be deleted + --wait-timeout duration Maximum time to wait for the NAT gateway(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/networking/natgateways_list/_index.md b/docs/tcloud/networking/natgateways_list/_index.md index 731af4d..8c490cf 100644 --- a/docs/tcloud/networking/natgateways_list/_index.md +++ b/docs/tcloud/networking/natgateways_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking natgateways list" title: "networking natgateways list" slug: tcloud_networking_natgateways_list url: /docs/tcloud/networking/natgateways_list/ -weight: 9858 +weight: 9800 cascade: type: docs --- diff --git a/docs/tcloud/networking/natgateways_update/_index.md b/docs/tcloud/networking/natgateways_update/_index.md new file mode 100644 index 0000000..898e21e --- /dev/null +++ b/docs/tcloud/networking/natgateways_update/_index.md @@ -0,0 +1,60 @@ +--- +linkTitle: "tcloud networking natgateways update" +title: "networking natgateways update" +slug: tcloud_networking_natgateways_update +url: /docs/tcloud/networking/natgateways_update/ +weight: 9799 +cascade: + type: docs +--- +## tcloud networking natgateways update + +Update a NAT gateway + +### Synopsis + +Update properties of an existing NAT gateway. Unspecified fields are preserved. + +``` +tcloud networking natgateways update [flags] +``` + +### Examples + +``` +tcloud networking natgateways update ngw-123 --name egress-prod +tcloud networking natgateways update ngw-123 --reserved-ip rip-456 +tcloud networking natgateways update ngw-123 --detach-reserved-ip +``` + +### Options + +``` + --annotations strings Annotations in key=value format + --description string Description of the NAT gateway + --detach-reserved-ip Detach the currently associated reserved IP + -h, --help help for update + --labels strings Labels in key=value format + --name string Name of the NAT gateway + --reserved-ip string Reserved IP identity to attach or replace + --security-groups strings Security group identities to attach +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking natgateways](/docs/tcloud/networking/natgateways/) - Manage NAT gateways + diff --git a/docs/tcloud/networking/natgateways_view/_index.md b/docs/tcloud/networking/natgateways_view/_index.md index 2fb8f82..7e0b8d3 100644 --- a/docs/tcloud/networking/natgateways_view/_index.md +++ b/docs/tcloud/networking/natgateways_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking natgateways view" title: "networking natgateways view" slug: tcloud_networking_natgateways_view url: /docs/tcloud/networking/natgateways_view/ -weight: 9857 +weight: 9798 cascade: type: docs --- diff --git a/docs/tcloud/networking/reserved-ips/_index.md b/docs/tcloud/networking/reserved-ips/_index.md new file mode 100644 index 0000000..b23e4df --- /dev/null +++ b/docs/tcloud/networking/reserved-ips/_index.md @@ -0,0 +1,56 @@ +--- +linkTitle: "tcloud networking reserved-ips" +title: "networking reserved-ips" +slug: tcloud_networking_reserved-ips +url: /docs/tcloud/networking/reserved-ips/ +weight: 9789 +cascade: + type: docs +--- +## tcloud networking reserved-ips + +Manage reserved IP addresses + +### Synopsis + +Manage reserved public IP addresses that can be associated with load balancers or NAT gateways. + +### Examples + +``` +tcloud networking reserved-ips list +tcloud networking reserved-ips create --name my-ip --region nl-ams +tcloud networking reserved-ips associate rip-123 --nat-gateway ngw-456 +``` + +### Options + +``` + -h, --help help for reserved-ips +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking](/docs/tcloud/tcloud_networking/) - Manage networking resources +* [tcloud networking reserved-ips associate](/docs/tcloud/networking/reserved-ips_associate/) - Associate a reserved IP with a resource +* [tcloud networking reserved-ips create](/docs/tcloud/networking/reserved-ips_create/) - Create a reserved IP address +* [tcloud networking reserved-ips delete](/docs/tcloud/networking/reserved-ips_delete/) - Delete reserved IP address(es) +* [tcloud networking reserved-ips disassociate](/docs/tcloud/networking/reserved-ips_disassociate/) - Disassociate a reserved IP from its resource +* [tcloud networking reserved-ips list](/docs/tcloud/networking/reserved-ips_list/) - List reserved IP addresses +* [tcloud networking reserved-ips update](/docs/tcloud/networking/reserved-ips_update/) - Update a reserved IP address +* [tcloud networking reserved-ips view](/docs/tcloud/networking/reserved-ips_view/) - View reserved IP details + diff --git a/docs/tcloud/networking/reserved-ips_associate/_index.md b/docs/tcloud/networking/reserved-ips_associate/_index.md new file mode 100644 index 0000000..e72d073 --- /dev/null +++ b/docs/tcloud/networking/reserved-ips_associate/_index.md @@ -0,0 +1,55 @@ +--- +linkTitle: "tcloud networking reserved-ips associate" +title: "networking reserved-ips associate" +slug: tcloud_networking_reserved-ips_associate +url: /docs/tcloud/networking/reserved-ips_associate/ +weight: 9796 +cascade: + type: docs +--- +## tcloud networking reserved-ips associate + +Associate a reserved IP with a resource + +### Synopsis + +Associate a reserved IP with exactly one of a load balancer or NAT gateway. + +``` +tcloud networking reserved-ips associate [flags] +``` + +### Examples + +``` +tcloud networking reserved-ips associate rip-123 --loadbalancer lb-456 +tcloud networking reserved-ips associate rip-123 --nat-gateway ngw-789 +``` + +### Options + +``` + -h, --help help for associate + --loadbalancer string Load balancer identity to associate with + --nat-gateway string NAT gateway identity to associate with + --no-header Do not print the header +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses + diff --git a/docs/tcloud/networking/reserved-ips_create/_index.md b/docs/tcloud/networking/reserved-ips_create/_index.md new file mode 100644 index 0000000..155d183 --- /dev/null +++ b/docs/tcloud/networking/reserved-ips_create/_index.md @@ -0,0 +1,58 @@ +--- +linkTitle: "tcloud networking reserved-ips create" +title: "networking reserved-ips create" +slug: tcloud_networking_reserved-ips_create +url: /docs/tcloud/networking/reserved-ips_create/ +weight: 9795 +cascade: + type: docs +--- +## tcloud networking reserved-ips create + +Create a reserved IP address + +### Synopsis + +Create a new reserved public IP address in the specified region. + +``` +tcloud networking reserved-ips create [flags] +``` + +### Examples + +``` +tcloud networking reserved-ips create --name my-ip --region nl-ams +tcloud networking reserved-ips create --name lb-ip --region nl-ams --description 'for production LB' +``` + +### Options + +``` + --annotations strings Annotations in key=value format + --description string Description of the reserved IP + -h, --help help for create + --labels strings Labels in key=value format + --name string Name of the reserved IP + --no-header Do not print the header + --region string Region for the reserved IP +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses + diff --git a/docs/tcloud/networking/reserved-ips_delete/_index.md b/docs/tcloud/networking/reserved-ips_delete/_index.md new file mode 100644 index 0000000..c07786b --- /dev/null +++ b/docs/tcloud/networking/reserved-ips_delete/_index.md @@ -0,0 +1,54 @@ +--- +linkTitle: "tcloud networking reserved-ips delete" +title: "networking reserved-ips delete" +slug: tcloud_networking_reserved-ips_delete +url: /docs/tcloud/networking/reserved-ips_delete/ +weight: 9794 +cascade: + type: docs +--- +## tcloud networking reserved-ips delete + +Delete reserved IP address(es) + +### Synopsis + +Delete reserved IP address(es) by identity or label selector. Attached reserved IPs are disassociated before deletion. + +``` +tcloud networking reserved-ips delete [flags] +``` + +### Examples + +``` +tcloud networking reserved-ips delete rip-123 --force +tcloud networking reserved-ips delete --selector env=test --force +``` + +### Options + +``` + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter reserved IPs (format: key1=value1,key2=value2) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses + diff --git a/docs/tcloud/networking/reserved-ips_disassociate/_index.md b/docs/tcloud/networking/reserved-ips_disassociate/_index.md new file mode 100644 index 0000000..dc717d7 --- /dev/null +++ b/docs/tcloud/networking/reserved-ips_disassociate/_index.md @@ -0,0 +1,52 @@ +--- +linkTitle: "tcloud networking reserved-ips disassociate" +title: "networking reserved-ips disassociate" +slug: tcloud_networking_reserved-ips_disassociate +url: /docs/tcloud/networking/reserved-ips_disassociate/ +weight: 9793 +cascade: + type: docs +--- +## tcloud networking reserved-ips disassociate + +Disassociate a reserved IP from its resource + +### Synopsis + +Detach a reserved IP from its currently associated load balancer or NAT gateway. + +``` +tcloud networking reserved-ips disassociate [flags] +``` + +### Examples + +``` +tcloud networking reserved-ips disassociate rip-123 +``` + +### Options + +``` + -h, --help help for disassociate + --no-header Do not print the header +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses + diff --git a/docs/tcloud/networking/reserved-ips_list/_index.md b/docs/tcloud/networking/reserved-ips_list/_index.md new file mode 100644 index 0000000..59c1b12 --- /dev/null +++ b/docs/tcloud/networking/reserved-ips_list/_index.md @@ -0,0 +1,58 @@ +--- +linkTitle: "tcloud networking reserved-ips list" +title: "networking reserved-ips list" +slug: tcloud_networking_reserved-ips_list +url: /docs/tcloud/networking/reserved-ips_list/ +weight: 9792 +cascade: + type: docs +--- +## tcloud networking reserved-ips list + +List reserved IP addresses + +### Synopsis + +List reserved IP addresses within your organisation. + +``` +tcloud networking reserved-ips list [flags] +``` + +### Examples + +``` +tcloud networking reserved-ips list +tcloud networking reserved-ips list --region nl-ams +tcloud networking reserved-ips list --selector env=prod +``` + +### Options + +``` + --exact-time Show exact creation time + -h, --help help for list + --no-header Do not print the header + --region string Filter by region + -l, --selector string Label selector to filter reserved IPs (format: key1=value1,key2=value2) + --show-labels Show labels +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses + diff --git a/docs/tcloud/networking/reserved-ips_update/_index.md b/docs/tcloud/networking/reserved-ips_update/_index.md new file mode 100644 index 0000000..4c3b466 --- /dev/null +++ b/docs/tcloud/networking/reserved-ips_update/_index.md @@ -0,0 +1,57 @@ +--- +linkTitle: "tcloud networking reserved-ips update" +title: "networking reserved-ips update" +slug: tcloud_networking_reserved-ips_update +url: /docs/tcloud/networking/reserved-ips_update/ +weight: 9791 +cascade: + type: docs +--- +## tcloud networking reserved-ips update + +Update a reserved IP address + +### Synopsis + +Update metadata of an existing reserved IP address. Unspecified fields are preserved from the current resource. + +``` +tcloud networking reserved-ips update [flags] +``` + +### Examples + +``` +tcloud networking reserved-ips update rip-123 --name new-name +tcloud networking reserved-ips update rip-123 --description 'updated' +``` + +### Options + +``` + --annotations strings Annotations in key=value format + --description string Description of the reserved IP + -h, --help help for update + --labels strings Labels in key=value format + --name string Name of the reserved IP + --no-header Do not print the header +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses + diff --git a/docs/tcloud/networking/reserved-ips_view/_index.md b/docs/tcloud/networking/reserved-ips_view/_index.md new file mode 100644 index 0000000..10da67a --- /dev/null +++ b/docs/tcloud/networking/reserved-ips_view/_index.md @@ -0,0 +1,53 @@ +--- +linkTitle: "tcloud networking reserved-ips view" +title: "networking reserved-ips view" +slug: tcloud_networking_reserved-ips_view +url: /docs/tcloud/networking/reserved-ips_view/ +weight: 9790 +cascade: + type: docs +--- +## tcloud networking reserved-ips view + +View reserved IP details + +### Synopsis + +View detailed information about a specific reserved IP address. + +``` +tcloud networking reserved-ips view [flags] +``` + +### Examples + +``` +tcloud networking reserved-ips view rip-123 +tcloud networking reserved-ips view rip-123 --output yaml +``` + +### Options + +``` + -h, --help help for view + -o, --output string Output format (yaml) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking reserved-ips](/docs/tcloud/networking/reserved-ips/) - Manage reserved IP addresses + diff --git a/docs/tcloud/networking/routetables/_index.md b/docs/tcloud/networking/routetables/_index.md index 7271393..f3af259 100644 --- a/docs/tcloud/networking/routetables/_index.md +++ b/docs/tcloud/networking/routetables/_index.md @@ -3,13 +3,25 @@ linkTitle: "tcloud networking routetables" title: "networking routetables" slug: tcloud_networking_routetables url: /docs/tcloud/networking/routetables/ -weight: 9854 +weight: 9776 cascade: type: docs --- ## tcloud networking routetables -Manage routetables +Manage route tables + +### Synopsis + +Manage VPC route tables and their routes within the Thalassa Cloud Platform. + +### Examples + +``` +tcloud networking routetables list +tcloud networking routetables create --name custom --vpc vpc-123 +tcloud networking routetables routes list rt-123 +``` ### Options @@ -34,5 +46,10 @@ Manage routetables ### SEE ALSO * [tcloud networking](/docs/tcloud/tcloud_networking/) - Manage networking resources +* [tcloud networking routetables create](/docs/tcloud/networking/routetables_create/) - Create a route table +* [tcloud networking routetables delete](/docs/tcloud/networking/routetables_delete/) - Delete route table(s) * [tcloud networking routetables list](/docs/tcloud/networking/routetables_list/) - Get a list of routetables +* [tcloud networking routetables routes](/docs/tcloud/networking/routetables_routes/) - Manage routes in a route table +* [tcloud networking routetables update](/docs/tcloud/networking/routetables_update/) - Update a route table +* [tcloud networking routetables view](/docs/tcloud/networking/routetables_view/) - View a route table and its routes diff --git a/docs/tcloud/networking/routetables_create/_index.md b/docs/tcloud/networking/routetables_create/_index.md new file mode 100644 index 0000000..e331481 --- /dev/null +++ b/docs/tcloud/networking/routetables_create/_index.md @@ -0,0 +1,52 @@ +--- +linkTitle: "tcloud networking routetables create" +title: "networking routetables create" +slug: tcloud_networking_routetables_create +url: /docs/tcloud/networking/routetables_create/ +weight: 9788 +cascade: + type: docs +--- +## tcloud networking routetables create + +Create a route table + +``` +tcloud networking routetables create [flags] +``` + +### Examples + +``` +tcloud networking routetables create --name private --vpc vpc-123 +``` + +### Options + +``` + --annotations strings Annotations as key=value (repeatable) + --description string Description + -h, --help help for create + --labels strings Labels as key=value (repeatable) + --name string Name of the route table + --vpc string VPC identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage route tables + diff --git a/docs/tcloud/networking/routetables_delete/_index.md b/docs/tcloud/networking/routetables_delete/_index.md new file mode 100644 index 0000000..3f26679 --- /dev/null +++ b/docs/tcloud/networking/routetables_delete/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud networking routetables delete" +title: "networking routetables delete" +slug: tcloud_networking_routetables_delete +url: /docs/tcloud/networking/routetables_delete/ +weight: 9787 +cascade: + type: docs +--- +## tcloud networking routetables delete + +Delete route table(s) + +``` +tcloud networking routetables delete ROUTE_TABLE [ROUTE_TABLE...] [flags] +``` + +### Options + +``` + --force Skip confirmation + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage route tables + diff --git a/docs/tcloud/networking/routetables_list/_index.md b/docs/tcloud/networking/routetables_list/_index.md index 74784be..5f5e0de 100644 --- a/docs/tcloud/networking/routetables_list/_index.md +++ b/docs/tcloud/networking/routetables_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking routetables list" title: "networking routetables list" slug: tcloud_networking_routetables_list url: /docs/tcloud/networking/routetables_list/ -weight: 9855 +weight: 9786 cascade: type: docs --- @@ -40,5 +40,5 @@ tcloud networking routetables list [flags] ### SEE ALSO -* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage routetables +* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage route tables diff --git a/docs/tcloud/networking/routetables_routes/_index.md b/docs/tcloud/networking/routetables_routes/_index.md new file mode 100644 index 0000000..9caea7c --- /dev/null +++ b/docs/tcloud/networking/routetables_routes/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud networking routetables routes" +title: "networking routetables routes" +slug: tcloud_networking_routetables_routes +url: /docs/tcloud/networking/routetables_routes/ +weight: 9779 +cascade: + type: docs +--- +## tcloud networking routetables routes + +Manage routes in a route table + +### Options + +``` + -h, --help help for routes +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage route tables +* [tcloud networking routetables routes create](/docs/tcloud/networking/routetables_routes_create/) - Create a route in a route table +* [tcloud networking routetables routes delete](/docs/tcloud/networking/routetables_routes_delete/) - Delete a route from a route table +* [tcloud networking routetables routes list](/docs/tcloud/networking/routetables_routes_list/) - List routes in a route table +* [tcloud networking routetables routes set](/docs/tcloud/networking/routetables_routes_set/) - Replace all routes in a route table from a JSON file +* [tcloud networking routetables routes update](/docs/tcloud/networking/routetables_routes_update/) - Update a route in a route table +* [tcloud networking routetables routes view](/docs/tcloud/networking/routetables_routes_view/) - View a route in a route table + diff --git a/docs/tcloud/networking/routetables_routes_create/_index.md b/docs/tcloud/networking/routetables_routes_create/_index.md new file mode 100644 index 0000000..e438706 --- /dev/null +++ b/docs/tcloud/networking/routetables_routes_create/_index.md @@ -0,0 +1,47 @@ +--- +linkTitle: "tcloud networking routetables routes create" +title: "networking routetables routes create" +slug: tcloud_networking_routetables_routes_create +url: /docs/tcloud/networking/routetables_routes_create/ +weight: 9785 +cascade: + type: docs +--- +## tcloud networking routetables routes create + +Create a route in a route table + +``` +tcloud networking routetables routes create ROUTE_TABLE [flags] +``` + +### Options + +``` + --destination string Destination CIDR block + --gateway string Target gateway identity + --gateway-address string Gateway address + -h, --help help for create + --nat-gateway string Target NAT gateway identity + --no-header Do not print the header + --vpc-peering string Target VPC peering connection identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables routes](/docs/tcloud/networking/routetables_routes/) - Manage routes in a route table + diff --git a/docs/tcloud/networking/routetables_routes_delete/_index.md b/docs/tcloud/networking/routetables_routes_delete/_index.md new file mode 100644 index 0000000..d14ee1c --- /dev/null +++ b/docs/tcloud/networking/routetables_routes_delete/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud networking routetables routes delete" +title: "networking routetables routes delete" +slug: tcloud_networking_routetables_routes_delete +url: /docs/tcloud/networking/routetables_routes_delete/ +weight: 9784 +cascade: + type: docs +--- +## tcloud networking routetables routes delete + +Delete a route from a route table + +``` +tcloud networking routetables routes delete ROUTE_TABLE ROUTE [flags] +``` + +### Options + +``` + --force Force the deletion and skip the confirmation + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables routes](/docs/tcloud/networking/routetables_routes/) - Manage routes in a route table + diff --git a/docs/tcloud/networking/routetables_routes_list/_index.md b/docs/tcloud/networking/routetables_routes_list/_index.md new file mode 100644 index 0000000..cf808ef --- /dev/null +++ b/docs/tcloud/networking/routetables_routes_list/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud networking routetables routes list" +title: "networking routetables routes list" +slug: tcloud_networking_routetables_routes_list +url: /docs/tcloud/networking/routetables_routes_list/ +weight: 9783 +cascade: + type: docs +--- +## tcloud networking routetables routes list + +List routes in a route table + +``` +tcloud networking routetables routes list ROUTE_TABLE [flags] +``` + +### Options + +``` + -h, --help help for list + --no-header Do not print the header +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables routes](/docs/tcloud/networking/routetables_routes/) - Manage routes in a route table + diff --git a/docs/tcloud/networking/routetables_routes_set/_index.md b/docs/tcloud/networking/routetables_routes_set/_index.md new file mode 100644 index 0000000..45df715 --- /dev/null +++ b/docs/tcloud/networking/routetables_routes_set/_index.md @@ -0,0 +1,53 @@ +--- +linkTitle: "tcloud networking routetables routes set" +title: "networking routetables routes set" +slug: tcloud_networking_routetables_routes_set +url: /docs/tcloud/networking/routetables_routes_set/ +weight: 9782 +cascade: + type: docs +--- +## tcloud networking routetables routes set + +Replace all routes in a route table from a JSON file + +### Synopsis + +Batch-update routes for a route table. The file must contain a JSON array of UpdateRouteTableRoute objects. + +``` +tcloud networking routetables routes set ROUTE_TABLE [flags] +``` + +### Examples + +``` +tcloud networking routetables routes set rt-123 --file routes.json +``` + +### Options + +``` + --file string JSON file containing an array of routes + -h, --help help for set + --no-header Do not print the header +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables routes](/docs/tcloud/networking/routetables_routes/) - Manage routes in a route table + diff --git a/docs/tcloud/networking/routetables_routes_update/_index.md b/docs/tcloud/networking/routetables_routes_update/_index.md new file mode 100644 index 0000000..c71673c --- /dev/null +++ b/docs/tcloud/networking/routetables_routes_update/_index.md @@ -0,0 +1,47 @@ +--- +linkTitle: "tcloud networking routetables routes update" +title: "networking routetables routes update" +slug: tcloud_networking_routetables_routes_update +url: /docs/tcloud/networking/routetables_routes_update/ +weight: 9781 +cascade: + type: docs +--- +## tcloud networking routetables routes update + +Update a route in a route table + +``` +tcloud networking routetables routes update ROUTE_TABLE ROUTE [flags] +``` + +### Options + +``` + --destination string Destination CIDR block + --gateway string Target gateway identity + --gateway-address string Gateway address + -h, --help help for update + --nat-gateway string Target NAT gateway identity + --no-header Do not print the header + --vpc-peering string Target VPC peering connection identity +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables routes](/docs/tcloud/networking/routetables_routes/) - Manage routes in a route table + diff --git a/docs/tcloud/networking/routetables_routes_view/_index.md b/docs/tcloud/networking/routetables_routes_view/_index.md new file mode 100644 index 0000000..f2f8e0f --- /dev/null +++ b/docs/tcloud/networking/routetables_routes_view/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud networking routetables routes view" +title: "networking routetables routes view" +slug: tcloud_networking_routetables_routes_view +url: /docs/tcloud/networking/routetables_routes_view/ +weight: 9780 +cascade: + type: docs +--- +## tcloud networking routetables routes view + +View a route in a route table + +``` +tcloud networking routetables routes view ROUTE_TABLE ROUTE [flags] +``` + +### Options + +``` + -h, --help help for view + -o, --output string Output format (yaml) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables routes](/docs/tcloud/networking/routetables_routes/) - Manage routes in a route table + diff --git a/docs/tcloud/networking/routetables_update/_index.md b/docs/tcloud/networking/routetables_update/_index.md new file mode 100644 index 0000000..5ff8e9f --- /dev/null +++ b/docs/tcloud/networking/routetables_update/_index.md @@ -0,0 +1,45 @@ +--- +linkTitle: "tcloud networking routetables update" +title: "networking routetables update" +slug: tcloud_networking_routetables_update +url: /docs/tcloud/networking/routetables_update/ +weight: 9778 +cascade: + type: docs +--- +## tcloud networking routetables update + +Update a route table + +``` +tcloud networking routetables update ROUTE_TABLE [flags] +``` + +### Options + +``` + --annotations strings Annotations as key=value (repeatable) + --description string Description + -h, --help help for update + --labels strings Labels as key=value (repeatable) + --name string Name +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage route tables + diff --git a/docs/tcloud/networking/routetables_view/_index.md b/docs/tcloud/networking/routetables_view/_index.md new file mode 100644 index 0000000..fc95386 --- /dev/null +++ b/docs/tcloud/networking/routetables_view/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud networking routetables view" +title: "networking routetables view" +slug: tcloud_networking_routetables_view +url: /docs/tcloud/networking/routetables_view/ +weight: 9777 +cascade: + type: docs +--- +## tcloud networking routetables view + +View a route table and its routes + +``` +tcloud networking routetables view ROUTE_TABLE [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for view + --no-header Do not print the header +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking routetables](/docs/tcloud/networking/routetables/) - Manage route tables + diff --git a/docs/tcloud/networking/security-groups/_index.md b/docs/tcloud/networking/security-groups/_index.md index 85719db..6f9a783 100644 --- a/docs/tcloud/networking/security-groups/_index.md +++ b/docs/tcloud/networking/security-groups/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking security-groups" title: "networking security-groups" slug: tcloud_networking_security-groups url: /docs/tcloud/networking/security-groups/ -weight: 9849 +weight: 9767 cascade: type: docs --- @@ -49,5 +49,7 @@ tcloud networking security-groups delete sg-456 * [tcloud networking security-groups create](/docs/tcloud/networking/security-groups_create/) - Create a security group * [tcloud networking security-groups delete](/docs/tcloud/networking/security-groups_delete/) - Delete security group(s) * [tcloud networking security-groups list](/docs/tcloud/networking/security-groups_list/) - Get a list of security groups +* [tcloud networking security-groups rules](/docs/tcloud/networking/security-groups_rules/) - Manage security group rules +* [tcloud networking security-groups update](/docs/tcloud/networking/security-groups_update/) - Update security group metadata * [tcloud networking security-groups view](/docs/tcloud/networking/security-groups_view/) - View security group details diff --git a/docs/tcloud/networking/security-groups_create/_index.md b/docs/tcloud/networking/security-groups_create/_index.md index 6eebc5e..134ab6f 100644 --- a/docs/tcloud/networking/security-groups_create/_index.md +++ b/docs/tcloud/networking/security-groups_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking security-groups create" title: "networking security-groups create" slug: tcloud_networking_security-groups_create url: /docs/tcloud/networking/security-groups_create/ -weight: 9853 +weight: 9775 cascade: type: docs --- diff --git a/docs/tcloud/networking/security-groups_delete/_index.md b/docs/tcloud/networking/security-groups_delete/_index.md index e6b6909..21ba87e 100644 --- a/docs/tcloud/networking/security-groups_delete/_index.md +++ b/docs/tcloud/networking/security-groups_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking security-groups delete" title: "networking security-groups delete" slug: tcloud_networking_security-groups_delete url: /docs/tcloud/networking/security-groups_delete/ -weight: 9852 +weight: 9774 cascade: type: docs --- diff --git a/docs/tcloud/networking/security-groups_list/_index.md b/docs/tcloud/networking/security-groups_list/_index.md index c948fd7..fb61827 100644 --- a/docs/tcloud/networking/security-groups_list/_index.md +++ b/docs/tcloud/networking/security-groups_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking security-groups list" title: "networking security-groups list" slug: tcloud_networking_security-groups_list url: /docs/tcloud/networking/security-groups_list/ -weight: 9851 +weight: 9773 cascade: type: docs --- diff --git a/docs/tcloud/networking/security-groups_rules/_index.md b/docs/tcloud/networking/security-groups_rules/_index.md new file mode 100644 index 0000000..6957d77 --- /dev/null +++ b/docs/tcloud/networking/security-groups_rules/_index.md @@ -0,0 +1,39 @@ +--- +linkTitle: "tcloud networking security-groups rules" +title: "networking security-groups rules" +slug: tcloud_networking_security-groups_rules +url: /docs/tcloud/networking/security-groups_rules/ +weight: 9770 +cascade: + type: docs +--- +## tcloud networking security-groups rules + +Manage security group rules + +### Options + +``` + -h, --help help for rules +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking security-groups](/docs/tcloud/networking/security-groups/) - Manage security groups +* [tcloud networking security-groups rules set-egress](/docs/tcloud/networking/security-groups_rules_set-egress/) - Replace all egress rules from a JSON file +* [tcloud networking security-groups rules set-ingress](/docs/tcloud/networking/security-groups_rules_set-ingress/) - Replace all ingress rules from a JSON file + diff --git a/docs/tcloud/networking/security-groups_rules_set-egress/_index.md b/docs/tcloud/networking/security-groups_rules_set-egress/_index.md new file mode 100644 index 0000000..aa3cd54 --- /dev/null +++ b/docs/tcloud/networking/security-groups_rules_set-egress/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud networking security-groups rules set-egress" +title: "networking security-groups rules set-egress" +slug: tcloud_networking_security-groups_rules_set-egress +url: /docs/tcloud/networking/security-groups_rules_set-egress/ +weight: 9772 +cascade: + type: docs +--- +## tcloud networking security-groups rules set-egress + +Replace all egress rules from a JSON file + +``` +tcloud networking security-groups rules set-egress SECURITY_GROUP [flags] +``` + +### Options + +``` + --file string JSON array of SecurityGroupRule objects + -h, --help help for set-egress +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking security-groups rules](/docs/tcloud/networking/security-groups_rules/) - Manage security group rules + diff --git a/docs/tcloud/networking/security-groups_rules_set-ingress/_index.md b/docs/tcloud/networking/security-groups_rules_set-ingress/_index.md new file mode 100644 index 0000000..d824eda --- /dev/null +++ b/docs/tcloud/networking/security-groups_rules_set-ingress/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud networking security-groups rules set-ingress" +title: "networking security-groups rules set-ingress" +slug: tcloud_networking_security-groups_rules_set-ingress +url: /docs/tcloud/networking/security-groups_rules_set-ingress/ +weight: 9771 +cascade: + type: docs +--- +## tcloud networking security-groups rules set-ingress + +Replace all ingress rules from a JSON file + +``` +tcloud networking security-groups rules set-ingress SECURITY_GROUP [flags] +``` + +### Options + +``` + --file string JSON array of SecurityGroupRule objects + -h, --help help for set-ingress +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking security-groups rules](/docs/tcloud/networking/security-groups_rules/) - Manage security group rules + diff --git a/docs/tcloud/networking/security-groups_update/_index.md b/docs/tcloud/networking/security-groups_update/_index.md new file mode 100644 index 0000000..83d5501 --- /dev/null +++ b/docs/tcloud/networking/security-groups_update/_index.md @@ -0,0 +1,50 @@ +--- +linkTitle: "tcloud networking security-groups update" +title: "networking security-groups update" +slug: tcloud_networking_security-groups_update +url: /docs/tcloud/networking/security-groups_update/ +weight: 9769 +cascade: + type: docs +--- +## tcloud networking security-groups update + +Update security group metadata + +### Synopsis + +Update name, description, labels, annotations, or allow-same-group. Rules are left unchanged. + +``` +tcloud networking security-groups update SECURITY_GROUP [flags] +``` + +### Options + +``` + --allow-same-group Allow traffic between instances in the same security group + --annotations strings Annotations as key=value (repeatable) + --description string Description + -h, --help help for update + --labels strings Labels as key=value (repeatable) + --name string Name +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud networking security-groups](/docs/tcloud/networking/security-groups/) - Manage security groups + diff --git a/docs/tcloud/networking/security-groups_view/_index.md b/docs/tcloud/networking/security-groups_view/_index.md index 32bc54d..cabac19 100644 --- a/docs/tcloud/networking/security-groups_view/_index.md +++ b/docs/tcloud/networking/security-groups_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking security-groups view" title: "networking security-groups view" slug: tcloud_networking_security-groups_view url: /docs/tcloud/networking/security-groups_view/ -weight: 9850 +weight: 9768 cascade: type: docs --- diff --git a/docs/tcloud/networking/subnets/_index.md b/docs/tcloud/networking/subnets/_index.md index 19ec139..8a8515a 100644 --- a/docs/tcloud/networking/subnets/_index.md +++ b/docs/tcloud/networking/subnets/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking subnets" title: "networking subnets" slug: tcloud_networking_subnets url: /docs/tcloud/networking/subnets/ -weight: 9845 +weight: 9763 cascade: type: docs --- diff --git a/docs/tcloud/networking/subnets_create/_index.md b/docs/tcloud/networking/subnets_create/_index.md index daa4d1a..924dc26 100644 --- a/docs/tcloud/networking/subnets_create/_index.md +++ b/docs/tcloud/networking/subnets_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking subnets create" title: "networking subnets create" slug: tcloud_networking_subnets_create url: /docs/tcloud/networking/subnets_create/ -weight: 9848 +weight: 9766 cascade: type: docs --- @@ -18,13 +18,15 @@ tcloud networking subnets create [flags] ### Options ``` - --cidr string CIDR of the subnet - --description string Description of the subnet - -h, --help help for create - --name string Name of the subnet - --no-header Do not print the header - --vpc string VPC of the subnet - --wait Wait for the subnet to be ready before returning + --cidr string CIDR of the subnet + --description string Description of the subnet + -h, --help help for create + --labels strings Labels in key=value format (can be specified multiple times) + --name string Name of the subnet + --no-header Do not print the header + --vpc string VPC of the subnet + --wait Wait for the subnet to be ready before returning + --wait-timeout duration Maximum time to wait for the subnet to be ready (default 10m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/networking/subnets_delete/_index.md b/docs/tcloud/networking/subnets_delete/_index.md index 7a57a81..4b9df9e 100644 --- a/docs/tcloud/networking/subnets_delete/_index.md +++ b/docs/tcloud/networking/subnets_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking subnets delete" title: "networking subnets delete" slug: tcloud_networking_subnets_delete url: /docs/tcloud/networking/subnets_delete/ -weight: 9847 +weight: 9765 cascade: type: docs --- @@ -30,10 +30,11 @@ tcloud networking subnets delete --selector environment=test --force ### Options ``` - --force Force the deletion and skip the confirmation - -h, --help help for delete - -l, --selector string Label selector to filter subnets (format: key1=value1,key2=value2) - --wait Wait for the subnet(s) to be deleted + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter subnets (format: key1=value1,key2=value2) + --wait Wait for the subnet(s) to be deleted + --wait-timeout duration Maximum time to wait for the subnet(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/networking/subnets_list/_index.md b/docs/tcloud/networking/subnets_list/_index.md index 1d0adcc..05138e1 100644 --- a/docs/tcloud/networking/subnets_list/_index.md +++ b/docs/tcloud/networking/subnets_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking subnets list" title: "networking subnets list" slug: tcloud_networking_subnets_list url: /docs/tcloud/networking/subnets_list/ -weight: 9846 +weight: 9764 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups/_index.md b/docs/tcloud/networking/target-groups/_index.md index 3bcfab4..fda9065 100644 --- a/docs/tcloud/networking/target-groups/_index.md +++ b/docs/tcloud/networking/target-groups/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups" title: "networking target-groups" slug: tcloud_networking_target-groups url: /docs/tcloud/networking/target-groups/ -weight: 9836 +weight: 9754 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_attach/_index.md b/docs/tcloud/networking/target-groups_attach/_index.md index d74b152..97b5eb5 100644 --- a/docs/tcloud/networking/target-groups_attach/_index.md +++ b/docs/tcloud/networking/target-groups_attach/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups attach" title: "networking target-groups attach" slug: tcloud_networking_target-groups_attach url: /docs/tcloud/networking/target-groups_attach/ -weight: 9844 +weight: 9762 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_create/_index.md b/docs/tcloud/networking/target-groups_create/_index.md index 4eb2e86..88b6519 100644 --- a/docs/tcloud/networking/target-groups_create/_index.md +++ b/docs/tcloud/networking/target-groups_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups create" title: "networking target-groups create" slug: tcloud_networking_target-groups_create url: /docs/tcloud/networking/target-groups_create/ -weight: 9843 +weight: 9761 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_delete/_index.md b/docs/tcloud/networking/target-groups_delete/_index.md index eb32fda..29e3922 100644 --- a/docs/tcloud/networking/target-groups_delete/_index.md +++ b/docs/tcloud/networking/target-groups_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups delete" title: "networking target-groups delete" slug: tcloud_networking_target-groups_delete url: /docs/tcloud/networking/target-groups_delete/ -weight: 9842 +weight: 9760 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_detach/_index.md b/docs/tcloud/networking/target-groups_detach/_index.md index 523fe53..b56cc72 100644 --- a/docs/tcloud/networking/target-groups_detach/_index.md +++ b/docs/tcloud/networking/target-groups_detach/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups detach" title: "networking target-groups detach" slug: tcloud_networking_target-groups_detach url: /docs/tcloud/networking/target-groups_detach/ -weight: 9841 +weight: 9759 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_list/_index.md b/docs/tcloud/networking/target-groups_list/_index.md index d605552..22d02a8 100644 --- a/docs/tcloud/networking/target-groups_list/_index.md +++ b/docs/tcloud/networking/target-groups_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups list" title: "networking target-groups list" slug: tcloud_networking_target-groups_list url: /docs/tcloud/networking/target-groups_list/ -weight: 9840 +weight: 9758 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_set-attachments/_index.md b/docs/tcloud/networking/target-groups_set-attachments/_index.md index ee04510..8cf024c 100644 --- a/docs/tcloud/networking/target-groups_set-attachments/_index.md +++ b/docs/tcloud/networking/target-groups_set-attachments/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups set-attachments" title: "networking target-groups set-attachments" slug: tcloud_networking_target-groups_set-attachments url: /docs/tcloud/networking/target-groups_set-attachments/ -weight: 9839 +weight: 9757 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_update/_index.md b/docs/tcloud/networking/target-groups_update/_index.md index a226251..d653d1b 100644 --- a/docs/tcloud/networking/target-groups_update/_index.md +++ b/docs/tcloud/networking/target-groups_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups update" title: "networking target-groups update" slug: tcloud_networking_target-groups_update url: /docs/tcloud/networking/target-groups_update/ -weight: 9838 +weight: 9756 cascade: type: docs --- diff --git a/docs/tcloud/networking/target-groups_view/_index.md b/docs/tcloud/networking/target-groups_view/_index.md index 3f2bbc5..b0455a3 100644 --- a/docs/tcloud/networking/target-groups_view/_index.md +++ b/docs/tcloud/networking/target-groups_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking target-groups view" title: "networking target-groups view" slug: tcloud_networking_target-groups_view url: /docs/tcloud/networking/target-groups_view/ -weight: 9837 +weight: 9755 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpc-peering/_index.md b/docs/tcloud/networking/vpc-peering/_index.md index c7ffe80..59fc14f 100644 --- a/docs/tcloud/networking/vpc-peering/_index.md +++ b/docs/tcloud/networking/vpc-peering/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpc-peering" title: "networking vpc-peering" slug: tcloud_networking_vpc-peering url: /docs/tcloud/networking/vpc-peering/ -weight: 9829 +weight: 9747 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpc-peering_accept/_index.md b/docs/tcloud/networking/vpc-peering_accept/_index.md index 5661261..fe31f76 100644 --- a/docs/tcloud/networking/vpc-peering_accept/_index.md +++ b/docs/tcloud/networking/vpc-peering_accept/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpc-peering accept" title: "networking vpc-peering accept" slug: tcloud_networking_vpc-peering_accept url: /docs/tcloud/networking/vpc-peering_accept/ -weight: 9835 +weight: 9753 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpc-peering_create/_index.md b/docs/tcloud/networking/vpc-peering_create/_index.md index 0836c91..efdce38 100644 --- a/docs/tcloud/networking/vpc-peering_create/_index.md +++ b/docs/tcloud/networking/vpc-peering_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpc-peering create" title: "networking vpc-peering create" slug: tcloud_networking_vpc-peering_create url: /docs/tcloud/networking/vpc-peering_create/ -weight: 9834 +weight: 9752 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpc-peering_delete/_index.md b/docs/tcloud/networking/vpc-peering_delete/_index.md index ab90202..01ec90c 100644 --- a/docs/tcloud/networking/vpc-peering_delete/_index.md +++ b/docs/tcloud/networking/vpc-peering_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpc-peering delete" title: "networking vpc-peering delete" slug: tcloud_networking_vpc-peering_delete url: /docs/tcloud/networking/vpc-peering_delete/ -weight: 9833 +weight: 9751 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpc-peering_list/_index.md b/docs/tcloud/networking/vpc-peering_list/_index.md index b5545e0..98024e5 100644 --- a/docs/tcloud/networking/vpc-peering_list/_index.md +++ b/docs/tcloud/networking/vpc-peering_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpc-peering list" title: "networking vpc-peering list" slug: tcloud_networking_vpc-peering_list url: /docs/tcloud/networking/vpc-peering_list/ -weight: 9832 +weight: 9750 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpc-peering_reject/_index.md b/docs/tcloud/networking/vpc-peering_reject/_index.md index 2a9a1cd..cfb7359 100644 --- a/docs/tcloud/networking/vpc-peering_reject/_index.md +++ b/docs/tcloud/networking/vpc-peering_reject/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpc-peering reject" title: "networking vpc-peering reject" slug: tcloud_networking_vpc-peering_reject url: /docs/tcloud/networking/vpc-peering_reject/ -weight: 9831 +weight: 9749 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpc-peering_update/_index.md b/docs/tcloud/networking/vpc-peering_update/_index.md index a04eb49..555389a 100644 --- a/docs/tcloud/networking/vpc-peering_update/_index.md +++ b/docs/tcloud/networking/vpc-peering_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpc-peering update" title: "networking vpc-peering update" slug: tcloud_networking_vpc-peering_update url: /docs/tcloud/networking/vpc-peering_update/ -weight: 9830 +weight: 9748 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpcs/_index.md b/docs/tcloud/networking/vpcs/_index.md index dabea81..84cdb41 100644 --- a/docs/tcloud/networking/vpcs/_index.md +++ b/docs/tcloud/networking/vpcs/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpcs" title: "networking vpcs" slug: tcloud_networking_vpcs url: /docs/tcloud/networking/vpcs/ -weight: 9825 +weight: 9743 cascade: type: docs --- diff --git a/docs/tcloud/networking/vpcs_create/_index.md b/docs/tcloud/networking/vpcs_create/_index.md index 3e11200..84394cc 100644 --- a/docs/tcloud/networking/vpcs_create/_index.md +++ b/docs/tcloud/networking/vpcs_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpcs create" title: "networking vpcs create" slug: tcloud_networking_vpcs_create url: /docs/tcloud/networking/vpcs_create/ -weight: 9828 +weight: 9746 cascade: type: docs --- @@ -18,13 +18,15 @@ tcloud networking vpcs create [flags] ### Options ``` - --cidrs strings CIDRs of the vpc (default [10.0.0.0/16]) - --description string Description of the vpc - -h, --help help for create - --name string Name of the vpc - --no-header Do not print the header - --region string Region of the vpc - --wait Wait for the VPC to be ready before returning + --cidrs strings CIDRs of the vpc (default [10.0.0.0/16]) + --description string Description of the vpc + -h, --help help for create + --labels strings Labels in key=value format (can be specified multiple times) + --name string Name of the vpc + --no-header Do not print the header + --region string Region of the vpc + --wait Wait for the VPC to be ready before returning + --wait-timeout duration Maximum time to wait for the VPC to be ready (default 10m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/networking/vpcs_delete/_index.md b/docs/tcloud/networking/vpcs_delete/_index.md index 120a4b3..4385d5a 100644 --- a/docs/tcloud/networking/vpcs_delete/_index.md +++ b/docs/tcloud/networking/vpcs_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpcs delete" title: "networking vpcs delete" slug: tcloud_networking_vpcs_delete url: /docs/tcloud/networking/vpcs_delete/ -weight: 9827 +weight: 9745 cascade: type: docs --- @@ -30,10 +30,11 @@ tcloud networking vpcs delete --selector environment=test --force ### Options ``` - --force Force the deletion and skip the confirmation - -h, --help help for delete - -l, --selector string Label selector to filter VPCs (format: key1=value1,key2=value2) - --wait Wait for the VPC(s) to be deleted + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter VPCs (format: key1=value1,key2=value2) + --wait Wait for the VPC(s) to be deleted + --wait-timeout duration Maximum time to wait for the VPC(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/networking/vpcs_list/_index.md b/docs/tcloud/networking/vpcs_list/_index.md index eed2db4..23baa8d 100644 --- a/docs/tcloud/networking/vpcs_list/_index.md +++ b/docs/tcloud/networking/vpcs_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud networking vpcs list" title: "networking vpcs list" slug: tcloud_networking_vpcs_list url: /docs/tcloud/networking/vpcs_list/ -weight: 9826 +weight: 9744 cascade: type: docs --- diff --git a/docs/tcloud/object-storage/_index.md b/docs/tcloud/object-storage/_index.md index 3dbe7a1..4d4a24b 100644 --- a/docs/tcloud/object-storage/_index.md +++ b/docs/tcloud/object-storage/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud object-storage" title: "object-storage" slug: tcloud_object-storage url: /docs/tcloud/tcloud_object-storage/ -weight: 9819 +weight: 9737 cascade: type: docs --- diff --git a/docs/tcloud/object-storage/create/_index.md b/docs/tcloud/object-storage/create/_index.md index a582ef2..a7a2777 100644 --- a/docs/tcloud/object-storage/create/_index.md +++ b/docs/tcloud/object-storage/create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud object-storage create" title: "object-storage create" slug: tcloud_object-storage_create url: /docs/tcloud/object-storage/create/ -weight: 9823 +weight: 9741 cascade: type: docs --- diff --git a/docs/tcloud/object-storage/delete/_index.md b/docs/tcloud/object-storage/delete/_index.md index 7e42a00..81f5f53 100644 --- a/docs/tcloud/object-storage/delete/_index.md +++ b/docs/tcloud/object-storage/delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud object-storage delete" title: "object-storage delete" slug: tcloud_object-storage_delete url: /docs/tcloud/object-storage/delete/ -weight: 9822 +weight: 9740 cascade: type: docs --- diff --git a/docs/tcloud/object-storage/list/_index.md b/docs/tcloud/object-storage/list/_index.md index f5aef07..bfa40a1 100644 --- a/docs/tcloud/object-storage/list/_index.md +++ b/docs/tcloud/object-storage/list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud object-storage list" title: "object-storage list" slug: tcloud_object-storage_list url: /docs/tcloud/object-storage/list/ -weight: 9821 +weight: 9739 cascade: type: docs --- diff --git a/docs/tcloud/object-storage/update/_index.md b/docs/tcloud/object-storage/update/_index.md index 3baa255..255e5cb 100644 --- a/docs/tcloud/object-storage/update/_index.md +++ b/docs/tcloud/object-storage/update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud object-storage update" title: "object-storage update" slug: tcloud_object-storage_update url: /docs/tcloud/object-storage/update/ -weight: 9820 +weight: 9738 cascade: type: docs --- diff --git a/docs/tcloud/oidc/_index.md b/docs/tcloud/oidc/_index.md index 9bc162a..cbd3ab1 100644 --- a/docs/tcloud/oidc/_index.md +++ b/docs/tcloud/oidc/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud oidc" title: "oidc" slug: tcloud_oidc url: /docs/tcloud/tcloud_oidc/ -weight: 9817 +weight: 9735 cascade: type: docs --- diff --git a/docs/tcloud/oidc/token-exchange/_index.md b/docs/tcloud/oidc/token-exchange/_index.md index bcbd1a0..892651e 100644 --- a/docs/tcloud/oidc/token-exchange/_index.md +++ b/docs/tcloud/oidc/token-exchange/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud oidc token-exchange" title: "oidc token-exchange" slug: tcloud_oidc_token-exchange url: /docs/tcloud/oidc/token-exchange/ -weight: 9818 +weight: 9736 cascade: type: docs --- diff --git a/docs/tcloud/projects/_index.md b/docs/tcloud/projects/_index.md index eb1d5d7..99b9984 100644 --- a/docs/tcloud/projects/_index.md +++ b/docs/tcloud/projects/_index.md @@ -3,19 +3,19 @@ linkTitle: "tcloud projects" title: "projects" slug: tcloud_projects url: /docs/tcloud/tcloud_projects/ -weight: 9815 +weight: 9729 cascade: type: docs --- ## tcloud projects -Manage projects (private beta) +Manage projects (beta) ### Synopsis Manage projects within the organisation selected in your context. -Note: This command is in private beta and requires the project feature gate to be enabled on your organisation. +Note: This command is in beta and requires the project feature gate to be enabled on your organisation. ### Options @@ -40,5 +40,9 @@ Note: This command is in private beta and requires the project feature gate to b ### SEE ALSO * [tcloud](/docs/tcloud/tcloud/) - A CLI for working with the Thalassa Cloud Platform +* [tcloud projects create](/docs/tcloud/projects/create/) - Create a project in the current organisation +* [tcloud projects delete](/docs/tcloud/projects/delete/) - Delete a project * [tcloud projects list](/docs/tcloud/projects/list/) - List projects in the current organisation +* [tcloud projects update](/docs/tcloud/projects/update/) - Update a project (only flags you set are changed) +* [tcloud projects view](/docs/tcloud/projects/view/) - View a project diff --git a/docs/tcloud/projects/create/_index.md b/docs/tcloud/projects/create/_index.md new file mode 100644 index 0000000..ff11c8c --- /dev/null +++ b/docs/tcloud/projects/create/_index.md @@ -0,0 +1,47 @@ +--- +linkTitle: "tcloud projects create" +title: "projects create" +slug: tcloud_projects_create +url: /docs/tcloud/projects/create/ +weight: 9734 +cascade: + type: docs +--- +## tcloud projects create + +Create a project in the current organisation + +``` +tcloud projects create [flags] +``` + +### Options + +``` + --annotations strings Annotations as key=value (repeatable) + --description string Project description + -h, --help help for create + --labels strings Labels as key=value (repeatable) + --name string Project display name + --no-header do not print headers + --parent string Parent project identity or slug +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (beta) + diff --git a/docs/tcloud/projects/delete/_index.md b/docs/tcloud/projects/delete/_index.md new file mode 100644 index 0000000..2a92f62 --- /dev/null +++ b/docs/tcloud/projects/delete/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud projects delete" +title: "projects delete" +slug: tcloud_projects_delete +url: /docs/tcloud/projects/delete/ +weight: 9733 +cascade: + type: docs +--- +## tcloud projects delete + +Delete a project + +``` +tcloud projects delete [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt and delete + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (beta) + diff --git a/docs/tcloud/projects/list/_index.md b/docs/tcloud/projects/list/_index.md index a5e6f7c..9a17bce 100644 --- a/docs/tcloud/projects/list/_index.md +++ b/docs/tcloud/projects/list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud projects list" title: "projects list" slug: tcloud_projects_list url: /docs/tcloud/projects/list/ -weight: 9816 +weight: 9732 cascade: type: docs --- @@ -40,5 +40,5 @@ tcloud projects list [flags] ### SEE ALSO -* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (private beta) +* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (beta) diff --git a/docs/tcloud/projects/update/_index.md b/docs/tcloud/projects/update/_index.md new file mode 100644 index 0000000..9e81283 --- /dev/null +++ b/docs/tcloud/projects/update/_index.md @@ -0,0 +1,47 @@ +--- +linkTitle: "tcloud projects update" +title: "projects update" +slug: tcloud_projects_update +url: /docs/tcloud/projects/update/ +weight: 9731 +cascade: + type: docs +--- +## tcloud projects update + +Update a project (only flags you set are changed) + +``` +tcloud projects update [flags] +``` + +### Options + +``` + --annotations strings Replace annotations (key=value, repeatable) + --description string Project description + -h, --help help for update + --labels strings Replace labels (key=value, repeatable) + --name string Project display name + --no-header do not print headers + --parent string Parent project identity or slug (empty clears parent) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (beta) + diff --git a/docs/tcloud/projects/view/_index.md b/docs/tcloud/projects/view/_index.md new file mode 100644 index 0000000..0702fc8 --- /dev/null +++ b/docs/tcloud/projects/view/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud projects view" +title: "projects view" +slug: tcloud_projects_view +url: /docs/tcloud/projects/view/ +weight: 9730 +cascade: + type: docs +--- +## tcloud projects view + +View a project + +``` +tcloud projects view [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for view +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (beta) + diff --git a/docs/tcloud/quotas/_index.md b/docs/tcloud/quotas/_index.md index 23d948c..9f5617c 100644 --- a/docs/tcloud/quotas/_index.md +++ b/docs/tcloud/quotas/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud quotas" title: "quotas" slug: tcloud_quotas url: /docs/tcloud/tcloud_quotas/ -weight: 9811 +weight: 9725 cascade: type: docs --- diff --git a/docs/tcloud/quotas/get/_index.md b/docs/tcloud/quotas/get/_index.md index 874218e..cc470ca 100644 --- a/docs/tcloud/quotas/get/_index.md +++ b/docs/tcloud/quotas/get/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud quotas get" title: "quotas get" slug: tcloud_quotas_get url: /docs/tcloud/quotas/get/ -weight: 9814 +weight: 9728 cascade: type: docs --- diff --git a/docs/tcloud/quotas/list/_index.md b/docs/tcloud/quotas/list/_index.md index 817c224..7292200 100644 --- a/docs/tcloud/quotas/list/_index.md +++ b/docs/tcloud/quotas/list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud quotas list" title: "quotas list" slug: tcloud_quotas_list url: /docs/tcloud/quotas/list/ -weight: 9813 +weight: 9727 cascade: type: docs --- diff --git a/docs/tcloud/quotas/request-increase/_index.md b/docs/tcloud/quotas/request-increase/_index.md index 0248445..ce8f179 100644 --- a/docs/tcloud/quotas/request-increase/_index.md +++ b/docs/tcloud/quotas/request-increase/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud quotas request-increase" title: "quotas request-increase" slug: tcloud_quotas_request-increase url: /docs/tcloud/quotas/request-increase/ -weight: 9812 +weight: 9726 cascade: type: docs --- diff --git a/docs/tcloud/regions/_index.md b/docs/tcloud/regions/_index.md index f035b13..24b9704 100644 --- a/docs/tcloud/regions/_index.md +++ b/docs/tcloud/regions/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud regions" title: "regions" slug: tcloud_regions url: /docs/tcloud/tcloud_regions/ -weight: 9809 +weight: 9723 cascade: type: docs --- diff --git a/docs/tcloud/regions/list/_index.md b/docs/tcloud/regions/list/_index.md index 15d4834..64583dd 100644 --- a/docs/tcloud/regions/list/_index.md +++ b/docs/tcloud/regions/list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud regions list" title: "regions list" slug: tcloud_regions_list url: /docs/tcloud/regions/list/ -weight: 9810 +weight: 9724 cascade: type: docs --- diff --git a/docs/tcloud/registry/_index.md b/docs/tcloud/registry/_index.md index 008069a..0961627 100644 --- a/docs/tcloud/registry/_index.md +++ b/docs/tcloud/registry/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry" title: "registry" slug: tcloud_registry url: /docs/tcloud/tcloud_registry/ -weight: 9790 +weight: 9704 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces/_index.md b/docs/tcloud/registry/namespaces/_index.md index 9ec211e..5e216b6 100644 --- a/docs/tcloud/registry/namespaces/_index.md +++ b/docs/tcloud/registry/namespaces/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces" title: "registry namespaces" slug: tcloud_registry_namespaces url: /docs/tcloud/registry/namespaces/ -weight: 9796 +weight: 9710 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_configuration/_index.md b/docs/tcloud/registry/namespaces_configuration/_index.md index 20c98bb..3b4d554 100644 --- a/docs/tcloud/registry/namespaces_configuration/_index.md +++ b/docs/tcloud/registry/namespaces_configuration/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces configuration" title: "registry namespaces configuration" slug: tcloud_registry_namespaces_configuration url: /docs/tcloud/registry/namespaces_configuration/ -weight: 9804 +weight: 9718 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_configuration_create/_index.md b/docs/tcloud/registry/namespaces_configuration_create/_index.md index 15bf277..0f6d2f0 100644 --- a/docs/tcloud/registry/namespaces_configuration_create/_index.md +++ b/docs/tcloud/registry/namespaces_configuration_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces configuration create" title: "registry namespaces configuration create" slug: tcloud_registry_namespaces_configuration_create url: /docs/tcloud/registry/namespaces_configuration_create/ -weight: 9808 +weight: 9722 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_configuration_delete/_index.md b/docs/tcloud/registry/namespaces_configuration_delete/_index.md index 6c527ce..49112e8 100644 --- a/docs/tcloud/registry/namespaces_configuration_delete/_index.md +++ b/docs/tcloud/registry/namespaces_configuration_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces configuration delete" title: "registry namespaces configuration delete" slug: tcloud_registry_namespaces_configuration_delete url: /docs/tcloud/registry/namespaces_configuration_delete/ -weight: 9807 +weight: 9721 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_configuration_update/_index.md b/docs/tcloud/registry/namespaces_configuration_update/_index.md index a8ba990..cedd093 100644 --- a/docs/tcloud/registry/namespaces_configuration_update/_index.md +++ b/docs/tcloud/registry/namespaces_configuration_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces configuration update" title: "registry namespaces configuration update" slug: tcloud_registry_namespaces_configuration_update url: /docs/tcloud/registry/namespaces_configuration_update/ -weight: 9806 +weight: 9720 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_configuration_view/_index.md b/docs/tcloud/registry/namespaces_configuration_view/_index.md index 0474b03..f243bed 100644 --- a/docs/tcloud/registry/namespaces_configuration_view/_index.md +++ b/docs/tcloud/registry/namespaces_configuration_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces configuration view" title: "registry namespaces configuration view" slug: tcloud_registry_namespaces_configuration_view url: /docs/tcloud/registry/namespaces_configuration_view/ -weight: 9805 +weight: 9719 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_create/_index.md b/docs/tcloud/registry/namespaces_create/_index.md index 1b7ca5b..f72943f 100644 --- a/docs/tcloud/registry/namespaces_create/_index.md +++ b/docs/tcloud/registry/namespaces_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces create" title: "registry namespaces create" slug: tcloud_registry_namespaces_create url: /docs/tcloud/registry/namespaces_create/ -weight: 9803 +weight: 9717 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_delete/_index.md b/docs/tcloud/registry/namespaces_delete/_index.md index f5afba2..0c0fecc 100644 --- a/docs/tcloud/registry/namespaces_delete/_index.md +++ b/docs/tcloud/registry/namespaces_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces delete" title: "registry namespaces delete" slug: tcloud_registry_namespaces_delete url: /docs/tcloud/registry/namespaces_delete/ -weight: 9802 +weight: 9716 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_list/_index.md b/docs/tcloud/registry/namespaces_list/_index.md index 1717f68..7c395eb 100644 --- a/docs/tcloud/registry/namespaces_list/_index.md +++ b/docs/tcloud/registry/namespaces_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces list" title: "registry namespaces list" slug: tcloud_registry_namespaces_list url: /docs/tcloud/registry/namespaces_list/ -weight: 9801 +weight: 9715 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_retention/_index.md b/docs/tcloud/registry/namespaces_retention/_index.md index b6841f4..5705b74 100644 --- a/docs/tcloud/registry/namespaces_retention/_index.md +++ b/docs/tcloud/registry/namespaces_retention/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces retention" title: "registry namespaces retention" slug: tcloud_registry_namespaces_retention url: /docs/tcloud/registry/namespaces_retention/ -weight: 9799 +weight: 9713 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_retention_run/_index.md b/docs/tcloud/registry/namespaces_retention_run/_index.md index 48b2ea0..6d74a82 100644 --- a/docs/tcloud/registry/namespaces_retention_run/_index.md +++ b/docs/tcloud/registry/namespaces_retention_run/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces retention run" title: "registry namespaces retention run" slug: tcloud_registry_namespaces_retention_run url: /docs/tcloud/registry/namespaces_retention_run/ -weight: 9800 +weight: 9714 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_update/_index.md b/docs/tcloud/registry/namespaces_update/_index.md index 41041e9..5a1c6ac 100644 --- a/docs/tcloud/registry/namespaces_update/_index.md +++ b/docs/tcloud/registry/namespaces_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces update" title: "registry namespaces update" slug: tcloud_registry_namespaces_update url: /docs/tcloud/registry/namespaces_update/ -weight: 9798 +weight: 9712 cascade: type: docs --- diff --git a/docs/tcloud/registry/namespaces_view/_index.md b/docs/tcloud/registry/namespaces_view/_index.md index 53cb381..33633a5 100644 --- a/docs/tcloud/registry/namespaces_view/_index.md +++ b/docs/tcloud/registry/namespaces_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry namespaces view" title: "registry namespaces view" slug: tcloud_registry_namespaces_view url: /docs/tcloud/registry/namespaces_view/ -weight: 9797 +weight: 9711 cascade: type: docs --- diff --git a/docs/tcloud/registry/repositories/_index.md b/docs/tcloud/registry/repositories/_index.md index f37e44f..95efc0a 100644 --- a/docs/tcloud/registry/repositories/_index.md +++ b/docs/tcloud/registry/repositories/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry repositories" title: "registry repositories" slug: tcloud_registry_repositories url: /docs/tcloud/registry/repositories/ -weight: 9791 +weight: 9705 cascade: type: docs --- diff --git a/docs/tcloud/registry/repositories_delete-artifacts/_index.md b/docs/tcloud/registry/repositories_delete-artifacts/_index.md index 3dcc037..46c423e 100644 --- a/docs/tcloud/registry/repositories_delete-artifacts/_index.md +++ b/docs/tcloud/registry/repositories_delete-artifacts/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry repositories delete-artifacts" title: "registry repositories delete-artifacts" slug: tcloud_registry_repositories_delete-artifacts url: /docs/tcloud/registry/repositories_delete-artifacts/ -weight: 9794 +weight: 9708 cascade: type: docs --- diff --git a/docs/tcloud/registry/repositories_delete/_index.md b/docs/tcloud/registry/repositories_delete/_index.md index 79bcf63..a77af80 100644 --- a/docs/tcloud/registry/repositories_delete/_index.md +++ b/docs/tcloud/registry/repositories_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry repositories delete" title: "registry repositories delete" slug: tcloud_registry_repositories_delete url: /docs/tcloud/registry/repositories_delete/ -weight: 9795 +weight: 9709 cascade: type: docs --- diff --git a/docs/tcloud/registry/repositories_list/_index.md b/docs/tcloud/registry/repositories_list/_index.md index e421ad1..ab511a3 100644 --- a/docs/tcloud/registry/repositories_list/_index.md +++ b/docs/tcloud/registry/repositories_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry repositories list" title: "registry repositories list" slug: tcloud_registry_repositories_list url: /docs/tcloud/registry/repositories_list/ -weight: 9793 +weight: 9707 cascade: type: docs --- diff --git a/docs/tcloud/registry/repositories_view/_index.md b/docs/tcloud/registry/repositories_view/_index.md index 918f378..3edf48a 100644 --- a/docs/tcloud/registry/repositories_view/_index.md +++ b/docs/tcloud/registry/repositories_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud registry repositories view" title: "registry repositories view" slug: tcloud_registry_repositories_view url: /docs/tcloud/registry/repositories_view/ -weight: 9792 +weight: 9706 cascade: type: docs --- diff --git a/docs/tcloud/secrets/_index.md b/docs/tcloud/secrets/_index.md new file mode 100644 index 0000000..40092ce --- /dev/null +++ b/docs/tcloud/secrets/_index.md @@ -0,0 +1,53 @@ +--- +linkTitle: "tcloud secrets" +title: "secrets" +slug: tcloud_secrets +url: /docs/tcloud/tcloud_secrets/ +weight: 9694 +cascade: + type: docs +--- +## tcloud secrets + +Manage secrets (beta) + +### Synopsis + +Manage Secrets Manager paths, versions, and access policies. + +Note: This command is in beta. Commands that list or view secrets show +metadata only; use get-value when you intentionally need secret material. + +### Options + +``` + -h, --help help for secrets +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud](/docs/tcloud/tcloud/) - A CLI for working with the Thalassa Cloud Platform +* [tcloud secrets browse](/docs/tcloud/secrets/browse/) - Browse secret prefixes and secrets at a path +* [tcloud secrets create](/docs/tcloud/secrets/create/) - Create a secret (metadata response only; use get-value to read material) +* [tcloud secrets delete](/docs/tcloud/secrets/delete/) - Delete a secret +* [tcloud secrets destroy-version](/docs/tcloud/secrets/destroy-version/) - Permanently destroy a secret version +* [tcloud secrets get-value](/docs/tcloud/secrets/get-value/) - Print secret material to stdout +* [tcloud secrets list](/docs/tcloud/secrets/list/) - List secrets under a path prefix (metadata only) +* [tcloud secrets policy](/docs/tcloud/secrets/policy/) - Replace a secret access policy from a JSON file +* [tcloud secrets put](/docs/tcloud/secrets/put/) - Put a new secret version +* [tcloud secrets view](/docs/tcloud/secrets/view/) - View secret metadata (does not print secret material) + diff --git a/docs/tcloud/secrets/browse/_index.md b/docs/tcloud/secrets/browse/_index.md new file mode 100644 index 0000000..3d8cfef --- /dev/null +++ b/docs/tcloud/secrets/browse/_index.md @@ -0,0 +1,56 @@ +--- +linkTitle: "tcloud secrets browse" +title: "secrets browse" +slug: tcloud_secrets_browse +url: /docs/tcloud/secrets/browse/ +weight: 9703 +cascade: + type: docs +--- +## tcloud secrets browse + +Browse secret prefixes and secrets at a path + +### Synopsis + +Browse Secrets Manager prefixes and secrets. + +In a terminal with fzf available, browse is interactive: select prefixes to +descend, ".." to go up, and secrets to view metadata (optionally reveal values +after confirmation). Press Esc to quit. + +When stdout is not a terminal, or TC_IGNORE_FZF is set, prints a non-interactive +table for the given --path. + +``` +tcloud secrets browse [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for browse + --no-header Do not print table headers + --path string Path to browse (default "/") + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/create/_index.md b/docs/tcloud/secrets/create/_index.md new file mode 100644 index 0000000..c7807f0 --- /dev/null +++ b/docs/tcloud/secrets/create/_index.md @@ -0,0 +1,61 @@ +--- +linkTitle: "tcloud secrets create" +title: "secrets create" +slug: tcloud_secrets_create +url: /docs/tcloud/secrets/create/ +weight: 9702 +cascade: + type: docs +--- +## tcloud secrets create + +Create a secret (metadata response only; use get-value to read material) + +``` +tcloud secrets create [flags] +``` + +### Examples + +``` + tcloud secrets create --region nl-01 --path /app/prod/db --kms-key kms-123 --generate-bytes + tcloud secrets create --region nl-01 --path /app/prod/db --kms-key kms-123 --generate-bytes=64 + tcloud secrets create --region nl-01 --path /app/prod/token --kms-key kms-123 --from-file ./token.txt +``` + +### Options + +``` + --annotations strings Annotations as key=value (repeatable) + --description string Description + --from-file string Read secret string from a file + --generate-bytes int[=32] Generate a random secret of this many bytes (16-4096; bare --generate-bytes uses 32) + -h, --help help for create + --kms-key string KMS key identity used to encrypt the secret + --kv strings Secret key/value pairs as key=value (repeatable) + --labels strings Labels as key=value (repeatable) + --no-header Do not print table headers + --path string Secret path + --policy-file string JSON file with an access policy + --region string Region + --string string Secret string value +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/delete/_index.md b/docs/tcloud/secrets/delete/_index.md new file mode 100644 index 0000000..f01e2c6 --- /dev/null +++ b/docs/tcloud/secrets/delete/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud secrets delete" +title: "secrets delete" +slug: tcloud_secrets_delete +url: /docs/tcloud/secrets/delete/ +weight: 9701 +cascade: + type: docs +--- +## tcloud secrets delete + +Delete a secret + +``` +tcloud secrets delete [flags] +``` + +### Options + +``` + --force Skip confirmation + -h, --help help for delete + --path string Secret path + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/destroy-version/_index.md b/docs/tcloud/secrets/destroy-version/_index.md new file mode 100644 index 0000000..f5e72be --- /dev/null +++ b/docs/tcloud/secrets/destroy-version/_index.md @@ -0,0 +1,45 @@ +--- +linkTitle: "tcloud secrets destroy-version" +title: "secrets destroy-version" +slug: tcloud_secrets_destroy-version +url: /docs/tcloud/secrets/destroy-version/ +weight: 9700 +cascade: + type: docs +--- +## tcloud secrets destroy-version + +Permanently destroy a secret version + +``` +tcloud secrets destroy-version [flags] +``` + +### Options + +``` + --force Skip confirmation + -h, --help help for destroy-version + --path string Secret path + --region string Region + --version int Version to destroy +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/get-value/_index.md b/docs/tcloud/secrets/get-value/_index.md new file mode 100644 index 0000000..f36b4b1 --- /dev/null +++ b/docs/tcloud/secrets/get-value/_index.md @@ -0,0 +1,48 @@ +--- +linkTitle: "tcloud secrets get-value" +title: "secrets get-value" +slug: tcloud_secrets_get-value +url: /docs/tcloud/secrets/get-value/ +weight: 9699 +cascade: + type: docs +--- +## tcloud secrets get-value + +Print secret material to stdout + +### Synopsis + +Fetches and prints the secret value. Prefer piping to a file and avoid logging the output. + +``` +tcloud secrets get-value [flags] +``` + +### Options + +``` + -h, --help help for get-value + --path string Secret path + --region string Region + --version int Specific version (defaults to current) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/list/_index.md b/docs/tcloud/secrets/list/_index.md new file mode 100644 index 0000000..f055f83 --- /dev/null +++ b/docs/tcloud/secrets/list/_index.md @@ -0,0 +1,45 @@ +--- +linkTitle: "tcloud secrets list" +title: "secrets list" +slug: tcloud_secrets_list +url: /docs/tcloud/secrets/list/ +weight: 9698 +cascade: + type: docs +--- +## tcloud secrets list + +List secrets under a path prefix (metadata only) + +``` +tcloud secrets list [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for list + --no-header Do not print table headers + --prefix string Path prefix (default "/") + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/policy/_index.md b/docs/tcloud/secrets/policy/_index.md new file mode 100644 index 0000000..70f95e7 --- /dev/null +++ b/docs/tcloud/secrets/policy/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud secrets policy" +title: "secrets policy" +slug: tcloud_secrets_policy +url: /docs/tcloud/secrets/policy/ +weight: 9697 +cascade: + type: docs +--- +## tcloud secrets policy + +Replace a secret access policy from a JSON file + +``` +tcloud secrets policy [flags] +``` + +### Options + +``` + --file string JSON file containing the access policy + -h, --help help for policy + --path string Secret path + --region string Region +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/put/_index.md b/docs/tcloud/secrets/put/_index.md new file mode 100644 index 0000000..e332b83 --- /dev/null +++ b/docs/tcloud/secrets/put/_index.md @@ -0,0 +1,47 @@ +--- +linkTitle: "tcloud secrets put" +title: "secrets put" +slug: tcloud_secrets_put +url: /docs/tcloud/secrets/put/ +weight: 9696 +cascade: + type: docs +--- +## tcloud secrets put + +Put a new secret version + +``` +tcloud secrets put [flags] +``` + +### Options + +``` + --from-file string Read secret string from a file + --generate-bytes int[=32] Generate a random secret of this many bytes (16-4096; bare --generate-bytes uses 32) + -h, --help help for put + --kv strings Secret key/value pairs as key=value (repeatable) + --path string Secret path + --region string Region + --string string Secret string value +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/secrets/view/_index.md b/docs/tcloud/secrets/view/_index.md new file mode 100644 index 0000000..8d777fa --- /dev/null +++ b/docs/tcloud/secrets/view/_index.md @@ -0,0 +1,46 @@ +--- +linkTitle: "tcloud secrets view" +title: "secrets view" +slug: tcloud_secrets_view +url: /docs/tcloud/secrets/view/ +weight: 9695 +cascade: + type: docs +--- +## tcloud secrets view + +View secret metadata (does not print secret material) + +``` +tcloud secrets view [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for view + --no-header Do not print table headers + --path string Secret path + --region string Region + --versions Include version history +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) + diff --git a/docs/tcloud/storage/_index.md b/docs/tcloud/storage/_index.md index 3c42f45..b19b866 100644 --- a/docs/tcloud/storage/_index.md +++ b/docs/tcloud/storage/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage" title: "storage" slug: tcloud_storage url: /docs/tcloud/tcloud_storage/ -weight: 9772 +weight: 9670 cascade: type: docs --- @@ -34,6 +34,7 @@ Manage storage resources ### SEE ALSO * [tcloud](/docs/tcloud/tcloud/) - A CLI for working with the Thalassa Cloud Platform +* [tcloud storage snapshot-policies](/docs/tcloud/storage/snapshot-policies/) - Manage snapshot policies * [tcloud storage snapshots](/docs/tcloud/storage/snapshots/) - Manage volume snapshots * [tcloud storage tfs](/docs/tcloud/storage/tfs/) - Manage TFS (Thalassa File System) instances * [tcloud storage volumes](/docs/tcloud/storage/volumes/) - Manage storage volumes diff --git a/docs/tcloud/storage/snapshot-policies/_index.md b/docs/tcloud/storage/snapshot-policies/_index.md new file mode 100644 index 0000000..1b4c13b --- /dev/null +++ b/docs/tcloud/storage/snapshot-policies/_index.md @@ -0,0 +1,53 @@ +--- +linkTitle: "tcloud storage snapshot-policies" +title: "storage snapshot-policies" +slug: tcloud_storage_snapshot-policies +url: /docs/tcloud/storage/snapshot-policies/ +weight: 9688 +cascade: + type: docs +--- +## tcloud storage snapshot-policies + +Manage snapshot policies + +### Synopsis + +Manage automated volume snapshot policies within the Thalassa Cloud Platform. + +### Examples + +``` +tcloud storage snapshot-policies list +tcloud storage snapshot-policies create --name daily --region nl-ams --schedule '0 2 * * *' --ttl 168h --target-type selector --selector backup=true +``` + +### Options + +``` + -h, --help help for snapshot-policies +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud storage](/docs/tcloud/tcloud_storage/) - Manage storage resources +* [tcloud storage snapshot-policies create](/docs/tcloud/storage/snapshot-policies_create/) - Create a snapshot policy +* [tcloud storage snapshot-policies delete](/docs/tcloud/storage/snapshot-policies_delete/) - Delete snapshot policy(ies) +* [tcloud storage snapshot-policies list](/docs/tcloud/storage/snapshot-policies_list/) - List snapshot policies +* [tcloud storage snapshot-policies update](/docs/tcloud/storage/snapshot-policies_update/) - Update a snapshot policy +* [tcloud storage snapshot-policies view](/docs/tcloud/storage/snapshot-policies_view/) - View snapshot policy details + diff --git a/docs/tcloud/storage/snapshot-policies_create/_index.md b/docs/tcloud/storage/snapshot-policies_create/_index.md new file mode 100644 index 0000000..dfa55b9 --- /dev/null +++ b/docs/tcloud/storage/snapshot-policies_create/_index.md @@ -0,0 +1,66 @@ +--- +linkTitle: "tcloud storage snapshot-policies create" +title: "storage snapshot-policies create" +slug: tcloud_storage_snapshot-policies_create +url: /docs/tcloud/storage/snapshot-policies_create/ +weight: 9693 +cascade: + type: docs +--- +## tcloud storage snapshot-policies create + +Create a snapshot policy + +### Synopsis + +Create a new automated snapshot policy for volumes in a region. + +``` +tcloud storage snapshot-policies create [flags] +``` + +### Examples + +``` +tcloud storage snapshot-policies create --name daily --region nl-ams --schedule "0 2 * * *" --ttl 168h --timezone UTC --target-type selector --selector backup=true +tcloud storage snapshot-policies create --name weekly --region nl-ams --schedule "0 3 * * 0" --ttl 720h --target-type explicit --volumes vol-1,vol-2 +``` + +### Options + +``` + --annotations strings Annotations in key=value format + --description string Description of the snapshot policy + --enabled Enable the snapshot policy (default true) + -h, --help help for create + --keep-count int Maximum number of snapshots to retain + --labels strings Labels in key=value format + --name string Name of the snapshot policy + --no-header Do not print the header + --region string Region of the snapshot policy + --schedule string Cron schedule for snapshot creation + --selector strings Label selectors for target volumes (key=value) + --target-type string Target type: selector or explicit + --timezone string Timezone for the schedule (default "UTC") + --ttl string Snapshot retention duration (e.g. 24h, 168h) + --volumes strings Volume identities when target-type is explicit +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud storage snapshot-policies](/docs/tcloud/storage/snapshot-policies/) - Manage snapshot policies + diff --git a/docs/tcloud/storage/snapshot-policies_delete/_index.md b/docs/tcloud/storage/snapshot-policies_delete/_index.md new file mode 100644 index 0000000..39e937d --- /dev/null +++ b/docs/tcloud/storage/snapshot-policies_delete/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud storage snapshot-policies delete" +title: "storage snapshot-policies delete" +slug: tcloud_storage_snapshot-policies_delete +url: /docs/tcloud/storage/snapshot-policies_delete/ +weight: 9692 +cascade: + type: docs +--- +## tcloud storage snapshot-policies delete + +Delete snapshot policy(ies) + +``` +tcloud storage snapshot-policies delete [flags] +``` + +### Options + +``` + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter snapshot policies +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud storage snapshot-policies](/docs/tcloud/storage/snapshot-policies/) - Manage snapshot policies + diff --git a/docs/tcloud/storage/snapshot-policies_list/_index.md b/docs/tcloud/storage/snapshot-policies_list/_index.md new file mode 100644 index 0000000..ff51a05 --- /dev/null +++ b/docs/tcloud/storage/snapshot-policies_list/_index.md @@ -0,0 +1,46 @@ +--- +linkTitle: "tcloud storage snapshot-policies list" +title: "storage snapshot-policies list" +slug: tcloud_storage_snapshot-policies_list +url: /docs/tcloud/storage/snapshot-policies_list/ +weight: 9691 +cascade: + type: docs +--- +## tcloud storage snapshot-policies list + +List snapshot policies + +``` +tcloud storage snapshot-policies list [flags] +``` + +### Options + +``` + --exact-time Show exact time instead of relative time + -h, --help help for list + --no-header Do not print the header + --region string Filter by region + -l, --selector string Label selector to filter snapshot policies + --show-labels Show labels +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud storage snapshot-policies](/docs/tcloud/storage/snapshot-policies/) - Manage snapshot policies + diff --git a/docs/tcloud/storage/snapshot-policies_update/_index.md b/docs/tcloud/storage/snapshot-policies_update/_index.md new file mode 100644 index 0000000..42ea441 --- /dev/null +++ b/docs/tcloud/storage/snapshot-policies_update/_index.md @@ -0,0 +1,58 @@ +--- +linkTitle: "tcloud storage snapshot-policies update" +title: "storage snapshot-policies update" +slug: tcloud_storage_snapshot-policies_update +url: /docs/tcloud/storage/snapshot-policies_update/ +weight: 9690 +cascade: + type: docs +--- +## tcloud storage snapshot-policies update + +Update a snapshot policy + +### Synopsis + +Update an existing snapshot policy. Unspecified fields are preserved from the current resource. + +``` +tcloud storage snapshot-policies update [flags] +``` + +### Options + +``` + --annotations strings Annotations in key=value format + --description string Description of the snapshot policy + --enabled Enable or disable the snapshot policy (default true) + -h, --help help for update + --keep-count int Maximum number of snapshots to retain + --labels strings Labels in key=value format + --name string Name of the snapshot policy + --no-header Do not print the header + --schedule string Cron schedule for snapshot creation + --selector strings Label selectors for target volumes (key=value) + --target-type string Target type: selector or explicit + --timezone string Timezone for the schedule + --ttl string Snapshot retention duration (e.g. 24h, 168h) + --volumes strings Volume identities when target-type is explicit +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud storage snapshot-policies](/docs/tcloud/storage/snapshot-policies/) - Manage snapshot policies + diff --git a/docs/tcloud/storage/snapshot-policies_view/_index.md b/docs/tcloud/storage/snapshot-policies_view/_index.md new file mode 100644 index 0000000..74774b5 --- /dev/null +++ b/docs/tcloud/storage/snapshot-policies_view/_index.md @@ -0,0 +1,42 @@ +--- +linkTitle: "tcloud storage snapshot-policies view" +title: "storage snapshot-policies view" +slug: tcloud_storage_snapshot-policies_view +url: /docs/tcloud/storage/snapshot-policies_view/ +weight: 9689 +cascade: + type: docs +--- +## tcloud storage snapshot-policies view + +View snapshot policy details + +``` +tcloud storage snapshot-policies view [flags] +``` + +### Options + +``` + -h, --help help for view + -o, --output string Output format (yaml) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud storage snapshot-policies](/docs/tcloud/storage/snapshot-policies/) - Manage snapshot policies + diff --git a/docs/tcloud/storage/snapshots/_index.md b/docs/tcloud/storage/snapshots/_index.md index baf0f2f..cbdacca 100644 --- a/docs/tcloud/storage/snapshots/_index.md +++ b/docs/tcloud/storage/snapshots/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage snapshots" title: "storage snapshots" slug: tcloud_storage_snapshots url: /docs/tcloud/storage/snapshots/ -weight: 9786 +weight: 9684 cascade: type: docs --- diff --git a/docs/tcloud/storage/snapshots_create/_index.md b/docs/tcloud/storage/snapshots_create/_index.md index 22da9e0..60d99a6 100644 --- a/docs/tcloud/storage/snapshots_create/_index.md +++ b/docs/tcloud/storage/snapshots_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage snapshots create" title: "storage snapshots create" slug: tcloud_storage_snapshots_create url: /docs/tcloud/storage/snapshots_create/ -weight: 9789 +weight: 9687 cascade: type: docs --- @@ -22,13 +22,14 @@ tcloud storage snapshots create [flags] ### Options ``` - --annotations strings Annotations in key=value format (can be specified multiple times) - --delete-protection Enable delete protection for the snapshot - --description string Description of the snapshot - -h, --help help for create - --labels strings Labels in key=value format (can be specified multiple times) - --volume string Volume identity to create snapshot from - --wait Wait for the snapshot to be ready for use + --annotations strings Annotations in key=value format (can be specified multiple times) + --delete-protection Enable delete protection for the snapshot + --description string Description of the snapshot + -h, --help help for create + --labels strings Labels in key=value format (can be specified multiple times) + --volume string Volume identity to create snapshot from + --wait Wait for the snapshot to be ready for use + --wait-timeout duration Maximum time to wait for the snapshot to be ready (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/storage/snapshots_delete/_index.md b/docs/tcloud/storage/snapshots_delete/_index.md index 4243917..cd1ed0d 100644 --- a/docs/tcloud/storage/snapshots_delete/_index.md +++ b/docs/tcloud/storage/snapshots_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage snapshots delete" title: "storage snapshots delete" slug: tcloud_storage_snapshots_delete url: /docs/tcloud/storage/snapshots_delete/ -weight: 9788 +weight: 9686 cascade: type: docs --- @@ -22,10 +22,11 @@ tcloud storage snapshots delete [flags] ### Options ``` - --force Force the deletion and skip the confirmation - -h, --help help for delete - -l, --selector string Label selector to filter snapshots (format: key1=value1,key2=value2) - --wait Wait for the snapshot(s) to be deleted + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter snapshots (format: key1=value1,key2=value2) + --wait Wait for the snapshot(s) to be deleted + --wait-timeout duration Maximum time to wait for the snapshot(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/storage/snapshots_list/_index.md b/docs/tcloud/storage/snapshots_list/_index.md index 2227610..0f42231 100644 --- a/docs/tcloud/storage/snapshots_list/_index.md +++ b/docs/tcloud/storage/snapshots_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage snapshots list" title: "storage snapshots list" slug: tcloud_storage_snapshots_list url: /docs/tcloud/storage/snapshots_list/ -weight: 9787 +weight: 9685 cascade: type: docs --- diff --git a/docs/tcloud/storage/tfs/_index.md b/docs/tcloud/storage/tfs/_index.md index d4c71c9..a984248 100644 --- a/docs/tcloud/storage/tfs/_index.md +++ b/docs/tcloud/storage/tfs/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage tfs" title: "storage tfs" slug: tcloud_storage_tfs url: /docs/tcloud/storage/tfs/ -weight: 9780 +weight: 9678 cascade: type: docs --- diff --git a/docs/tcloud/storage/tfs_create/_index.md b/docs/tcloud/storage/tfs_create/_index.md index 16ba0af..efd31e8 100644 --- a/docs/tcloud/storage/tfs_create/_index.md +++ b/docs/tcloud/storage/tfs_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage tfs create" title: "storage tfs create" slug: tcloud_storage_tfs_create url: /docs/tcloud/storage/tfs_create/ -weight: 9785 +weight: 9683 cascade: type: docs --- @@ -22,18 +22,19 @@ tcloud storage tfs create [flags] ### Options ``` - --annotations strings Annotations in key=value format (can be specified multiple times) - --delete-protection Enable delete protection - --description string Description of the TFS instance - -h, --help help for create - --labels strings Labels in key=value format (can be specified multiple times) - --name string Name of the TFS instance (required) - --no-header Do not print the header - --region string Region of the TFS instance (required) - --size int Size of the TFS instance in GB (required) (default 1) - --subnet string Subnet of the TFS instance (required) - --vpc string VPC of the TFS instance (required) - --wait Wait for the TFS instance to be available before returning + --annotations strings Annotations in key=value format (can be specified multiple times) + --delete-protection Enable delete protection + --description string Description of the TFS instance + -h, --help help for create + --labels strings Labels in key=value format (can be specified multiple times) + --name string Name of the TFS instance (required) + --no-header Do not print the header + --region string Region of the TFS instance (required) + --size int Size of the TFS instance in GB (required) (default 1) + --subnet string Subnet of the TFS instance (required) + --vpc string VPC of the TFS instance (required) + --wait Wait for the TFS instance to be available before returning + --wait-timeout duration Maximum time to wait for the TFS instance to be available (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/storage/tfs_delete/_index.md b/docs/tcloud/storage/tfs_delete/_index.md index ecdd7ea..d3f8279 100644 --- a/docs/tcloud/storage/tfs_delete/_index.md +++ b/docs/tcloud/storage/tfs_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage tfs delete" title: "storage tfs delete" slug: tcloud_storage_tfs_delete url: /docs/tcloud/storage/tfs_delete/ -weight: 9784 +weight: 9682 cascade: type: docs --- @@ -22,10 +22,11 @@ tcloud storage tfs delete [flags] ### Options ``` - --force Force the deletion and skip the confirmation - -h, --help help for delete - -l, --selector string Label selector to filter TFS instances (format: key1=value1,key2=value2) - --wait Wait for the TFS instance(s) to be deleted + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter TFS instances (format: key1=value1,key2=value2) + --wait Wait for the TFS instance(s) to be deleted + --wait-timeout duration Maximum time to wait for the TFS instance(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/storage/tfs_list/_index.md b/docs/tcloud/storage/tfs_list/_index.md index de332eb..43a7d6a 100644 --- a/docs/tcloud/storage/tfs_list/_index.md +++ b/docs/tcloud/storage/tfs_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage tfs list" title: "storage tfs list" slug: tcloud_storage_tfs_list url: /docs/tcloud/storage/tfs_list/ -weight: 9783 +weight: 9681 cascade: type: docs --- diff --git a/docs/tcloud/storage/tfs_update/_index.md b/docs/tcloud/storage/tfs_update/_index.md index 307e048..54c407b 100644 --- a/docs/tcloud/storage/tfs_update/_index.md +++ b/docs/tcloud/storage/tfs_update/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage tfs update" title: "storage tfs update" slug: tcloud_storage_tfs_update url: /docs/tcloud/storage/tfs_update/ -weight: 9782 +weight: 9680 cascade: type: docs --- diff --git a/docs/tcloud/storage/tfs_view/_index.md b/docs/tcloud/storage/tfs_view/_index.md index c1359e3..45cbc50 100644 --- a/docs/tcloud/storage/tfs_view/_index.md +++ b/docs/tcloud/storage/tfs_view/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage tfs view" title: "storage tfs view" slug: tcloud_storage_tfs_view url: /docs/tcloud/storage/tfs_view/ -weight: 9781 +weight: 9679 cascade: type: docs --- diff --git a/docs/tcloud/storage/volumes/_index.md b/docs/tcloud/storage/volumes/_index.md index 065a604..a3b31d4 100644 --- a/docs/tcloud/storage/volumes/_index.md +++ b/docs/tcloud/storage/volumes/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage volumes" title: "storage volumes" slug: tcloud_storage_volumes url: /docs/tcloud/storage/volumes/ -weight: 9773 +weight: 9671 cascade: type: docs --- diff --git a/docs/tcloud/storage/volumes_attach/_index.md b/docs/tcloud/storage/volumes_attach/_index.md index 64052e5..e3b0edc 100644 --- a/docs/tcloud/storage/volumes_attach/_index.md +++ b/docs/tcloud/storage/volumes_attach/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage volumes attach" title: "storage volumes attach" slug: tcloud_storage_volumes_attach url: /docs/tcloud/storage/volumes_attach/ -weight: 9779 +weight: 9677 cascade: type: docs --- diff --git a/docs/tcloud/storage/volumes_create/_index.md b/docs/tcloud/storage/volumes_create/_index.md index 7b52c00..64528d2 100644 --- a/docs/tcloud/storage/volumes_create/_index.md +++ b/docs/tcloud/storage/volumes_create/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage volumes create" title: "storage volumes create" slug: tcloud_storage_volumes_create url: /docs/tcloud/storage/volumes_create/ -weight: 9778 +weight: 9676 cascade: type: docs --- @@ -22,17 +22,18 @@ tcloud storage volumes create [flags] ### Options ``` - --annotations strings Annotations in key=value format (can be specified multiple times) - --delete-protection Enable delete protection - --description string Description of the volume - -h, --help help for create - --labels strings Labels in key=value format (can be specified multiple times) - --name string Name of the volume (required) - --no-header Do not print the header - --region string Region of the volume (required) - --size int Size of the volume in GB (required) - --type string Volume type (default "block") - --wait Wait for the volume to be available before returning + --annotations strings Annotations in key=value format (can be specified multiple times) + --delete-protection Enable delete protection + --description string Description of the volume + -h, --help help for create + --labels strings Labels in key=value format (can be specified multiple times) + --name string Name of the volume (required) + --no-header Do not print the header + --region string Region of the volume (required) + --size int Size of the volume in GB (required) + --type string Volume type (default "block") + --wait Wait for the volume to be available before returning + --wait-timeout duration Maximum time to wait for the volume to be available (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/storage/volumes_delete/_index.md b/docs/tcloud/storage/volumes_delete/_index.md index 301f0ef..cf84fb5 100644 --- a/docs/tcloud/storage/volumes_delete/_index.md +++ b/docs/tcloud/storage/volumes_delete/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage volumes delete" title: "storage volumes delete" slug: tcloud_storage_volumes_delete url: /docs/tcloud/storage/volumes_delete/ -weight: 9777 +weight: 9675 cascade: type: docs --- @@ -22,10 +22,11 @@ tcloud storage volumes delete [flags] ### Options ``` - --force Force the deletion and skip the confirmation - -h, --help help for delete - -l, --selector string Label selector to filter volumes (format: key1=value1,key2=value2) - --wait Wait for the volume(s) to be deleted + --force Force the deletion and skip the confirmation + -h, --help help for delete + -l, --selector string Label selector to filter volumes (format: key1=value1,key2=value2) + --wait Wait for the volume(s) to be deleted + --wait-timeout duration Maximum time to wait for the volume(s) to be deleted (default 20m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/storage/volumes_detach/_index.md b/docs/tcloud/storage/volumes_detach/_index.md index b8a5a18..ea7be14 100644 --- a/docs/tcloud/storage/volumes_detach/_index.md +++ b/docs/tcloud/storage/volumes_detach/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage volumes detach" title: "storage volumes detach" slug: tcloud_storage_volumes_detach url: /docs/tcloud/storage/volumes_detach/ -weight: 9776 +weight: 9674 cascade: type: docs --- diff --git a/docs/tcloud/storage/volumes_list/_index.md b/docs/tcloud/storage/volumes_list/_index.md index a9f215d..7141d43 100644 --- a/docs/tcloud/storage/volumes_list/_index.md +++ b/docs/tcloud/storage/volumes_list/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage volumes list" title: "storage volumes list" slug: tcloud_storage_volumes_list url: /docs/tcloud/storage/volumes_list/ -weight: 9775 +weight: 9673 cascade: type: docs --- diff --git a/docs/tcloud/storage/volumes_resize/_index.md b/docs/tcloud/storage/volumes_resize/_index.md index 6caecdc..34b4d53 100644 --- a/docs/tcloud/storage/volumes_resize/_index.md +++ b/docs/tcloud/storage/volumes_resize/_index.md @@ -3,7 +3,7 @@ linkTitle: "tcloud storage volumes resize" title: "storage volumes resize" slug: tcloud_storage_volumes_resize url: /docs/tcloud/storage/volumes_resize/ -weight: 9774 +weight: 9672 cascade: type: docs --- @@ -22,11 +22,12 @@ tcloud storage volumes resize [volume-id...] [flags] ### Options ``` - --force Force the resize and skip the confirmation - -h, --help help for resize - -l, --selector string Label selector to filter volumes (format: key1=value1,key2=value2) - --size int New size in GB (required) - --wait Wait for the resize operation to complete + --force Force the resize and skip the confirmation + -h, --help help for resize + -l, --selector string Label selector to filter volumes (format: key1=value1,key2=value2) + --size int New size in GB (required) + --wait Wait for the resize operation to complete + --wait-timeout duration Maximum time to wait for the resize operation to complete (default 10m0s) ``` ### Options inherited from parent commands diff --git a/docs/tcloud/tcloud.md b/docs/tcloud/tcloud.md index 5b6d73d..082e63b 100644 --- a/docs/tcloud/tcloud.md +++ b/docs/tcloud/tcloud.md @@ -3,7 +3,7 @@ linkTitle: "tcloud" title: "tcloud" slug: tcloud url: /docs/tcloud/tcloud/ -weight: 9770 +weight: 9668 cascade: type: docs --- @@ -33,16 +33,19 @@ A CLI for working with the Thalassa Cloud Platform * [tcloud compute](/docs/tcloud/tcloud_compute/) - Manage compute resources * [tcloud context](/docs/tcloud/tcloud_context/) - Manage context * [tcloud dbaas](/docs/tcloud/tcloud_dbaas/) - Manage database clusters and related services +* [tcloud dns](/docs/tcloud/tcloud_dns/) - Manage DNS zones and records (beta) * [tcloud iam](/docs/tcloud/tcloud_iam/) - Identity and access management for your organisation +* [tcloud kms](/docs/tcloud/tcloud_kms/) - Manage KMS keys and cryptographic operations (beta) * [tcloud kubernetes](/docs/tcloud/tcloud_kubernetes/) - Manage Kubernetes clusters, node pools and more services related to Kubernetes * [tcloud me](/docs/tcloud/tcloud_me/) - Get information about the current user * [tcloud networking](/docs/tcloud/tcloud_networking/) - Manage networking resources * [tcloud object-storage](/docs/tcloud/tcloud_object-storage/) - Manage object storage buckets * [tcloud oidc](/docs/tcloud/tcloud_oidc/) - OIDC token operations -* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (private beta) +* [tcloud projects](/docs/tcloud/tcloud_projects/) - Manage projects (beta) * [tcloud quotas](/docs/tcloud/tcloud_quotas/) - View and request changes to organisation resource quotas * [tcloud regions](/docs/tcloud/tcloud_regions/) - Thalassa Cloud Platform Regions * [tcloud registry](/docs/tcloud/tcloud_registry/) - Manage the Thalassa container registry +* [tcloud secrets](/docs/tcloud/tcloud_secrets/) - Manage secrets (beta) * [tcloud storage](/docs/tcloud/tcloud_storage/) - Manage storage resources * [tcloud version](/docs/tcloud/tcloud_version/) - Print version information diff --git a/docs/tcloud/tcloud_version.md b/docs/tcloud/tcloud_version.md index 8e78ebc..7d03db1 100644 --- a/docs/tcloud/tcloud_version.md +++ b/docs/tcloud/tcloud_version.md @@ -3,7 +3,7 @@ linkTitle: "tcloud version" title: "version" slug: tcloud_version url: /docs/tcloud/tcloud_version/ -weight: 9771 +weight: 9669 cascade: type: docs --- From b56a1a5bb8fcc02893987ec2ad21fdd9fccfb04a Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 10 Sep 2026 19:05:18 +0200 Subject: [PATCH 19/19] chore(docs): update README to remove development notice Signed-off-by: Thomas Kooi --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cece9f1..e426757 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Thalassa Cloud CLI (tcloud) +# Thalassa Cloud CLI (`tcloud`) -A command-line interface for managing your Thalassa Cloud Installation. +`tcloud` is the official CLI for the Thalassa Cloud Platform. Use it to authenticate against your organisation, select a project context, and manage platform resources from the terminal — including compute, networking, storage, Kubernetes, DBaaS, IAM, DNS, KMS, secrets, and more. -> This project is still in beta. Commands and UX may change while the project is in initial development. +Run `tcloud --help` for the full command list, or see the generated docs under [`docs/tcloud`](docs/tcloud). ## Installation