Skip to content

Commit

Permalink
Load target env from other registry or local dir
Browse files Browse the repository at this point in the history
Signed-off-by: itowlson <[email protected]>
  • Loading branch information
itowlson committed Sep 24, 2024
1 parent 68fbd1a commit 76cb8af
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 12 deletions.
6 changes: 3 additions & 3 deletions crates/build/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use spin_manifest::{schema::v2, ManifestVersion};
pub enum ManifestBuildInfo {
Loadable {
components: Vec<ComponentBuildInfo>,
deployment_targets: Vec<String>,
deployment_targets: Vec<spin_manifest::schema::v2::TargetEnvironmentRef>,
manifest: spin_manifest::schema::v2::AppManifest,
},
Unloadable {
Expand All @@ -32,7 +32,7 @@ impl ManifestBuildInfo {
}
}

pub fn deployment_targets(&self) -> &[String] {
pub fn deployment_targets(&self) -> &[spin_manifest::schema::v2::TargetEnvironmentRef] {
match self {
Self::Loadable {
deployment_targets, ..
Expand Down Expand Up @@ -113,7 +113,7 @@ fn build_configs_from_manifest(

fn deployment_targets_from_manifest(
manifest: &spin_manifest::schema::v2::AppManifest,
) -> Vec<String> {
) -> Vec<spin_manifest::schema::v2::TargetEnvironmentRef> {
manifest.application.targets.clone()
}

Expand Down
82 changes: 75 additions & 7 deletions crates/environments/src/environment_definition.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,46 @@
use std::path::Path;

use anyhow::Context;
use spin_common::ui::quoted_path;
use spin_manifest::schema::v2::TargetEnvironmentRef;

const DEFAULT_REGISTRY: &str = "fermyon.com";

/// Loads the given `TargetEnvironment` from a registry.
pub async fn load_environment(env_id: impl AsRef<str>) -> anyhow::Result<TargetEnvironment> {
use futures_util::TryStreamExt;
pub async fn load_environment(env_id: &TargetEnvironmentRef) -> anyhow::Result<TargetEnvironment> {
match env_id {
TargetEnvironmentRef::DefaultRegistry(package) => {
load_environment_from_registry(DEFAULT_REGISTRY, package).await
}
TargetEnvironmentRef::Registry { registry, package } => {
load_environment_from_registry(registry, package).await
}
TargetEnvironmentRef::WitDirectory { path } => load_environment_from_dir(path),
}
}

let env_id = env_id.as_ref();
async fn load_environment_from_registry(
registry: &str,
env_id: &str,
) -> anyhow::Result<TargetEnvironment> {
use futures_util::TryStreamExt;

let (pkg_name, pkg_ver) = env_id.split_once('@').with_context(|| format!("Failed to parse target environment {env_id} as package reference - is the target correct?"))?;
let env_pkg_ref: wasm_pkg_loader::PackageRef = pkg_name
.parse()
.with_context(|| format!("Environment {pkg_name} is not a valid package name"))?;

let registry: wasm_pkg_loader::Registry = registry
.parse()
.with_context(|| format!("Registry {registry} is not a valid registry name"))?;

// TODO: this requires wkg configuration which shouldn't be on users:
// is there a better way to handle it?
let mut client = wasm_pkg_loader::Client::with_global_defaults()
.context("Failed to create a package loader from your global settings")?;
let mut wkg_config = wasm_pkg_loader::Config::global_defaults()
.unwrap_or_else(|_| wasm_pkg_loader::Config::empty());
wkg_config.set_package_registry_override(env_pkg_ref, registry);

let mut client = wasm_pkg_loader::Client::new(wkg_config);

let package = pkg_name
.to_owned()
Expand All @@ -35,7 +64,14 @@ pub async fn load_environment(env_id: impl AsRef<str>) -> anyhow::Result<TargetE
.with_context(|| format!("Failed to get {env_id} package data from registry"))?
.to_vec();

TargetEnvironment::new(env_id.to_owned(), bytes)
TargetEnvironment::from_package_bytes(env_id.to_owned(), bytes)
}

fn load_environment_from_dir(path: &Path) -> anyhow::Result<TargetEnvironment> {
let mut resolve = wit_parser::Resolve::default();
let (pkg_id, _) = resolve.push_dir(path)?;
let decoded = wit_parser::decoding::DecodedWasm::WitPackage(resolve, pkg_id);
TargetEnvironment::from_decoded_wasm(path, decoded)
}

/// A parsed document representing a deployment environment, e.g. Spin 2.7,
Expand All @@ -57,7 +93,7 @@ pub struct TargetEnvironment {
}

impl TargetEnvironment {
fn new(name: String, bytes: Vec<u8>) -> anyhow::Result<Self> {
fn from_package_bytes(name: String, bytes: Vec<u8>) -> anyhow::Result<Self> {
let decoded = wit_component::decode(&bytes)
.with_context(|| format!("Failed to decode package for environment {name}"))?;
let package_id = decoded.package();
Expand All @@ -79,6 +115,38 @@ impl TargetEnvironment {
})
}

fn from_decoded_wasm(
source: &Path,
decoded: wit_parser::decoding::DecodedWasm,
) -> anyhow::Result<Self> {
let package_id = decoded.package();
let package = decoded
.resolve()
.packages
.get(package_id)
.with_context(|| {
format!(
"The {} environment is invalid (no package for decoded package ID)",
quoted_path(source)
)
})?
.clone();
let name = package.name.to_string();

// This versionm of wit_component requires a flag for v2 encoding.
// v1 encoding is retired in wit_component main. You can remove the
// flag when this breaks next time we upgrade the crate!
let bytes = wit_component::encode(Some(true), decoded.resolve(), package_id)?;

Ok(Self {
name,
decoded,
package,
package_id,
package_bytes: bytes,
})
}

/// Returns true if the given trigger type provides the world identified by
/// `world` in this environment.
pub fn is_world_for(&self, trigger_type: &TriggerType, world: &wit_parser::World) -> bool {
Expand Down
3 changes: 2 additions & 1 deletion crates/environments/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use environment_definition::{load_environment, TargetEnvironment, TriggerType};
use futures::future::try_join_all;
pub use loader::ApplicationToValidate;
use loader::ComponentToValidate;
use spin_manifest::schema::v2::TargetEnvironmentRef;

pub async fn validate_application_against_environment_ids(
application: &ApplicationToValidate,
env_ids: &[impl AsRef<str>],
env_ids: &[TargetEnvironmentRef],
) -> anyhow::Result<Vec<anyhow::Error>> {
if env_ids.is_empty() {
return Ok(Default::default());
Expand Down
23 changes: 22 additions & 1 deletion crates/manifest/src/schema/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub struct AppDetails {
pub authors: Vec<String>,
/// `targets = ["spin-2.5", "fermyon-cloud", "spinkube-0.4"]`
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub targets: Vec<String>,
pub targets: Vec<TargetEnvironmentRef>,
/// `[application.triggers.<type>]`
#[serde(rename = "trigger", default, skip_serializing_if = "Map::is_empty")]
pub trigger_global_configs: Map<String, toml::Table>,
Expand Down Expand Up @@ -340,6 +340,27 @@ impl ComponentDependencies {
}
}

/// Identifies a deployment target.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged, deny_unknown_fields)]
pub enum TargetEnvironmentRef {
/// Environment package reference e.g. `spin:[email protected]`. This is looked up
/// in the default environment registry.
DefaultRegistry(String),
/// A target package in a registry other than the default
Registry {
/// Registry hosting the environment package e.g. `fermyon.com``.
registry: String,
/// Environment package reference e.g. `my:[email protected]`.
package: String,
},
/// A filesystem directory. This is expected to contain a WIT package.
WitDirectory {
/// The directory containing the environment WIT.
path: PathBuf,
},
}

mod kebab_or_snake_case {
use serde::{Deserialize, Serialize};
pub use spin_serde::{KebabId, SnakeId};
Expand Down

0 comments on commit 76cb8af

Please sign in to comment.