Skip to content

Commit

Permalink
Add fastly_acl hostcalls (#438)
Browse files Browse the repository at this point in the history
This adds support for two new hostcalls:
- fastly_acl::open
- fastly_acl::lookup
  • Loading branch information
awilliams-fastly authored Nov 4, 2024
1 parent 274230f commit eeac2f4
Show file tree
Hide file tree
Showing 26 changed files with 1,019 additions and 30 deletions.
2 changes: 2 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ async fn create_execution_context(

if let Some(config_path) = args.config_path() {
let config = FastlyConfig::from_file(config_path)?;
let acls = config.acls();
let backends = config.backends();
let device_detection = config.device_detection();
let geolocation = config.geolocation();
Expand All @@ -321,6 +322,7 @@ async fn create_execution_context(
let backend_names = itertools::join(backends.keys(), ", ");

ctx = ctx
.with_acls(acls.clone())
.with_backends(backends.clone())
.with_device_detection(device_detection.clone())
.with_geolocation(geolocation.clone())
Expand Down
113 changes: 113 additions & 0 deletions cli/tests/integration/acl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use crate::{common::Test, common::TestResult, viceroy_test};
use hyper::{body::to_bytes, StatusCode};
use viceroy_lib::config::FastlyConfig;
use viceroy_lib::error::{AclConfigError, FastlyConfigError};

viceroy_test!(acl_works, |is_component| {
const FASTLY_TOML: &str = r#"
name = "acl"
description = "acl test"
authors = ["Test User <test_user@fastly.com>"]
language = "rust"
[local_server]
acls.my-acl-1 = "../test-fixtures/data/my-acl-1.json"
acls.my-acl-2 = {file = "../test-fixtures/data/my-acl-2.json"}
"#;

let resp = Test::using_fixture("acl.wasm")
.adapt_component(is_component)
.using_fastly_toml(FASTLY_TOML)?
.log_stderr()
.log_stdout()
.against_empty()
.await?;

assert_eq!(resp.status(), StatusCode::OK);
assert!(to_bytes(resp.into_body())
.await
.expect("can read body")
.to_vec()
.is_empty());

Ok(())
});

fn bad_config_test(local_server_fragment: &str) -> Result<FastlyConfig, FastlyConfigError> {
let toml = format!(
r#"
name = "acl"
description = "acl test"
authors = ["Test User <test_user@fastly.com>"]
language = "rust"
[local_server]
{}
"#,
local_server_fragment
);

toml.parse::<FastlyConfig>()
}

#[tokio::test(flavor = "multi_thread")]
async fn bad_config_invalid_path() -> TestResult {
const TOML_FRAGMENT: &str = "acls.bad = 1";
match bad_config_test(TOML_FRAGMENT) {
Err(FastlyConfigError::InvalidAclDefinition {
err: AclConfigError::InvalidType,
..
}) => (),
Err(_) => panic!(
"expected a FastlyConfigError::InvalidAclDefinition with AclConfigError::InvalidType"
),
_ => panic!("Expected an error"),
}
Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn bad_config_missing_key() -> TestResult {
const TOML_FRAGMENT: &str = "acls.bad = { \"other\" = true }";
match bad_config_test(TOML_FRAGMENT) {
Err(FastlyConfigError::InvalidAclDefinition {
err: AclConfigError::MissingFile,
..
}) => (),
Err(_) => panic!(
"expected a FastlyConfigError::InvalidAclDefinition with AclConfigError::MissingFile"
),
_ => panic!("Expected an error"),
}
Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn bad_config_missing_file() -> TestResult {
const TOML_FRAGMENT: &str = "acls.bad = \"/does/not/exist\"";
match bad_config_test(TOML_FRAGMENT) {
Err(FastlyConfigError::InvalidAclDefinition {
err: AclConfigError::IoError(_),
..
}) => (),
Err(_) => panic!(
"expected a FastlyConfigError::InvalidAclDefinition with AclConfigError::IoError"
),
_ => panic!("Expected an error"),
}
Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn bad_config_invalid_json() -> TestResult {
const TOML_FRAGMENT: &str = "acls.bad = \"../Cargo.toml\"";
match bad_config_test(TOML_FRAGMENT) {
Err(FastlyConfigError::InvalidAclDefinition {
err: AclConfigError::JsonError(_),
..
}) => (),
Err(_) => panic!(
"expected a FastlyConfigError::InvalidAclDefinition with AclConfigError::JsonError"
),
_ => panic!("Expected an error"),
}
Ok(())
}
7 changes: 6 additions & 1 deletion cli/tests/integration/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use viceroy_lib::config::UnknownImportBehavior;
use viceroy_lib::{
body::Body,
config::{
DeviceDetection, Dictionaries, FastlyConfig, Geolocation, ObjectStores, SecretStores,
Acls, DeviceDetection, Dictionaries, FastlyConfig, Geolocation, ObjectStores, SecretStores,
},
ExecuteCtx, ProfilingStrategy, ViceroyService,
};
Expand Down Expand Up @@ -77,6 +77,7 @@ pub type TestResult = Result<(), Error>;
/// A builder for running individual requests through a wasm fixture.
pub struct Test {
module_path: PathBuf,
acls: Acls,
backends: TestBackends,
device_detection: DeviceDetection,
dictionaries: Dictionaries,
Expand All @@ -99,6 +100,7 @@ impl Test {

Self {
module_path,
acls: Acls::new(),
backends: TestBackends::new(),
device_detection: DeviceDetection::new(),
dictionaries: Dictionaries::new(),
Expand All @@ -121,6 +123,7 @@ impl Test {

Self {
module_path,
acls: Acls::new(),
backends: TestBackends::new(),
device_detection: DeviceDetection::new(),
dictionaries: Dictionaries::new(),
Expand All @@ -140,6 +143,7 @@ impl Test {
pub fn using_fastly_toml(self, fastly_toml: &str) -> Result<Self, Error> {
let config = fastly_toml.parse::<FastlyConfig>()?;
Ok(Self {
acls: config.acls().to_owned(),
backends: TestBackends::from_backend_configs(config.backends()),
device_detection: config.device_detection().to_owned(),
dictionaries: config.dictionaries().to_owned(),
Expand Down Expand Up @@ -328,6 +332,7 @@ impl Test {
self.unknown_import_behavior,
self.adapt_component,
)?
.with_acls(self.acls.clone())
.with_backends(self.backends.backend_configs().await)
.with_dictionaries(self.dictionaries.clone())
.with_device_detection(self.device_detection.clone())
Expand Down
1 change: 1 addition & 0 deletions cli/tests/integration/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod acl;
mod args;
mod async_io;
mod body;
Expand Down
66 changes: 59 additions & 7 deletions crates/adapter/src/fastly/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,20 @@ pub enum HttpKeepaliveMode {
NoKeepalive = 1,
}

pub type PendingObjectStoreLookupHandle = u32;
pub type PendingObjectStoreInsertHandle = u32;
pub type AclHandle = u32;
pub type AsyncItemHandle = u32;
pub type BodyHandle = u32;
pub type DictionaryHandle = u32;
pub type KVStoreHandle = u32;
pub type PendingObjectStoreDeleteHandle = u32;
pub type PendingObjectStoreInsertHandle = u32;
pub type PendingObjectStoreListHandle = u32;
pub type BodyHandle = u32;
pub type PendingObjectStoreLookupHandle = u32;
pub type PendingRequestHandle = u32;
pub type RequestHandle = u32;
pub type ResponseHandle = u32;
pub type DictionaryHandle = u32;
pub type KVStoreHandle = u32;
pub type SecretStoreHandle = u32;
pub type SecretHandle = u32;
pub type AsyncItemHandle = u32;
pub type SecretStoreHandle = u32;

const INVALID_HANDLE: u32 = u32::MAX - 1;

Expand Down Expand Up @@ -303,6 +304,57 @@ pub struct InspectConfig {
pub workspace_len: u32,
}

pub mod fastly_acl {
use super::*;
use crate::bindings::fastly::api::acl;
use core::slice;

#[export_name = "fastly_acl#open"]
pub fn open(
acl_name_ptr: *const u8,
acl_name_len: usize,
acl_handle_out: *mut AclHandle,
) -> FastlyStatus {
let acl_name = unsafe { slice::from_raw_parts(acl_name_ptr, acl_name_len) };
match acl::open(acl_name) {
Ok(res) => {
unsafe {
*acl_handle_out = res;
}
FastlyStatus::OK
}
Err(e) => e.into(),
}
}

#[export_name = "fastly_acl#lookup"]
pub fn lookup(
acl_handle: acl::AclHandle,
ip_octets: *const u8,
ip_len: usize,
body_handle_out: *mut BodyHandle,
acl_error_out: *mut acl::AclError,
) -> FastlyStatus {
let ip = unsafe { slice::from_raw_parts(ip_octets, ip_len) };
match acl::lookup(acl_handle, ip, u64::try_from(ip_len).trapping_unwrap()) {
Ok((Some(body_handle), acl_error)) => {
unsafe {
*body_handle_out = body_handle;
*acl_error_out = acl_error;
}
FastlyStatus::OK
}
Ok((None, acl_error)) => {
unsafe {
*acl_error_out = acl_error;
}
FastlyStatus::OK
}
Err(e) => e.into(),
}
}
}

pub mod fastly_abi {
use super::*;

Expand Down
16 changes: 16 additions & 0 deletions lib/compute-at-edge-abi/compute-at-edge.witx
Original file line number Diff line number Diff line change
Expand Up @@ -1142,3 +1142,19 @@
(result $err (expected $vcpu_ms (error $fastly_status)))
)
)

(module $fastly_acl
(@interface func (export "open")
(param $name string)
(result $err (expected $acl_handle (error $fastly_status)))
)

(@interface func (export "lookup")
(param $acl $acl_handle)
(param $ip_octets (@witx const_pointer (@witx char8)))
(param $ip_len (@witx usize))
(param $body_handle_out (@witx pointer $body_handle))
(param $acl_error_out (@witx pointer $acl_error))
(result $err (expected (error $fastly_status)))
)
)
16 changes: 16 additions & 0 deletions lib/compute-at-edge-abi/typenames.witx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
(typename $secret_store_handle (handle))
;;; A handle to an individual secret.
(typename $secret_handle (handle))
;;; A handle to an ACL.
(typename $acl_handle (handle))
;;; A handle to an object supporting generic async operations.
;;; Can be either a `body_handle` or a `pending_request_handle`.
;;;
Expand Down Expand Up @@ -491,3 +493,17 @@
;;; This will map to the api's 429 codes
$too_many_requests
))

(typename $acl_error
(enum (@witx tag u32)
;;; The $acl_error has not been initialized.
$uninitialized
;;; There was no error.
$ok
;;; This will map to the api's 204 code.
;;; It indicates that the request succeeded, yet returned nothing.
$no_content
;;; This will map to the api's 429 code.
;;; Too many requests have been made.
$too_many_requests
))
Binary file modified lib/data/viceroy-component-adapter.wasm
Binary file not shown.
Loading

0 comments on commit eeac2f4

Please sign in to comment.