1pub(crate) mod manifest;
3#[allow(clippy::module_inception)]
4pub(crate) mod package;
5pub(crate) mod strictness;
6pub(crate) mod volume;
7
8pub use self::{
9 manifest::ManifestError,
10 package::{Package, WasmerPackageError},
11 strictness::Strictness,
12 volume::{fs::*, in_memory::*, WasmerPackageVolume},
13};
14
15#[cfg(test)]
16mod tests {
17 use sha2::Digest;
18 use shared_buffer::OwnedBuffer;
19 use tempfile::TempDir;
20
21 use webc::{
22 metadata::annotations::FileSystemMapping,
23 migration::{are_semantically_equivalent, v2_to_v3, v3_to_v2},
24 };
25
26 use crate::{package::Package, utils::from_bytes};
27
28 #[test]
29 fn migration_roundtrip() {
30 let temp = TempDir::new().unwrap();
31 let wasmer_toml = r#"
32 [package]
33 name = "some/package"
34 version = "0.0.0"
35 description = "Test package"
36 [fs]
37 "/first" = "first"
38 second = "nested/dir"
39 "second/child" = "third"
40 empty = "empty"
41 "#;
42 let manifest = temp.path().join("wasmer.toml");
43 std::fs::write(&manifest, wasmer_toml).unwrap();
44 let first = temp.path().join("first");
59 std::fs::create_dir_all(&first).unwrap();
60 std::fs::write(first.join("file.txt"), "File").unwrap();
61 let second = temp.path().join("nested").join("dir");
63 std::fs::create_dir_all(&second).unwrap();
64 std::fs::write(second.join("README.md"), "please").unwrap();
65 let another_dir = temp.path().join("nested").join("dir").join("another-dir");
66 std::fs::create_dir_all(&another_dir).unwrap();
67 std::fs::write(another_dir.join("empty.txt"), "").unwrap();
68 let third = temp.path().join("third");
70 std::fs::create_dir_all(&third).unwrap();
71 std::fs::write(third.join("file.txt"), "Hello, World!").unwrap();
72 let empty_dir = temp.path().join("empty");
74 std::fs::create_dir_all(empty_dir).unwrap();
75
76 let package = Package::from_manifest(manifest).unwrap();
77
78 let webc = package.serialize().unwrap();
79
80 let webc_v2 = v3_to_v2(webc.clone()).unwrap();
81
82 are_semantically_equivalent(webc_v2.clone(), webc.into()).unwrap();
83
84 let container = from_bytes(webc_v2.clone().into_bytes()).unwrap();
85 let manifest = container.manifest();
86 let fs_table = manifest.filesystem().unwrap().unwrap();
87 assert_eq!(
88 fs_table,
89 [
90 FileSystemMapping {
91 from: None,
92 volume_name: "atom".to_string(),
93 host_path: Some("/first".to_string()),
94 mount_path: "/first".to_string(),
95 },
96 FileSystemMapping {
97 from: None,
98 volume_name: "atom".to_string(),
99 host_path: Some("/nested/dir".to_string()),
100 mount_path: "/second".to_string(),
101 },
102 FileSystemMapping {
103 from: None,
104 volume_name: "atom".to_string(),
105 host_path: Some("/third".to_string()),
106 mount_path: "/second/child".to_string(),
107 },
108 FileSystemMapping {
109 from: None,
110 volume_name: "atom".to_string(),
111 host_path: Some("/empty".to_string()),
112 mount_path: "/empty".to_string(),
113 },
114 ]
115 );
116
117 let atom_volume = container.get_volume("atom").unwrap();
118 assert_eq!(
119 atom_volume.read_file("/first/file.txt").unwrap(),
120 (OwnedBuffer::from(b"File".as_slice()), None)
121 );
122 assert_eq!(
123 atom_volume.read_file("/nested/dir/README.md").unwrap(),
124 (OwnedBuffer::from(b"please".as_slice()), None),
125 );
126 assert_eq!(
127 atom_volume
128 .read_file("/nested/dir/another-dir/empty.txt")
129 .unwrap(),
130 (OwnedBuffer::from(b"".as_slice()), None)
131 );
132 assert_eq!(
133 atom_volume.read_file("/third/file.txt").unwrap(),
134 (OwnedBuffer::from(b"Hello, World!".as_slice()), None)
135 );
136 assert_eq!(
137 atom_volume.read_dir("/empty").unwrap().len(),
138 0,
139 "Directories should be included, even if empty"
140 );
141
142 let webc_v3 = v2_to_v3(webc_v2.clone()).unwrap();
144
145 are_semantically_equivalent(webc_v2, webc_v3.clone()).unwrap();
146
147 let container = from_bytes(webc_v3.into_bytes()).unwrap();
148 let manifest = container.manifest();
149 let fs_table = manifest.filesystem().unwrap().unwrap();
150 assert_eq!(
151 fs_table,
152 [
153 FileSystemMapping {
154 from: None,
155 volume_name: "/first".to_string(),
156 host_path: None,
157 mount_path: "/first".to_string(),
158 },
159 FileSystemMapping {
160 from: None,
161 volume_name: "/nested/dir".to_string(),
162 host_path: None,
163 mount_path: "/second".to_string(),
164 },
165 FileSystemMapping {
166 from: None,
167 volume_name: "/third".to_string(),
168 host_path: None,
169 mount_path: "/second/child".to_string(),
170 },
171 FileSystemMapping {
172 from: None,
173 volume_name: "/empty".to_string(),
174 host_path: None,
175 mount_path: "/empty".to_string(),
176 },
177 ]
178 );
179
180 let first_file_hash: [u8; 32] = sha2::Sha256::digest(b"File").into();
181 let readme_hash: [u8; 32] = sha2::Sha256::digest(b"please").into();
182 let empty_hash: [u8; 32] = sha2::Sha256::digest(b"").into();
183 let third_file_hash: [u8; 32] = sha2::Sha256::digest(b"Hello, World!").into();
184
185 let first_volume = container.get_volume("/first").unwrap();
186 assert_eq!(
187 first_volume.read_file("/file.txt").unwrap(),
188 (b"File".as_slice().into(), Some(first_file_hash)),
189 );
190
191 let nested_dir_volume = container.get_volume("/nested/dir").unwrap();
192 assert_eq!(
193 nested_dir_volume.read_file("README.md").unwrap(),
194 (b"please".as_slice().into(), Some(readme_hash)),
195 );
196 assert_eq!(
197 nested_dir_volume
198 .read_file("/another-dir/empty.txt")
199 .unwrap(),
200 (b"".as_slice().into(), Some(empty_hash))
201 );
202
203 let third_volume = container.get_volume("/third").unwrap();
204 assert_eq!(
205 third_volume.read_file("/file.txt").unwrap(),
206 (b"Hello, World!".as_slice().into(), Some(third_file_hash))
207 );
208
209 let empty_volume = container.get_volume("/empty").unwrap();
210 assert_eq!(
211 empty_volume.read_dir("/").unwrap().len(),
212 0,
213 "Directories should be included, even if empty"
214 );
215 }
216
217 #[test]
218 fn fs_entry_is_not_required_for_migration() {
219 let temp = TempDir::new().unwrap();
220 let wasmer_toml = r#"
221 [package]
222 name = "some/package"
223 version = "0.0.0"
224 description = "Test package"
225 "#;
226 let manifest = temp.path().join("wasmer.toml");
227 std::fs::write(&manifest, wasmer_toml).unwrap();
228 let package = Package::from_manifest(manifest).unwrap();
229
230 let webc = package.serialize().unwrap();
231
232 let webc_v2 = v3_to_v2(webc).unwrap();
233 let container = from_bytes(webc_v2.clone().into_bytes()).unwrap();
234 let manifest = container.manifest();
235 assert!(manifest.filesystem().unwrap().is_none());
236
237 let webc_v3 = v2_to_v3(webc_v2).unwrap();
239 let container = from_bytes(webc_v3.into_bytes()).unwrap();
240 let manifest = container.manifest();
241 assert!(manifest.filesystem().unwrap().is_none());
242 }
243
244 #[test]
245 fn container_unpacks_atoms() {
246 let temp = TempDir::new().unwrap();
247 let wasmer_toml = r#"
248 [package]
249 name = "some/package"
250 version = "0.0.0"
251 description = "Test package"
252 [[module]]
253 name = "foo"
254 source = "foo.wasm"
255 abi = "wasi"
256 [fs]
257 "/bar" = "bar"
258 "#;
259
260 let manifest = temp.path().join("wasmer.toml");
261 std::fs::write(&manifest, wasmer_toml).unwrap();
262
263 let atom_path = temp.path().join("foo.wasm");
264 std::fs::write(&atom_path, b"").unwrap();
265
266 let bar = temp.path().join("bar");
267 std::fs::create_dir(&bar).unwrap();
268
269 let webc = Package::from_manifest(&manifest)
270 .unwrap()
271 .serialize()
272 .unwrap();
273 let container = from_bytes(webc).unwrap();
274
275 let out_dir = temp.path().join("out");
276 container.unpack(&out_dir, false).unwrap();
277
278 let expected_entries = [
279 "bar", "metadata", "foo", "manifest.json",
283 ];
284 let entries = std::fs::read_dir(&out_dir)
285 .unwrap()
286 .map(|e| e.unwrap())
287 .collect::<Vec<_>>();
288
289 assert_eq!(expected_entries.len(), entries.len());
290 assert!(expected_entries.iter().all(|e| {
291 entries
292 .iter()
293 .any(|entry| entry.file_name().as_os_str() == *e)
294 }))
295 }
296}