wasmcloud_provider_wadm/
config.rs1use anyhow::{anyhow, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use tracing::warn;
5use wasmcloud_provider_sdk::{core::secrets::SecretValue, LinkConfig};
6
7const DEFAULT_CTL_HOST: &str = "0.0.0.0";
8const DEFAULT_CTL_PORT: u16 = 4222;
9const DEFAULT_LATTICE: &str = "default";
10
11const CONFIG_LATTICE: &str = "lattice";
13const CONFIG_APP_NAME: &str = "app_name";
14const CONFIG_CTL_HOST: &str = "ctl_host";
15const CONFIG_CTL_PORT: &str = "ctl_port";
16const CONFIG_CTL_JWT: &str = "ctl_jwt";
17const CONFIG_CTL_SEED: &str = "ctl_seed";
18const CONFIG_CTL_CREDSFILE: &str = "ctl_credsfile";
19const CONFIG_CTL_TLS_CA_FILE: &str = "ctl_tls_ca_file";
20const CONFIG_CTL_TLS_FIRST: &str = "ctl_tls_first";
21const CONFIG_JS_DOMAIN: &str = "js_domain";
22
23fn default_lattice() -> String {
24 DEFAULT_LATTICE.to_string()
25}
26
27fn default_ctl_host() -> String {
28 DEFAULT_CTL_HOST.to_string()
29}
30
31fn default_ctl_port() -> u16 {
32 DEFAULT_CTL_PORT
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
38pub(crate) struct ClientConfig {
39 #[serde(default = "default_lattice")]
41 pub lattice: String,
42 #[serde(default)]
45 pub app_name: Option<String>,
46 #[serde(default = "default_ctl_host")]
48 pub ctl_host: String,
49 #[serde(default = "default_ctl_port")]
51 pub ctl_port: u16,
52 #[serde(default)]
54 pub ctl_jwt: Option<String>,
55 #[serde(default)]
57 pub ctl_seed: Option<String>,
58 #[serde(default)]
60 pub ctl_credsfile: Option<String>,
61 #[serde(default)]
63 pub ctl_tls_ca_file: Option<String>,
64 #[serde(default)]
66 pub ctl_tls_first: bool,
67 #[serde(default)]
69 pub js_domain: Option<String>,
70}
71
72impl Default for ClientConfig {
73 fn default() -> Self {
74 ClientConfig {
75 lattice: default_lattice(),
76 app_name: None,
77 ctl_host: default_ctl_host(),
78 ctl_port: default_ctl_port(),
79 ctl_jwt: None,
80 ctl_seed: None,
81 ctl_credsfile: None,
82 ctl_tls_ca_file: None,
83 ctl_tls_first: false,
84 js_domain: None,
85 }
86 }
87}
88
89impl TryFrom<HashMap<String, String>> for ClientConfig {
90 type Error = anyhow::Error;
91
92 fn try_from(values: HashMap<String, String>) -> Result<Self> {
93 let mut config = ClientConfig::default();
94
95 if let Some(ctl_host) = values.get(CONFIG_CTL_HOST) {
96 config.ctl_host = ctl_host.clone();
97 }
98 if let Some(ctl_port) = values.get(CONFIG_CTL_PORT) {
99 config.ctl_port = ctl_port.parse().map_err(|_| anyhow!("Invalid ctl_port"))?;
100 }
101 if let Some(ctl_jwt) = values.get(CONFIG_CTL_JWT) {
102 config.ctl_jwt = Some(ctl_jwt.clone());
103 }
104 if let Some(ctl_seed) = values.get(CONFIG_CTL_SEED) {
105 config.ctl_seed = Some(ctl_seed.clone());
106 }
107 if let Some(ctl_credsfile) = values.get(CONFIG_CTL_CREDSFILE) {
108 config.ctl_credsfile = Some(ctl_credsfile.clone());
109 }
110 if let Some(ctl_tls_ca_file) = values.get(CONFIG_CTL_TLS_CA_FILE) {
111 config.ctl_tls_ca_file = Some(ctl_tls_ca_file.clone());
112 }
113 if let Some(ctl_tls_first) = values.get(CONFIG_CTL_TLS_FIRST) {
114 config.ctl_tls_first = ctl_tls_first
115 .parse()
116 .map_err(|_| anyhow!("Invalid ctl_tls_first value"))?;
117 }
118 if let Some(js_domain) = values.get(CONFIG_JS_DOMAIN) {
119 config.js_domain = Some(js_domain.clone());
120 }
121 if let Some(lattice) = values.get(CONFIG_LATTICE) {
122 config.lattice = lattice.clone();
123 }
124 if let Some(app_name) = values.get(CONFIG_APP_NAME) {
125 config.app_name = Some(app_name.clone());
126 }
127
128 Ok(config)
129 }
130}
131
132impl ClientConfig {
133 pub fn merge(&self, extra: &ClientConfig) -> ClientConfig {
134 let mut out = self.clone();
135
136 if !extra.ctl_host.is_empty() {
137 out.ctl_host = extra.ctl_host.clone();
138 }
139 if extra.ctl_port != 0 {
140 out.ctl_port = extra.ctl_port;
141 }
142 if extra.ctl_jwt.is_some() {
143 out.ctl_jwt = extra.ctl_jwt.clone();
144 }
145 if extra.ctl_seed.is_some() {
146 out.ctl_seed = extra.ctl_seed.clone();
147 }
148 if extra.ctl_credsfile.is_some() {
149 out.ctl_credsfile = extra.ctl_credsfile.clone();
150 }
151 if extra.ctl_tls_ca_file.is_some() {
152 out.ctl_tls_ca_file = extra.ctl_tls_ca_file.clone();
153 }
154 if extra.ctl_tls_first {
155 out.ctl_tls_first = extra.ctl_tls_first;
156 }
157 if extra.js_domain.is_some() {
158 out.js_domain = extra.js_domain.clone();
159 }
160 if !extra.lattice.is_empty() {
161 out.lattice = extra.lattice.clone();
162 }
163 if extra.app_name.is_some() {
164 out.app_name = extra.app_name.clone();
165 }
166
167 out
168 }
169}
170
171pub(crate) fn extract_wadm_config(
172 link_config: &LinkConfig,
173 is_subscription: bool,
174) -> Option<ClientConfig> {
175 let LinkConfig {
176 config, secrets, ..
177 } = link_config;
178 let mut client_config = ClientConfig::default();
179
180 if is_subscription {
182 let app_name = config.get(CONFIG_APP_NAME);
183 if app_name.is_none() {
184 warn!("Subscription config missing required app_name field");
185 return None;
186 }
187 client_config.app_name = app_name.cloned();
188 }
189
190 if let Some(host) = config.get(CONFIG_CTL_HOST) {
191 client_config.ctl_host = host.clone();
192 }
193 if let Some(port) = config.get(CONFIG_CTL_PORT) {
194 if let Ok(port_num) = port.parse() {
195 client_config.ctl_port = port_num;
196 }
197 }
198
199 if let Some(jwt_val) = config.get(CONFIG_CTL_JWT) {
200 client_config.ctl_jwt = Some(jwt_val.clone());
201 }
202
203 if let Some(seed_secret) = secrets
205 .get(CONFIG_CTL_SEED)
206 .and_then(SecretValue::as_string)
207 {
208 client_config.ctl_seed = Some(seed_secret.to_string());
209 } else if let Some(seed_val) = config.get(CONFIG_CTL_SEED) {
210 warn!("Seed found in config instead of secrets - consider moving to secrets");
211 client_config.ctl_seed = Some(seed_val.clone());
212 }
213
214 if let Some(lattice) = config.get(CONFIG_LATTICE) {
215 client_config.lattice = lattice.clone();
216 }
217 if let Some(credsfile) = config.get(CONFIG_CTL_CREDSFILE) {
218 client_config.ctl_credsfile = Some(credsfile.clone());
219 }
220 if let Some(tls_ca_file) = config.get(CONFIG_CTL_TLS_CA_FILE) {
221 client_config.ctl_tls_ca_file = Some(tls_ca_file.clone());
222 }
223 if let Some(tls_first) = config.get(CONFIG_CTL_TLS_FIRST) {
224 client_config.ctl_tls_first = matches!(tls_first.to_lowercase().as_str(), "true" | "yes");
225 }
226 if let Some(js_domain) = config.get(CONFIG_JS_DOMAIN) {
227 client_config.js_domain = Some(js_domain.clone());
228 }
229
230 Some(client_config)
231}