1use crate::app::app_cmd::AppQuota;
2use crate::codec::*;
3use crate::coreobj::CoreObjectType;
4use crate::DecAppId;
5use cyfs_base::*;
6use serde::Serialize;
7
8use core::fmt;
9use int_enum::IntEnum;
10use std::collections::HashMap;
11
12pub const APP_LOCAL_STATUS_MAIN_PATH: &str = "/app_local_status";
13
14#[derive(Clone, Copy, Eq, PartialEq, Debug, IntEnum, Serialize)]
15#[repr(u8)]
16pub enum AppLocalStatusCode {
17 Init = 0,
18 Installing = 1, InstallFailed = 3,
20
21 NoService = 4,
22 Stopping = 5,
23 Stop = 6,
24 StopFailed = 7,
25
26 Starting = 8,
27 Running = 9,
28 StartFailed = 10,
29
30 Uninstalling = 11,
31 UninstallFailed = 12,
32 Uninstalled = 13,
33
34 RunException = 15, ErrStatus = 255,
39}
40
41impl fmt::Display for AppLocalStatusCode {
42 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 match self {
44 &AppLocalStatusCode::Init => write!(f, "Init"),
45 &AppLocalStatusCode::Installing => write!(f, "Installing"),
46 &AppLocalStatusCode::InstallFailed => write!(f, "InstallFailed"),
47 &AppLocalStatusCode::NoService => write!(f, "NoService"),
48 &AppLocalStatusCode::Stopping => write!(f, "Stopping"),
49 &AppLocalStatusCode::Stop => write!(f, "Stop"),
50 &AppLocalStatusCode::StopFailed => write!(f, "StopFailed"),
51 &AppLocalStatusCode::Starting => write!(f, "Starting"),
52 &AppLocalStatusCode::Running => write!(f, "Running"),
53 &AppLocalStatusCode::StartFailed => write!(f, "StartFailed"),
54 &AppLocalStatusCode::Uninstalling => write!(f, "Uninstalling"),
55 &AppLocalStatusCode::UninstallFailed => write!(f, "UninstallFailed"),
56 &AppLocalStatusCode::Uninstalled => write!(f, "Uninstalled"),
57 &AppLocalStatusCode::RunException => write!(f, "RunException"),
59 &AppLocalStatusCode::ErrStatus => write!(f, "ErrStatus"),
60 }
61 }
62}
63
64impl std::convert::From<u8> for AppLocalStatusCode {
65 fn from(value: u8) -> Self {
66 match Self::from_int(value) {
67 Ok(v) => v,
68 Err(e) => {
69 error!("unknown AppLocalStatusCode value: {} {}", value, e);
70 Self::ErrStatus
71 }
72 }
73 }
74}
75
76#[derive(Clone, Copy, Eq, PartialEq, Debug, IntEnum, Serialize)]
78#[repr(u8)]
79pub enum SubErrorCode {
80 None = 0,
81 Incompatible = 1,
82 NoVersion = 2,
83 DownloadFailed = 3,
84 DockerFailed = 4,
85 CommondFailed = 5,
86 AppNotFound = 6,
87 QueryPermissionError = 7,
88 LoadFailed = 8,
89 RemoveFailed = 9,
90 AssignContainerIpFailed = 10,
91 RegisterAppFailed = 11,
92 PubDirFailed = 12,
93 Unknown = 255,
94}
95
96impl fmt::Display for SubErrorCode {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 match self {
99 &SubErrorCode::None => write!(f, "None"),
100 &SubErrorCode::Incompatible => write!(f, "Incompatible"),
101 &SubErrorCode::NoVersion => write!(f, "NoVersion"),
102 &SubErrorCode::DownloadFailed => write!(f, "DownloadFailed"),
103 &SubErrorCode::DockerFailed => write!(f, "DockerFailed"),
104 &SubErrorCode::CommondFailed => write!(f, "CommondFailed"),
105 &SubErrorCode::AppNotFound => write!(f, "AppNotFound"),
106 &SubErrorCode::QueryPermissionError => write!(f, "QueryPermissionError"),
107 &SubErrorCode::LoadFailed => write!(f, "LoadFailed"),
108 &SubErrorCode::RemoveFailed => write!(f, "RemoveFailed"),
109 &SubErrorCode::AssignContainerIpFailed => write!(f, "AssignContainerIpFailed"),
110 &SubErrorCode::RegisterAppFailed => write!(f, "RegisterAppFailed"),
111 &SubErrorCode::PubDirFailed => write!(f, "PubDirFailed"),
112 &SubErrorCode::Unknown => write!(f, "Unknown"),
113 }
114 }
115}
116
117impl std::convert::From<u8> for SubErrorCode {
118 fn from(value: u8) -> Self {
119 match Self::from_int(value) {
120 Ok(v) => v,
121 Err(e) => {
122 error!("unknown SubErrorCode value: {} {}", value, e);
123 Self::Unknown
124 }
125 }
126 }
127}
128
129#[derive(Clone, Copy, Eq, PartialEq, Debug, IntEnum, Serialize)]
130#[repr(u8)]
131pub enum PermissionState {
132 Unhandled = 0, Blocked = 1, Granted = 2, }
136
137impl fmt::Display for PermissionState {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 match self {
140 &PermissionState::Unhandled => write!(f, "Unhandled"),
141 &PermissionState::Blocked => write!(f, "Blocked"),
142 &PermissionState::Granted => write!(f, "Granted"),
143 }
144 }
145}
146
147impl std::convert::From<u8> for PermissionState {
148 fn from(value: u8) -> Self {
149 match Self::from_int(value) {
150 Ok(v) => v,
151 Err(e) => {
152 error!("unknown AppLocalStatusCode value: {} {}", value, e);
153 Self::Unhandled
154 }
155 }
156 }
157}
158
159#[derive(Clone, Debug, Serialize)]
161pub struct PermissionNode {
162 reason: String,
163 state: PermissionState,
164}
165
166#[derive(Clone, ProtobufEncode, ProtobufDecode, ProtobufTransformType, Serialize)]
167#[cyfs_protobuf_type(crate::codec::protos::AppLocalStatusDesc)]
168pub struct AppLocalStatusDesc {
169 id: DecAppId,
170 status: AppLocalStatusCode,
171 version: Option<String>,
172 web_dir: Option<ObjectId>,
173 permissions: HashMap<String, PermissionNode>,
174 quota: AppQuota,
175 last_status_update_time: u64,
176 sub_error: SubErrorCode,
177 auto_update: bool,
178}
179impl DescContent for AppLocalStatusDesc {
180 fn obj_type() -> u16 {
181 CoreObjectType::AppLocalStatus as u16
182 }
183
184 fn format(&self) -> u8 {
185 OBJECT_CONTENT_CODEC_FORMAT_PROTOBUF
186 }
187
188 type OwnerType = Option<ObjectId>;
189 type AreaType = SubDescNone;
190 type AuthorType = SubDescNone;
191 type PublicKeyType = SubDescNone;
192}
193
194impl ProtobufTransform<protos::AppLocalStatusDesc> for AppLocalStatusDesc {
195 fn transform(value: protos::AppLocalStatusDesc) -> BuckyResult<Self> {
196 let mut permissions = HashMap::new();
197 for permission_item in value.permissions {
198 let permission = permission_item.permission;
199 let reason = permission_item.reason;
200 let state = ProtobufCodecHelper::decode_value(permission_item.state as u8)?;
201 permissions.insert(permission, PermissionNode { reason, state });
202 }
203
204 let quota = ProtobufTransform::transform(value.quota.unwrap())?;
205
206 let mut ret = Self {
207 id: ProtobufCodecHelper::decode_buf(value.id)?,
208 status: ProtobufCodecHelper::decode_value(value.status as u8)?,
209 version: None,
210 web_dir: None,
211 permissions,
212 quota,
213 last_status_update_time: value.last_status_update_time.parse::<u64>()?,
214 sub_error: ProtobufCodecHelper::decode_value(value.sub_error as u8)?,
215 auto_update: value.auto_update,
216 };
217 if value.version.is_some() {
218 ret.version = Some(value.version.unwrap());
219 }
220 if value.web_dir.is_some() {
221 ret.web_dir = Some(ProtobufCodecHelper::decode_buf(value.web_dir.unwrap())?);
222 }
223
224 Ok(ret)
225 }
226}
227
228impl ProtobufTransform<&AppLocalStatusDesc> for protos::AppLocalStatusDesc {
229 fn transform(value: &AppLocalStatusDesc) -> BuckyResult<Self> {
230 let quota = ProtobufTransform::transform(&value.quota)?;
236 let mut ret = Self {
237 id: vec![],
238 status: value.status as u32,
239 web_dir: None,
240 version: None,
241 permissions: vec![],
242 quota: Some(quota),
243 last_status_update_time: value.last_status_update_time.to_string(),
244 sub_error: value.sub_error as u32,
245 auto_update: value.auto_update,
246 };
247 ret.id = value.id.to_vec()?;
248 if let Some(dir) = &value.web_dir {
249 ret.web_dir = Some(dir.to_vec()?);
250 }
251 if let Some(version) = &value.version {
252 ret.version = Some(version.to_owned());
253 }
254
255 let mut permissions = Vec::new();
256 for (k, v) in &value.permissions {
257 let item = protos::AppPermission {
258 permission: k.to_owned(),
259 reason: v.reason.to_owned(),
260 state: v.state as u32,
261 };
262 permissions.push(item);
263 }
264 permissions.sort_by(|left, right| left.permission.partial_cmp(&right.permission).unwrap());
265 ret.permissions = permissions.into();
266
267 Ok(ret)
268 }
269}
270
271type AppLocalStatusType = NamedObjType<AppLocalStatusDesc, AppLocalStatusBody>;
272type AppLocalStatusBuilder = NamedObjectBuilder<AppLocalStatusDesc, AppLocalStatusBody>;
273
274pub type AppLocalStatusId = NamedObjectId<AppLocalStatusType>;
275pub type AppLocalStatus = NamedObjectBase<AppLocalStatusType>;
276
277pub trait AppLocalStatusObj {
278 fn create(owner: ObjectId, id: DecAppId) -> Self;
279
280 fn app_id(&self) -> &DecAppId;
281 fn status(&self) -> AppLocalStatusCode;
282 fn web_dir(&self) -> Option<&ObjectId>;
283 fn version(&self) -> Option<&str>;
284 fn permissions(&self) -> &HashMap<String, PermissionNode>;
285 fn quota(&self) -> &AppQuota;
286 fn permission_unhandled(&self) -> Option<HashMap<String, String>>;
288 fn last_status_update_time(&self) -> u64;
289 fn sub_error(&self) -> SubErrorCode;
290 fn auto_update(&self) -> bool;
291
292 fn set_status(&mut self, status: AppLocalStatusCode);
293 fn set_web_dir(&mut self, web_dir: Option<ObjectId>);
294 fn set_version(&mut self, version: &str);
295 fn add_permissions(&mut self, permissions: &HashMap<String, String>) -> bool;
297 fn set_permissions(&mut self, permissions: &HashMap<String, PermissionState>) -> bool;
299 fn set_quota(&mut self, quota: &AppQuota) -> bool;
301 fn set_sub_error(&mut self, code: SubErrorCode);
302 fn set_auto_update(&mut self, auto_update: bool) -> bool;
304
305 fn output(&self) -> String;
306}
307
308impl AppLocalStatusObj for AppLocalStatus {
309 fn create(owner: ObjectId, id: DecAppId) -> Self {
310 let quota = AppQuota {
312 mem: 0,
313 disk_space: 0,
314 cpu: 0,
315 };
316 let desc = AppLocalStatusDesc {
317 id,
318 status: AppLocalStatusCode::Init,
319 version: None,
320 web_dir: None,
321 permissions: HashMap::new(),
322 quota,
323 last_status_update_time: bucky_time_now(),
324 sub_error: SubErrorCode::None,
325 auto_update: true,
326 };
327 let body = AppLocalStatusBody {};
328 AppLocalStatusBuilder::new(desc, body)
329 .owner(owner)
330 .no_create_time()
331 .build()
332 }
333
334 fn app_id(&self) -> &DecAppId {
335 &self.desc().content().id
336 }
337
338 fn status(&self) -> AppLocalStatusCode {
339 self.desc().content().status
340 }
341
342 fn sub_error(&self) -> SubErrorCode {
343 self.desc().content().sub_error
344 }
345
346 fn auto_update(&self) -> bool {
347 self.desc().content().auto_update
348 }
349
350 fn last_status_update_time(&self) -> u64 {
351 self.desc().content().last_status_update_time
352 }
353
354 fn set_status(&mut self, status: AppLocalStatusCode) {
355 self.desc_mut().content_mut().status = status;
356 self.desc_mut().content_mut().last_status_update_time = bucky_time_now();
357 }
358
359 fn web_dir(&self) -> Option<&ObjectId> {
360 self.desc().content().web_dir.as_ref()
361 }
362
363 fn set_web_dir(&mut self, web_dir: Option<ObjectId>) {
364 self.desc_mut().content_mut().web_dir = web_dir;
365 }
366
367 fn version(&self) -> Option<&str> {
368 self.desc().content().version.as_ref().map(|s| s.as_str())
369 }
370
371 fn set_version(&mut self, version: &str) {
372 self.desc_mut().content_mut().version = Some(version.to_string());
373 }
374
375 fn permissions(&self) -> &HashMap<String, PermissionNode> {
376 &self.desc().content().permissions
377 }
378
379 fn permission_unhandled(&self) -> Option<HashMap<String, String>> {
380 let permissions = self.permissions();
381 let mut unhandled = HashMap::new();
382 for (k, v) in permissions {
383 if v.state == PermissionState::Unhandled {
384 unhandled.insert(k.to_string(), v.reason.clone());
385 }
386 }
387
388 if unhandled.is_empty() {
389 None
390 } else {
391 Some(unhandled)
392 }
393 }
394
395 fn add_permissions(&mut self, permissions: &HashMap<String, String>) -> bool {
396 let cur_permissions = &mut self.desc_mut().content_mut().permissions;
397 let mut changed = false;
398 for (k, v) in permissions {
399 if !cur_permissions.contains_key(k) {
400 cur_permissions.insert(
401 k.to_string(),
402 PermissionNode {
403 reason: v.to_string(),
404 state: PermissionState::Unhandled,
405 },
406 );
407 changed = true;
408 }
409 }
410 changed
411 }
412
413 fn set_permissions(&mut self, permissions: &HashMap<String, PermissionState>) -> bool {
414 let cur_permissions = &mut self.desc_mut().content_mut().permissions;
415 let mut changed = false;
416 for (k, v) in permissions {
417 if let Some(cur_node) = cur_permissions.get_mut(k) {
418 if cur_node.state != *v {
419 cur_node.state = *v;
420 changed = true;
421 }
422 }
423 }
424 changed
425 }
426
427 fn quota(&self) -> &AppQuota {
428 &self.desc().content().quota
429 }
430
431 fn set_quota(&mut self, quota: &AppQuota) -> bool {
432 let cur_quota = &mut self.desc_mut().content_mut().quota;
433 let mut changed = false;
434 if cur_quota.mem != quota.mem {
435 cur_quota.mem = quota.mem;
436 changed = true;
437 }
438 if cur_quota.disk_space != quota.disk_space {
439 cur_quota.disk_space = quota.disk_space;
440 changed = true;
441 }
442 if cur_quota.cpu != quota.cpu {
443 cur_quota.cpu = quota.cpu;
444 changed = true;
445 }
446 changed
447 }
448
449 fn set_sub_error(&mut self, code: SubErrorCode) {
450 self.desc_mut().content_mut().sub_error = code;
451 }
452
453 fn set_auto_update(&mut self, auto_update: bool) -> bool {
454 let old_value = self.auto_update();
455 self.desc_mut().content_mut().auto_update = auto_update;
456 old_value
457 }
458
459 fn output(&self) -> String {
460 let app_id = self.app_id();
461 let status = self.status();
462 let ver = self.version();
463 let sub_err = self.sub_error();
465 let self_id = self.desc().calculate_id();
466 let auto_update = self.auto_update();
467 format!(
468 "[AppLocalStatus] appid:{} statusid:{}, status:{}, ver:{:?}, auto_update:{}, sub err:{}",
469 app_id, self_id, status, ver, auto_update, sub_err
470 )
471 }
472}
473
474#[derive(Clone, Default, ProtobufEmptyEncode, ProtobufEmptyDecode, Serialize)]
475pub struct AppLocalStatusBody {}
476
477impl BodyContent for AppLocalStatusBody {
478 fn format(&self) -> u8 {
479 OBJECT_CONTENT_CODEC_FORMAT_PROTOBUF
480 }
481}