cyfs_core/app/
app_local_status.rs

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, //安装成功进入Stop或者NoService,所以没有Installed
19    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    //Removed = 14,      //已经删除
35    RunException = 15, //运行异常
36
37    //setpermissioning? upgrading? setversion?
38    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::Removed => write!(f, "Removed"),
58            &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/*更小粒度的错误,比如安装失败时,可以细分错误:检查兼容性失败,下载失败,等等*/
77#[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, //未处理
133    Blocked = 1,   //阻止
134    Granted = 2,   //已授权
135}
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//state表示是否通过 -1:未处理,0:不同意,1:同意
160#[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 = protos::AppQuota {
231        //     mem: value.quota.mem,
232        //     disk_space: value.quota.disk_space,
233        //     cpu: value.quota.cpu,
234        // };
235        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    //获取没有被处理过的权限
287    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    //添加权限,如果权限列表里已经存在的会被pass,返回值表示有没有新的权限被添加
296    fn add_permissions(&mut self, permissions: &HashMap<String, String>) -> bool;
297    //设置已有的权限,返回值表示权限有没有变化(是否与现有权限相同)
298    fn set_permissions(&mut self, permissions: &HashMap<String, PermissionState>) -> bool;
299    //设置配额,返回值表示配额有没有变化(是否与现有权限相同)
300    fn set_quota(&mut self, quota: &AppQuota) -> bool;
301    fn set_sub_error(&mut self, code: SubErrorCode);
302    //return old auto_update value
303    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        //默认配额
311        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 web_dir = self.web_dir();
464        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}