package kube
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Native OCaml Kubernetes client and controller runtime
Install
dune-project
Dependency
Authors
Maintainers
Sources
v0.1.3.tar.gz
md5=3c6916dea849fb2842c4a2e8a92b6cdd
sha512=b57daa3bb4879e1f33efc157cf5c648f88455e0f7c87484d572096d46345d6a8081cc7cefb4887aa95507af8daf73b3b8570b8583106953a5ba96a874159ff00
doc/src/kube.test/kube_test.ml.html
Source file kube_test.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261module Transport = struct type request = { meth : Kube.Http.meth; target : string; headers : (string * string) list; body : string option; streaming : bool; max_body_bytes : int option; } type reply = { status : int; reason : string; headers : (string * string) list; chunks : string list; wait_for_cancel : bool; } type outcome = Reply of reply | Failure of string let default_reason = function | 200 -> "OK" | 201 -> "Created" | 202 -> "Accepted" | 204 -> "No Content" | 400 -> "Bad Request" | 401 -> "Unauthorized" | 403 -> "Forbidden" | 404 -> "Not Found" | 409 -> "Conflict" | 410 -> "Gone" | 422 -> "Unprocessable Entity" | 429 -> "Too Many Requests" | 500 -> "Internal Server Error" | 503 -> "Service Unavailable" | _ -> "" let validate_status status = if status < 100 || status > 599 then invalid_arg "Kube_test.Transport: status must be between 100 and 599" let make_reply ?(status = 200) ?reason ?(headers = []) ?(wait_for_cancel = false) chunks = validate_status status; Reply { status; reason = Option.value ~default:(default_reason status) reason; headers; chunks; wait_for_cancel; } let respond ?status ?reason ?headers body = make_reply ?status ?reason ?headers [ body ] let respond_json ?status ?reason ?(headers = []) json = respond ?status ?reason ~headers: (if List.exists (fun (name, _) -> String.lowercase_ascii name = "content-type") headers then headers else ("Content-Type", "application/json") :: headers) (Yojson.Safe.to_string json) let stream ?status ?reason ?headers ?wait_for_cancel chunks = make_reply ?status ?reason ?headers ?wait_for_cancel chunks let fail message = Failure message type source = Handler of (request -> outcome) | Scripted of outcome Queue.t type state = { lock : Mutex.t; source : source; mutable requests_rev : request list; mutable unexpected_rev : string list; closed : bool Atomic.t; } type t = { state : state; transport : Kube.Client.Transport.t } let capture (request : Kube.Client.Transport.request) = { meth = request.meth; target = request.target; headers = request.headers; body = request.body; streaming = Option.is_some request.on_chunk; max_body_bytes = request.max_body_bytes; } let cancelled (request : Kube.Client.Transport.request) = Option.fold ~none:false ~some:Kube.Cancel.is_cancelled request.cancel let body_limit (request : Kube.Client.Transport.request) = Option.value ~default:(32 * 1024 * 1024) request.max_body_bytes let deliver request = function | Failure message -> Error message | Reply reply -> ( let limit = body_limit request in if limit < 0 then Error "max_body_bytes must not be negative" else if cancelled request then Error "request cancelled" else let successful = reply.status >= 200 && reply.status < 300 in let stream_success = successful && Option.is_some request.on_chunk in let no_body = (reply.status >= 100 && reply.status < 200) || reply.status = 204 || reply.status = 304 in let chunks = if no_body then [] else reply.chunks in let body = Buffer.create 256 in let buffered = ref 0 in let rec consume = function | [] -> Ok () | chunk :: rest -> ( if cancelled request then Error "request cancelled" else if not stream_success then ( buffered := !buffered + String.length chunk; if !buffered > limit then Error (Printf.sprintf "HTTP body exceeds %d bytes" limit) else ( Buffer.add_string body chunk; consume rest)) else match request.on_chunk with | None -> assert false | Some callback -> ( try callback chunk; consume rest with exn -> Error ("stream callback raised: " ^ Printexc.to_string exn)) ) in match consume chunks with | Error _ as error -> error | Ok () -> if reply.wait_for_cancel then match request.cancel with | None -> Error "wait_for_cancel response requires a request \ cancellation token" | Some cancel -> if Kube.Cancel.sleep cancel 86_400. then Error "wait_for_cancel response reached its safety timeout" else Error "request cancelled" else Ok { Kube.Http.status = reply.status; reason = reply.reason; headers = reply.headers; body = Buffer.contents body; }) let source_outcome state request = Mutex.lock state.lock; let captured = capture request in state.requests_rev <- captured :: state.requests_rev; let outcome = match state.source with | Handler handler -> `Handler handler | Scripted queue -> if Queue.is_empty queue then ( let message = Printf.sprintf "unexpected request %s" (match captured.meth with | `GET -> "GET " ^ captured.target | `POST -> "POST " ^ captured.target | `PUT -> "PUT " ^ captured.target | `PATCH -> "PATCH " ^ captured.target | `DELETE -> "DELETE " ^ captured.target) in state.unexpected_rev <- message :: state.unexpected_rev; `Outcome (Failure message)) else `Outcome (Queue.pop queue) in Mutex.unlock state.lock; match outcome with | `Outcome outcome -> outcome | `Handler handler -> handler captured let make source = let state = { lock = Mutex.create (); source; requests_rev = []; unexpected_rev = []; closed = Atomic.make false; } in let transport = Kube.Client.Transport.make ~close:(fun () -> Atomic.set state.closed true) (fun request -> source_outcome state request |> deliver request) in { state; transport } let create handler = make (Handler handler) let scripted outcomes = let queue = Queue.create () in List.iter (fun outcome -> Queue.push outcome queue) outcomes; make (Scripted queue) let client_transport test = test.transport let protect state fn = Mutex.lock state.lock; Fun.protect ~finally:(fun () -> Mutex.unlock state.lock) fn let requests test = protect test.state (fun () -> List.rev test.state.requests_rev) let request_count test = protect test.state (fun () -> List.length test.state.requests_rev) let remaining test = protect test.state (fun () -> match test.state.source with | Handler _ -> None | Scripted queue -> Some (Queue.length queue)) let is_closed test = Atomic.get test.state.closed let verify_complete test = protect test.state (fun () -> match (test.state.source, List.rev test.state.unexpected_rev) with | Handler _, _ -> Ok () | Scripted _, message :: _ -> Error message | Scripted queue, [] when Queue.is_empty queue -> Ok () | Scripted queue, [] -> Error (Printf.sprintf "%d scripted response(s) were not consumed" (Queue.length queue))) let header (request : request) name = let name = String.lowercase_ascii name in List.find_map (fun (candidate, value) -> if String.lowercase_ascii candidate = name then Some value else None) request.headers end let config ?(server = Uri.of_string "https://kubernetes.test") ?namespace ?(credential = Kube.Config.Anonymous) () = Kube.Config.make ?namespace ~credential server let client ?configuration ?rate_limiter ?logger transport = let configuration = Option.value ~default:(config ()) configuration in Kube.Client.create_with_transport ?rate_limiter ?logger ~transport:(Transport.client_transport transport) configuration
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>