summaryrefslogtreecommitdiffstats
path: root/crates/core/tedge/src/cli/disconnect/disconnect_bridge.rs
blob: d1854a8ceb1875ca145210724690f81a5ff20c14 (plain)
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
132
133
134
135
136
use crate::cli::disconnect::error::*;
use crate::command::*;
use crate::system_services::*;
use std::sync::Arc;
use tedge_config::TEdgeConfigLocation;
use which::which;

const TEDGE_BRIDGE_CONF_DIR_PATH: &str = "mosquitto-conf";

#[derive(Copy, Clone, Debug)]
pub enum Cloud {
    C8y,
    Azure,
}

impl Cloud {
    fn dependent_mapper_service(&self) -> SystemService {
        match self {
            Cloud::Azure => SystemService::TEdgeMapperAz,
            Cloud::C8y => SystemService::TEdgeMapperC8y,
        }
    }
}

impl From<Cloud> for String {
    fn from(val: Cloud) -> Self {
        match val {
            Cloud::C8y => "Cumulocity".into(),
            Cloud::Azure => "Azure".into(),
        }
    }
}

#[derive(Debug)]
pub struct DisconnectBridgeCommand {
    pub config_location: TEdgeConfigLocation,
    pub config_file: String,
    pub cloud: Cloud,
    pub use_mapper: bool,
    pub use_agent: bool,
    pub service_manager: Arc<dyn SystemServiceManager>,
}

impl Command for DisconnectBridgeCommand {
    fn description(&self) -> String {
        format!("remove the bridge to disconnect {:?} cloud", self.cloud)
    }

    fn execute(&self) -> anyhow::Result<()> {
        match self.stop_bridge() {
            Ok(()) | Err(DisconnectBridgeError::BridgeFileDoesNotExist) => Ok(()),
            Err(err) => Err(err.into()),
        }
    }
}

impl DisconnectBridgeCommand {
    fn service_manager(&self) -> &dyn SystemServiceManager {
        self.service_manager.as_ref()
    }

    fn stop_bridge(&self) -> Result<(), DisconnectBridgeError> {
        // If this fails, do not continue with applying changes and stopping/disabling tedge-mapper.
        self.remove_bridge_config_file()?;

        if let Err(SystemServiceError::ServiceManagerUnavailable(name)) =
            self.service_manager.check_operational()
        {
            println!(
                "Service manager '{}' is not available, skipping stopping/disabling of tedge components.",
                name
            );
            return Ok(());
        }

        // Ignore failure
        let _ = self.apply_changes_to_mosquitto();

        // Only C8Y changes the status of tedge-mapper
        if self.use_mapper && which("tedge_mapper").is_ok() {
            self.service_manager()
                .stop_and_disable_service(self.cloud.dependent_mapper_service(), std::io::stdout());
        }

        if self.use_agent && which("tedge_agent").is_ok() {
            self.service_manager()
                .stop_and_disable_service(SystemService::TEdgeSMMapperC8Y, std::io::stdout());
            self.service_manager()
                .stop_and_disable_service(SystemService::TEdgeSMAgent, std::io::stdout());
        }

        Ok(())
    }

    fn remove_bridge_config_file(&self) -> Result<(), DisconnectBridgeError> {
        // Check if bridge exists and stop with code 0 if it doesn't.
        let bridge_conf_path = self
            .config_location
            .tedge_config_root_path
            .join(TEDGE_BRIDGE_CONF_DIR_PATH)
            .join(&self.config_file);

        println!("Removing {:?} bridge.\n", self.cloud);
        match std::fs::remove_file(&bridge_conf_path) {
            // If we find the bridge config file we remove it
            // and carry on to see if we need to restart mosquitto.
            Ok(()) => Ok(()),

            // If bridge config file was not found we assume that the bridge doesn't exist,
            // We finish early returning exit code 0.
            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
                println!("Bridge doesn't exist. Operation finished!");
                Err(DisconnectBridgeError::BridgeFileDoesNotExist)
            }

            Err(e) => Err(DisconnectBridgeError::FileOperationFailed(
                e,
                bridge_conf_path,
            )),
        }
    }

    // Deviation from specification:
    // Check if mosquitto is running, restart only if it was active before, if not don't do anything.
    fn apply_changes_to_mosquitto(&self) -> Result<(), DisconnectBridgeError> {
        println!("Applying changes to mosquitto.\n");

        if self
            .service_manager()
            .restart_service_if_running(SystemService::Mosquitto)?
        {
            println!("{:?} Bridge successfully disconnected!\n", self.cloud);
        }
        Ok(())
    }
}