diff --git a/tezt/lib_cloud/agent.ml b/tezt/lib_cloud/agent.ml index 8561deb21565bedbfcc3fea031f5c3c0dbe3360d..86c0b9fece4fd11cd03c9e34baa1b8a627265907 100644 --- a/tezt/lib_cloud/agent.ml +++ b/tezt/lib_cloud/agent.ml @@ -17,11 +17,12 @@ module Configuration = struct let uri_of_docker_image docker_image = match (docker_image, Env.mode) with - | Types.Agent_configuration.Gcp {alias}, (`Cloud | `Host | `Orchestrator) -> + | ( Types.Agent_configuration.Gcp {alias}, + (`Cloud | `Host | `Orchestrator | `Ssh_host _) ) -> let* registry_uri = Env.registry_uri () in Lwt.return (Format.asprintf "%s/%s" registry_uri alias) | Gcp {alias}, `Localhost -> Lwt.return alias - | Octez_release _, (`Cloud | `Host | `Orchestrator) -> + | Octez_release _, (`Cloud | `Host | `Orchestrator | `Ssh_host _) -> let* registry_uri = Env.registry_uri () in Lwt.return (Format.asprintf "%s/octez" registry_uri) | Octez_release _, `Localhost -> Lwt.return "octez" diff --git a/tezt/lib_cloud/cli.ml b/tezt/lib_cloud/cli.ml index cac2031b1d77b12a1674b3c25a0c7852c2892f94..cafc432a8e12e9a122b4724f17aa951a38fbccf3 100644 --- a/tezt/lib_cloud/cli.ml +++ b/tezt/lib_cloud/cli.ml @@ -22,6 +22,13 @@ let localhost = ~description:"If set, the test is run locally" false +let ssh_host = + Clap.optional_string + ~section + ~long:"ssh-host" + ~description:"Whether to provision a non-gcp vm host via ssh" + () + let monitoring = Clap.flag ~section diff --git a/tezt/lib_cloud/cli.mli b/tezt/lib_cloud/cli.mli index cf80d28babd10b082c441863700a9148e51905f6..bd119f3207665756dc1a030706bbb64b3d82b5cc 100644 --- a/tezt/lib_cloud/cli.mli +++ b/tezt/lib_cloud/cli.mli @@ -143,3 +143,6 @@ val binaries_path : string Defaults to 300 Use 0 to disable log-rotation *) val log_rotation : int + +(** The hostname of the host accessed by ssh on which to deploy *) +val ssh_host : string option diff --git a/tezt/lib_cloud/cloud.ml b/tezt/lib_cloud/cloud.ml index 9a00dec0983b59113fc6285fec54b743b6f221c4..bb7e7010fa7b0837b9b39824d146ccbb788d80f0 100644 --- a/tezt/lib_cloud/cloud.ml +++ b/tezt/lib_cloud/cloud.ml @@ -455,7 +455,15 @@ let init_proxy ?(proxy_files = []) ?(proxy_args = []) deployement = in let process = let args = + (* remove "--ssh-host host" from the commande line *) + let rec filter_ssh acc args = + match args with + | [] -> List.rev acc + | "--ssh-host" :: _host :: args -> "--proxy" :: filter_ssh acc args + | arg :: args -> filter_ssh (arg :: acc) args + in let args = Sys.argv |> Array.to_list |> List.tl in + let args = filter_ssh [] args in args @ ["--localhost"; "--tezt-cloud"; Env.tezt_cloud] (* [--localhost] will be combined with --proxy, this enables to detect we want to run in [`Orchestrator]. @@ -537,7 +545,7 @@ let register ?proxy_files ?proxy_args ?vms ~__FILE__ ~title ~tags ?seed ?alerts future." ; match Env.mode with | `Localhost | `Cloud -> None - | `Host | `Orchestrator -> + | `Host | `Orchestrator | `Ssh_host (_, _) -> (* In Host mode, we want to run a deployment deploying the Proxy VM. In orchestrator mode, there is few initialisation steps needed. By using [Some []], we @@ -592,6 +600,7 @@ let register ?proxy_files ?proxy_args ?vms ~__FILE__ ~title ~tags ?seed ?alerts deployement = None; } | Some configurations -> ( + let* ssh_public_key = Ssh.public_key () in let sorted_names = configurations |> List.map (fun Agent.Configuration.{name; _} -> name) @@ -635,14 +644,16 @@ let register ?proxy_files ?proxy_args ?vms ~__FILE__ ~title ~tags ?seed ?alerts orchestrator ?alerts ?tasks deployement f | `Localhost -> (* The scenario is executed locally and the VM are on the host machine. *) - let* () = Jobs.docker_build ~push:false () in + let* () = Jobs.docker_build ~push:false ~ssh_public_key () in let* deployement = Deployement.deploy ~configurations in let* () = ensure_ready deployement in orchestrator ?alerts ?tasks deployement f | `Cloud -> (* The scenario is executed locally and the VMs are on the cloud. *) let* () = Jobs.deploy_docker_registry () in - let* () = Jobs.docker_build ~push:Env.push_docker () in + let* () = + Jobs.docker_build ~push:Env.push_docker ~ssh_public_key () + in let* deployement = Deployement.deploy ~configurations in let* () = ensure_ready deployement in orchestrator ?alerts ?tasks deployement f @@ -651,10 +662,23 @@ let register ?proxy_files ?proxy_args ?vms ~__FILE__ ~title ~tags ?seed ?alerts let* proxy_running = try_reattach () in if not proxy_running then let* () = Jobs.deploy_docker_registry () in - let* () = Jobs.docker_build ~push:Env.push_docker () in + let* () = + Jobs.docker_build ~push:Env.push_docker ~ssh_public_key () + in let* deployement = Deployement.deploy ~configurations in let* () = ensure_ready deployement in init_proxy ?proxy_files ?proxy_args deployement + else Lwt.return_unit + | `Ssh_host (_host, _port) -> + let* proxy_running = try_reattach () in + if not proxy_running then + let* () = Jobs.deploy_docker_registry () in + let* () = + Jobs.docker_build ~push:Env.push_docker ~ssh_public_key () + in + let* deployment = Deployement.deploy ~configurations in + let* () = ensure_ready deployment in + init_proxy ?proxy_files ?proxy_args deployment else Lwt.return_unit) let agents t = @@ -689,7 +713,7 @@ let agents t = in [default_agent] | agents -> agents) - | `Host | `Cloud | `Localhost -> t.agents + | `Host | `Cloud | `Localhost | `Ssh_host _ -> t.agents let write_website t = match t.website with diff --git a/tezt/lib_cloud/deployement.ml b/tezt/lib_cloud/deployement.ml index bd3e323ab916016d6609d0941684474d817a61bc..191a3c3fcc1c3dde0903beab81bd136c6c6c5f34 100644 --- a/tezt/lib_cloud/deployement.ml +++ b/tezt/lib_cloud/deployement.ml @@ -5,6 +5,62 @@ (* *) (*****************************************************************************) +(* This module introduces some functions to create resources of GCP that + are not strictly bound to a deployment and we could avoid to create + each time. This is for example, the service accounts, that are bound + to a deployment by terraform, instead of being bound to users. *) +module Common = struct + let create_service_account ~project ~name = + (* FIXME: service_account_fullname is strictly bound to dal team and should + be configured and stored in a tezt-cloud ~/.config directory. + Here, preserving backward compatibility with terraform naming *) + let service_account_fullname = + Format.asprintf "%s-id@nl-dal.iam.gserviceaccount.com" Env.tezt_cloud + in + (* FIXME: delete the service account if already exists and recreate it + to create new keys. *) + let* () = + Gcloud.delete_iam_service_account ~fullname:service_account_fullname + in + let* () = Gcloud.create_iam_service_account ~name ~project in + Lwt.return service_account_fullname + + (* This function creates a access token to allow access to the + docker registry, and returns its filename *) + let create_access_token_for_gcp_docker_registry ~project = + let service_account_name = Format.asprintf "%s-id" Env.tezt_cloud in + let* service_account = + create_service_account ~project ~name:service_account_name + in + let iam_key_filename = "/tmp/iam-keys" in + let* () = + Gcloud.create_iam_service_account_key + ~filename:iam_key_filename + ~account:service_account + ~project + in + let* () = + Process.spawn + "gcloud" + [ + "artifacts"; + "repositories"; + "add-iam-policy-binding"; + Format.asprintf "%s-docker-registry" Env.tezt_cloud; + "--location"; + "europe-west1"; + "--member"; + Format.asprintf "serviceAccount:%s" service_account; + "--role"; + "roles/artifactregistry.reader"; + ] + |> Process.check + in + Log.report "waiting for the iam key to become valid" ; + let* () = Lwt_unix.sleep 1.0 in + Lwt.return iam_key_filename +end + (* Infrastructure to deploy on Google Cloud *) module Remote = struct type point_info = {workspace_name : string; gcp_name : string} @@ -187,7 +243,7 @@ module Remote = struct let () = Log.report ~color:Log.Color.FG.green - "DNS registrered successfully: '%s'" + "DNS registered successfully: '%s'" domain in Lwt.return_unit @@ -222,10 +278,12 @@ module Remote = struct (fun (workspace_name, (vm_configuration, configurations, number_of_vms)) -> + let* ssh_public_key = Ssh.public_key () in let* () = Jobs.docker_build ~docker_image:vm_configuration.Agent.Configuration.docker_image ~push:Env.push_docker + ~ssh_public_key () in let* () = Terraform.VM.Workspace.select workspace_name in @@ -294,6 +352,316 @@ module Remote = struct Lwt.return_unit) end +(* Deployment on a SSH reachable VM. At this moment, it is expected to be a + debian bookworm, and apt must be executable by the user connecting with sudo. + At this moment, this deployment launches a proxy mode docker. *) +module Ssh_host = struct + type t = {point : string * string * int; agents : Agent.t list} + + let ssh_cmd ~user ~host ~port cmd args = + Process.spawn + "ssh" + ([ + user ^ "@" ^ host; + "-i"; + Env.ssh_private_key_filename (); + "-p"; + string_of_int port; + cmd; + ] + @ args) + + (* This function allows to setup the prerequisites to run the deployment + of containers on a vm which can be connected to via ssh. + Multiple hosts are currently supported: + - a gcp debian vm + - a qemu vm + - a physical machine. + All that is required is that the host can be contacter either via root + account or a sudo enabled account. *) + let initial_host_provisionning user host port = + let* () = + (* Allows direct connections as root on debian, using key authentication. + This is not insecure (as key logging is secure) as long as users are + logging as root are prudent. This allows to not be embarrassed with sudo *) + if user = "root" then Lwt.return_unit + else + let* () = + ssh_cmd + ~user + ~host + ~port + "sudo" + [ + "sed"; + "-i"; + "'s/PermitRootLogin no/PermitRootLogin prohibit-password/'"; + "/etc/ssh/sshd_config"; + ] + |> Process.check + in + with_open_in (Env.ssh_public_key_filename ()) @@ fun fd -> + let ssh_public_key_content = input_line fd in + let* () = + ssh_cmd + ~user + ~host + ~port + "--" + [ + Format.asprintf + "echo %s | sudo tee -a /root/.ssh/authorized_keys" + ssh_public_key_content; + ] + |> Process.check + in + ssh_cmd ~user ~host ~port "sudo" ["systemctl"; "restart"; "ssh.service"] + |> Process.check + in + let user = "root" in + (* Installs docker *) + Log.report "Installing docker" ; + let* () = ssh_cmd ~user ~host ~port "apt-get" ["update"] |> Process.check in + let* () = + ssh_cmd + ~user + ~host + ~port + "apt-get" + ["install"; "-y"; "docker.io"; "libev4"] + |> Process.check + in + let* project = Env.project_id () in + (* Generate locally an access token to access to gcp docker registry *) + let* iam_key_filename = + Common.create_access_token_for_gcp_docker_registry ~project + in + (* Upload the key to the vm via ssh *) + let* () = + Process.run + "scp" + (["-i"; Env.ssh_private_key_filename (); iam_key_filename] + @ (if port <> 22 then ["-p"; string_of_int port] else []) + @ [Format.asprintf "%s@%s:%s" user host iam_key_filename]) + in + let* () = + let rec retry () = + let* status = + ssh_cmd + ~user + ~host + ~port + "--" + [ + Format.asprintf + "cat %s | docker login -u _json_key --password-stdin \ + https://europe-west1-docker.pkg.dev" + iam_key_filename; + ] + |> Process.wait + in + match status with WEXITED 0 -> Lwt.return_unit | _ -> retry () + in + retry () + in + Lwt.return_unit + + let deploy_proxy host port = + let configuration = Proxy.make_config () in + let next_available_port = + let cpt = ref 30_000 in + fun () -> + incr cpt ; + !cpt + in + let* docker_image = + Agent.Configuration.uri_of_docker_image configuration.vm.docker_image + in + let* () = + ssh_cmd + ~user:"root" + ~host + ~port + "docker" + ["rm"; "--force"; configuration.name] + |> Process.check + in + let ssh_listening_port = next_available_port () in + let* () = + ssh_cmd + ~user:"root" + ~host + ~port + "docker" + [ + "run"; + "--rm"; + "--network"; + "host"; + "-d"; + Format.asprintf "-p%d:%d" ssh_listening_port ssh_listening_port; + "-v"; + "/var/run/docker.sock:/var/run/docker.sock"; + "-v"; + "/tmp/prometheus:/tmp/prometheus"; + "-v"; + "/tmp/website:/tmp/website"; + "-v"; + "/tmp/grafana:/tmp/grafana"; + "-v"; + "/tmp/alert_manager:/tmp/alert_manager"; + "-v"; + "/tmp/otel:/tmp/otel"; + "--name"; + configuration.name; + "--entrypoint"; + "/usr/sbin/sshd"; + docker_image; + "-D"; + "-p"; + Format.asprintf "%d" ssh_listening_port; + ] + |> Process.check + in + let agent = + Agent.make + ~vm_name:None + ~configuration + ~next_available_port + ~point:(host, ssh_listening_port) + ~ssh_id:(Env.ssh_private_key_filename ()) + ~process_monitor:None + () + in + Lwt.return agent + + let deploy ~user ~host ~port ~(configurations : Agent.Configuration.t list) () + = + (* Provision the ssh machine to allow deployment of dockers *) + let* () = initial_host_provisionning user host port in + (* Deploys the proxy *) + let* proxy = deploy_proxy host port in + (* Deploys all agents *) + let configurations = configurations in + let* agents = + Lwt_list.mapi_p + (fun i (configuration : Agent.Configuration.t) -> + let* ssh_public_key = Ssh.public_key () in + let* () = + Jobs.docker_build + ~docker_image:configuration.Agent.Configuration.vm.docker_image + ~push:Env.push_docker + ~ssh_public_key + () + in + let* docker_image = + Agent.Configuration.uri_of_docker_image + configuration.vm.docker_image + in + let* () = + ssh_cmd ~user ~host ~port "docker" ["pull"; docker_image] + |> Process.check + in + let ssh_port = Agent.next_available_port proxy in + (* FIXME move this constants elsewhere *) + let base_port = 30_050 in + let range = 50 in + let* () = + ssh_cmd + ~user + ~host + ~port + "docker" + [ + "run"; + "-d"; + "-p"; + string_of_int ssh_port ^ ":" ^ string_of_int ssh_port; + "-p"; + Format.asprintf + "%d-%d:%d-%d" + (base_port + (i * range)) + (base_port + ((i + 1) * range) - 1) + (base_port + (i * range)) + (base_port + ((i + 1) * range) - 1); + "--name"; + configuration.name; + "--network"; + "host"; + "--entrypoint"; + "/usr/sbin/sshd"; + docker_image; + "-D"; + "-p"; + ssh_port |> string_of_int; + "-e"; + ] + |> Process.check + in + let* address = + if Env.docker_host_network then Lwt.return host + else + (* Determine the ip in bridged networking, but not yet supported *) + let* output = + ssh_cmd + ~user + ~host + ~port + "docker" + [ + "inspect -f '{{range \ + .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' " + ^ configuration.name; + ] + |> Process.check_and_read_stdout + in + Lwt.return (String.trim output) + in + let () = + Log.warn + "Deployed agent: %s on (%s, %d)" + configuration.name + address + ssh_port + in + let agent = + Agent.make + ~vm_name:None + ~configuration + ~next_available_port: + (let cpt = ref (base_port + (i * range)) in + fun () -> + incr cpt ; + !cpt) + ~process_monitor:None + ~point:(address, ssh_port) + ~ssh_id:(Env.ssh_private_key_filename ()) + () + in + Lwt.return agent) + configurations + in + let agents = proxy :: agents in + Lwt.return {point = (user, host, port); agents} + + let agents t = t.agents + + let terminate {point; agents} = + let _user, host, port = point in + let* () = + Lwt_list.iter_p + (fun agent -> + let name = Agent.name agent in + let* _ = + ssh_cmd ~user:"root" ~host ~port "docker" ["rm"; "--force"; name] + |> Process.wait + in + Lwt.return_unit) + agents + in + Lwt.return_unit +end + (* Infrastructure to deploy locally using Docker *) module Localhost = struct type t = { @@ -324,6 +692,7 @@ module Localhost = struct in Lwt.return docker_network in + let* ssh_public_key = Ssh.public_key () in let* processes = List.to_seq configurations |> Seq.mapi (fun i configuration -> @@ -336,6 +705,7 @@ module Localhost = struct Jobs.docker_build ~docker_image:configuration.Agent.Configuration.vm.docker_image ~push:false + ~ssh_public_key () in let* docker_image = @@ -444,7 +814,10 @@ module Localhost = struct else Lwt.return_unit end -type t = Remote of Remote.t | Localhost of Localhost.t +type t = + | Remote of Remote.t + | Ssh_host of Ssh_host.t + | Localhost of Localhost.t let deploy ~configurations = match Env.mode with @@ -458,15 +831,22 @@ let deploy ~configurations = let* remote = Remote.deploy ~proxy:true ~configurations in Lwt.return (Remote remote) | `Orchestrator -> assert false + | `Ssh_host (host, port) -> + let* host = + Ssh_host.deploy ~user:(Sys.getenv "USER") ~host ~port ~configurations () + in + Lwt.return (Ssh_host host) let agents t = match t with | Remote remote -> Remote.agents remote | Localhost localhost -> Localhost.agents localhost + | Ssh_host remote -> Ssh_host.agents remote let terminate ?exn t = match t with | Remote remote -> Remote.terminate ?exn remote | Localhost localhost -> Localhost.terminate ?exn localhost + | Ssh_host remote -> Ssh_host.terminate remote let of_agents agents = Remote {agents} diff --git a/tezt/lib_cloud/env.ml b/tezt/lib_cloud/env.ml index 0f43ea86b1aef9243509164a5b2e72b0b6e002ec..5627e78cd897719ad072bc4b831d9e4e75a1aa48 100644 --- a/tezt/lib_cloud/env.ml +++ b/tezt/lib_cloud/env.ml @@ -12,6 +12,22 @@ let tezt_cloud = (* This is a lazy value to be sure that this is evaluated only inside a Tezt test. *) match Sys.getenv_opt "TEZT_CLOUD" with None -> "" | Some value -> value) +let mode = + match (Cli.localhost, Cli.proxy, Cli.ssh_host) with + | true, true, None -> `Orchestrator + | true, false, None -> `Localhost + | false, true, None -> `Host + | false, false, None -> `Cloud + | _, _, Some endpoint -> + let uri = Uri.of_string (Format.asprintf "tcp://%s" endpoint) in + let host = + match Uri.host uri with + | None -> Test.fail "No valid hostname specified: %s" endpoint + | Some host -> host + in + let port = Option.value ~default:22 (Uri.port uri) in + `Ssh_host (host, port) + let ssh_private_key_filename ?(home = Sys.getenv "HOME") () = home // ".ssh" // Format.asprintf "%s-tf" tezt_cloud @@ -19,13 +35,6 @@ let ssh_public_key_filename ?home () = let ssh_key = ssh_private_key_filename ?home () in Format.asprintf "%s.pub" ssh_key -let mode = - match (Cli.localhost, Cli.proxy) with - | true, true -> `Orchestrator - | true, false -> `Localhost - | false, true -> `Host - | false, false -> `Cloud - let prometheus = Cli.prometheus let prometheus_export = Cli.prometheus_export @@ -100,7 +109,7 @@ let init () = "The tezt-cloud value should be set. Either via the CLI or via the \ environment variable 'TEZT_CLOUD'" ; match mode with - | `Localhost | `Orchestrator -> Lwt.return_unit + | `Localhost | `Orchestrator | `Ssh_host _ -> Lwt.return_unit | `Host | `Cloud -> let* project_id = project_id () in Log.info "Initializing docker registry..." ; @@ -190,7 +199,8 @@ let dns_domains () = match domain with | None -> Lwt.return Cli.dns_domains | Some domain -> Lwt.return (domain :: Cli.dns_domains)) - | `Orchestrator | `Localhost | `Cloud -> Lwt.return Cli.dns_domains + | `Orchestrator | `Localhost | `Cloud | `Ssh_host _ -> + Lwt.return Cli.dns_domains in (* A fully-qualified domain name requires to end with a dot. However, the usage tends to omit this final dot. Because having a diff --git a/tezt/lib_cloud/env.mli b/tezt/lib_cloud/env.mli index a507c48fe41c2cc0d636569a823cb084e9fea250..b2ba3c8012757c0edcdb6e2b5dc6ce81bf0e1dce 100644 --- a/tezt/lib_cloud/env.mli +++ b/tezt/lib_cloud/env.mli @@ -31,8 +31,13 @@ val ssh_public_key_filename : ?home:string -> unit -> string - [`Host]: This mode is run by the host machine that initializes the orchestrator running on a VM. + + - [`Ssh_host (host, port)]: This mode is similar to the Orchestrator mode, but + on a non-gcp vm. It's purpose is to make an initial provisionning + of docker if it is not setup. *) -val mode : [`Localhost | `Cloud | `Orchestrator | `Host] +val mode : + [`Localhost | `Ssh_host of string * int | `Cloud | `Orchestrator | `Host] (** Equivalent to [Cli.prometheus]. *) val prometheus : bool diff --git a/tezt/lib_cloud/gcloud.ml b/tezt/lib_cloud/gcloud.ml index 38fba6a4f1ede34f07c4caeb1a5f8a118b5c97fe..acef1581d7203a2302cd6d485ac426ffd4ba4131 100644 --- a/tezt/lib_cloud/gcloud.ml +++ b/tezt/lib_cloud/gcloud.ml @@ -81,6 +81,10 @@ let list_vms ~prefix = in Lwt.return (String.trim output) +let gcloud_run args = + let* _ = Process.spawn "gcloud" args |> Process.wait in + Lwt.return_unit + let gcloud_run_stdout args = let* output = Process.run_and_read_stdout "gcloud" (["--format"; "json"] @ args) @@ -122,6 +126,36 @@ let list_instance_groups ?filter () = let list_iam_service_accounts ?filter () = gcloud_run_stdout_filter ~filter ["iam"; "service-accounts"; "list"] +let create_iam_service_account ~name ~project = + gcloud_run + [ + "iam"; + "service-accounts"; + "create"; + "--project"; + project; + name; + "--display-name"; + name; + ] + +let delete_iam_service_account ~fullname = + gcloud_run ["iam"; "service-accounts"; "delete"; fullname] + +let create_iam_service_account_key ~filename ~account ~project = + gcloud_run + [ + "iam"; + "service-accounts"; + "keys"; + "create"; + filename; + "--iam-account"; + account; + "--project"; + project; + ] + let list_disks ?filter () = gcloud_run_stdout_filter ~filter ["compute"; "disks"; "list"] diff --git a/tezt/lib_cloud/gcloud.mli b/tezt/lib_cloud/gcloud.mli index ed65cbd64dfe26f77b19e2d9533a0f36e950267b..11e6784508085e74e04d9b78c6ddf44368805ae8 100644 --- a/tezt/lib_cloud/gcloud.mli +++ b/tezt/lib_cloud/gcloud.mli @@ -77,6 +77,19 @@ val list_firewalls : ?filter:string -> unit -> JSON.t Lwt.t with details in json format. Filter can be used to filter by name *) val list_iam_service_accounts : ?filter:string -> unit -> JSON.t Lwt.t +(** [create_iam_service_account name project] creates a new GCP + service account [name] on gcp [project] *) +val create_iam_service_account : name:string -> project:string -> unit Lwt.t + +(** [delete_iam_service_account fullname] deletes the iam service account + [fullname] on GCP *) +val delete_iam_service_account : fullname:string -> unit Lwt.t + +(** [create_iam_service_account_key filename account project] creates a new + key written in [filename] given [account] iam service account and [project] *) +val create_iam_service_account_key : + filename:string -> account:string -> project:string -> unit Lwt.t + (** [list_disks ?filter] retrieves a list of disks with details in json format. Filter can be used to filter by name *) val list_disks : ?filter:string -> unit -> JSON.t Lwt.t diff --git a/tezt/lib_cloud/jobs.ml b/tezt/lib_cloud/jobs.ml index 12ce5194655d6300529940d323fe974269bc3ca3..77393257e0c365c2c96aba96304b07ab8696c2af 100644 --- a/tezt/lib_cloud/jobs.ml +++ b/tezt/lib_cloud/jobs.ml @@ -14,25 +14,13 @@ let docker_build = let cache = Hashtbl.create 11 in fun ?(docker_image = Agent.Configuration.Gcp {alias = Env.dockerfile_alias}) ~push + ~ssh_public_key () -> if Hashtbl.mem cache docker_image then ( Log.info "Docker image is already built. Nothing to do" ; Lwt.return_unit) - else - let ssh_public_key_filename = Env.ssh_public_key_filename () in + else ( Hashtbl.replace cache docker_image () ; - Log.info - "Checking the existence of ssh public key '%s'..." - ssh_public_key_filename ; - let* ssh_public_key = - let* () = - if not (Sys.file_exists ssh_public_key_filename) then ( - Log.info "SSH public key not found, creating it..." ; - Ssh.generate_key ()) - else Lwt.return_unit - in - Process.run_and_read_stdout ~name:"cat" "cat" [ssh_public_key_filename] - in let alias = match docker_image with | Gcp {alias} -> alias @@ -92,7 +80,7 @@ let docker_build = Lwt.return_unit) else Lwt.return_unit in - unit + unit) let deploy_docker_registry () = Log.info "Tezt_Cloud found with value: %s" Env.tezt_cloud ; diff --git a/tezt/lib_cloud/jobs.mli b/tezt/lib_cloud/jobs.mli index 58934d7008b934b092a6a8a84868cb48eb717703..26186ed244cf39bd31ea8da21e009f35c1591057 100644 --- a/tezt/lib_cloud/jobs.mli +++ b/tezt/lib_cloud/jobs.mli @@ -12,6 +12,7 @@ val deploy_docker_registry : unit -> unit Lwt.t val docker_build : ?docker_image:Agent.Configuration.docker_image -> push:bool -> + ssh_public_key:string -> unit -> unit Lwt.t diff --git a/tezt/lib_cloud/ssh.ml b/tezt/lib_cloud/ssh.ml index 86d7f429106d82593a43eeef146092c4015a70f9..bee0fabcc7832b7e5ea735df40f0575d821b9cdd 100644 --- a/tezt/lib_cloud/ssh.ml +++ b/tezt/lib_cloud/ssh.ml @@ -11,3 +11,16 @@ let generate_key () = -N is for the passphrase (no passphrase here) *) Process.run "ssh-keygen" ["-t"; "rsa"; "-f"; path; "-C"; path; "-N"; ""] + +let public_key () = + let ssh_public_key_filename = Env.ssh_public_key_filename () in + let* () = + if not (Sys.file_exists ssh_public_key_filename) then ( + Log.info "SSH public key not found, creating it..." ; + generate_key ()) + else Lwt.return_unit + in + let* content = + Process.run_and_read_stdout ~name:"cat" "cat" [ssh_public_key_filename] + in + Lwt.return content diff --git a/tezt/lib_cloud/ssh.mli b/tezt/lib_cloud/ssh.mli index c17679024736e062f1fa872094118e0041f5322c..594ceedc1146444567b188d0eb1e247ff0173d97 100644 --- a/tezt/lib_cloud/ssh.mli +++ b/tezt/lib_cloud/ssh.mli @@ -8,3 +8,7 @@ (** [generate_key ()] generates an ssh key based on the [Env.tezt_cloud] variable environment. *) val generate_key : unit -> unit Lwt.t + +(** [ssh_public_key()] returns the ssh public key associated to the generate_key + It calls [generate_key] if it does not exist *) +val public_key : unit -> string Lwt.t diff --git a/tezt/lib_cloud/tezt_cloud.ml b/tezt/lib_cloud/tezt_cloud.ml index 2d6407247649a5894c2fa20f5c6e3a828052289a..d1ded25c0ba3ebd158f20697e6a2c771385fa600 100644 --- a/tezt/lib_cloud/tezt_cloud.ml +++ b/tezt/lib_cloud/tezt_cloud.ml @@ -42,7 +42,9 @@ let register_docker_push ~tags = ~__FILE__ ~title:"Push the dockerfile to the GCP registry" ~tags:("docker" :: "push" :: tags) - @@ fun _cloud -> Jobs.docker_build ~push:true () + @@ fun _cloud -> + let* ssh_public_key = Ssh.public_key () in + Jobs.docker_build ~push:true ~ssh_public_key () let register_docker_build ~tags = Cloud.register @@ -50,7 +52,9 @@ let register_docker_build ~tags = ~__FILE__ ~title:"Build the dockerfile" ~tags:("docker" :: "build" :: tags) - @@ fun _cloud -> Jobs.docker_build ~push:false () + @@ fun _cloud -> + let* ssh_public_key = Ssh.public_key () in + Jobs.docker_build ~push:false ~ssh_public_key () let register_deploy_docker_registry ~tags = Cloud.register diff --git a/tezt/lib_cloud/web.ml b/tezt/lib_cloud/web.ml index 5a830cb7bed7726545f73e82e82792fb966d9e83..a4fd374461822893ae417c607e410e19b625213b 100644 --- a/tezt/lib_cloud/web.ml +++ b/tezt/lib_cloud/web.ml @@ -24,7 +24,8 @@ let pp_docker_image fmt = function let domain agents = match Env.mode with - | `Orchestrator -> Proxy.get_agent agents |> Agent.point |> Option.get |> fst + | `Orchestrator | `Ssh_host _ -> + Proxy.get_agent agents |> Agent.point |> Option.get |> fst | `Host | `Localhost | `Cloud -> "localhost" let string_docker_command agent =