From 9af88e6ea1039763e24a1aa72f28da936a49fd04 Mon Sep 17 00:00:00 2001 From: Van Thong Nguyen Date: Mon, 16 Sep 2024 21:44:41 +0200 Subject: [PATCH] allow to set timeout --- cmd/info.go | 9 +++++++-- cmd/ip.go | 30 ++++++++++++++++++++++++++++++ cmd/isoImage.go | 15 +++++++++++++++ cmd/kubernetes.go | 37 ++++++++++++++++++++++++++++++++----- cmd/network.go | 15 +++++++++++++++ cmd/postgresql.go | 5 +++++ cmd/root.go | 14 ++++++++++++++ cmd/server.go | 40 ++++++++++++++++++++++++++++++++++++++++ cmd/sshKey.go | 15 +++++++++++++++ cmd/storage.go | 15 +++++++++++++++ cmd/template.go | 10 ++++++++++ 11 files changed, 198 insertions(+), 7 deletions(-) diff --git a/cmd/info.go b/cmd/info.go index d3b0e9bf..d7f8bfed 100644 --- a/cmd/info.go +++ b/cmd/info.go @@ -129,14 +129,19 @@ Show summary for a given account: var wg sync.WaitGroup ch := make(chan objectCount) + ctxWithTimeout := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctxWithTimeout, cancel = context.WithTimeout(ctxWithTimeout, rootFlags.timeout) + defer cancel() + } for k, v := range funcs { wg.Add(1) - cCopy := context.Background() go func(obj string, f func(context.Context, *gsclient.Client) (map[string]interface{}, error)) { defer wg.Done() - agg, err := f(cCopy, client) + agg, err := f(ctxWithTimeout, client) if err != nil { ch <- objectCount{obj, nil, NewError(cmd, fmt.Sprintf("Could not get %s", obj), err)} } diff --git a/cmd/ip.go b/cmd/ip.go index 12c9f8ae..c81f5aef 100644 --- a/cmd/ip.go +++ b/cmd/ip.go @@ -47,6 +47,11 @@ var ipLsCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { ipOp := rt.IPOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } ipAddresses, err := ipOp.GetIPList(ctx) if err != nil { return NewError(cmd, "Could not get list of IP addresses", err) @@ -123,6 +128,11 @@ Delete by address: var id string var err error ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } ipOp := rt.IPOperator() address := net.ParseIP(args[0]) if address != nil { @@ -160,6 +170,11 @@ Set PTR entry and name on an existing IP: var err error address := net.ParseIP(args[0]) ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } ipOp := rt.IPOperator() if address != nil { id, err = idForAddress(ctx, address, ipOp) @@ -223,6 +238,11 @@ Create a new IPv4 address: } ipOp := rt.IPOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } ipAddress, err := ipOp.CreateIP(ctx, gsclient.IPCreateRequest{ Family: family, Failover: ipFlags.failover, @@ -264,6 +284,11 @@ Releasing an unassigned IP address will exit with status code 0: var ipID string var err error ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } ipOp := rt.IPOperator() address := net.ParseIP(args[0]) if address != nil { @@ -319,6 +344,11 @@ Assign an IPv4 address to a server or load balancer: var err error ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } ipOp := rt.IPOperator() addr := net.ParseIP(args[0]) if addr != nil { diff --git a/cmd/isoImage.go b/cmd/isoImage.go index e19fd752..126f9b97 100644 --- a/cmd/isoImage.go +++ b/cmd/isoImage.go @@ -34,6 +34,11 @@ var isoImageLsCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { imageOp := rt.ISOImageOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } images, err := imageOp.GetISOImageList(ctx) if err != nil { return NewError(cmd, "Could not get list of images", err) @@ -82,6 +87,11 @@ var isoImageRmCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { imageOp := rt.ISOImageOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } err := imageOp.DeleteISOImage(ctx, args[0]) if err != nil { return NewError(cmd, "Deleting image failed", err) @@ -111,6 +121,11 @@ Create a Fedora CoreOS image: imageOp := rt.ISOImageOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } image, err := imageOp.CreateISOImage(ctx, gsclient.ISOImageCreateRequest{ Name: isoImageFlags.name, SourceURL: isoImageFlags.sourceURL, diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index 995fd6ba..a8aaace5 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -55,6 +55,11 @@ var getKubernetesReleasesCmd = &cobra.Command{ Long: "Prints all available Kubernetes releases. The latest three releases are supported.", RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) op := rt.PaaSOperator() paasTemplates, err := op.GetPaaSTemplateList(ctx) @@ -97,6 +102,11 @@ var getKubernetesVersionsCmd = &cobra.Command{ Long: "Prints all available GS Kubernetes versions.", RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) op := rt.PaaSOperator() paasTemplates, err := op.GetPaaSTemplateList(ctx) @@ -136,6 +146,11 @@ var getKubernetesVersionsCmd = &cobra.Command{ func clusterLsCmdRun(cmd *cobra.Command, args []string) error { paasOp := rt.PaaSOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) paasList, err := paasOp.GetPaaSServiceList(ctx) if err != nil { @@ -201,6 +216,12 @@ KUBECONFIG Specifies the path to the kubeconfig. Gets overriden by --kubeconfig `, RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } kubeConfigFile, _ := cmd.Flags().GetString("kubeconfig") clusterID, _ := cmd.Flags().GetString("cluster") credentialPlugin, _ := cmd.Flags().GetBool("credential-plugin") @@ -225,7 +246,7 @@ KUBECONFIG } op := rt.KubernetesOperator() - newKubeConfig, _, err := fetchKubeConfigFromProvider(op, clusterID) + newKubeConfig, _, err := fetchKubeConfigFromProvider(ctx, op, clusterID) if err != nil { return NewError(cmd, "Invalid kubeconfig", err) } @@ -302,6 +323,12 @@ var execCredentialCmd = &cobra.Command{ Short: "Provides client credentials to kubectl command", Long: "exec-credential provides client credentials to kubectl command.", RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } kubeConfigFile, _ := cmd.Flags().GetString("kubeconfig") clusterID, _ := cmd.Flags().GetString("cluster") @@ -323,7 +350,7 @@ var execCredentialCmd = &cobra.Command{ op := rt.KubernetesOperator() if execCredential == nil { - newKubeConfig, expirationTime, err := fetchKubeConfigFromProvider(op, clusterID) + newKubeConfig, expirationTime, err := fetchKubeConfigFromProvider(ctx, op, clusterID) if err != nil { return NewError(cmd, "Could not fetch kubeconfig", err) } @@ -384,15 +411,15 @@ func init() { rootCmd.AddCommand(kubernetesCmd) } -func fetchKubeConfigFromProvider(op runtime.KubernetesOperator, id string) (kubeConfig, time.Time, error) { +func fetchKubeConfigFromProvider(ctx context.Context, op runtime.KubernetesOperator, id string) (kubeConfig, time.Time, error) { var kc kubeConfig var expirationTime time.Time - if err := op.RenewK8sCredentials(context.Background(), id); err != nil { + if err := op.RenewK8sCredentials(ctx, id); err != nil { return kubeConfig{}, time.Time{}, err } - platformService, err := op.GetPaaSService(context.Background(), id) + platformService, err := op.GetPaaSService(ctx, id) if err != nil { return kubeConfig{}, time.Time{}, err } diff --git a/cmd/network.go b/cmd/network.go index 201bfbb6..bf35324d 100644 --- a/cmd/network.go +++ b/cmd/network.go @@ -33,6 +33,11 @@ var networkLsCmd = &cobra.Command{ Long: `List networks.`, RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) networkOps := rt.NetworkOperator() networks, err := networkOps.GetNetworkList(ctx) @@ -91,6 +96,11 @@ Create a network: networkOp := rt.NetworkOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } network, err := networkOp.CreateNetwork(ctx, gsclient.NetworkCreateRequest{ Name: networkFlags.networkName, }) @@ -115,6 +125,11 @@ var networkRmCmd = &cobra.Command{ Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } networkOps := rt.NetworkOperator() err := networkOps.DeleteNetwork(ctx, args[0]) if err != nil { diff --git a/cmd/postgresql.go b/cmd/postgresql.go index ae59a0cc..856f14ee 100644 --- a/cmd/postgresql.go +++ b/cmd/postgresql.go @@ -35,6 +35,11 @@ var postgresReleasesCmd = &cobra.Command{ Long: "Returns the available PostgreSQL releases", RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) op := rt.PaaSOperator() paasTemplates, err := op.GetPaaSTemplateList(ctx) diff --git a/cmd/root.go b/cmd/root.go index e18451c4..5a3638be 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "time" "github.com/gridscale/gscloud/render" "github.com/gridscale/gscloud/runtime" @@ -35,6 +36,8 @@ type rootCmdFlags struct { json bool quiet bool debug bool + timeoutStr string + timeout time.Duration } var ( @@ -182,6 +185,17 @@ func init() { rootCmd.PersistentFlags().BoolVarP(&rootFlags.quiet, "quiet", "q", false, "Print only object IDs") rootCmd.PersistentFlags().BoolVar(&rootFlags.debug, "debug", false, "Debug mode") rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage") + rootCmd.PersistentFlags().StringVar(&rootFlags.timeoutStr, "timeout", "", "Timeout for API requests. Examples: 10s, 10m, 1h") + rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + if rootFlags.timeoutStr != "" { + var err error + rootFlags.timeout, err = time.ParseDuration(rootFlags.timeoutStr) + if err != nil { + return fmt.Errorf("invalid timeout: %s", err) + } + } + return nil + } } func exists(path string) bool { diff --git a/cmd/server.go b/cmd/server.go index ea7fac39..ab3ba287 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -46,6 +46,11 @@ var serverCmd = &cobra.Command{ func serverLsCmdRun(cmd *cobra.Command, args []string) error { serverOp := rt.ServerOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) servers, err := serverOp.GetServerList(ctx) if err != nil { @@ -95,6 +100,11 @@ var serverLsCmd = &cobra.Command{ func serverOnCmdRun(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } serverOp := rt.ServerOperator() err := serverOp.StartServer(ctx, args[0]) if err != nil { @@ -112,6 +122,11 @@ var serverOnCmd = &cobra.Command{ func serverOffCmdRun(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } serverOp := rt.ServerOperator() if serverFlags.forceShutdown { err := serverOp.StopServer(ctx, args[0]) @@ -137,6 +152,11 @@ var serverOffCmd = &cobra.Command{ func serverRmCmdRun(cmd *cobra.Command, args []string) error { serverOp := rt.ServerOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } id := args[0] s, err := serverOp.GetServer(ctx, id) if err != nil { @@ -287,6 +307,11 @@ To create a server without any storage just omit --with-template flag: serverOp := rt.ServerOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } profile, err := toHardwareProfile(serverFlags.profile) if err != nil { return NewError(cmd, "Cannot create server", err) @@ -393,6 +418,11 @@ var serverSetCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { serverOp := rt.ServerOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } serverUpdateRequest := gsclient.ServerUpdateRequest{ Cores: serverFlags.cores, Memory: serverFlags.memory, @@ -426,6 +456,11 @@ var serverAssignCmd = &cobra.Command{ serverID = args[0] ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } ipOp := rt.IPOperator() addr := net.ParseIP(args[1]) if addr != nil { @@ -464,6 +499,11 @@ Only list request IDs of a server (in case you need to tell suport what happened RunE: func(cmd *cobra.Command, args []string) error { serverID := args[0] ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } serverOp := rt.ServerOperator() events, err := serverOp.GetServerEventList(ctx, serverID) if err != nil { diff --git a/cmd/sshKey.go b/cmd/sshKey.go index 07e5b778..fbbebb46 100644 --- a/cmd/sshKey.go +++ b/cmd/sshKey.go @@ -33,6 +33,11 @@ var sshKeyLsCmd = &cobra.Command{ Long: `List SSH key objects.`, RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) op := rt.SSHKeyOperator() sshkeys, err := op.GetSshkeyList(ctx) @@ -80,6 +85,11 @@ var sshKeyAddCmd = &cobra.Command{ return NewError(cmd, "Error reading file", err) } ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } op := rt.SSHKeyOperator() _, err = op.CreateSshkey(ctx, gsclient.SshkeyCreateRequest{ Name: sshKeyFlags.name, @@ -100,6 +110,11 @@ var sshKeyRmCmd = &cobra.Command{ Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } op := rt.SSHKeyOperator() err := op.DeleteSshkey(ctx, args[0]) if err != nil { diff --git a/cmd/storage.go b/cmd/storage.go index ec1434af..f054e379 100644 --- a/cmd/storage.go +++ b/cmd/storage.go @@ -40,6 +40,11 @@ var storageLsCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { storageOp := rt.StorageOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) storages, err := storageOp.GetStorageList(ctx) if err != nil { @@ -106,6 +111,11 @@ Shrink a storage: }, RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } storageOp := rt.StorageOperator() updateReq := gsclient.StorageUpdateRequest{} if len(storageFlags.name) > 0 { @@ -145,6 +155,11 @@ var storageRmCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { storageOp := rt.StorageOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } err := storageOp.DeleteStorage(ctx, args[0]) if err != nil { return NewError(cmd, "Deleting storage failed", err) diff --git a/cmd/template.go b/cmd/template.go index ab49dc2d..37d0e16b 100644 --- a/cmd/template.go +++ b/cmd/template.go @@ -26,6 +26,11 @@ var templateLsCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { templateOp := rt.TemplateOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } out := new(bytes.Buffer) templates, err := templateOp.GetTemplateList(ctx) if err != nil { @@ -70,6 +75,11 @@ var templateRmCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { storageOp := rt.TemplateOperator() ctx := context.Background() + if rootFlags.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, rootFlags.timeout) + defer cancel() + } err := storageOp.DeleteTemplate(ctx, args[0]) if err != nil { return NewError(cmd, "Deleting template failed", err)