@synopackageland/templates 0.35.5 → 0.37.0

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.
Files changed (31) hide show
  1. package/README.md +34 -17
  2. package/package.json +1 -1
  3. package/templates/compose/dsm-package.yaml +1 -1
  4. package/templates/native-go/README.md +2 -2
  5. package/templates/native-go/backend/THIRD_PARTY_NOTICES.txt +1 -1
  6. package/templates/native-go/backend/go.mod +1 -1
  7. package/templates/native-go/backend/go.sum +2 -2
  8. package/templates/native-go/backend/internal/app/background.go +10 -12
  9. package/templates/native-go/backend/internal/app/background_inbox_test.go +37 -0
  10. package/templates/native-go/backend/internal/app/background_owner.go +14 -0
  11. package/templates/native-go/backend/internal/app/background_owner_test.go +2 -2
  12. package/templates/native-go/backend/internal/app/background_test.go +27 -8
  13. package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +1 -1
  14. package/templates/native-go-delegate/frontend/test/capability-panels.test.ts +1 -0
  15. package/templates/native-go-vue/frontend/package-lock.json +658 -9
  16. package/templates/native-go-vue/frontend/package.json +2 -2
  17. package/templates/native-node/README.md +2 -2
  18. package/templates/native-node/backend/package-lock.json +2 -2
  19. package/templates/native-node/dsm-package.yaml +1 -1
  20. package/templates/native-node-delegate/backend/src/delegate.ts +20 -8
  21. package/templates/native-node-delegate/backend/test/delegate.test.ts +18 -9
  22. package/templates/native-python/README.md +2 -2
  23. package/templates/native-python/dsm-package.yaml +1 -1
  24. package/templates/native-python-delegate/backend/app/delegate.py +19 -7
  25. package/templates/native-python-delegate/backend/tests/test_delegate.py +18 -8
  26. package/templates/native-rust/README.md +2 -2
  27. package/templates/native-rust/backend/Cargo.lock +2 -2
  28. package/templates/native-rust/backend/Cargo.toml +1 -1
  29. package/templates/native-rust-delegate/backend/Cargo.lock +203 -0
  30. package/templates/native-rust-delegate/backend/Cargo.toml +17 -0
  31. package/templates/native-rust-delegate/backend/src/delegate.rs +78 -23
@@ -6,6 +6,7 @@
6
6
  use crate::routes::{Config, internal, utc_now};
7
7
  use serde::Deserialize;
8
8
  use serde_json::{Value, json};
9
+ use sha2::{Digest, Sha256};
9
10
  use std::fs::OpenOptions;
10
11
  use std::io::{ErrorKind, Write};
11
12
  use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt};
@@ -31,12 +32,25 @@ fn valid_job_id(id: &str) -> bool {
31
32
 
32
33
  fn save(cfg: &Config, ctx: &Context, params: &Params) -> Result<Value, Error> {
33
34
  let p: SaveReport = params.decode()?;
34
- if !valid_job_id(&p.job_id) {
35
+ if !valid_job_id(&p.job_id) || !valid_job_id(&p.delegation_id) {
35
36
  return Err(Error::bad_request(
36
37
  "job_id must be 1 to 128 letters, digits, '.', '_' or '-'",
37
38
  ));
38
39
  }
39
- let staging = cfg.data_dir.join("executor-staging");
40
+ let owner = format!(
41
+ "{:x}",
42
+ Sha256::digest(format!("{}\0{}", ctx.user, p.delegation_id).as_bytes())
43
+ );
44
+ let job_id = format!(
45
+ "{:x}-{}",
46
+ Sha256::digest(format!("{}\0{}\0{}", ctx.user, p.delegation_id, p.job_id).as_bytes()),
47
+ &owner[..32]
48
+ );
49
+ let staging = cfg
50
+ .data_dir
51
+ .join("executor-staging")
52
+ .join("owners")
53
+ .join(&owner);
40
54
  std::fs::DirBuilder::new()
41
55
  .recursive(true)
42
56
  .mode(0o700)
@@ -44,7 +58,8 @@ fn save(cfg: &Config, ctx: &Context, params: &Params) -> Result<Value, Error> {
44
58
  .map_err(internal)?;
45
59
  let name = format!("report-{}.txt", p.job_id);
46
60
  let path = staging.join(&name);
47
- // A retried save sends the file it wrote the first time.
61
+ // An admitted or uncertain save retries the original source.
62
+ let mut created = false;
48
63
  match OpenOptions::new()
49
64
  .write(true)
50
65
  .create_new(true)
@@ -52,6 +67,7 @@ fn save(cfg: &Config, ctx: &Context, params: &Params) -> Result<Value, Error> {
52
67
  .open(&path)
53
68
  {
54
69
  Ok(mut file) => {
70
+ created = true;
55
71
  let report = format!("Report for {}, {}\n", ctx.user, utc_now());
56
72
  file.write_all(report.as_bytes()).map_err(internal)?;
57
73
  }
@@ -59,10 +75,20 @@ fn save(cfg: &Config, ctx: &Context, params: &Params) -> Result<Value, Error> {
59
75
  Err(e) => return Err(internal(e)),
60
76
  }
61
77
  let size = std::fs::metadata(&path).map_err(internal)?.len();
62
- ctx.runtime(
78
+ let result: Result<Value, Error> = ctx.runtime(
63
79
  "delegate.move_from_staging",
64
- &json!({"delegation_id": p.delegation_id, "job_id": p.job_id, "staging": name, "name": name, "size": size}),
65
- )
80
+ &json!({"delegation_id": p.delegation_id, "job_id": job_id, "staging": format!("owners/{owner}/{name}"), "name": name, "size": size}),
81
+ );
82
+ // A transport failure may already have been admitted; keep its source.
83
+ if created
84
+ && result
85
+ .as_ref()
86
+ .err()
87
+ .is_some_and(|e| e.code == "DELEGATION_DENIED")
88
+ {
89
+ std::fs::remove_file(&path).map_err(internal)?;
90
+ }
91
+ result
66
92
  }
67
93
 
68
94
  #[cfg(test)]
@@ -106,11 +132,26 @@ mod tests {
106
132
  let (_dir, cfg) = setup();
107
133
  let reply = save(&cfg, "alice", &moved());
108
134
  assert_eq!(reply.data, json!({"state": "queued"}));
109
- let path = cfg.data_dir.join("executor-staging/report-job-1.txt");
135
+ let path = cfg
136
+ .data_dir
137
+ .join("executor-staging")
138
+ .join(reply.services[0].1["staging"].as_str().unwrap());
110
139
  assert!(std::fs::read_to_string(&path).unwrap().contains("alice"));
111
140
  let meta = std::fs::metadata(&path).unwrap();
112
141
  assert_eq!(meta.permissions().mode() & 0o777, 0o600);
113
- let params = json!({"delegation_id": "d1", "job_id": "job-1", "staging": "report-job-1.txt", "name": "report-job-1.txt", "size": meta.len()});
142
+ let owner = reply.services[0].1["staging"]
143
+ .as_str()
144
+ .unwrap()
145
+ .split('/')
146
+ .nth(1)
147
+ .unwrap();
148
+ assert!(
149
+ reply.services[0].1["job_id"]
150
+ .as_str()
151
+ .unwrap()
152
+ .ends_with(&format!("-{}", &owner[..32]))
153
+ );
154
+ let params = json!({"delegation_id": "d1", "job_id": reply.services[0].1["job_id"], "staging": reply.services[0].1["staging"], "name": "report-job-1.txt", "size": meta.len()});
114
155
  assert_eq!(
115
156
  reply.services,
116
157
  vec![("delegate.move_from_staging".to_string(), params)]
@@ -118,29 +159,43 @@ mod tests {
118
159
  }
119
160
 
120
161
  #[test]
121
- fn a_repeated_save_sends_the_same_file_again() {
162
+ fn retries_reuse_only_the_same_owner_and_grant_source() {
122
163
  let (_dir, cfg) = setup();
123
164
  let first = save(&cfg, "alice", &moved()).services;
124
- let path = cfg.data_dir.join("executor-staging/report-job-1.txt");
165
+ let path = cfg
166
+ .data_dir
167
+ .join("executor-staging")
168
+ .join(first[0].1["staging"].as_str().unwrap());
125
169
  let report = std::fs::read_to_string(&path).unwrap();
126
- assert_eq!(save(&cfg, "bob", &moved()).services, first);
170
+ assert_eq!(save(&cfg, "alice", &moved()).services, first);
171
+ let other = save(&cfg, "bob", &moved()).services;
172
+ assert_ne!(other[0].1["staging"], first[0].1["staging"]);
173
+ assert_ne!(other[0].1["job_id"], first[0].1["job_id"]);
174
+ let other_path = cfg
175
+ .data_dir
176
+ .join("executor-staging")
177
+ .join(other[0].1["staging"].as_str().unwrap());
178
+ assert!(std::fs::read_to_string(other_path).unwrap().contains("bob"));
127
179
  assert_eq!(std::fs::read_to_string(&path).unwrap(), report);
128
180
  }
129
181
 
130
182
  #[test]
131
- fn save_passes_on_the_executors_refusal() {
183
+ fn rejection_removes_new_source_but_timeout_keeps_retry() {
132
184
  let (_dir, cfg) = setup();
133
- let services: Services = [(
134
- "delegate.move_from_staging",
135
- Box::new(|_: &Value| {
136
- Err(Error::new(
137
- "DELEGATION_DENIED",
138
- "The destination is no longer allowed.",
139
- ))
140
- }) as _,
141
- )]
142
- .into();
143
- assert_eq!(save(&cfg, "alice", &services).code(), "DELEGATION_DENIED");
185
+ for (code, retained) in [("DELEGATION_DENIED", false), ("EXECUTOR_TIMEOUT", true)] {
186
+ let services: Services = [(
187
+ "delegate.move_from_staging",
188
+ Box::new(move |_: &Value| Err(Error::new(code, "Save failed"))) as _,
189
+ )]
190
+ .into();
191
+ let reply = save(&cfg, "alice", &services);
192
+ assert_eq!(reply.code(), code);
193
+ let path = cfg
194
+ .data_dir
195
+ .join("executor-staging")
196
+ .join(reply.services[0].1["staging"].as_str().unwrap());
197
+ assert_eq!(path.exists(), retained);
198
+ }
144
199
  }
145
200
 
146
201
  #[test]