diff --git a/tezt/lib_cloud/cli.ml b/tezt/lib_cloud/cli.ml index ab63df141f16718ea26a51ad7ab43eb17611145d..25f20b594dd8b4275666a39ca4297b84c0ce5469 100644 --- a/tezt/lib_cloud/cli.ml +++ b/tezt/lib_cloud/cli.ml @@ -275,3 +275,13 @@ let push_docker = VMs that are already running, it is useful to prevent those containers \ to be pushed." true + +let auto_approve = + Clap.flag + ~section + ~set_long:"auto-approve" + ~unset_long:"no-auto-approve" + ~description: + "If set to true (default), don't ask confirmation before updating a \ + deployment via terraform." + true diff --git a/tezt/lib_cloud/cli.mli b/tezt/lib_cloud/cli.mli index 9434b77b21661d2924f1985525d9ee8fdc8d6ccc..b877f02fa4d100951ceac5e5644023b4dfe08ff6 100644 --- a/tezt/lib_cloud/cli.mli +++ b/tezt/lib_cloud/cli.mli @@ -116,3 +116,6 @@ val check_file_consistency : bool (** Specify if the docker container should be pushed. Only considered for remote mode. *) val push_docker : bool + +(** Auto approve the deployment plan. *) +val auto_approve : bool diff --git a/tezt/lib_cloud/cloud.ml b/tezt/lib_cloud/cloud.ml index 48292b10c1864a9540dfdf37f5f3eb2900a89210..407ec85ac8be13323f1884f7bb4b84b2e8436671 100644 --- a/tezt/lib_cloud/cloud.ml +++ b/tezt/lib_cloud/cloud.ml @@ -27,59 +27,6 @@ let sigint = previous_behaviour := previous_handler ; promise -module Input : sig - (** This module should be the only one that reads on [stdin]. *) - - (** [next ()] returns the next line on stdin or none if stdin is closed. *) - val next : unit -> string option Lwt.t -end = struct - type t = { - mutable resolvers : string option Lwt.u list; - mutable stdin_closed : bool; - } - - let state = {resolvers = []; stdin_closed = false} - - let next () = - if state.stdin_closed then Lwt.return_none - else - let t, u = Lwt.task () in - state.resolvers <- u :: state.resolvers ; - t - - let rec loop () = - let* input = Lwt_io.read_line Lwt_io.stdin in - state.resolvers - |> List.iter (fun resolver -> Lwt.wakeup_later resolver (Some input)) ; - state.resolvers <- [] ; - loop () - - let _ = - Lwt.catch - (fun () -> loop ()) - (fun _exn -> - state.resolvers - |> List.iter (fun resolver -> Lwt.wakeup_later resolver None) ; - state.stdin_closed <- true ; - Lwt.return_unit) -end - -let eof = - let promise, resolver = Lwt.task () in - Lwt.dont_wait - (fun () -> - let rec loop () = - let* input = Input.next () in - match input with - | None -> - Lwt.wakeup resolver () ; - Lwt.return_unit - | Some _ -> loop () - in - loop ()) - (fun _ -> Lwt.wakeup resolver ()) ; - promise - (* This exception is raised when the test is interrupted by Ctrl+C. *) exception Interrupted @@ -312,7 +259,7 @@ let attach agent = Process.spawn ~hooks cmd (["-o"; "StrictHostKeyChecking=no"] @ args) |> Process.check in - let* _ = eof in + let* _ = Input.eof in let* () = let process = Process.spawn ~runner "pkill" ["screen"] in let* _ = Process.wait process in @@ -333,7 +280,7 @@ let attach agent = Lwt.return_unit in let on_eof = - let* () = eof in + let* () = Input.eof in Log.info "Detach from the proxy process." ; if !has_sigint then on_sigint else diff --git a/tezt/lib_cloud/deployement.ml b/tezt/lib_cloud/deployement.ml index 68f28d5577e7ff85614b95bd1346e41cb2e32643..65624de8ff5a5d885eff725c0ae05bf65f6e8b5a 100644 --- a/tezt/lib_cloud/deployement.ml +++ b/tezt/lib_cloud/deployement.ml @@ -56,8 +56,10 @@ module Remote = struct let ports_per_vm = Env.ports_per_vm in let base_port = Env.vm_base_port in let os = configuration.os in + let auto_approve = Env.auto_approve in let* () = Terraform.VM.deploy + ~auto_approve ~max_run_duration ~machine_type ~base_port diff --git a/tezt/lib_cloud/env.ml b/tezt/lib_cloud/env.ml index 7ecffac7d9537c5493dd16a125c176f4eed394b6..ad64cab477ccc2d06e11e45097f0895dd293634e 100644 --- a/tezt/lib_cloud/env.ml +++ b/tezt/lib_cloud/env.ml @@ -85,6 +85,8 @@ let check_file_consistency = Cli.check_file_consistency let push_docker = Cli.push_docker +let auto_approve = Cli.auto_approve + let project_id = Gcloud.project_id let init () = diff --git a/tezt/lib_cloud/env.mli b/tezt/lib_cloud/env.mli index 2ace4f4fab29986a046e1483b7cd1bb1c6209f8a..0a07cb68cfc024c4dfe39a9e2e6bee686d66c644 100644 --- a/tezt/lib_cloud/env.mli +++ b/tezt/lib_cloud/env.mli @@ -117,6 +117,9 @@ val check_file_consistency : bool (** Equivalent to [Cli.push_docker]. *) val push_docker : bool +(** Equivalent to [Cli.auto_approve]. *) +val auto_approve : bool + (** Equivalent to [Gcloud.project_id]. *) val project_id : unit -> string Lwt.t diff --git a/tezt/lib_cloud/input.ml b/tezt/lib_cloud/input.ml new file mode 100644 index 0000000000000000000000000000000000000000..fb19878e0d3918ce94e90feb3b25ea216ad48d49 --- /dev/null +++ b/tezt/lib_cloud/input.ml @@ -0,0 +1,52 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +let resolvers : string option Lwt.u list ref = ref [] + +let stdin_closed = ref false + +let next () = + if !stdin_closed then Lwt.return_none + else + let t, u = Lwt.task () in + resolvers := u :: !resolvers ; + t + +let rec loop () = + let* input = Lwt_io.read_line Lwt_io.stdin in + !resolvers + |> List.iter (fun resolver -> Lwt.wakeup_later resolver (Some input)) ; + resolvers := [] ; + loop () + +let _ = + (* This is a bit weird since this is executed outside of + `Lwt_main.run`. It works because this function only uses pure + `Lwt` code without relying on Unix dependencies. However, any + call to `next` must be done within `Lwt_main.run`. *) + Lwt.catch + (fun () -> loop ()) + (fun _exn -> + !resolvers |> List.iter (fun resolver -> Lwt.wakeup_later resolver None) ; + stdin_closed := true ; + Lwt.return_unit) + +let eof = + let promise, resolver = Lwt.task () in + Lwt.dont_wait + (fun () -> + let rec loop () = + let* input = next () in + match input with + | None -> + Lwt.wakeup resolver () ; + Lwt.return_unit + | Some _ -> loop () + in + loop ()) + (fun _ -> Lwt.wakeup resolver ()) ; + promise diff --git a/tezt/lib_cloud/input.mli b/tezt/lib_cloud/input.mli new file mode 100644 index 0000000000000000000000000000000000000000..bc70c49b575476be331eb3602fd422c2de8efc7b --- /dev/null +++ b/tezt/lib_cloud/input.mli @@ -0,0 +1,14 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +(** This module should be the only one that reads on [stdin]. *) + +(** [next ()] returns the next line on stdin or none if stdin is closed. *) +val next : unit -> string option Lwt.t + +(** [eof] is resolved when the [stdin] is closed. *) +val eof : unit Lwt.t diff --git a/tezt/lib_cloud/terraform.ml b/tezt/lib_cloud/terraform.ml index 2e42a41e09e145dbfb93769de6d675930a749e6a..ed673f28aa81e0ead7e0017e58fc6429ca50beb8 100644 --- a/tezt/lib_cloud/terraform.ml +++ b/tezt/lib_cloud/terraform.ml @@ -153,8 +153,8 @@ module VM = struct let init () = Process.run ~name ~color "terraform" (chdir Path.terraform_vm @ ["init"]) - let deploy ~max_run_duration ~machine_type ~base_port ~ports_per_vm - ~number_of_vms ~docker_image ~os = + let deploy ~auto_approve ~max_run_duration ~machine_type ~base_port + ~ports_per_vm ~number_of_vms ~docker_image ~os = let* project_id = Gcloud.project_id () in let max_run_duration = match max_run_duration with @@ -180,11 +180,26 @@ module VM = struct Format.asprintf "os=%s" os; ] in - Process.run - ~name - ~color - "terraform" - (chdir Path.terraform_vm @ ["apply"; "--auto-approve"] @ args) + if auto_approve then + Process.run + ~name + ~color + "terraform" + (chdir Path.terraform_vm @ ["apply"; "--auto-approve"] @ args) + else + let process, output_channel = + Process.spawn_with_stdin + ~name + ~color + "terraform" + (chdir Path.terraform_vm @ ["apply"] @ args) + in + let* input = Input.next () in + (* If the user pressed Ctrl+D, i.e. input is [None], we don't + care what the input is. *) + let input = Option.value ~default:"" input in + let* () = Lwt_io.write_line output_channel input in + Process.check process let points () = let* output = diff --git a/tezt/lib_cloud/terraform.mli b/tezt/lib_cloud/terraform.mli index 4993cd15e7233109d03c74fe39df9ed4c0d37bd5..d020fad2e3e6cec1151c8667732b7c7b20b89e9a 100644 --- a/tezt/lib_cloud/terraform.mli +++ b/tezt/lib_cloud/terraform.mli @@ -31,6 +31,7 @@ module VM : sig val init : unit -> unit Lwt.t val deploy : + auto_approve:bool -> max_run_duration:int option -> machine_type:string -> base_port:int ->