Skip to content

Commit

Permalink
Add global timeout flag
Browse files Browse the repository at this point in the history
This patch adds a global timeout flag (viewable with `oc options`) with
a default value of `0s` (meaning no timeout). It renames all local
`timeout` flags previously declared in specific commands, such as
`kubectl delete` and `kubectl scale`, to avoid conflict with the new
global timeout flag.

No new functionality is added to enforce a timeout. The timeout value is
added to the default http client, so that zero values and default behavior
is enforced by the client.

**Example**
```
$ kubectl get pods # no timeout flag set - default to 0s (which means no
timeout)
NAME                      READY     STATUS    RESTARTS   AGE
docker-registry-1-h7etw   1/1       Running   1          2h
router-1-uv0f9            1/1       Running   1          2h

$ kubectl get pods --timeout=0 # zero means no timeout no timeout flag set
NAME                      READY     STATUS    RESTARTS   AGE
docker-registry-1-h7etw   1/1       Running   1          2h
router-1-uv0f9            1/1       Running   1          2h

$kubectl get pods --timeout=1ms
Unable to connect to the server: net/http: request canceled while
waiting for connection (Client.Timeout exceeded while awaiting headers)
```
  • Loading branch information
juanvallejo committed Oct 3, 2016
1 parent 3933ddb commit dd809e8
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 13 deletions.
10 changes: 10 additions & 0 deletions pkg/client/restclient/config.go
Expand Up @@ -25,6 +25,7 @@ import (
"path"
gruntime "runtime"
"strings"
"time"

"github.com/golang/glog"

Expand Down Expand Up @@ -109,6 +110,9 @@ type Config struct {
// Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
RateLimiter flowcontrol.RateLimiter

// The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
Timeout time.Duration

// Version forces a specific version to be used (if registered)
// Do we need this?
// Version string
Expand Down Expand Up @@ -185,6 +189,9 @@ func RESTClientFor(config *Config) (*RESTClient, error) {
var httpClient *http.Client
if transport != http.DefaultTransport {
httpClient = &http.Client{Transport: transport}
if config.Timeout > 0 {
httpClient.Timeout = config.Timeout
}
}

return NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, qps, burst, config.RateLimiter, httpClient)
Expand All @@ -210,6 +217,9 @@ func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
var httpClient *http.Client
if transport != http.DefaultTransport {
httpClient = &http.Client{Transport: transport}
if config.Timeout > 0 {
httpClient.Timeout = config.Timeout
}
}

versionConfig := config.ContentConfig
Expand Down
1 change: 1 addition & 0 deletions pkg/client/unversioned/clientcmd/client_config.go
Expand Up @@ -108,6 +108,7 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {

clientConfig := &restclient.Config{}
clientConfig.Host = configClusterInfo.Server
clientConfig.Timeout = config.overrides.Timeout
if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
u.RawQuery = ""
u.Fragment = ""
Expand Down
23 changes: 22 additions & 1 deletion pkg/client/unversioned/clientcmd/overrides.go
Expand Up @@ -18,6 +18,7 @@ package clientcmd

import (
"strconv"
"time"

"github.com/spf13/pflag"

Expand All @@ -33,6 +34,7 @@ type ConfigOverrides struct {
ClusterInfo clientcmdapi.Cluster
Context clientcmdapi.Context
CurrentContext string
Timeout time.Duration
}

// ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly
Expand All @@ -42,6 +44,7 @@ type ConfigOverrideFlags struct {
ClusterOverrideFlags ClusterOverrideFlags
ContextOverrideFlags ContextOverrideFlags
CurrentContext FlagInfo
Timeout FlagInfo
}

// AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects
Expand Down Expand Up @@ -105,6 +108,20 @@ func (f FlagInfo) BindBoolFlag(flags *pflag.FlagSet, target *bool) {
}
}

// BindDurationFlag binds the flag based on the provided info. If LongName == "", nothing is registered
func (f FlagInfo) BindDurationFlag(flags *pflag.FlagSet, target *time.Duration) {
// you can't register a flag without a long name
if len(f.LongName) > 0 {
// try to parse Default as a Duration. If it fails, default to 0s
durationVal, err := time.ParseDuration(f.Default)
if err != nil {
durationVal = 0
}

flags.DurationVarP(target, f.LongName, f.ShortName, durationVal, f.Description)
}
}

const (
FlagClusterName = "cluster"
FlagAuthInfoName = "user"
Expand All @@ -121,6 +138,7 @@ const (
FlagImpersonate = "as"
FlagUsername = "username"
FlagPassword = "password"
FlagTimeout = "timeout"
)

// RecommendedAuthOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
Expand Down Expand Up @@ -151,7 +169,9 @@ func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags {
AuthOverrideFlags: RecommendedAuthOverrideFlags(prefix),
ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix),
ContextOverrideFlags: RecommendedContextOverrideFlags(prefix),
CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"},

CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"},
Timeout: FlagInfo{prefix + FlagTimeout, "", "", "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests."},
}
}

Expand Down Expand Up @@ -190,6 +210,7 @@ func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNam
BindClusterFlags(&overrides.ClusterInfo, flags, flagNames.ClusterOverrideFlags)
BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags)
flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext)
flagNames.Timeout.BindDurationFlag(flags, &overrides.Timeout)
}

// BindFlags is a convenience method to bind the specified flags to their associated variables
Expand Down
4 changes: 2 additions & 2 deletions pkg/kubectl/cmd/delete.go
Expand Up @@ -102,7 +102,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().Bool("cascade", true, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.")
cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.")
cmd.Flags().Bool("now", false, "If true, resources are force terminated without graceful deletion (same as --grace-period=0).")
cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
cmd.Flags().Duration("delete-timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
cmdutil.AddOutputFlagsForMutation(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
return cmd
Expand Down Expand Up @@ -153,7 +153,7 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
shortOutput := cmdutil.GetFlagString(cmd, "output") == "name"
// By default use a reaper to delete all related resources.
if cmdutil.GetFlagBool(cmd, "cascade") {
return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, cmdutil.GetFlagDuration(cmd, "timeout"), gracePeriod, shortOutput, mapper, false)
return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, cmdutil.GetFlagDuration(cmd, "delete-timeout"), gracePeriod, shortOutput, mapper, false)
}
return DeleteResult(r, out, ignoreNotFound, shortOutput, mapper)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/kubectl/cmd/replace.go
Expand Up @@ -80,7 +80,7 @@ func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().Bool("force", false, "Delete and re-create the specified resource")
cmd.Flags().Bool("cascade", false, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).")
cmd.Flags().Int("grace-period", -1, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.")
cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmd.Flags().Duration("delete-timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmdutil.AddValidateFlags(cmd)
cmdutil.AddOutputFlagsForMutation(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
Expand Down Expand Up @@ -118,8 +118,8 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st
return fmt.Errorf("--grace-period must have --force specified")
}

if cmdutil.GetFlagDuration(cmd, "timeout") != 0 {
return fmt.Errorf("--timeout must have --force specified")
if cmdutil.GetFlagDuration(cmd, "delete-timeout") != 0 {
return fmt.Errorf("--delete-timeout must have --force specified")
}

mapper, typer := f.Object()
Expand Down Expand Up @@ -207,7 +207,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
}
//Replace will create a resource if it doesn't exist already, so ignore not found error
ignoreNotFound := true
timeout := cmdutil.GetFlagDuration(cmd, "timeout")
timeout := cmdutil.GetFlagDuration(cmd, "delete-timeout")
// By default use a reaper to delete all related resources.
if cmdutil.GetFlagBool(cmd, "cascade") {
glog.Warningf("\"cascade\" is set, kubectl will delete and re-create all resources managed by this resource (e.g. Pods created by a ReplicationController). Consider using \"kubectl rolling-update\" if you want to update a ReplicationController together with its Pods.")
Expand Down
4 changes: 2 additions & 2 deletions pkg/kubectl/cmd/rollingupdate.go
Expand Up @@ -91,7 +91,7 @@ func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
}
cmd.Flags().Duration("update-period", updatePeriod, `Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
cmd.Flags().Duration("poll-interval", pollInterval, `Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
cmd.Flags().Duration("timeout", timeout, `Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
cmd.Flags().Duration("update-timeout", timeout, `Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
usage := "Filename or URL to file to use to create the new replication controller."
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
cmd.MarkFlagRequired("filename")
Expand Down Expand Up @@ -158,7 +158,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
rollback := cmdutil.GetFlagBool(cmd, "rollback")
period := cmdutil.GetFlagDuration(cmd, "update-period")
interval := cmdutil.GetFlagDuration(cmd, "poll-interval")
timeout := cmdutil.GetFlagDuration(cmd, "timeout")
timeout := cmdutil.GetFlagDuration(cmd, "update-timeout")
dryrun := cmdutil.GetDryRunFlag(cmd)
outputFormat := cmdutil.GetFlagString(cmd, "output")
container := cmdutil.GetFlagString(cmd, "container")
Expand Down
4 changes: 2 additions & 2 deletions pkg/kubectl/cmd/scale.go
Expand Up @@ -82,7 +82,7 @@ func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().Int("current-replicas", -1, "Precondition for current size. Requires that the current size of the resource match this value in order to scale.")
cmd.Flags().Int("replicas", -1, "The new desired number of replicas. Required.")
cmd.MarkFlagRequired("replicas")
cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a scale operation, zero means don't wait. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmd.Flags().Duration("scale-timeout", 0, "The length of time to wait before giving up on a scale operation, zero means don't wait. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmdutil.AddOutputFlagsForMutation(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
Expand Down Expand Up @@ -154,7 +154,7 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout)

var waitForReplicas *kubectl.RetryParams
if timeout := cmdutil.GetFlagDuration(cmd, "timeout"); timeout != 0 {
if timeout := cmdutil.GetFlagDuration(cmd, "scale-timeout"); timeout != 0 {
waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout)
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/kubectl/cmd/stop.go
Expand Up @@ -69,7 +69,7 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().Bool("all", false, "[-all] to select all the specified resources.")
cmd.Flags().Bool("ignore-not-found", false, "Treat \"resource not found\" as a successful stop.")
cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.")
cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
cmd.Flags().Duration("delete-timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
cmdutil.AddOutputFlagsForMutation(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
return cmd
Expand All @@ -95,5 +95,5 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Write
return r.Err()
}
shortOutput := cmdutil.GetFlagString(cmd, "output") == "name"
return ReapResult(r, f, out, false, cmdutil.GetFlagBool(cmd, "ignore-not-found"), cmdutil.GetFlagDuration(cmd, "timeout"), cmdutil.GetFlagInt(cmd, "grace-period"), shortOutput, mapper, false)
return ReapResult(r, f, out, false, cmdutil.GetFlagBool(cmd, "ignore-not-found"), cmdutil.GetFlagDuration(cmd, "delete-timeout"), cmdutil.GetFlagInt(cmd, "grace-period"), shortOutput, mapper, false)
}

0 comments on commit dd809e8

Please sign in to comment.