|
| 1 | +/// FPM Controller Support |
| 2 | +/// FPM cli supports communication with fpm controller. This is an optional feature, and is only |
| 3 | +/// available when controller feature is enabled, which is not enabled by default. |
| 4 | +/// Controller Communication |
| 5 | +/// When controller feature is enabled, fpm serve will first communicate with the FPM controller |
| 6 | +/// service’s /get-package/ API. |
| 7 | +
|
| 8 | +/// FPM Controller Service Endpoint |
| 9 | +/// The FPM Controller Service’s endpoint is computed by using environment variable FPM_CONTROLLER, |
| 10 | +/// which will look something like this: https://controller.fifthtry.com, with the API path. |
| 11 | +/// FPM Controller Service has more than one APIs: /get-package/ and /fpm-ready/. |
| 12 | +
|
| 13 | +/// get-package: |
| 14 | +/// Through an environment variable FPM_INSTANCE_ID, the fpm serve will learn it’s instance id, and |
| 15 | +/// it will pass the instance id to the get-package API. |
| 16 | +/// The API returns the URL of the package to be downloaded, git repository URL and the package name. |
| 17 | +/// FPM will clone the git repository in the current directory. The current directory will contain |
| 18 | +/// FPM.ftd and other files of the package. |
| 19 | +/// FPM will then calls fpm install on it. |
| 20 | +
|
| 21 | +/// fpm-ready: |
| 22 | +/// Once dependencies are ready fpm calls /fpm-ready/ API on the controller. We will pass the |
| 23 | +/// FPM_INSTANCE_ID and the git commit hash as input to the API |
| 24 | +/// The API will return with success, and once it is done fpm will start receiving HTTP traffic |
| 25 | +/// from the controller service. |
| 26 | +
|
| 27 | +#[derive(serde::Deserialize, Debug)] |
| 28 | +struct ApiResponse<T> { |
| 29 | + success: bool, |
| 30 | + result: Option<T>, |
| 31 | + message: Option<String>, |
| 32 | +} |
| 33 | + |
| 34 | +#[derive(serde::Deserialize, Debug)] |
| 35 | +struct PackageResult { |
| 36 | + package: String, |
| 37 | + git: String, |
| 38 | +} |
| 39 | + |
| 40 | +pub async fn resolve_dependencies(fpm_instance: String, fpm_controller: String) -> fpm::Result<()> { |
| 41 | + // First call get_package API to get package details and resolve dependencies |
| 42 | + |
| 43 | + // response from get-package API |
| 44 | + let package_response = get_package(fpm_instance.as_str(), fpm_controller.as_str()).await?; |
| 45 | + |
| 46 | + // Clone the git package into the current directory |
| 47 | + // Need to execute shell commands from rust |
| 48 | + // git_url https format: https://github.com/<user>/<repo>.git |
| 49 | + |
| 50 | + let package = |
| 51 | + fpm::Package::new(package_response.package.as_str()).with_zip(package_response.git); |
| 52 | + |
| 53 | + package.unzip_package().await?; |
| 54 | + fpm::Config::read(None).await?; |
| 55 | + |
| 56 | + /*let out = std::process::Command::new("git") |
| 57 | + .arg("clone") |
| 58 | + .arg(git_url) |
| 59 | + .output() |
| 60 | + .expect("unable to execute git clone command"); |
| 61 | +
|
| 62 | + if out.status.success() { |
| 63 | + // By this time the cloned repo should be available in the current directory |
| 64 | + println!("Git cloning successful for the package {}", package_name); |
| 65 | + // Resolve dependencies by reading the FPM.ftd using config.read() |
| 66 | + // Assuming package_name and repo name are identical |
| 67 | + let _config = fpm::Config::read(Some(package_name.to_string())).await?; |
| 68 | + }*/ |
| 69 | + |
| 70 | + // Once the dependencies are resolved for the package |
| 71 | + // then call fpm_ready API to ensure that the controller service is now ready |
| 72 | + |
| 73 | + // response from fpm_ready API |
| 74 | + |
| 75 | + fpm_ready(fpm_instance.as_str(), fpm_controller.as_str()).await?; |
| 76 | + |
| 77 | + Ok(()) |
| 78 | +} |
| 79 | + |
| 80 | +/// get-package API |
| 81 | +/// input: fpm_instance |
| 82 | +/// output: package_name and git repo URL |
| 83 | +/// format: { |
| 84 | +/// "success": true, |
| 85 | +/// "result": { |
| 86 | +/// "package": "<package name>" |
| 87 | +/// "git": "<git url>" |
| 88 | +/// } |
| 89 | +/// } |
| 90 | +async fn get_package(fpm_instance: &str, fpm_controller: &str) -> fpm::Result<PackageResult> { |
| 91 | + let controller_api = format!( |
| 92 | + "{}/v1/fpm/get-package?ec2_reservation={}", |
| 93 | + fpm_controller, fpm_instance |
| 94 | + ); |
| 95 | + |
| 96 | + let url = url::Url::parse(controller_api.as_str())?; |
| 97 | + |
| 98 | + let mut headers = reqwest::header::HeaderMap::new(); |
| 99 | + headers.insert( |
| 100 | + reqwest::header::USER_AGENT, |
| 101 | + reqwest::header::HeaderValue::from_static("fpm"), |
| 102 | + ); |
| 103 | + |
| 104 | + let resp: ApiResponse<PackageResult> = fpm::library::http::get_with_type(url, headers).await?; |
| 105 | + |
| 106 | + if !resp.success { |
| 107 | + return Err(fpm::Error::APIResponseError(format!( |
| 108 | + "get_package api error: {:?}", |
| 109 | + resp.message |
| 110 | + ))); |
| 111 | + } |
| 112 | + |
| 113 | + resp.result.ok_or({ |
| 114 | + fpm::Error::APIResponseError(format!("get_package api error: {:?}", &resp.message)) |
| 115 | + }) |
| 116 | +} |
| 117 | + |
| 118 | +/// fpm-ready API |
| 119 | +/// input: fpm_instance, *(git commit hash) |
| 120 | +/// output: success: true/false |
| 121 | +/// format: lang: json |
| 122 | +/// { |
| 123 | +/// "success": true |
| 124 | +/// } |
| 125 | +
|
| 126 | +/// Git commit hash needs to be computed before making a call to the fpm_ready API |
| 127 | +async fn fpm_ready(fpm_instance: &str, fpm_controller: &str) -> fpm::Result<()> { |
| 128 | + let git_commit = "<dummy-git-commit-hash-xxx123>"; |
| 129 | + |
| 130 | + let controller_api = format!( |
| 131 | + "{}/v1/fpm/fpm-ready?ec2_reservation={}&hash={}", |
| 132 | + fpm_controller, fpm_instance, git_commit |
| 133 | + ); |
| 134 | + |
| 135 | + let url = url::Url::parse(controller_api.as_str())?; |
| 136 | + |
| 137 | + // This request should be put request for fpm_ready API to update the instance status to ready |
| 138 | + // Using http::_get() function to make request to this API for now |
| 139 | + let mut headers = reqwest::header::HeaderMap::new(); |
| 140 | + headers.insert( |
| 141 | + reqwest::header::USER_AGENT, |
| 142 | + reqwest::header::HeaderValue::from_static("fpm"), |
| 143 | + ); |
| 144 | + |
| 145 | + // TODO: here Map is wrong, |
| 146 | + let resp: ApiResponse<std::collections::HashMap<String, String>> = |
| 147 | + fpm::library::http::get_with_type(url, headers).await?; |
| 148 | + if !resp.success { |
| 149 | + return Err(fpm::Error::APIResponseError(format!( |
| 150 | + "fpm_ready api error: {:?}", |
| 151 | + resp.message |
| 152 | + ))); |
| 153 | + } |
| 154 | + Ok(()) |
| 155 | +} |
0 commit comments