mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-22 16:11:38 +03:00
Merge pull request #246 from kinode-dao/dr/app-store-auto-install
app_store: make `auto-update` real, add support for selecting mirror from FE
This commit is contained in:
commit
7a4e774dff
@ -188,8 +188,38 @@ fn serve_paths(
|
||||
}
|
||||
Method::PUT => {
|
||||
// update an app
|
||||
// TODO
|
||||
Ok((StatusCode::NO_CONTENT, None, format!("TODO").into_bytes()))
|
||||
let pkg_listing: &PackageListing = state
|
||||
.get_listing(&package_id)
|
||||
.ok_or(anyhow::anyhow!("No package"))?;
|
||||
let pkg_state: &PackageState = state
|
||||
.downloaded_packages
|
||||
.get(&package_id)
|
||||
.ok_or(anyhow::anyhow!("No package"))?;
|
||||
let download_from = pkg_state
|
||||
.mirrored_from
|
||||
.as_ref()
|
||||
.ok_or(anyhow::anyhow!("No mirror for package {package_id}"))?
|
||||
.to_string();
|
||||
match crate::start_download(
|
||||
our,
|
||||
requested_packages,
|
||||
&package_id,
|
||||
&download_from,
|
||||
pkg_state.mirroring,
|
||||
pkg_state.auto_update,
|
||||
&None,
|
||||
) {
|
||||
DownloadResponse::Started => Ok((
|
||||
StatusCode::CREATED,
|
||||
None,
|
||||
format!("Downloading").into_bytes(),
|
||||
)),
|
||||
DownloadResponse::Failure => Ok((
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
None,
|
||||
format!("Failed to download").into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
Method::DELETE => {
|
||||
// uninstall an app
|
||||
@ -235,10 +265,15 @@ fn serve_paths(
|
||||
}
|
||||
Method::POST => {
|
||||
// download an app
|
||||
// TODO get fields from POST body
|
||||
let pkg_listing: &PackageListing = state
|
||||
.get_listing(&package_id)
|
||||
.ok_or(anyhow::anyhow!("No package"))?;
|
||||
// from POST body, look for download_from field and use that as the mirror
|
||||
let body = crate::get_blob()
|
||||
.ok_or(anyhow::anyhow!("missing blob"))?
|
||||
.bytes;
|
||||
let body_json: serde_json::Value =
|
||||
serde_json::from_slice(&body).unwrap_or_default();
|
||||
let mirrors: &Vec<NodeId> = pkg_listing
|
||||
.metadata
|
||||
.as_ref()
|
||||
@ -246,11 +281,15 @@ fn serve_paths(
|
||||
.mirrors
|
||||
.as_ref()
|
||||
.ok_or(anyhow::anyhow!("No mirrors for package {package_id}"))?;
|
||||
// TODO select on FE
|
||||
let download_from = mirrors
|
||||
.first()
|
||||
.ok_or(anyhow::anyhow!("No mirrors for package {package_id}"))?;
|
||||
// TODO select on FE
|
||||
let download_from = body_json
|
||||
.get("download_from")
|
||||
.unwrap_or(&json!(mirrors
|
||||
.first()
|
||||
.ok_or(anyhow::anyhow!("No mirrors for package {package_id}"))?))
|
||||
.as_str()
|
||||
.ok_or(anyhow::anyhow!("download_from not a string"))?
|
||||
.to_string();
|
||||
// TODO select on FE? or after download but before install?
|
||||
let mirror = false;
|
||||
let auto_update = false;
|
||||
let desired_version_hash = None;
|
||||
@ -258,7 +297,7 @@ fn serve_paths(
|
||||
our,
|
||||
requested_packages,
|
||||
&package_id,
|
||||
download_from,
|
||||
&download_from,
|
||||
mirror,
|
||||
auto_update,
|
||||
&desired_version_hash,
|
||||
|
@ -178,7 +178,7 @@ fn handle_message(
|
||||
if source.node() != our.node() || source.process != "eth:distro:sys" {
|
||||
return Err(anyhow::anyhow!("eth sub event from weird addr: {source}"));
|
||||
}
|
||||
handle_eth_sub_event(&mut state, e)?;
|
||||
handle_eth_sub_event(our, &mut state, e)?;
|
||||
}
|
||||
Req::Http(incoming) => {
|
||||
if source.node() != our.node()
|
||||
@ -187,7 +187,7 @@ fn handle_message(
|
||||
return Err(anyhow::anyhow!("http_server from non-local node"));
|
||||
}
|
||||
if let HttpServerRequest::Http(req) = incoming {
|
||||
http_api::handle_http_request(&our, &mut state, requested_packages, &req)?;
|
||||
http_api::handle_http_request(our, &mut state, requested_packages, &req)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -269,6 +269,7 @@ fn handle_local_request(
|
||||
our_version,
|
||||
installed: false,
|
||||
caps_approved: true, // TODO see if we want to auto-approve local installs
|
||||
manifest_hash: None, // generated in the add fn
|
||||
mirroring: *mirror,
|
||||
auto_update: false, // can't auto-update a local package
|
||||
metadata: None, // TODO
|
||||
@ -407,7 +408,7 @@ fn handle_receive_download(
|
||||
Some(hash) => {
|
||||
if download_hash != hash {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: downloaded package is not latest version--rejecting download!"
|
||||
"app store: downloaded package is not desired version--rejecting download! download hash: {download_hash}, desired hash: {hash}"
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -422,7 +423,7 @@ fn handle_receive_download(
|
||||
if let Some(latest_hash) = metadata.versions.clone().unwrap_or(vec![]).last() {
|
||||
if &download_hash != latest_hash {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: downloaded package is not latest version--rejecting download!"
|
||||
"app store: downloaded package is not latest version--rejecting download! download hash: {download_hash}, latest hash: {latest_hash}"
|
||||
));
|
||||
}
|
||||
} else {
|
||||
@ -436,6 +437,14 @@ fn handle_receive_download(
|
||||
}
|
||||
}
|
||||
|
||||
let old_manifest_hash = match state.downloaded_packages.get(&package_id) {
|
||||
Some(package_state) => package_state
|
||||
.manifest_hash
|
||||
.clone()
|
||||
.unwrap_or("OLD".to_string()),
|
||||
_ => "OLD".to_string(),
|
||||
};
|
||||
|
||||
state.add_downloaded_package(
|
||||
&package_id,
|
||||
PackageState {
|
||||
@ -443,12 +452,28 @@ fn handle_receive_download(
|
||||
our_version: download_hash,
|
||||
installed: false,
|
||||
caps_approved: false,
|
||||
manifest_hash: None, // generated in the add fn
|
||||
mirroring: requested_package.mirror,
|
||||
auto_update: requested_package.auto_update,
|
||||
metadata: None, // TODO
|
||||
},
|
||||
Some(blob.bytes),
|
||||
)
|
||||
)?;
|
||||
|
||||
let new_manifest_hash = match state.downloaded_packages.get(&package_id) {
|
||||
Some(package_state) => package_state
|
||||
.manifest_hash
|
||||
.clone()
|
||||
.unwrap_or("NEW".to_string()),
|
||||
_ => "NEW".to_string(),
|
||||
};
|
||||
|
||||
// lastly, if auto_update is true, AND the caps_hash has NOT changed,
|
||||
// trigger install!
|
||||
if requested_package.auto_update && old_manifest_hash == new_manifest_hash {
|
||||
handle_install(our, state, &package_id)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_ft_worker_result(body: &[u8], context: &[u8]) -> anyhow::Result<()> {
|
||||
@ -470,11 +495,15 @@ fn handle_ft_worker_result(body: &[u8], context: &[u8]) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_eth_sub_event(state: &mut State, event: EthSubEvent) -> anyhow::Result<()> {
|
||||
fn handle_eth_sub_event(
|
||||
our: &Address,
|
||||
state: &mut State,
|
||||
event: EthSubEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
let EthSubEvent::Log(log) = event else {
|
||||
return Err(anyhow::anyhow!("app store: got non-log event"));
|
||||
};
|
||||
state.ingest_listings_contract_event(log)
|
||||
state.ingest_listings_contract_event(our, log)
|
||||
}
|
||||
|
||||
fn fetch_package_manifest(package: &PackageId) -> anyhow::Result<Vec<kt::PackageManifestEntry>> {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::LocalRequest;
|
||||
use alloy_rpc_types::Log;
|
||||
use alloy_sol_types::{sol, SolEvent};
|
||||
use kinode_process_lib::kernel_types as kt;
|
||||
@ -86,6 +87,7 @@ pub struct PackageState {
|
||||
pub our_version: String,
|
||||
pub installed: bool,
|
||||
pub caps_approved: bool,
|
||||
pub manifest_hash: Option<String>,
|
||||
/// are we serving this package to others?
|
||||
pub mirroring: bool,
|
||||
/// if we get a listing data update, will we try to download it?
|
||||
@ -171,7 +173,7 @@ impl State {
|
||||
pub fn add_downloaded_package(
|
||||
&mut self,
|
||||
package_id: &PackageId,
|
||||
package_state: PackageState,
|
||||
mut package_state: PackageState,
|
||||
package_bytes: Option<Vec<u8>>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(package_bytes) = package_bytes {
|
||||
@ -215,6 +217,13 @@ impl State {
|
||||
})?)
|
||||
.blob(blob)
|
||||
.send_and_await_response(5)??;
|
||||
|
||||
let manifest_file = vfs::File {
|
||||
path: format!("/{}/pkg/manifest.json", package_id),
|
||||
};
|
||||
let manifest_bytes = manifest_file.read()?;
|
||||
let manifest_hash = generate_metadata_hash(&manifest_bytes);
|
||||
package_state.manifest_hash = Some(manifest_hash);
|
||||
}
|
||||
self.downloaded_packages
|
||||
.insert(package_id.to_owned(), package_state);
|
||||
@ -296,6 +305,10 @@ impl State {
|
||||
// generate entry from this data
|
||||
// for the version hash, take the SHA-256 hash of the zip file
|
||||
let our_version = generate_version_hash(&zip_file_bytes);
|
||||
let manifest_file = vfs::File {
|
||||
path: format!("/{}/pkg/manifest.json", package_id),
|
||||
};
|
||||
let manifest_bytes = manifest_file.read()?;
|
||||
// the user will need to turn mirroring and auto-update back on if they
|
||||
// have to reset the state of their app store for some reason. the apps
|
||||
// themselves will remain on disk unless explicitly deleted.
|
||||
@ -306,6 +319,7 @@ impl State {
|
||||
our_version,
|
||||
installed: true,
|
||||
caps_approved: true, // since it's already installed this must be true
|
||||
manifest_hash: Some(generate_metadata_hash(&manifest_bytes)),
|
||||
mirroring: false,
|
||||
auto_update: false,
|
||||
metadata: None,
|
||||
@ -362,7 +376,11 @@ impl State {
|
||||
}
|
||||
|
||||
/// only saves state if last_saved_block is more than 1000 blocks behind
|
||||
pub fn ingest_listings_contract_event(&mut self, log: Log) -> anyhow::Result<()> {
|
||||
pub fn ingest_listings_contract_event(
|
||||
&mut self,
|
||||
our: &Address,
|
||||
log: Log,
|
||||
) -> anyhow::Result<()> {
|
||||
let block_number: u64 = log
|
||||
.block_number
|
||||
.ok_or(anyhow::anyhow!("app store: got log with no block number"))?
|
||||
@ -454,6 +472,33 @@ impl State {
|
||||
|
||||
current_listing.metadata_hash = metadata_hash;
|
||||
current_listing.metadata = metadata;
|
||||
|
||||
let package_id = PackageId::new(¤t_listing.name, ¤t_listing.publisher);
|
||||
|
||||
// if we have this app installed, and we have auto_update set to true,
|
||||
// we should try to download new version from the mirrored_from node
|
||||
// and install it if successful.
|
||||
if let Some(package_state) = self.downloaded_packages.get(&package_id) {
|
||||
if package_state.auto_update {
|
||||
if let Some(mirrored_from) = &package_state.mirrored_from {
|
||||
crate::print_to_terminal(
|
||||
1,
|
||||
&format!(
|
||||
"app store: auto-updating package {package_id} from {mirrored_from}"
|
||||
),
|
||||
);
|
||||
Request::to(our)
|
||||
.body(serde_json::to_vec(&LocalRequest::Download {
|
||||
package: package_id,
|
||||
download_from: mirrored_from.clone(),
|
||||
mirror: package_state.mirroring,
|
||||
auto_update: package_state.auto_update,
|
||||
desired_version_hash: None,
|
||||
})?)
|
||||
.send()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Transfer::SIGNATURE_HASH => {
|
||||
let from = alloy_primitives::Address::from_word(log.topics[1]);
|
||||
|
Loading…
Reference in New Issue
Block a user