Skip to content

Commit 0d807b0

Browse files
authored
chore: prefer http.jl backend with openapi (#65)
The recent version of OpenAPI.jl added ability to switch the HTTP library between Downloads.jl and HTTP.jl. With Kuber.jl we prefer HTTP.jl as it might help avoid certain concurrency issues observed earlier.
1 parent 37c5301 commit 0d807b0

3 files changed

Lines changed: 64 additions & 11 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ authors = ["JuliaHub Inc."]
44
keywords = ["kubernetes", "client"]
55
license = "MIT"
66
desc = "Julia Kubernetes Client"
7-
version = "0.7.7"
7+
version = "0.7.8"
88

99
[deps]
1010
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"

src/helpers.jl

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,39 @@ Backoff to use when retrying k8s APIs. The default minimum is 2 TPS.
3838
"""
3939
k8s_retry(f; max_tries=1, tps=2) = retry(f, delays=k8s_delay(tps,max_tries), check=k8s_retry_cond)()
4040

41+
"""
42+
Build keyword arguments for OpenAPI.Clients.Client constructor.
43+
Detects if HTTP.jl backend is available (OpenAPI >= 0.2.1) and uses it by default.
44+
45+
Args:
46+
- httplib: Optional HTTP library to use (:http or :downloads). Defaults to HTTP.jl if available.
47+
"""
48+
function _openapi_client_kwargs(httplib::Union{Nothing,Symbol}=nothing)
49+
kwargs = Dict{Symbol,Any}()
50+
if isdefined(OpenAPI.Clients, :HTTPLib)
51+
if httplib === nothing
52+
kwargs[:httplib] = OpenAPI.Clients.HTTPLib.HTTP # Default to HTTP.jl
53+
else
54+
kwargs[:httplib] = httplib
55+
end
56+
end
57+
return kwargs
58+
end
59+
60+
"""
61+
Return appropriate Connection header value based on HTTP backend.
62+
63+
Args:
64+
- httplib: Optional HTTP library being used (:http or :downloads). Defaults to HTTP.jl logic.
65+
"""
66+
function _connection_header_value(httplib::Union{Nothing,Symbol}=nothing)
67+
if !isdefined(OpenAPI.Clients, :HTTPLib)
68+
return "close"
69+
end
70+
effective_lib = httplib === nothing ? OpenAPI.Clients.HTTPLib.HTTP : httplib
71+
return effective_lib === OpenAPI.Clients.HTTPLib.HTTP ? "Keep-Alive" : "close"
72+
end
73+
4174
const KuberEventStream = Channel{Any}
4275

4376
struct KApi
@@ -54,13 +87,15 @@ mutable struct KuberContext
5487
default_retries::Int
5588
retry_all_apis::Bool
5689
initialized::Bool
90+
httplib::Union{Nothing,Symbol}
5791

58-
function KuberContext(apimodule::Module=ApiImpl; kwargs...)
92+
function KuberContext(apimodule::Module=ApiImpl; httplib::Union{Nothing,Symbol}=nothing, kwargs...)
5993
kctx = new(apimodule)
6094

6195
rtfn = (return_types,response_code,response_data)->kuber_type(kctx, return_types, response_code, response_data)
62-
openapiclient = OpenAPI.Clients.Client(DEFAULT_URI; get_return_type=rtfn, kwargs...)
63-
openapiclient.headers["Connection"] = "close"
96+
client_kwargs = _openapi_client_kwargs(httplib)
97+
openapiclient = OpenAPI.Clients.Client(DEFAULT_URI; get_return_type=rtfn, client_kwargs..., kwargs...)
98+
openapiclient.headers["Connection"] = _connection_header_value(httplib)
6499

65100
kctx.client = openapiclient
66101
kctx.apis = Dict{Symbol,Vector}()
@@ -69,6 +104,7 @@ mutable struct KuberContext
69104
kctx.default_retries = 5
70105
kctx.retry_all_apis = false
71106
kctx.initialized = false
107+
kctx.httplib = httplib
72108
return kctx
73109
end
74110
end
@@ -90,10 +126,10 @@ end
90126

91127
function KuberException(response::OpenAPI.Clients.ApiResponse, status::Union{Nothing,OpenAPI.APIModel})
92128
http_response = response.raw
93-
if !(200 <= http_response.status <= 299)
94-
message = http_response.message
95-
code = http_response.status
96-
end
129+
code = http_response.status
130+
131+
# HTTP.Response doesn't have .message field like Downloads.Response
132+
message = hasproperty(http_response, :message) ? http_response.message : "HTTP $code"
97133

98134
# if status is available, use it to override the message and code
99135
if !isnothing(status)
@@ -212,7 +248,7 @@ get_server(ctx::KuberContext) = ctx.client.root
212248
get_ns(ctx::KuberContext) = ctx.namespace
213249

214250
"""
215-
set_server(ctx, uri, reset_api_versions=false; max_tries=5, kwargs...)
251+
set_server(ctx, uri, reset_api_versions=false; max_tries=5, httplib=nothing, kwargs...)
216252
217253
Set the Kubernetes API server endpoint for a context.
218254
@@ -224,6 +260,7 @@ Args:
224260
Keyword Args:
225261
- max_tries: retries allowed while probing API versions from server
226262
- verbose: Log API versions
263+
- httplib: HTTP library to use (:http or :downloads). If not specified, uses the context's stored httplib setting.
227264
- kwargs: other keyword args to pass on while constructing the client for API server (see OpenAPI.jl - https://github.com/JuliaComputing/OpenAPI.jl#readme)
228265
"""
229266
function set_server(
@@ -233,11 +270,22 @@ function set_server(
233270
max_tries=retries(ctx, false),
234271
verbose::Bool=false,
235272
debug::Bool=false,
273+
httplib::Union{Nothing,Symbol}=nothing,
236274
kwargs...
237275
)
276+
# Use provided httplib, or fall back to context's stored value
277+
effective_httplib = httplib === nothing ? ctx.httplib : httplib
278+
238279
rtfn = (return_types,response_code,response_data)->kuber_type(ctx, return_types, response_code, response_data)
239-
ctx.client = OpenAPI.Clients.Client(uri; get_return_type=rtfn, verbose=debug, kwargs...)
240-
ctx.client.headers["Connection"] = "close"
280+
client_kwargs = _openapi_client_kwargs(effective_httplib)
281+
ctx.client = OpenAPI.Clients.Client(uri; get_return_type=rtfn, verbose=debug, client_kwargs..., kwargs...)
282+
ctx.client.headers["Connection"] = _connection_header_value(effective_httplib)
283+
284+
# Update stored httplib if explicitly provided
285+
if httplib !== nothing
286+
ctx.httplib = httplib
287+
end
288+
241289
reset_api_versions && set_api_versions!(
242290
ctx;
243291
max_tries=max_tries,

test/runtests.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ function init_context(override=nothing, verbose=true)
2121
set_ns(ctx, "default")
2222
set_retries(ctx; count=3, all_apis=false)
2323
Kuber.set_api_versions!(ctx; override=override, verbose=verbose)
24+
httplib_name = ctx.httplib === nothing ? "http (default)" : string(ctx.httplib)
25+
@info("KuberContext preferred HTTP library: $httplib_name")
26+
if hasproperty(ctx.client, :httplib)
27+
@info("OpenAPI client using HTTP library: $(ctx.client.httplib)")
28+
end
2429
ctx
2530
end
2631

0 commit comments

Comments
 (0)