Skip to content

Commit

Permalink
Merge pull request #2305 from itowlson/service-chaining
Browse files Browse the repository at this point in the history
Service chaining
  • Loading branch information
itowlson authored Mar 20, 2024
2 parents 20c9c8a + 4dbdc56 commit 46a9135
Show file tree
Hide file tree
Showing 32 changed files with 1,106 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions crates/app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,13 @@ impl<'a, L> App<'a, L> {
self.triggers()
.filter(move |trigger| trigger.locked.trigger_type == trigger_type)
}

/// Checks that the application does not have any host requirements
/// outside the supported set. The error case returns a comma-separated
/// list of unmet requirements.
pub fn ensure_needs_only(&self, supported: &[&str]) -> std::result::Result<(), String> {
self.locked.ensure_needs_only(supported)
}
}

impl<'a> App<'a> {
Expand Down
43 changes: 43 additions & 0 deletions crates/loader/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use spin_locked_app::{
values::{ValuesMap, ValuesMapBuilder},
};
use spin_manifest::schema::v2::{self, AppManifest, KebabId, WasiFilesMount};
use spin_outbound_networking::SERVICE_CHAINING_DOMAIN_SUFFIX;
use tokio::sync::Semaphore;

use crate::{cache::Cache, FilesMountStrategy};
Expand Down Expand Up @@ -79,6 +80,8 @@ impl LocalLoader {

let metadata = locked_metadata(application, triggers.keys().cloned())?;

let app_requires_service_chaining = components.values().any(requires_service_chaining);

let variables = variables
.into_iter()
.map(|(name, v)| Ok((name.to_string(), locked_variable(v)?)))
Expand All @@ -104,11 +107,27 @@ impl LocalLoader {
}))
.await?;

let mut host_requirements = ValuesMapBuilder::new();
if app_requires_service_chaining {
host_requirements.string(
spin_locked_app::locked::SERVICE_CHAINING_KEY,
spin_locked_app::locked::HOST_REQ_REQUIRED,
);
}
let host_requirements = host_requirements.build();

let mut must_understand = vec![];
if !host_requirements.is_empty() {
must_understand.push(spin_locked_app::locked::MustUnderstand::HostRequirements);
}

drop(sloth_guard);

Ok(LockedApp {
spin_lock_version: Default::default(),
metadata,
must_understand,
host_requirements,
variables,
triggers,
components,
Expand Down Expand Up @@ -534,6 +553,30 @@ fn file_url(path: impl AsRef<Path>) -> Result<String> {
Ok(Url::from_file_path(abs_path).unwrap().to_string())
}

fn requires_service_chaining(component: &spin_manifest::schema::v2::Component) -> bool {
component
.normalized_allowed_outbound_hosts()
.unwrap_or_default()
.iter()
.any(|h| is_chaining_host(h))
}

fn is_chaining_host(pattern: &str) -> bool {
use spin_outbound_networking::{AllowedHostConfig, HostConfig};

let Ok(allowed) = AllowedHostConfig::parse(pattern) else {
return false;
};

match allowed.host() {
HostConfig::List(hosts) => hosts
.iter()
.any(|h| h.ends_with(SERVICE_CHAINING_DOMAIN_SUFFIX)),
HostConfig::AnySubdomain(domain) => domain == SERVICE_CHAINING_DOMAIN_SUFFIX,
_ => false,
}
}

const SLOTH_WARNING_DELAY_MILLIS: u64 = 1250;

fn warn_if_component_load_slothful() -> sloth::SlothGuard {
Expand Down
85 changes: 85 additions & 0 deletions crates/loader/tests/ui/service-chaining.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"spin_lock_version": 1,
"must_understand": [
"host_requirements"
],
"metadata": {
"authors": [
"Gul Madred",
"Edward Jellico",
"JL"
],
"description": "A simple application that returns the number of lights",
"name": "chain-of-command",
"origin": "file://<test-dir>/service-chaining.toml",
"trigger": {
"type": "http"
},
"triggers": {},
"version": "6.11.2"
},
"host_requirements": {
"local_service_chaining": "required"
},
"triggers": [
{
"id": "four-lights-http-trigger",
"trigger_type": "http",
"trigger_config": {
"component": "four-lights",
"executor": {
"type": "http"
},
"route": "/lights"
}
},
{
"id": "old-test-http-trigger",
"trigger_type": "http",
"trigger_config": {
"component": "old-test",
"route": "/test"
}
},
{
"id": "web-http-trigger",
"trigger_type": "http",
"trigger_config": {
"component": "web",
"route": "/dont/test"
}
}
],
"components": [
{
"id": "four-lights",
"metadata": {
"allowed_outbound_hosts": [
"http://old-test.spin.internal"
]
},
"source": {
"content_type": "application/wasm",
"source": "file://<test-dir>/wasm/dummy.wasm"
},
"env": {
"env1": "first",
"env2": "second"
}
},
{
"id": "old-test",
"source": {
"content_type": "application/wasm",
"source": "file://<test-dir>/wasm/dummy.wasm"
}
},
{
"id": "web",
"source": {
"content_type": "application/wasm",
"source": "file://<cache-dir>/spin/registry/wasm/sha256:0000000000000000000000000000000000000000000000000000000000000000"
}
}
]
}
35 changes: 35 additions & 0 deletions crates/loader/tests/ui/service-chaining.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
spin_manifest_version = 2

[application]
authors = ["Gul Madred", "Edward Jellico", "JL"]
description = "A simple application that returns the number of lights"
name = "chain-of-command"
version = "6.11.2"

[[trigger.http]]
route = "/lights"
component = "four-lights"
executor = { type = "http" }

[[trigger.http]]
route = "/test"
component = "old-test"

[[trigger.http]]
route = "/dont/test"
component = "web"

[component.four-lights]
source = "wasm/dummy.wasm"
allowed_outbound_hosts = ["http://old-test.spin.internal"]
[component.four-lights.environment]
env1 = "first"
env2 = "second"

[component.old-test]
source = "wasm/dummy.wasm"

[component.web]
[component.web.source]
url = "https://example.com/wasm.wasm.wasm"
digest = "sha256:0000000000000000000000000000000000000000000000000000000000000000"
Loading

0 comments on commit 46a9135

Please sign in to comment.