greentic_setup/platform_setup/
persistence.rs1use std::path::{Path, PathBuf};
4
5use anyhow::{Context, Result};
6use serde::Deserialize;
7
8use crate::platform_setup::types::{StaticRoutesPolicy, TelemetryAnswers, TunnelAnswers};
9
10pub fn static_routes_artifact_path(bundle_root: &Path) -> PathBuf {
12 bundle_root
13 .join("state")
14 .join("config")
15 .join("platform")
16 .join("static-routes.json")
17}
18
19pub fn load_static_routes_artifact(bundle_root: &Path) -> Result<Option<StaticRoutesPolicy>> {
21 let path = static_routes_artifact_path(bundle_root);
22 if !path.exists() {
23 return Ok(None);
24 }
25 let raw = std::fs::read_to_string(&path)
26 .with_context(|| format!("failed to read {}", path.display()))?;
27 let policy = serde_json::from_str(&raw)
28 .or_else(|_| serde_yaml_bw::from_str(&raw))
29 .with_context(|| format!("failed to parse {}", path.display()))?;
30 Ok(Some(policy))
31}
32
33#[derive(Debug, Deserialize)]
34struct RuntimeEndpoints {
35 #[allow(dead_code)]
36 tenant: Option<String>,
37 #[allow(dead_code)]
38 team: Option<String>,
39 public_base_url: Option<String>,
40}
41
42pub fn load_runtime_public_base_url(
44 bundle_root: &Path,
45 tenant: &str,
46 team: Option<&str>,
47) -> Result<Option<String>> {
48 let team = team.unwrap_or("default");
49 let path = bundle_root
50 .join("state")
51 .join("runtime")
52 .join(format!("{tenant}.{team}"))
53 .join("endpoints.json");
54 if !path.exists() {
55 return Ok(None);
56 }
57 let raw = std::fs::read_to_string(&path)
58 .with_context(|| format!("failed to read {}", path.display()))?;
59 let endpoints: RuntimeEndpoints = serde_json::from_str(&raw)
60 .with_context(|| format!("failed to parse {}", path.display()))?;
61 Ok(endpoints
62 .public_base_url
63 .as_deref()
64 .map(str::trim)
65 .filter(|value| !value.is_empty())
66 .map(ToString::to_string))
67}
68
69pub fn load_effective_static_routes_defaults(
71 bundle_root: &Path,
72 tenant: &str,
73 team: Option<&str>,
74) -> Result<Option<StaticRoutesPolicy>> {
75 let mut policy = load_static_routes_artifact(bundle_root)?.unwrap_or_default();
76 if policy.public_base_url.is_none()
77 && let Some(runtime_url) = load_runtime_public_base_url(bundle_root, tenant, team)?
78 {
79 policy.public_base_url = Some(runtime_url);
80 }
81 if policy == StaticRoutesPolicy::disabled() {
82 return Ok(None);
83 }
84 Ok(Some(policy))
85}
86
87pub fn tunnel_artifact_path(bundle_root: &Path) -> PathBuf {
89 bundle_root.join(".greentic").join("tunnel.json")
90}
91
92pub fn load_tunnel_artifact(bundle_root: &Path) -> Result<Option<TunnelAnswers>> {
94 let path = tunnel_artifact_path(bundle_root);
95 if !path.exists() {
96 return Ok(None);
97 }
98 let raw = std::fs::read_to_string(&path)
99 .with_context(|| format!("failed to read {}", path.display()))?;
100 let answers: TunnelAnswers = serde_json::from_str(&raw)
101 .with_context(|| format!("failed to parse {}", path.display()))?;
102 Ok(Some(answers))
103}
104
105pub fn persist_tunnel_artifact(bundle_root: &Path, answers: &TunnelAnswers) -> Result<PathBuf> {
107 let path = tunnel_artifact_path(bundle_root);
108 if let Some(parent) = path.parent() {
109 std::fs::create_dir_all(parent)?;
110 }
111 let payload = serde_json::to_string_pretty(answers).context("serialize tunnel answers")?;
112 std::fs::write(&path, payload)
113 .with_context(|| format!("failed to write {}", path.display()))?;
114 Ok(path)
115}
116
117pub fn persist_static_routes_artifact(
119 bundle_root: &Path,
120 policy: &StaticRoutesPolicy,
121) -> Result<PathBuf> {
122 let path = static_routes_artifact_path(bundle_root);
123 if let Some(parent) = path.parent() {
124 std::fs::create_dir_all(parent)?;
125 }
126 let payload = serde_json::to_string_pretty(policy).context("serialize static routes policy")?;
127 std::fs::write(&path, payload)
128 .with_context(|| format!("failed to write {}", path.display()))?;
129 Ok(path)
130}
131
132pub fn telemetry_artifact_path(bundle_root: &Path) -> PathBuf {
134 bundle_root
135 .join("state")
136 .join("config")
137 .join("platform")
138 .join("telemetry.json")
139}
140
141pub fn load_telemetry_artifact(bundle_root: &Path) -> Result<Option<TelemetryAnswers>> {
143 let path = telemetry_artifact_path(bundle_root);
144 if !path.exists() {
145 return Ok(None);
146 }
147 let raw = std::fs::read_to_string(&path)
148 .with_context(|| format!("failed to read {}", path.display()))?;
149 let answers: TelemetryAnswers = serde_json::from_str(&raw)
150 .with_context(|| format!("failed to parse {}", path.display()))?;
151 Ok(Some(answers))
152}
153
154pub fn persist_telemetry_artifact(
156 bundle_root: &Path,
157 answers: &TelemetryAnswers,
158) -> Result<PathBuf> {
159 let path = telemetry_artifact_path(bundle_root);
160 if let Some(parent) = path.parent() {
161 std::fs::create_dir_all(parent)?;
162 }
163 let payload = serde_json::to_string_pretty(answers).context("serialize telemetry answers")?;
164 std::fs::write(&path, payload)
165 .with_context(|| format!("failed to write {}", path.display()))?;
166 Ok(path)
167}