Skip to content

Commit

Permalink
Add timeouts option
Browse files Browse the repository at this point in the history
Doesn't affect watch, only rest-client based functionality.
  • Loading branch information
cben committed Apr 27, 2017
1 parent e58c4de commit f0e691f
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 3 deletions.
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ uri = URI::HTTP.build(host: "somehostname", port: 8080)
client = Kubeclient::Client.new(uri)
```

### SSL

It is also possible to use https and configure ssl with:

```ruby
Expand Down Expand Up @@ -94,6 +96,8 @@ client = Kubeclient::Client.new(
)
```

### Authentication

If you are using basic authentication or bearer tokens as described
[here](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/authentication.md) then you can specify one
of the following:
Expand Down Expand Up @@ -148,7 +152,9 @@ client = Kubeclient::Client.new(

You can find information about tokens in [this guide](http://kubernetes.io/docs/user-guide/accessing-the-cluster/) and in [this reference](http://kubernetes.io/docs/admin/authentication/).

You can also use kubeclient with non-blocking sockets such as Celluloid::IO, see [here](https://github.com/httprb/http/wiki/Parallel-requests-with-Celluloid%3A%3AIO)
### Non-blocking IO

You can also use kubeclient with non-blocking sockets such as Celluloid::IO, see [here](https://github.com/httprb/http/wiki/Parallel-requests-with-Celluloid%3A%3AIO)
for details. For example:

```ruby
Expand All @@ -162,7 +168,11 @@ client = Kubeclient::Client.new(
)
```

You can also use kubeclient with an http proxy server such as tinyproxy. It can be entered as a string or a URI object
This affects only `.watch_*` sockets, not one-off actions like `.get_*`, `.delete_*` etc.

### Proxies

You can also use kubeclient with an http proxy server such as tinyproxy. It can be entered as a string or a URI object.
For example:
```ruby
proxy_uri = URI::HTTP.build(host: "myproxyhost", port: 8443)
Expand All @@ -171,6 +181,21 @@ client = Kubeclient::Client.new(
)
```


### Timeouts

Watching never times out.
One-off actions like `.get_*`, `.delete_*` etc. default to 60 sec to connect and 60 sec to receive response, but you can change them:
```ruby
timeouts = {
connect: 10, # unit is seconds
read: nil # nil means never time out
}
client = Kubeclient::Client.new(
'https://localhost:8443/api/', timeouts: timeouts
)
```

### Discovery

Discovery from the kube-apiserver is done lazily on method calls so it would not change behavior.
Expand Down
16 changes: 15 additions & 1 deletion lib/kubeclient/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ module ClientMixin
ssl_socket_class: nil
}.freeze

DEFAULT_TIMEOUTS = {
# These do NOT affect watch, watching never times out.
open: Net::HTTP.new('127.0.0.1').open_timeout, # depends on ruby version
read: Net::HTTP.new('127.0.0.1').read_timeout
}.freeze

DEFAULT_HTTP_PROXY_URI = nil

SEARCH_ARGUMENTS = {
Expand All @@ -50,6 +56,7 @@ def initialize_client(
ssl_options: DEFAULT_SSL_OPTIONS,
auth_options: DEFAULT_AUTH_OPTIONS,
socket_options: DEFAULT_SOCKET_OPTIONS,
timeouts: DEFAULT_TIMEOUTS,
http_proxy_uri: DEFAULT_HTTP_PROXY_URI
)
validate_auth_options(auth_options)
Expand All @@ -63,6 +70,9 @@ def initialize_client(
@ssl_options = ssl_options
@auth_options = auth_options
@socket_options = socket_options
# Allow passing partial timeouts hash, without unspecified
# @timeouts[:foo] == nil resulting in infinite timeout.
@timeouts = DEFAULT_TIMEOUTS.merge(timeouts)
@http_proxy_uri = http_proxy_uri.to_s if http_proxy_uri

if auth_options[:bearer_token]
Expand Down Expand Up @@ -215,7 +225,11 @@ def create_rest_client(path = nil)
ssl_client_key: @ssl_options[:client_key],
proxy: @http_proxy_uri,
user: @auth_options[:username],
password: @auth_options[:password]
password: @auth_options[:password],
# :timeout, :open_timeout combo works same in rest-client 1.x and 2.0.
# TODO: use cleaner :read_timeout when we bump rest-client >= 2.0.
timeout: @timeouts[:read],
open_timeout: @timeouts[:open]
}
RestClient::Resource.new(@api_endpoint.merge(path).to_s, options)
end
Expand Down
81 changes: 81 additions & 0 deletions test/test_kubeclient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ def test_api_ssl_failure
assert_equal(error_message, exception.message)
end

def test_api_timeout
stub_request(:get, 'http://localhost:8080/api').to_timeout

client = Kubeclient::Client.new('http://localhost:8080/api/')

exception = assert_raises(Kubeclient::HttpError) { client.api }
assert_match(/(timed out|timeout)/i, exception.message)
end

def test_api_valid
stub_request(:get, 'http://localhost:8080/api')
.to_return(status: 200, body: open_test_file('versions_list.json'))
Expand Down Expand Up @@ -614,6 +623,78 @@ def test_nil_items
client.get_persistent_volume_claims
end

# Timeouts

def test_timeouts_defaults
client = Kubeclient::Client.new(
'http://localhost:8080/api/'
)
rest_client = client.rest_client
assert_default_open_timeout(rest_client.open_timeout)
assert_equal(60, read_timeout(rest_client))
end

def test_timeouts_open
client = Kubeclient::Client.new(
'http://localhost:8080/api/',
timeouts: { open: 10 }
)
rest_client = client.rest_client
assert_equal(10, rest_client.open_timeout)
assert_equal(60, read_timeout(rest_client))
end

def test_timeouts_read
client = Kubeclient::Client.new(
'http://localhost:8080/api/',
timeouts: { read: 300 }
)
rest_client = client.rest_client
assert_default_open_timeout(rest_client.open_timeout)
assert_equal(300, read_timeout(rest_client))
end

def test_timeouts_both
client = Kubeclient::Client.new(
'http://localhost:8080/api/',
timeouts: { open: 10, read: 300 }
)
rest_client = client.rest_client
assert_equal(10, rest_client.open_timeout)
assert_equal(300, read_timeout(rest_client))
end

def test_timeouts_infinite
client = Kubeclient::Client.new(
'http://localhost:8080/api/',
timeouts: { open: nil, read: nil }
)
rest_client = client.rest_client
assert_nil(rest_client.open_timeout)
assert_nil(read_timeout(rest_client))
end

def read_timeout(rest_client)
# if rest_client.respond_to?(:read_timeout)
# rest_client.read_timeout # rest-client 2.0
# else
# rest_client.timeout # rest_client 1.x
# end

# That's not what happens. we always set :timeout, which on rest-client 2.0
# is not exposed as an accessor, but is sucessfully passed to Request.execute.
# Too brittle :-(
rest_client.options[:timeout]
end

def assert_default_open_timeout(actual)
if RUBY_VERSION >= '2.3'
assert_equal(60, actual)
else
assert_nil(actual)
end
end

private

# dup method creates a shallow copy which is not good in this case
Expand Down

0 comments on commit f0e691f

Please sign in to comment.