Skip to content

Commit a77e3b9

Browse files
committed
Better error handling, Clean up and add timout for ping Minecraft
1 parent 89568ab commit a77e3b9

File tree

12 files changed

+181
-119
lines changed

12 files changed

+181
-119
lines changed

src/controller/definitions/epsilon_instance.rs

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use std::sync::Arc;
33

44
use crate::epsilon::server::instances::common::instance_type::InstanceType;
55
use crate::epsilon::server::instances::common::state::EpsilonState;
6-
use crate::EResult;
76

7+
use crate::epsilon::epsilon_error::EpsilonError;
8+
use anyhow::format_err;
89
use async_minecraft_ping::{ConnectionConfig, StatusResponse};
910
use kube::CustomResource;
1011
use schemars::JsonSchema;
@@ -45,14 +46,24 @@ pub struct EpsilonInstanceStatus {
4546
}
4647

4748
impl EpsilonInstance {
48-
pub async fn to_json(&self) -> InstanceJson {
49-
InstanceJson {
50-
name: self.metadata.name.as_ref().unwrap().clone(),
49+
pub async fn to_json(&self) -> Result<InstanceJson, EpsilonError> {
50+
Ok(InstanceJson {
51+
name: self.get_name(),
5152
template: self.spec.template.clone(),
52-
state: self.get_state().clone(),
53-
slots: self.status.as_ref().unwrap().slots,
53+
state: *self.get_state(),
54+
55+
slots: self
56+
.status
57+
.as_ref()
58+
.ok_or(EpsilonError::RetrieveStatusError)?
59+
.slots,
60+
5461
online_count: self.get_online_count().await.unwrap_or(0),
55-
}
62+
})
63+
}
64+
65+
pub fn get_name(&self) -> String {
66+
self.metadata.name.as_ref().unwrap().to_owned()
5667
}
5768

5869
pub fn get_state(&self) -> &EpsilonState {
@@ -62,38 +73,51 @@ impl EpsilonInstance {
6273
}
6374
}
6475

65-
pub async fn get_info(&self) -> EResult<StatusResponse> {
66-
let status = self.status.as_ref().unwrap();
67-
let address = status.ip.as_ref().unwrap();
68-
let config = ConnectionConfig::build(address);
76+
pub async fn get_info(&self) -> Result<StatusResponse, EpsilonError> {
77+
let status = self
78+
.status
79+
.as_ref()
80+
.ok_or(EpsilonError::RetrieveStatusError)?;
6981

70-
let duration = Duration::from_millis(100);
82+
let address = status
83+
.ip
84+
.as_ref()
85+
.ok_or(EpsilonError::RetrieveStatusError)?;
7186

72-
let connect = timeout(duration, async move { config.connect().await }).await??;
87+
let config = ConnectionConfig::build(address);
88+
let duration = Duration::from_millis(150);
7389

74-
let status = timeout(duration, async move { connect.status().await }).await??;
90+
let status_result: Result<StatusResponse, EpsilonError> = timeout(duration, async move {
91+
Ok(config.connect().await?.status().await?.status)
92+
})
93+
.await?;
7594

76-
Ok(status.status)
95+
status_result
7796
}
7897

79-
pub async fn get_online_count(&self) -> EResult<i32> {
98+
pub async fn get_online_count(&self) -> Result<i32, EpsilonError> {
8099
Ok(self.get_info().await?.players.online as i32)
81100
}
82101

83-
pub async fn get_available_slots(&self) -> EResult<i32> {
84-
Ok(&self.status.as_ref().unwrap().slots - self.get_online_count().await?)
102+
pub async fn get_available_slots(&self) -> Result<i32, EpsilonError> {
103+
Ok(&self
104+
.status
105+
.as_ref()
106+
.ok_or(EpsilonError::RetrieveStatusError)?
107+
.slots
108+
- self.get_online_count().await?)
85109
}
86110
}
87111

88112
#[async_trait]
89113
pub trait VectorOfInstance {
90-
async fn get_available_slots(&self) -> EResult<i32>;
91-
async fn get_online_count(&self) -> EResult<i32>;
114+
async fn get_available_slots(&self) -> Result<i32, EpsilonError>;
115+
async fn get_online_count(&self) -> Result<i32, EpsilonError>;
92116
}
93117

94118
#[async_trait]
95119
impl VectorOfInstance for Vec<Arc<EpsilonInstance>> {
96-
async fn get_available_slots(&self) -> EResult<i32> {
120+
async fn get_available_slots(&self) -> Result<i32, EpsilonError> {
97121
let mut available_slots = 0;
98122

99123
for instance in self {
@@ -103,7 +127,7 @@ impl VectorOfInstance for Vec<Arc<EpsilonInstance>> {
103127
Ok(available_slots)
104128
}
105129

106-
async fn get_online_count(&self) -> EResult<i32> {
130+
async fn get_online_count(&self) -> Result<i32, EpsilonError> {
107131
let mut number = 0;
108132

109133
for instance in self {

src/controller/epsilon_controller.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::controller::definitions::epsilon_instance::{
33
EpsilonInstance, EpsilonInstanceSpec, EpsilonInstanceStatus,
44
};
55
use crate::controller::definitions::epsilon_queue::EpsilonQueue;
6+
use crate::epsilon::epsilon_error::EpsilonError;
67
use crate::epsilon::server::instances::common::state::EpsilonState;
78
use crate::{EResult, TemplateProvider};
89
use anyhow::format_err;
@@ -264,7 +265,10 @@ impl EpsilonController {
264265
)
265266
}
266267

267-
pub async fn create_epsilon_instance(&self, template_name: &str) -> EResult<EpsilonInstance> {
268+
pub async fn create_epsilon_instance(
269+
&self,
270+
template_name: &str,
271+
) -> Result<EpsilonInstance, EpsilonError> {
268272
let epsilon_instance_api = &self.context.epsilon_instance_api;
269273

270274
let epsilon_instance = EpsilonInstance {
@@ -278,17 +282,25 @@ impl EpsilonController {
278282
status: None,
279283
};
280284

281-
Ok(epsilon_instance_api
285+
let instance_result = epsilon_instance_api
282286
.create(&PostParams::default(), &epsilon_instance)
283-
.await?)
287+
.await
288+
.map_err(|_| EpsilonError::CreateInstanceError(String::from(template_name)));
289+
290+
Ok(instance_result?)
284291
}
285292

286-
pub async fn in_game_epsilon_instance(&self, instance_name: &str) -> EResult<()> {
293+
pub async fn in_game_epsilon_instance(&self, instance_name: &str) -> Result<(), EpsilonError> {
287294
let epsilon_instance_api = &self.context.epsilon_instance_api;
288295
let store = &self.store;
289296

290297
if let Some(epsilon_instance) = store.get(&ObjectRef::new(instance_name)) {
291-
let mut instance_status = epsilon_instance.status.as_ref().unwrap().clone();
298+
let mut instance_status = epsilon_instance
299+
.status
300+
.as_ref()
301+
.ok_or(EpsilonError::RetrieveStatusError)?
302+
.clone();
303+
292304
instance_status.state = EpsilonState::InGame;
293305

294306
epsilon_instance_api
@@ -297,12 +309,11 @@ impl EpsilonController {
297309
&PatchParams::default(),
298310
&Patch::Merge(json!({ "status": instance_status })),
299311
)
300-
.await?;
301-
302-
Ok(())
303-
} else {
304-
Err(format_err!("Instance not found: {}", instance_name))
312+
.await
313+
.map_err(|_| EpsilonError::RemoveInstanceError(String::from(instance_name)))?;
305314
}
315+
316+
Ok(())
306317
}
307318

308319
pub fn get_epsilon_instance_api(&self) -> &Api<EpsilonInstance> {

src/epsilon/epsilon_error.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,41 @@
1+
use rocket::http::Status;
2+
use rocket::http::StatusClass::ServerError;
3+
use rocket::response::Responder;
4+
use rocket::{response, Request, Response};
15
use thiserror::Error;
26

37
#[derive(Error, Debug)]
48
pub enum EpsilonError {
5-
#[error("API server error -> {0}")]
9+
#[error("API server error {0}")]
610
ApiServerError(String),
11+
12+
#[error("Failed to parse json")]
13+
ParseJsonError,
14+
15+
#[error("Create instance error, template is ({0})")]
16+
CreateInstanceError(String),
17+
18+
#[error("Remove instance error {0}")]
19+
RemoveInstanceError(String),
20+
21+
#[error("Retrieve instance error")]
22+
RetrieveInstanceError,
23+
24+
#[error("Retrieve status error")]
25+
RetrieveStatusError,
26+
27+
#[error("Request error {0}")]
28+
RequestError(#[from] reqwest::Error),
29+
30+
#[error("Ping response error {0}")]
31+
PingMinecraftError(#[from] async_minecraft_ping::ServerError),
32+
33+
#[error("Timeout error {0}")]
34+
TimeoutError(#[from] tokio::time::error::Elapsed),
35+
}
36+
37+
impl<'r> Responder<'r, 'static> for EpsilonError {
38+
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
39+
Response::build().status(Status::InternalServerError).ok()
40+
}
741
}

src/epsilon/queue/queue_provider.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use std::sync::Arc;
33

44
use tokio::sync::RwLock;
55

6+
use crate::epsilon::epsilon_error::EpsilonError;
67
use crate::epsilon::queue::common::epsilon_queue::Queue;
7-
use crate::{EResult, InstanceProvider, TemplateProvider};
8+
use crate::{InstanceProvider, TemplateProvider};
89

910
pub struct QueueProvider {
1011
queue_map: HashMap<String, RwLock<Queue>>,
@@ -14,7 +15,7 @@ impl QueueProvider {
1415
pub async fn new(
1516
_instance_provider: &InstanceProvider,
1617
template_provider: &Arc<TemplateProvider>,
17-
) -> EResult<QueueProvider> {
18+
) -> Result<QueueProvider, EpsilonError> {
1819
let mut map = HashMap::new();
1920

2021
for template in template_provider.get_templates().await? {

src/epsilon/server/instances/common/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use schemars::JsonSchema;
22
use serde::{Deserialize, Serialize};
33

4-
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, JsonSchema)]
4+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, JsonSchema)]
55
pub enum EpsilonState {
66
Starting,
77
Running,
Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
use futures::TryFutureExt;
12
use std::sync::Arc;
23

34
use kube::api::DeleteParams;
45
use kube::runtime::wait::await_condition;
56

67
use crate::controller::definitions::epsilon_instance::EpsilonInstance;
8+
use crate::epsilon::epsilon_error::EpsilonError;
79
use crate::epsilon::server::instances::common::instance_type::InstanceType;
810
use crate::epsilon::server::instances::common::state::EpsilonState;
9-
use crate::{EResult, EpsilonController};
11+
use crate::EpsilonController;
1012

1113
pub struct InstanceProvider {
1214
epsilon_controller: Arc<EpsilonController>,
@@ -19,68 +21,59 @@ impl InstanceProvider {
1921
}
2022
}
2123

22-
pub async fn start_instance(&self, template_name: &str) -> EResult<EpsilonInstance> {
24+
pub async fn start_instance(
25+
&self,
26+
template_name: &str,
27+
) -> Result<EpsilonInstance, EpsilonError> {
2328
Ok(self
2429
.epsilon_controller
2530
.create_epsilon_instance(template_name)
2631
.await?)
2732
}
2833

29-
pub async fn remove_instance(&self, name: &str) -> EResult<()> {
34+
pub async fn remove_instance(&self, name: &str) -> Result<(), EpsilonError> {
3035
info!("An instance has been removed (name={})", name);
3136

3237
self.epsilon_controller
3338
.get_epsilon_instance_api()
3439
.delete(name, &DeleteParams::default())
35-
.await?;
40+
.await
41+
.map_err(|_| EpsilonError::RemoveInstanceError(String::from(name)))?;
3642

3743
Ok(())
3844
}
3945

40-
pub async fn get_instance(&self, instance_name: &str) -> EResult<EpsilonInstance> {
46+
pub async fn get_instance(&self, instance_name: &str) -> Result<EpsilonInstance, EpsilonError> {
4147
Ok(self
4248
.epsilon_controller
4349
.get_epsilon_instance_api()
4450
.get(instance_name)
45-
.await?)
51+
.await
52+
.map_err(|_| EpsilonError::RetrieveInstanceError)?)
4653
}
4754

4855
pub async fn get_instances(
4956
&self,
5057
instance_type: &InstanceType,
5158
template_option: Option<&str>,
5259
state_option: Option<&EpsilonState>,
53-
) -> EResult<Vec<Arc<EpsilonInstance>>> {
60+
) -> Result<Vec<Arc<EpsilonInstance>>, EpsilonError> {
5461
let mut instances = self.epsilon_controller.get_epsilon_instance_store().state();
5562

5663
for instance in &instances {
64+
let name = instance.get_name();
65+
5766
let condition = await_condition(
5867
self.epsilon_controller.get_epsilon_instance_api().clone(),
59-
instance.metadata.name.as_ref().unwrap(),
68+
&name,
6069
move |object: Option<&EpsilonInstance>| {
61-
object.map_or(false, |instance| {
62-
info!(
63-
"Instance status ({}) : {}",
64-
instance.metadata.name.as_ref().unwrap(),
65-
instance.status.is_some()
66-
);
67-
68-
instance.status.is_some()
69-
})
70+
object.map_or(false, |instance| instance.status.is_some())
7071
},
7172
);
7273

7374
let _ = tokio::time::timeout(std::time::Duration::from_secs(5), condition).await?;
7475
}
7576

76-
for instance in &instances {
77-
info!(
78-
"Instance ({}) : {}",
79-
instance.metadata.name.as_ref().unwrap(),
80-
instance.status.is_some()
81-
);
82-
}
83-
8477
instances = instances
8578
.into_iter()
8679
.filter(|instance| instance.status.as_ref().unwrap().t == *instance_type)
@@ -103,7 +96,7 @@ impl InstanceProvider {
10396
Ok(instances)
10497
}
10598

106-
pub async fn enable_in_game_instance(&self, name: &str) -> EResult<()> {
99+
pub async fn enable_in_game_instance(&self, name: &str) -> Result<(), EpsilonError> {
107100
self.epsilon_controller.in_game_epsilon_instance(name).await
108101
}
109102
}

0 commit comments

Comments
 (0)