-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathhttp_client.rs
More file actions
131 lines (115 loc) · 4.71 KB
/
http_client.rs
File metadata and controls
131 lines (115 loc) · 4.71 KB
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
// Copyright (c) [2024-2025] SUSE LLC
//
// All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, contact SUSE LLC.
//
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.
use crate::{
http::{BaseHTTPClient, BaseHTTPClientError},
logs::LogsLists,
};
use agama_utils::api::{self, FinishMethod, Status};
use reqwest::header::CONTENT_ENCODING;
use std::path::{Path, PathBuf};
use std::{fs, io::Cursor, os::unix::fs::OpenOptionsExt};
#[derive(Debug, thiserror::Error)]
pub enum ManagerHTTPClientError {
#[error(transparent)]
HTTP(#[from] BaseHTTPClientError),
#[error("Cannot generate Agama logs: {0}")]
CannotGenerateLogs(String),
}
pub struct ManagerHTTPClient {
client: BaseHTTPClient,
}
impl ManagerHTTPClient {
pub fn new(base: BaseHTTPClient) -> Self {
Self { client: base }
}
/// Starts a "probing".
pub async fn probe(&self) -> Result<(), ManagerHTTPClientError> {
// BaseHTTPClient did not anticipate POST without request body
// so we pass () which is rendered as `null`
Ok(self.client.post_void("/manager/probe_sync", &()).await?)
}
/// Starts a "reprobing".
pub async fn reprobe(&self) -> Result<(), ManagerHTTPClientError> {
// BaseHTTPClient did not anticipate POST without request body
// so we pass () which is rendered as `null`
Ok(self.client.post_void("/manager/reprobe_sync", &()).await?)
}
/// Starts the installation.
pub async fn install(&self) -> Result<(), ManagerHTTPClientError> {
let action = api::Action::Install;
self.client.post_void("/v2/action", &action).await?;
Ok(())
}
/// Finishes the installation.
///
/// * `method`: halt, reboot, stop or poweroff the system.
pub async fn finish(&self, method: FinishMethod) -> Result<(), ManagerHTTPClientError> {
let action = api::Action::Finish(method);
self.client.post_void("/v2/action", &action).await?;
Ok(())
}
/// Downloads package of logs from the backend
///
/// For now the path is path to a destination file without an extension. Extension
/// will be added according to the compression type found in the response
///
/// Returns path to logs
pub async fn store(&self, path: &Path) -> Result<PathBuf, ManagerHTTPClientError> {
// 1) response with logs
let response = self.client.get_raw("/v2/private/download_logs").await?;
// 2) find out the destination file name
let ext = &response.headers().get(CONTENT_ENCODING).ok_or(
ManagerHTTPClientError::CannotGenerateLogs(String::from("Invalid response")),
)?;
let mut destination = path.to_path_buf();
destination.set_extension(ext.to_str().map_err(|_| {
ManagerHTTPClientError::CannotGenerateLogs(String::from("Invalid response"))
})?);
// 3) store response's binary content (logs) in a file
let mut file = fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.mode(0o600)
.open(&destination)
.map_err(|_| {
ManagerHTTPClientError::CannotGenerateLogs(String::from(
"Cannot store received response",
))
})?;
let mut content = Cursor::new(response.bytes().await.map_err(BaseHTTPClientError::HTTP)?);
std::io::copy(&mut content, &mut file).map_err(|_| {
ManagerHTTPClientError::CannotGenerateLogs(String::from(
"Cannot store received response",
))
})?;
Ok(destination)
}
/// Asks backend for lists of log files and commands used for creating logs archive returned by
/// store (/logs/store) backed HTTP API command
pub async fn list(&self) -> Result<LogsLists, ManagerHTTPClientError> {
Ok(self.client.get("/manager/logs/list").await?)
}
/// Returns the installer status.
pub async fn status(&self) -> Result<Status, ManagerHTTPClientError> {
let status = self.client.get::<Status>("/v2/status").await?;
Ok(status)
}
}