fix: release Dever 0.1.6 with shared runtime assets and unbounded workers
This commit is contained in:
1 parent
a900d7e8db
commit
5589b657db
17 files changed
+357
-49
No files matched your search
@@ -858,7 +858,7 @@ impl InstalledRegistry {
|
||||
|
||||
fn signed_artifact(&self, relative: &Path) -> Result<Vec<u8>, String> {
|
||||
let name = relative.to_str().ok_or("runtime path is not UTF-8")?;
|
||||
self.resources.read(name)
|
||||
self.resources.read_shared(name)
|
||||
}
|
||||
|
||||
fn process_runtime_resources(
|
||||
@@ -868,7 +868,7 @@ impl InstalledRegistry {
|
||||
let prefix = format!("process-runtime/{}/", target.platform());
|
||||
let mut resources = Vec::new();
|
||||
let mut files = Vec::new();
|
||||
for artifact in self.resources.artifacts(&prefix)? {
|
||||
for artifact in self.resources.catalog_artifacts(&prefix) {
|
||||
let Some(relative) = artifact.path.strip_prefix(&prefix) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -270,6 +270,92 @@ impl SignedResources {
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub fn read_shared(&self, logical_path: &str) -> Result<Vec<u8>, String> {
|
||||
if self
|
||||
.manifest
|
||||
.artifacts
|
||||
.iter()
|
||||
.any(|artifact| artifact.path == logical_path)
|
||||
{
|
||||
return self.read(logical_path);
|
||||
}
|
||||
let artifact = self
|
||||
.manifest
|
||||
.artifacts
|
||||
.iter()
|
||||
.chain(
|
||||
self.manifest
|
||||
.extensions
|
||||
.iter()
|
||||
.flat_map(|extension| &extension.artifacts),
|
||||
)
|
||||
.find(|artifact| artifact.path == logical_path)
|
||||
.ok_or_else(|| format!("signed release does not declare resource '{logical_path}'"))?;
|
||||
super::service::resource_get(
|
||||
&self.layout,
|
||||
&self.manifest.version,
|
||||
&self.catalog,
|
||||
self.manifest
|
||||
.extensions
|
||||
.iter()
|
||||
.find(|extension| {
|
||||
extension
|
||||
.artifacts
|
||||
.iter()
|
||||
.any(|artifact| artifact.path == logical_path)
|
||||
})
|
||||
.map(Extension::id),
|
||||
&super::ArtifactReceipt {
|
||||
sha256: artifact.sha256.clone(),
|
||||
bytes: artifact.bytes,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn open_requested(
|
||||
&self,
|
||||
catalog: &str,
|
||||
extension: Option<&ExtensionId>,
|
||||
expected: &super::ArtifactReceipt,
|
||||
) -> Result<fs::File, String> {
|
||||
if catalog != self.catalog {
|
||||
return Err("signed resource catalog differs from the installed release".into());
|
||||
}
|
||||
let artifacts = match extension {
|
||||
Some(id) => &self.extension(id)?.artifacts,
|
||||
None => &self.manifest.artifacts,
|
||||
};
|
||||
let artifact = artifacts
|
||||
.iter()
|
||||
.find(|artifact| artifact.sha256 == expected.sha256 && artifact.bytes == expected.bytes)
|
||||
.ok_or("signed release does not declare the requested resource identity")?;
|
||||
let path = self.path(&artifact.path)?;
|
||||
let file = fs::File::open(path)
|
||||
.map_err(|error| format!("cannot open signed resource: {error}"))?;
|
||||
let metadata = file
|
||||
.metadata()
|
||||
.map_err(|error| format!("cannot inspect signed resource: {error}"))?;
|
||||
if !metadata.is_file() || metadata.len() != artifact.bytes {
|
||||
return Err("signed resource changed while opening".into());
|
||||
}
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
pub fn catalog_artifacts(&self, prefix: &str) -> Vec<Artifact> {
|
||||
self.manifest
|
||||
.artifacts
|
||||
.iter()
|
||||
.chain(
|
||||
self.manifest
|
||||
.extensions
|
||||
.iter()
|
||||
.flat_map(|extension| &extension.artifacts),
|
||||
)
|
||||
.filter(|artifact| artifact.path.starts_with(prefix))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn artifacts(&self, prefix: &str) -> Result<Vec<Artifact>, String> {
|
||||
let mut artifacts = Vec::new();
|
||||
for artifact in self
|
||||
|
||||
@@ -105,6 +105,12 @@ enum Operation {
|
||||
version: Version,
|
||||
extension: super::ExtensionId,
|
||||
},
|
||||
ResourceGet {
|
||||
version: Version,
|
||||
catalog: String,
|
||||
extension: Option<super::ExtensionId>,
|
||||
artifact: ArtifactReceipt,
|
||||
},
|
||||
Compile {
|
||||
bytes: u64,
|
||||
},
|
||||
@@ -361,14 +367,7 @@ pub fn artifact_get_optional(
|
||||
return Ok(None);
|
||||
}
|
||||
require_artifact(response, &expected)?;
|
||||
let mut bytes = vec![0; expected.bytes as usize];
|
||||
stream
|
||||
.read_exact(&mut bytes)
|
||||
.map_err(|error| format!("deverd artifact download is incomplete: {error}"))?;
|
||||
if hex(&Sha256::digest(&bytes)) != expected.sha256 {
|
||||
return Err("deverd artifact download failed SHA-256 verification".into());
|
||||
}
|
||||
Ok(Some(bytes))
|
||||
read_artifact(&mut stream, &expected).map(Some)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
@@ -401,6 +400,59 @@ pub fn ensure_extension(
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn resource_get(
|
||||
layout: &Layout,
|
||||
version: &Version,
|
||||
catalog: &str,
|
||||
extension: Option<super::ExtensionId>,
|
||||
expected: &ArtifactReceipt,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let (mut stream, token) = connect(layout)?;
|
||||
stream
|
||||
.set_read_timeout(Some(std::time::Duration::from_secs(30)))
|
||||
.map_err(|error| format!("cannot configure signed resource IPC timeout: {error}"))?;
|
||||
write_frame(
|
||||
&mut stream,
|
||||
&Request {
|
||||
token,
|
||||
operation: Operation::ResourceGet {
|
||||
version: version.clone(),
|
||||
catalog: catalog.into(),
|
||||
extension,
|
||||
artifact: expected.clone(),
|
||||
},
|
||||
installed: Vec::new(),
|
||||
},
|
||||
)?;
|
||||
require_artifact(read_frame(&mut stream)?, expected)?;
|
||||
read_artifact(&mut stream, expected)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
let _ = (layout, version, catalog, extension, expected);
|
||||
Err("deverd signed resource IPC is not implemented for this platform".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn read_artifact(
|
||||
stream: &mut std::os::unix::net::UnixStream,
|
||||
expected: &ArtifactReceipt,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let length = usize::try_from(expected.bytes)
|
||||
.map_err(|_| "deverd artifact is too large for this platform")?;
|
||||
let mut bytes = vec![0; length];
|
||||
stream
|
||||
.read_exact(&mut bytes)
|
||||
.map_err(|error| format!("deverd artifact download is incomplete: {error}"))?;
|
||||
if hex(&Sha256::digest(&bytes)) != expected.sha256 {
|
||||
return Err("deverd artifact download failed SHA-256 verification".into());
|
||||
}
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn require_artifact(response: Response, expected: &ArtifactReceipt) -> Result<(), String> {
|
||||
if !response.ok {
|
||||
return Err(response
|
||||
@@ -597,6 +649,36 @@ fn handle(
|
||||
Err(error) => failure(error),
|
||||
};
|
||||
}
|
||||
Operation::ResourceGet {
|
||||
version,
|
||||
catalog,
|
||||
extension,
|
||||
artifact,
|
||||
} => {
|
||||
let file = match super::SignedResources::load(layout, &version).and_then(|resources| {
|
||||
resources.open_requested(&catalog, extension.as_ref(), &artifact)
|
||||
}) {
|
||||
Ok(file) => file,
|
||||
Err(error) => return failure(error),
|
||||
};
|
||||
let length = artifact.bytes;
|
||||
if let Err(error) = write_frame(stream, &artifact_response(artifact)) {
|
||||
return failure(error);
|
||||
}
|
||||
match std::io::copy(&mut file.take(length), stream) {
|
||||
Ok(copied) if copied == length => {}
|
||||
Ok(_) => return failure("deverd signed resource transfer is truncated".into()),
|
||||
Err(error) => {
|
||||
return failure(format!("deverd signed resource transfer failed: {error}"));
|
||||
}
|
||||
}
|
||||
return Response {
|
||||
ok: true,
|
||||
status: None,
|
||||
artifact: None,
|
||||
error: None,
|
||||
};
|
||||
}
|
||||
Operation::Compile { bytes } => {
|
||||
return match compile::serve(layout, stream, bytes) {
|
||||
Ok(()) => Response {
|
||||
|
||||
Reference in new issue
Block a user