sentry-options 0.0.4__tar.gz → 0.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sentry_options-0.0.4 → sentry_options-0.0.6}/Cargo.lock +131 -5
- {sentry_options-0.0.4 → sentry_options-0.0.6}/Cargo.toml +2 -2
- {sentry_options-0.0.4 → sentry_options-0.0.6}/PKG-INFO +1 -1
- {sentry_options-0.0.4 → sentry_options-0.0.6}/clients/python/src/lib.rs +2 -1
- {sentry_options-0.0.4 → sentry_options-0.0.6}/clients/rust/src/lib.rs +9 -12
- {sentry_options-0.0.4 → sentry_options-0.0.6}/pyproject.toml +1 -1
- {sentry_options-0.0.4 → sentry_options-0.0.6}/sentry-options-validation/src/lib.rs +142 -13
- {sentry_options-0.0.4 → sentry_options-0.0.6}/clients/python/Cargo.toml +0 -0
- {sentry_options-0.0.4 → sentry_options-0.0.6}/clients/python/tests/options_test.py +0 -0
- {sentry_options-0.0.4 → sentry_options-0.0.6}/clients/python/uv.lock +0 -0
- {sentry_options-0.0.4 → sentry_options-0.0.6}/clients/rust/Cargo.toml +0 -0
- {sentry_options-0.0.4 → sentry_options-0.0.6}/sentry-options-validation/Cargo.toml +0 -0
- {sentry_options-0.0.4 → sentry_options-0.0.6}/sentry-options-validation/src/namespace-schema.json +0 -0
|
@@ -31,6 +31,15 @@ version = "0.2.21"
|
|
|
31
31
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
32
32
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
|
33
33
|
|
|
34
|
+
[[package]]
|
|
35
|
+
name = "android_system_properties"
|
|
36
|
+
version = "0.1.5"
|
|
37
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
38
|
+
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
|
39
|
+
dependencies = [
|
|
40
|
+
"libc",
|
|
41
|
+
]
|
|
42
|
+
|
|
34
43
|
[[package]]
|
|
35
44
|
name = "anstream"
|
|
36
45
|
version = "0.6.21"
|
|
@@ -150,12 +159,33 @@ version = "1.11.0"
|
|
|
150
159
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
151
160
|
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
|
152
161
|
|
|
162
|
+
[[package]]
|
|
163
|
+
name = "cc"
|
|
164
|
+
version = "1.2.52"
|
|
165
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
166
|
+
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
|
|
167
|
+
dependencies = [
|
|
168
|
+
"find-msvc-tools",
|
|
169
|
+
"shlex",
|
|
170
|
+
]
|
|
171
|
+
|
|
153
172
|
[[package]]
|
|
154
173
|
name = "cfg-if"
|
|
155
174
|
version = "1.0.4"
|
|
156
175
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
157
176
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
158
177
|
|
|
178
|
+
[[package]]
|
|
179
|
+
name = "chrono"
|
|
180
|
+
version = "0.4.43"
|
|
181
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
182
|
+
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
|
183
|
+
dependencies = [
|
|
184
|
+
"iana-time-zone",
|
|
185
|
+
"num-traits",
|
|
186
|
+
"windows-link",
|
|
187
|
+
]
|
|
188
|
+
|
|
159
189
|
[[package]]
|
|
160
190
|
name = "clap"
|
|
161
191
|
version = "4.5.53"
|
|
@@ -202,6 +232,12 @@ version = "1.0.4"
|
|
|
202
232
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
203
233
|
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
|
204
234
|
|
|
235
|
+
[[package]]
|
|
236
|
+
name = "core-foundation-sys"
|
|
237
|
+
version = "0.8.7"
|
|
238
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
239
|
+
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|
240
|
+
|
|
205
241
|
[[package]]
|
|
206
242
|
name = "displaydoc"
|
|
207
243
|
version = "0.2.5"
|
|
@@ -240,7 +276,7 @@ dependencies = [
|
|
|
240
276
|
|
|
241
277
|
[[package]]
|
|
242
278
|
name = "example"
|
|
243
|
-
version = "0.0.
|
|
279
|
+
version = "0.0.6"
|
|
244
280
|
dependencies = [
|
|
245
281
|
"anyhow",
|
|
246
282
|
"sentry-options",
|
|
@@ -264,6 +300,12 @@ version = "2.3.0"
|
|
|
264
300
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
265
301
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|
266
302
|
|
|
303
|
+
[[package]]
|
|
304
|
+
name = "find-msvc-tools"
|
|
305
|
+
version = "0.1.7"
|
|
306
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
307
|
+
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
|
|
308
|
+
|
|
267
309
|
[[package]]
|
|
268
310
|
name = "fluent-uri"
|
|
269
311
|
version = "0.4.1"
|
|
@@ -472,6 +514,30 @@ dependencies = [
|
|
|
472
514
|
"tracing",
|
|
473
515
|
]
|
|
474
516
|
|
|
517
|
+
[[package]]
|
|
518
|
+
name = "iana-time-zone"
|
|
519
|
+
version = "0.1.64"
|
|
520
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
521
|
+
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
|
522
|
+
dependencies = [
|
|
523
|
+
"android_system_properties",
|
|
524
|
+
"core-foundation-sys",
|
|
525
|
+
"iana-time-zone-haiku",
|
|
526
|
+
"js-sys",
|
|
527
|
+
"log",
|
|
528
|
+
"wasm-bindgen",
|
|
529
|
+
"windows-core",
|
|
530
|
+
]
|
|
531
|
+
|
|
532
|
+
[[package]]
|
|
533
|
+
name = "iana-time-zone-haiku"
|
|
534
|
+
version = "0.1.2"
|
|
535
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
536
|
+
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
|
537
|
+
dependencies = [
|
|
538
|
+
"cc",
|
|
539
|
+
]
|
|
540
|
+
|
|
475
541
|
[[package]]
|
|
476
542
|
name = "icu_collections"
|
|
477
543
|
version = "2.1.1"
|
|
@@ -1111,7 +1177,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
|
1111
1177
|
|
|
1112
1178
|
[[package]]
|
|
1113
1179
|
name = "sentry-options"
|
|
1114
|
-
version = "0.0.
|
|
1180
|
+
version = "0.0.6"
|
|
1115
1181
|
dependencies = [
|
|
1116
1182
|
"sentry-options-validation",
|
|
1117
1183
|
"serde_json",
|
|
@@ -1121,8 +1187,9 @@ dependencies = [
|
|
|
1121
1187
|
|
|
1122
1188
|
[[package]]
|
|
1123
1189
|
name = "sentry-options-cli"
|
|
1124
|
-
version = "0.0.
|
|
1190
|
+
version = "0.0.6"
|
|
1125
1191
|
dependencies = [
|
|
1192
|
+
"chrono",
|
|
1126
1193
|
"clap",
|
|
1127
1194
|
"sentry-options-validation",
|
|
1128
1195
|
"serde",
|
|
@@ -1135,7 +1202,7 @@ dependencies = [
|
|
|
1135
1202
|
|
|
1136
1203
|
[[package]]
|
|
1137
1204
|
name = "sentry-options-python"
|
|
1138
|
-
version = "0.0.
|
|
1205
|
+
version = "0.0.6"
|
|
1139
1206
|
dependencies = [
|
|
1140
1207
|
"pyo3",
|
|
1141
1208
|
"sentry-options",
|
|
@@ -1144,7 +1211,7 @@ dependencies = [
|
|
|
1144
1211
|
|
|
1145
1212
|
[[package]]
|
|
1146
1213
|
name = "sentry-options-validation"
|
|
1147
|
-
version = "0.0.
|
|
1214
|
+
version = "0.0.6"
|
|
1148
1215
|
dependencies = [
|
|
1149
1216
|
"anyhow",
|
|
1150
1217
|
"jsonschema",
|
|
@@ -1222,6 +1289,12 @@ dependencies = [
|
|
|
1222
1289
|
"unsafe-libyaml",
|
|
1223
1290
|
]
|
|
1224
1291
|
|
|
1292
|
+
[[package]]
|
|
1293
|
+
name = "shlex"
|
|
1294
|
+
version = "1.3.0"
|
|
1295
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1296
|
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
1297
|
+
|
|
1225
1298
|
[[package]]
|
|
1226
1299
|
name = "slab"
|
|
1227
1300
|
version = "0.4.11"
|
|
@@ -1600,12 +1673,65 @@ dependencies = [
|
|
|
1600
1673
|
"windows-sys 0.61.2",
|
|
1601
1674
|
]
|
|
1602
1675
|
|
|
1676
|
+
[[package]]
|
|
1677
|
+
name = "windows-core"
|
|
1678
|
+
version = "0.62.2"
|
|
1679
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1680
|
+
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
|
1681
|
+
dependencies = [
|
|
1682
|
+
"windows-implement",
|
|
1683
|
+
"windows-interface",
|
|
1684
|
+
"windows-link",
|
|
1685
|
+
"windows-result",
|
|
1686
|
+
"windows-strings",
|
|
1687
|
+
]
|
|
1688
|
+
|
|
1689
|
+
[[package]]
|
|
1690
|
+
name = "windows-implement"
|
|
1691
|
+
version = "0.60.2"
|
|
1692
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1693
|
+
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|
1694
|
+
dependencies = [
|
|
1695
|
+
"proc-macro2",
|
|
1696
|
+
"quote",
|
|
1697
|
+
"syn",
|
|
1698
|
+
]
|
|
1699
|
+
|
|
1700
|
+
[[package]]
|
|
1701
|
+
name = "windows-interface"
|
|
1702
|
+
version = "0.59.3"
|
|
1703
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1704
|
+
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|
1705
|
+
dependencies = [
|
|
1706
|
+
"proc-macro2",
|
|
1707
|
+
"quote",
|
|
1708
|
+
"syn",
|
|
1709
|
+
]
|
|
1710
|
+
|
|
1603
1711
|
[[package]]
|
|
1604
1712
|
name = "windows-link"
|
|
1605
1713
|
version = "0.2.1"
|
|
1606
1714
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1607
1715
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
1608
1716
|
|
|
1717
|
+
[[package]]
|
|
1718
|
+
name = "windows-result"
|
|
1719
|
+
version = "0.4.1"
|
|
1720
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1721
|
+
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
|
1722
|
+
dependencies = [
|
|
1723
|
+
"windows-link",
|
|
1724
|
+
]
|
|
1725
|
+
|
|
1726
|
+
[[package]]
|
|
1727
|
+
name = "windows-strings"
|
|
1728
|
+
version = "0.5.1"
|
|
1729
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1730
|
+
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
|
1731
|
+
dependencies = [
|
|
1732
|
+
"windows-link",
|
|
1733
|
+
]
|
|
1734
|
+
|
|
1609
1735
|
[[package]]
|
|
1610
1736
|
name = "windows-sys"
|
|
1611
1737
|
version = "0.60.2"
|
|
@@ -11,13 +11,13 @@ default-members = [
|
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
[workspace.package]
|
|
14
|
-
version = "0.0.
|
|
14
|
+
version = "0.0.6"
|
|
15
15
|
edition = "2024"
|
|
16
16
|
repository = "https://github.com/getsentry/sentry-options"
|
|
17
17
|
license = "Apache-2.0"
|
|
18
18
|
|
|
19
19
|
[workspace.dependencies]
|
|
20
|
-
sentry-options-validation = { path = "sentry-options-validation", version = "0.0.
|
|
20
|
+
sentry-options-validation = { path = "sentry-options-validation", version = "0.0.6" }
|
|
21
21
|
serde = { version = "1.0", features = ["derive"] }
|
|
22
22
|
serde_json = "1.0.145"
|
|
23
23
|
anyhow = "1.0.100"
|
|
@@ -79,7 +79,8 @@ fn options_err(err: RustOptionsError) -> PyErr {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/// Initialize global options
|
|
82
|
+
/// Initialize global options using fallback chain: SENTRY_OPTIONS_DIR env var,
|
|
83
|
+
/// then /etc/sentry-options if it exists, otherwise sentry-options/.
|
|
83
84
|
#[pyfunction]
|
|
84
85
|
fn init() -> PyResult<()> {
|
|
85
86
|
let opts = RustOptions::new().map_err(options_err)?;
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
//! Options client for reading validated configuration values.
|
|
2
2
|
|
|
3
3
|
use std::collections::HashMap;
|
|
4
|
-
use std::path::
|
|
4
|
+
use std::path::Path;
|
|
5
5
|
use std::sync::{Arc, OnceLock, RwLock};
|
|
6
6
|
|
|
7
|
-
use sentry_options_validation::{
|
|
7
|
+
use sentry_options_validation::{
|
|
8
|
+
SchemaRegistry, ValidationError, ValuesWatcher, resolve_options_dir,
|
|
9
|
+
};
|
|
8
10
|
use serde_json::Value;
|
|
9
11
|
use thiserror::Error;
|
|
10
12
|
|
|
11
|
-
const DEFAULT_OPTIONS_DIR: &str = "/etc/sentry-options";
|
|
12
|
-
const OPTIONS_DIR_ENV: &str = "SENTRY_OPTIONS_DIR";
|
|
13
|
-
|
|
14
13
|
static GLOBAL_OPTIONS: OnceLock<Options> = OnceLock::new();
|
|
15
14
|
|
|
16
15
|
#[derive(Debug, Error)]
|
|
@@ -38,14 +37,11 @@ pub struct Options {
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
impl Options {
|
|
41
|
-
/// Load options
|
|
40
|
+
/// Load options using fallback chain: `SENTRY_OPTIONS_DIR` env var, then `/etc/sentry-options`
|
|
41
|
+
/// if it exists, otherwise `sentry-options/`.
|
|
42
42
|
/// Expects `{dir}/schemas/` and `{dir}/values/` subdirectories.
|
|
43
43
|
pub fn new() -> Result<Self> {
|
|
44
|
-
|
|
45
|
-
.map(PathBuf::from)
|
|
46
|
-
.unwrap_or_else(|_| PathBuf::from(DEFAULT_OPTIONS_DIR));
|
|
47
|
-
|
|
48
|
-
Self::from_directory(&base_dir)
|
|
44
|
+
Self::from_directory(&resolve_options_dir())
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
/// Load options from a specific directory (useful for testing).
|
|
@@ -98,7 +94,8 @@ impl Options {
|
|
|
98
94
|
}
|
|
99
95
|
}
|
|
100
96
|
|
|
101
|
-
/// Initialize global options
|
|
97
|
+
/// Initialize global options using fallback chain: `SENTRY_OPTIONS_DIR` env var,
|
|
98
|
+
/// then `/etc/sentry-options` if it exists, otherwise `sentry-options/`.
|
|
102
99
|
pub fn init() -> Result<()> {
|
|
103
100
|
let opts = Options::new()?;
|
|
104
101
|
GLOBAL_OPTIONS
|
|
@@ -26,6 +26,32 @@ const VALUES_FILE_NAME: &str = "values.json";
|
|
|
26
26
|
/// Time between file polls in seconds
|
|
27
27
|
const POLLING_DELAY: u64 = 5;
|
|
28
28
|
|
|
29
|
+
/// Production path where options are deployed via config map
|
|
30
|
+
pub const PRODUCTION_OPTIONS_DIR: &str = "/etc/sentry-options";
|
|
31
|
+
|
|
32
|
+
/// Local fallback path for development
|
|
33
|
+
pub const LOCAL_OPTIONS_DIR: &str = "sentry-options";
|
|
34
|
+
|
|
35
|
+
/// Environment variable to override options directory
|
|
36
|
+
pub const OPTIONS_DIR_ENV: &str = "SENTRY_OPTIONS_DIR";
|
|
37
|
+
|
|
38
|
+
/// Resolve options directory using fallback chain:
|
|
39
|
+
/// 1. `SENTRY_OPTIONS_DIR` env var (if set)
|
|
40
|
+
/// 2. `/etc/sentry-options` (if exists)
|
|
41
|
+
/// 3. `sentry-options/` (local fallback)
|
|
42
|
+
pub fn resolve_options_dir() -> PathBuf {
|
|
43
|
+
if let Ok(dir) = std::env::var(OPTIONS_DIR_ENV) {
|
|
44
|
+
return PathBuf::from(dir);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let prod_path = PathBuf::from(PRODUCTION_OPTIONS_DIR);
|
|
48
|
+
if prod_path.exists() {
|
|
49
|
+
return prod_path;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
PathBuf::from(LOCAL_OPTIONS_DIR)
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
/// Result type for validation operations
|
|
30
56
|
pub type ValidationResult<T> = Result<T, ValidationError>;
|
|
31
57
|
|
|
@@ -52,12 +78,56 @@ pub enum ValidationError {
|
|
|
52
78
|
|
|
53
79
|
#[error("Failed to parse JSON: {0}")]
|
|
54
80
|
JSONParse(#[from] serde_json::Error),
|
|
81
|
+
|
|
82
|
+
#[error("{} validation error(s)", .0.len())]
|
|
83
|
+
ValidationErrors(Vec<ValidationError>),
|
|
84
|
+
|
|
85
|
+
#[error("Invalid {label} '{name}': {reason}")]
|
|
86
|
+
InvalidName {
|
|
87
|
+
label: String,
|
|
88
|
+
name: String,
|
|
89
|
+
reason: String,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Validate a name component is valid for K8s (lowercase alphanumeric, '-', '.')
|
|
94
|
+
pub fn validate_k8s_name_component(name: &str, label: &str) -> ValidationResult<()> {
|
|
95
|
+
if let Some(c) = name
|
|
96
|
+
.chars()
|
|
97
|
+
.find(|&c| !matches!(c, 'a'..='z' | '0'..='9' | '-' | '.'))
|
|
98
|
+
{
|
|
99
|
+
return Err(ValidationError::InvalidName {
|
|
100
|
+
label: label.to_string(),
|
|
101
|
+
name: name.to_string(),
|
|
102
|
+
reason: format!(
|
|
103
|
+
"character '{}' not allowed. Use lowercase alphanumeric, '-', or '.'",
|
|
104
|
+
c
|
|
105
|
+
),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if !name.starts_with(|c: char| c.is_ascii_alphanumeric())
|
|
109
|
+
|| !name.ends_with(|c: char| c.is_ascii_alphanumeric())
|
|
110
|
+
{
|
|
111
|
+
return Err(ValidationError::InvalidName {
|
|
112
|
+
label: label.to_string(),
|
|
113
|
+
name: name.to_string(),
|
|
114
|
+
reason: "must start and end with alphanumeric".to_string(),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
Ok(())
|
|
55
118
|
}
|
|
56
119
|
|
|
57
|
-
///
|
|
120
|
+
/// Metadata for a single option in a namespace schema
|
|
121
|
+
#[derive(Debug, Clone)]
|
|
122
|
+
pub struct OptionMetadata {
|
|
123
|
+
pub option_type: String,
|
|
124
|
+
pub default: Value,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// Schema for a namespace, containing validator and option metadata
|
|
58
128
|
pub struct NamespaceSchema {
|
|
59
129
|
pub namespace: String,
|
|
60
|
-
|
|
130
|
+
pub options: HashMap<String, OptionMetadata>,
|
|
61
131
|
validator: jsonschema::Validator,
|
|
62
132
|
}
|
|
63
133
|
|
|
@@ -85,7 +155,7 @@ impl NamespaceSchema {
|
|
|
85
155
|
/// Get the default value for an option key.
|
|
86
156
|
/// Returns None if the key doesn't exist in the schema.
|
|
87
157
|
pub fn get_default(&self, key: &str) -> Option<&Value> {
|
|
88
|
-
self.
|
|
158
|
+
self.options.get(key).map(|meta| &meta.default)
|
|
89
159
|
}
|
|
90
160
|
}
|
|
91
161
|
|
|
@@ -166,6 +236,8 @@ impl SchemaRegistry {
|
|
|
166
236
|
message: "Directory name contains invalid UTF-8".to_string(),
|
|
167
237
|
})?;
|
|
168
238
|
|
|
239
|
+
validate_k8s_name_component(&namespace, "namespace name")?;
|
|
240
|
+
|
|
169
241
|
let schema_file = entry.path().join(SCHEMA_FILE_NAME);
|
|
170
242
|
let schema = Self::load_schema(&schema_file, &namespace, &namespace_validator)?;
|
|
171
243
|
schemas.insert(namespace, schema);
|
|
@@ -254,8 +326,8 @@ impl SchemaRegistry {
|
|
|
254
326
|
message: format!("Failed to compile validator: {}", e),
|
|
255
327
|
})?;
|
|
256
328
|
|
|
257
|
-
// Extract
|
|
258
|
-
let mut
|
|
329
|
+
// Extract option metadata and validate types
|
|
330
|
+
let mut options = HashMap::new();
|
|
259
331
|
if let Some(properties) = schema.get("properties").and_then(|p| p.as_object()) {
|
|
260
332
|
for (prop_name, prop_value) in properties {
|
|
261
333
|
if let (Some(prop_type), Some(default_value)) = (
|
|
@@ -263,14 +335,20 @@ impl SchemaRegistry {
|
|
|
263
335
|
prop_value.get("default"),
|
|
264
336
|
) {
|
|
265
337
|
Self::validate_default_type(prop_name, prop_type, default_value, path)?;
|
|
266
|
-
|
|
338
|
+
options.insert(
|
|
339
|
+
prop_name.clone(),
|
|
340
|
+
OptionMetadata {
|
|
341
|
+
option_type: prop_type.to_string(),
|
|
342
|
+
default: default_value.clone(),
|
|
343
|
+
},
|
|
344
|
+
);
|
|
267
345
|
}
|
|
268
346
|
}
|
|
269
347
|
}
|
|
270
348
|
|
|
271
349
|
Ok(Arc::new(NamespaceSchema {
|
|
272
350
|
namespace: namespace.to_string(),
|
|
273
|
-
|
|
351
|
+
options,
|
|
274
352
|
validator,
|
|
275
353
|
}))
|
|
276
354
|
}
|
|
@@ -280,6 +358,11 @@ impl SchemaRegistry {
|
|
|
280
358
|
self.schemas.get(namespace)
|
|
281
359
|
}
|
|
282
360
|
|
|
361
|
+
/// Get all loaded schemas (for schema evolution validation)
|
|
362
|
+
pub fn schemas(&self) -> &HashMap<String, Arc<NamespaceSchema>> {
|
|
363
|
+
&self.schemas
|
|
364
|
+
}
|
|
365
|
+
|
|
283
366
|
/// Load and validate JSON values from a directory.
|
|
284
367
|
/// Expects structure: `{values_dir}/{namespace}/values.json`
|
|
285
368
|
/// Skips namespaces without a values.json file.
|
|
@@ -488,6 +571,52 @@ mod tests {
|
|
|
488
571
|
schema_file
|
|
489
572
|
}
|
|
490
573
|
|
|
574
|
+
#[test]
|
|
575
|
+
fn test_validate_k8s_name_component_valid() {
|
|
576
|
+
assert!(validate_k8s_name_component("relay", "namespace").is_ok());
|
|
577
|
+
assert!(validate_k8s_name_component("my-service", "namespace").is_ok());
|
|
578
|
+
assert!(validate_k8s_name_component("my.service", "namespace").is_ok());
|
|
579
|
+
assert!(validate_k8s_name_component("a1-b2.c3", "namespace").is_ok());
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
#[test]
|
|
583
|
+
fn test_validate_k8s_name_component_rejects_uppercase() {
|
|
584
|
+
let result = validate_k8s_name_component("MyService", "namespace");
|
|
585
|
+
assert!(matches!(result, Err(ValidationError::InvalidName { .. })));
|
|
586
|
+
assert!(result.unwrap_err().to_string().contains("'M' not allowed"));
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
#[test]
|
|
590
|
+
fn test_validate_k8s_name_component_rejects_underscore() {
|
|
591
|
+
let result = validate_k8s_name_component("my_service", "target");
|
|
592
|
+
assert!(matches!(result, Err(ValidationError::InvalidName { .. })));
|
|
593
|
+
assert!(result.unwrap_err().to_string().contains("'_' not allowed"));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
#[test]
|
|
597
|
+
fn test_validate_k8s_name_component_rejects_leading_hyphen() {
|
|
598
|
+
let result = validate_k8s_name_component("-service", "namespace");
|
|
599
|
+
assert!(matches!(result, Err(ValidationError::InvalidName { .. })));
|
|
600
|
+
assert!(
|
|
601
|
+
result
|
|
602
|
+
.unwrap_err()
|
|
603
|
+
.to_string()
|
|
604
|
+
.contains("start and end with alphanumeric")
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
#[test]
|
|
609
|
+
fn test_validate_k8s_name_component_rejects_trailing_dot() {
|
|
610
|
+
let result = validate_k8s_name_component("service.", "namespace");
|
|
611
|
+
assert!(matches!(result, Err(ValidationError::InvalidName { .. })));
|
|
612
|
+
assert!(
|
|
613
|
+
result
|
|
614
|
+
.unwrap_err()
|
|
615
|
+
.to_string()
|
|
616
|
+
.contains("start and end with alphanumeric")
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
491
620
|
#[test]
|
|
492
621
|
fn test_load_schema_valid() {
|
|
493
622
|
let temp_dir = TempDir::new().unwrap();
|
|
@@ -677,7 +806,7 @@ Error: \"version\" is a required property"
|
|
|
677
806
|
fn test_invalid_directory_structure() {
|
|
678
807
|
let temp_dir = TempDir::new().unwrap();
|
|
679
808
|
// Create a namespace directory without schema.json file
|
|
680
|
-
let schema_dir = temp_dir.path().join("
|
|
809
|
+
let schema_dir = temp_dir.path().join("missing-schema");
|
|
681
810
|
fs::create_dir_all(&schema_dir).unwrap();
|
|
682
811
|
|
|
683
812
|
let result = SchemaRegistry::from_directory(temp_dir.path());
|
|
@@ -893,7 +1022,7 @@ Error: \"version\" is a required property"
|
|
|
893
1022
|
let values_dir = temp_dir.path().join("values");
|
|
894
1023
|
|
|
895
1024
|
// Create two schemas
|
|
896
|
-
let schema_dir1 = schemas_dir.join("
|
|
1025
|
+
let schema_dir1 = schemas_dir.join("with-values");
|
|
897
1026
|
fs::create_dir_all(&schema_dir1).unwrap();
|
|
898
1027
|
fs::write(
|
|
899
1028
|
schema_dir1.join("schema.json"),
|
|
@@ -907,7 +1036,7 @@ Error: \"version\" is a required property"
|
|
|
907
1036
|
)
|
|
908
1037
|
.unwrap();
|
|
909
1038
|
|
|
910
|
-
let schema_dir2 = schemas_dir.join("
|
|
1039
|
+
let schema_dir2 = schemas_dir.join("without-values");
|
|
911
1040
|
fs::create_dir_all(&schema_dir2).unwrap();
|
|
912
1041
|
fs::write(
|
|
913
1042
|
schema_dir2.join("schema.json"),
|
|
@@ -922,7 +1051,7 @@ Error: \"version\" is a required property"
|
|
|
922
1051
|
.unwrap();
|
|
923
1052
|
|
|
924
1053
|
// Only create values for one namespace
|
|
925
|
-
let with_values_dir = values_dir.join("
|
|
1054
|
+
let with_values_dir = values_dir.join("with-values");
|
|
926
1055
|
fs::create_dir_all(&with_values_dir).unwrap();
|
|
927
1056
|
fs::write(with_values_dir.join("values.json"), r#"{"opt": "y"}"#).unwrap();
|
|
928
1057
|
|
|
@@ -930,8 +1059,8 @@ Error: \"version\" is a required property"
|
|
|
930
1059
|
let values = registry.load_values_json(&values_dir).unwrap();
|
|
931
1060
|
|
|
932
1061
|
assert_eq!(values.len(), 1);
|
|
933
|
-
assert!(values.contains_key("
|
|
934
|
-
assert!(!values.contains_key("
|
|
1062
|
+
assert!(values.contains_key("with-values"));
|
|
1063
|
+
assert!(!values.contains_key("without-values"));
|
|
935
1064
|
}
|
|
936
1065
|
|
|
937
1066
|
#[test]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sentry_options-0.0.4 → sentry_options-0.0.6}/sentry-options-validation/src/namespace-schema.json
RENAMED
|
File without changes
|