Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better error msgs #18

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
171 changes: 98 additions & 73 deletions unikernel.ml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,22 @@ module Main

(* cache control: all resources use last-modified + etag of last commit *)
let retrieve_last_commit store =
Store.digest store Mirage_kv.Key.empty >>= fun last_hash ->
Store.last_modified store Mirage_kv.Key.empty >|= fun r ->
let v = Result.fold ~ok:Fun.id ~error:(fun _ -> Ptime.v (Pclock.now_d_ps ())) r in
let last_date = ptime_to_http_date v in
last := (last_date, Result.get_ok last_hash)
Store.digest store Mirage_kv.Key.empty >>= function
| Error (`Not_found _) ->
let msg = Fmt.str "No commit found - are you on the correct branch?" in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's indeed a good way (we probably should print out which branch we want to clone).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which branch are you thinking about? My thinking was that the user knows what branch is passed, and multiple possible branches can be present - so I wouldn't know what branch to suggest

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the remote used by git-kv you can optionally specify a branch. It seems git-kv doesn't have a function to get the branch used - it may not be obvious what the default branch is, and I believe it changed from master to main a few months back.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, on Debian, the default branch still is master and we currently occur several cases where people don't really understand this kind of failure. I will try to provide an accessor on git-kv to get the branch used.

Lwt_result.fail @@ `Msg msg
| Error err ->
let msg = Fmt.str "%a" Store.pp_error err in
Lwt_result.fail @@ `Msg msg
| Ok last_hash ->
Store.last_modified store Mirage_kv.Key.empty >|= fun r ->
let v = Result.fold r
~ok:Fun.id
~error:(fun _ -> Pclock.now_d_ps () |> Ptime.v)
in
let last_date = ptime_to_http_date v in
last := (last_date, last_hash);
Ok ()

let not_modified request =
match Httpaf.Headers.get request.Httpaf.Request.headers "if-modified-since" with
Expand Down Expand Up @@ -132,11 +143,15 @@ module Main
Httpaf.Reqd.respond_with_string reqd resp data ;
Lwt.return_unit
| Error (`Msg msg) ->
Logs.err (fun m -> m "%s" msg);
(*> Note: Client shouldn't get internal error strings*)
let user_msg =
"Internal server error: See server log for more info" in
let headers = Httpaf.Headers.of_list
[ "content-length", string_of_int (String.length msg) ] in
[ "content-length", string_of_int (String.length user_msg) ] in
let resp = Httpaf.Response.create ~headers `Internal_server_error in
http_status resp;
Httpaf.Reqd.respond_with_string reqd resp msg ;
Httpaf.Reqd.respond_with_string reqd resp user_msg;
Lwt.return_unit
end
else
Expand Down Expand Up @@ -228,8 +243,10 @@ module Main
Git_kv.pull store >>= function
| Ok [] -> Lwt.return_ok "pulled, no changes"
| Ok _ ->
Last_modified.retrieve_last_commit store >>= fun () ->
Lwt.return_ok ("pulled " ^ Last_modified.etag ())
Last_modified.retrieve_last_commit store
|> Lwt_result.map (fun () ->
"pulled " ^ Last_modified.etag ()
)
| Error _ as e -> Lwt.return e
in
Dispatch.dispatch store hookf (Key_gen.hook ())
Expand All @@ -241,71 +258,79 @@ module Main
Logs.err (fun m -> m "cannot decode key type %s: %s" kt msg);
exit argument_error

let failwith_msg t = t >>= function
| Ok v -> Lwt.return v
| Error (`Msg msg) -> Lwt.fail_with msg

let start git_ctx () () stackv4v6 http_client =
Git_kv.connect git_ctx (Key_gen.remote ()) >>= fun store ->
Last_modified.retrieve_last_commit store >>= fun () ->
Last_modified.retrieve_last_commit store |> failwith_msg >>= fun () ->
Logs.info (fun m -> m "pulled %s" (Last_modified.etag ()));
Lwt.map
(function Ok () -> Lwt.return_unit | Error (`Msg msg) -> Lwt.fail_with msg)
(Logs.info (fun m -> m "store: %s" (Last_modified.etag ()));
if Key_gen.tls () then begin
let rec provision () =
Paf.init ~port:80 (Stack.tcp stackv4v6) >>= fun t ->
let service =
Paf.http_service ~error_handler (fun _ -> LE.request_handler)
in
let stop = Lwt_switch.create () in
let `Initialized th0 = Paf.serve ~stop service t in
Logs.info (fun m ->
m "listening on 80/HTTP (let's encrypt provisioning)");
let th1 =
LE.provision_certificate
~production:(Key_gen.production ())
{ LE.certificate_seed = Key_gen.cert_seed ()
; LE.certificate_key_type = key_type (Key_gen.cert_key_type ())
; LE.certificate_key_bits = Some (Key_gen.cert_bits ())
; LE.email = Option.bind (Key_gen.email ()) (fun e -> Emile.of_string e |> Result.to_option)
; LE.account_seed = Key_gen.account_seed ()
; LE.account_key_type = key_type (Key_gen.account_key_type ())
; LE.account_key_bits = Some (Key_gen.account_bits ())
; LE.hostname = Key_gen.hostname () |> Option.get |> Domain_name.of_string_exn |> Domain_name.host_exn }
http_client
>>? fun certificates ->
Lwt_switch.turn_off stop >>= fun () -> Lwt.return_ok certificates in
Lwt.both th0 th1 >>= function
| ((), (Error _ as err)) -> Lwt.return err
| ((), Ok certificates) ->
Logs.debug (fun m -> m "Got certificates from let's encrypt.") ;
let tls = Tls.Config.server ~certificates () in
Paf.init ~port:(Key_gen.https_port ()) (Stack.tcp stackv4v6) >>= fun t ->
let service =
Paf.https_service ~tls ~error_handler (request_handler store)
in
let stop = Lwt_switch.create () in
let `Initialized th0 = Paf.serve ~stop service t in
Logs.info (fun m ->
m "listening on %d/HTTPS" (Key_gen.port ()));
Paf.init ~port:(Key_gen.port ()) (Stack.tcp stackv4v6) >>= fun t ->
let service =
let to_port = (Key_gen.https_port ()) in
Paf.http_service ~error_handler (Dispatch.redirect to_port)
in
let `Initialized th1 = Paf.serve ~stop service t in
Logs.info (fun f -> f "listening on %d/HTTP, redirecting to %d/HTTPS"
(Key_gen.port ()) (Key_gen.https_port ()));
Lwt.join [ th0 ; th1 ;
(Time.sleep_ns (Duration.of_day 80) >>= fun () -> Lwt_switch.turn_off stop) ]
>>= fun () ->
provision ()
in
provision ()
end else begin
Paf.init ~port:(Key_gen.port ()) (Stack.tcp stackv4v6) >>= fun t ->
let service =
Paf.http_service ~error_handler (request_handler store)
in
let `Initialized th = Paf.serve service t in
Logs.info (fun f -> f "listening on %d/HTTP" (Key_gen.port ()));
(th >|= fun v -> Ok v)
end)
begin
Logs.info (fun m -> m "store: %s" (Last_modified.etag ()));
if Key_gen.tls () then begin
let rec provision () =
Paf.init ~port:80 (Stack.tcp stackv4v6) >>= fun t ->
let service =
Paf.http_service ~error_handler (fun _ -> LE.request_handler)
in
let stop = Lwt_switch.create () in
let `Initialized th0 = Paf.serve ~stop service t in
Logs.info (fun m ->
m "listening on 80/HTTP (let's encrypt provisioning)");
let th1 =
LE.provision_certificate
~production:(Key_gen.production ())
{ LE.certificate_seed = Key_gen.cert_seed ()
; LE.certificate_key_type = key_type (Key_gen.cert_key_type ())
; LE.certificate_key_bits = Some (Key_gen.cert_bits ())
; LE.email = Option.bind (Key_gen.email ()) (fun e -> Emile.of_string e |> Result.to_option)
; LE.account_seed = Key_gen.account_seed ()
; LE.account_key_type = key_type (Key_gen.account_key_type ())
; LE.account_key_bits = Some (Key_gen.account_bits ())
; LE.hostname = Key_gen.hostname () |> Option.get |> Domain_name.of_string_exn |> Domain_name.host_exn }
http_client
>>? fun certificates ->
Lwt_switch.turn_off stop >>= fun () -> Lwt.return_ok certificates in
Lwt.both th0 th1 >>= function
| ((), (Error _ as err)) -> Lwt.return err
| ((), Ok certificates) ->
Logs.debug (fun m -> m "Got certificates from let's encrypt.") ;
let tls = Tls.Config.server ~certificates () in
Paf.init ~port:(Key_gen.https_port ()) (Stack.tcp stackv4v6) >>= fun t ->
let service =
Paf.https_service ~tls ~error_handler (request_handler store)
in
let stop = Lwt_switch.create () in
let `Initialized th0 = Paf.serve ~stop service t in
Logs.info (fun m ->
m "listening on %d/HTTPS" (Key_gen.port ()));
Paf.init ~port:(Key_gen.port ()) (Stack.tcp stackv4v6) >>= fun t ->
let service =
let to_port = (Key_gen.https_port ()) in
Paf.http_service ~error_handler (Dispatch.redirect to_port)
in
let `Initialized th1 = Paf.serve ~stop service t in
Logs.info (fun f -> f "listening on %d/HTTP, redirecting to %d/HTTPS"
(Key_gen.port ()) (Key_gen.https_port ()));
Lwt.join [ th0 ; th1 ;
(Time.sleep_ns (Duration.of_day 80) >>= fun () -> Lwt_switch.turn_off stop) ]
>>= fun () ->
provision ()
in
provision ()
end else begin
Paf.init ~port:(Key_gen.port ()) (Stack.tcp stackv4v6) >>= fun t ->
let service =
Paf.http_service ~error_handler (request_handler store)
in
let `Initialized th = Paf.serve service t in
Logs.info (fun f -> f "listening on %d/HTTP" (Key_gen.port ()));
(th >|= fun v -> Ok v)
end
end
>>= function
| Ok () -> Lwt.return_unit
| Error (`Msg msg) -> Lwt.fail_with msg

end