From 540c74a3047270887bfe6f334fbc9e4909f3cb58 Mon Sep 17 00:00:00 2001 From: tan Date: Thu, 29 Jan 2026 12:21:22 +0530 Subject: [PATCH] chore: prefer http.jl backend with openapi 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. --- Project.toml | 2 +- src/helpers.jl | 68 +++++++++++++++++++++++++++++++++++++++++------- test/runtests.jl | 5 ++++ 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index 7740489..1271299 100644 --- a/Project.toml +++ b/Project.toml @@ -4,7 +4,7 @@ authors = ["JuliaHub Inc."] keywords = ["kubernetes", "client"] license = "MIT" desc = "Julia Kubernetes Client" -version = "0.7.7" +version = "0.7.8" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/src/helpers.jl b/src/helpers.jl index 39f7cce..eff6e63 100644 --- a/src/helpers.jl +++ b/src/helpers.jl @@ -38,6 +38,39 @@ Backoff to use when retrying k8s APIs. The default minimum is 2 TPS. """ k8s_retry(f; max_tries=1, tps=2) = retry(f, delays=k8s_delay(tps,max_tries), check=k8s_retry_cond)() +""" +Build keyword arguments for OpenAPI.Clients.Client constructor. +Detects if HTTP.jl backend is available (OpenAPI >= 0.2.1) and uses it by default. + +Args: +- httplib: Optional HTTP library to use (:http or :downloads). Defaults to HTTP.jl if available. +""" +function _openapi_client_kwargs(httplib::Union{Nothing,Symbol}=nothing) + kwargs = Dict{Symbol,Any}() + if isdefined(OpenAPI.Clients, :HTTPLib) + if httplib === nothing + kwargs[:httplib] = OpenAPI.Clients.HTTPLib.HTTP # Default to HTTP.jl + else + kwargs[:httplib] = httplib + end + end + return kwargs +end + +""" +Return appropriate Connection header value based on HTTP backend. + +Args: +- httplib: Optional HTTP library being used (:http or :downloads). Defaults to HTTP.jl logic. +""" +function _connection_header_value(httplib::Union{Nothing,Symbol}=nothing) + if !isdefined(OpenAPI.Clients, :HTTPLib) + return "close" + end + effective_lib = httplib === nothing ? OpenAPI.Clients.HTTPLib.HTTP : httplib + return effective_lib === OpenAPI.Clients.HTTPLib.HTTP ? "Keep-Alive" : "close" +end + const KuberEventStream = Channel{Any} struct KApi @@ -54,13 +87,15 @@ mutable struct KuberContext default_retries::Int retry_all_apis::Bool initialized::Bool + httplib::Union{Nothing,Symbol} - function KuberContext(apimodule::Module=ApiImpl; kwargs...) + function KuberContext(apimodule::Module=ApiImpl; httplib::Union{Nothing,Symbol}=nothing, kwargs...) kctx = new(apimodule) rtfn = (return_types,response_code,response_data)->kuber_type(kctx, return_types, response_code, response_data) - openapiclient = OpenAPI.Clients.Client(DEFAULT_URI; get_return_type=rtfn, kwargs...) - openapiclient.headers["Connection"] = "close" + client_kwargs = _openapi_client_kwargs(httplib) + openapiclient = OpenAPI.Clients.Client(DEFAULT_URI; get_return_type=rtfn, client_kwargs..., kwargs...) + openapiclient.headers["Connection"] = _connection_header_value(httplib) kctx.client = openapiclient kctx.apis = Dict{Symbol,Vector}() @@ -69,6 +104,7 @@ mutable struct KuberContext kctx.default_retries = 5 kctx.retry_all_apis = false kctx.initialized = false + kctx.httplib = httplib return kctx end end @@ -90,10 +126,10 @@ end function KuberException(response::OpenAPI.Clients.ApiResponse, status::Union{Nothing,OpenAPI.APIModel}) http_response = response.raw - if !(200 <= http_response.status <= 299) - message = http_response.message - code = http_response.status - end + code = http_response.status + + # HTTP.Response doesn't have .message field like Downloads.Response + message = hasproperty(http_response, :message) ? http_response.message : "HTTP $code" # if status is available, use it to override the message and code if !isnothing(status) @@ -212,7 +248,7 @@ get_server(ctx::KuberContext) = ctx.client.root get_ns(ctx::KuberContext) = ctx.namespace """ - set_server(ctx, uri, reset_api_versions=false; max_tries=5, kwargs...) + set_server(ctx, uri, reset_api_versions=false; max_tries=5, httplib=nothing, kwargs...) Set the Kubernetes API server endpoint for a context. @@ -224,6 +260,7 @@ Args: Keyword Args: - max_tries: retries allowed while probing API versions from server - verbose: Log API versions +- httplib: HTTP library to use (:http or :downloads). If not specified, uses the context's stored httplib setting. - kwargs: other keyword args to pass on while constructing the client for API server (see OpenAPI.jl - https://github.com/JuliaComputing/OpenAPI.jl#readme) """ function set_server( @@ -233,11 +270,22 @@ function set_server( max_tries=retries(ctx, false), verbose::Bool=false, debug::Bool=false, + httplib::Union{Nothing,Symbol}=nothing, kwargs... ) + # Use provided httplib, or fall back to context's stored value + effective_httplib = httplib === nothing ? ctx.httplib : httplib + rtfn = (return_types,response_code,response_data)->kuber_type(ctx, return_types, response_code, response_data) - ctx.client = OpenAPI.Clients.Client(uri; get_return_type=rtfn, verbose=debug, kwargs...) - ctx.client.headers["Connection"] = "close" + client_kwargs = _openapi_client_kwargs(effective_httplib) + ctx.client = OpenAPI.Clients.Client(uri; get_return_type=rtfn, verbose=debug, client_kwargs..., kwargs...) + ctx.client.headers["Connection"] = _connection_header_value(effective_httplib) + + # Update stored httplib if explicitly provided + if httplib !== nothing + ctx.httplib = httplib + end + reset_api_versions && set_api_versions!( ctx; max_tries=max_tries, diff --git a/test/runtests.jl b/test/runtests.jl index 610d8d3..96851bb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,6 +21,11 @@ function init_context(override=nothing, verbose=true) set_ns(ctx, "default") set_retries(ctx; count=3, all_apis=false) Kuber.set_api_versions!(ctx; override=override, verbose=verbose) + httplib_name = ctx.httplib === nothing ? "http (default)" : string(ctx.httplib) + @info("KuberContext preferred HTTP library: $httplib_name") + if hasproperty(ctx.client, :httplib) + @info("OpenAPI client using HTTP library: $(ctx.client.httplib)") + end ctx end