From c1861e0644246f03556ecc8e8993e3a40cb39cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Tue, 19 Mar 2024 00:27:56 +0100 Subject: [PATCH] Implement stack exports (#55) Fixes #54 --- .github/workflows/build.yml | 2 + Cargo.toml | 9 +- examples/simple/src/lib.rs | 5 +- examples/simple/tests/test.rs | 16 +- .../src/bindings.rs | 55 ++--- .../src/random.rs | 10 +- pulumi_wasm/src/bindings.rs | 202 +++++++++++++----- pulumi_wasm/src/finalizer.rs | 101 +++++++++ pulumi_wasm/src/lib.rs | 100 +++------ pulumi_wasm/src/output.rs | 17 +- pulumi_wasm/tests/test.rs | 23 +- pulumi_wasm_runner/src/main.rs | 14 +- pulumi_wasm_runner/src/pulumi.rs | 84 +++++++- pulumi_wasm_rust/src/lib.rs | 109 +--------- pulumi_wasm_rust/src/output.rs | 19 +- pulumi_wasm_rust/src/runner.rs | 84 ++++++++ pulumi_wasm_rust_macro/src/lib.rs | 2 +- wits/world.wit | 18 +- 18 files changed, 577 insertions(+), 293 deletions(-) create mode 100644 pulumi_wasm/src/finalizer.rs create mode 100644 pulumi_wasm_rust/src/runner.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af4db89a..2c234027 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,5 +67,7 @@ jobs: - name: Run tests run: cargo test --all --verbose + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Ensure no files have changed run: git diff --exit-code diff --git a/Cargo.toml b/Cargo.toml index 484480a9..73dfe5be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,11 @@ version = "0.1.0" edition = "2021" [workspace.dependencies] +pulumi_wasm_rust = { path = "pulumi_wasm_rust" } +pulumi_wasm_rust_macro = { path = "pulumi_wasm_rust_macro" } +wasm_common = { path = "wasm_common" } +pulumi_wasm_provider_random_rust = { path = "providers/pulumi_wasm_provider_random_rust" } + anyhow = "1.0.81" prost = "0.12.3" prost-types = "0.12.3" @@ -52,10 +57,6 @@ regex = "1.10.3" syn = { version = "2.0.53", features = ["full"] } quote = "1.0.35" proc-macro2 = "1.0.79" -pulumi_wasm_rust = { path = "pulumi_wasm_rust" } -pulumi_wasm_rust_macro = { path = "pulumi_wasm_rust_macro" } -wasm_common = { path = "wasm_common" } -pulumi_wasm_provider_random_rust = { path = "providers/pulumi_wasm_provider_random_rust" } assert_cmd = "2.0.14" predicates = "3.1.0" normpath = "1.2" diff --git a/examples/simple/src/lib.rs b/examples/simple/src/lib.rs index 6c62a463..7886e223 100644 --- a/examples/simple/src/lib.rs +++ b/examples/simple/src/lib.rs @@ -1,7 +1,7 @@ use anyhow::Error; use pulumi_wasm_provider_random_rust::random::{create_random_string, RandomStringArgs}; use pulumi_wasm_rust::output::Output; -use pulumi_wasm_rust::pulumi_main; +use pulumi_wasm_rust::{add_export, pulumi_main}; // Causes compilation errors // Generated by cargo-component @@ -10,9 +10,10 @@ use pulumi_wasm_rust::pulumi_main; #[pulumi_main] fn test_main() -> Result<(), Error> { let length: Output = Output::new(&12).map(|i: i32| i * 3); - let _ = create_random_string(RandomStringArgs { + let random_string = create_random_string(RandomStringArgs { name: "test", length, }); + add_export("result", random_string.result); Ok(()) } diff --git a/examples/simple/tests/test.rs b/examples/simple/tests/test.rs index 9b6c2e46..ab7d4b96 100644 --- a/examples/simple/tests/test.rs +++ b/examples/simple/tests/test.rs @@ -2,14 +2,21 @@ use std::process::Command; use assert_cmd::prelude::*; use std::str; use anyhow::anyhow; -use serde_json::{json, Value}; +use serde_json::Value; #[test] fn test_integration() -> Result<(), anyhow::Error> { + let github_token_env_vars = if let Ok(token) = std::env::var("GITHUB_TOKEN") { + vec![("GITHUB_TOKEN".to_string(), token)] + } else { + vec![] + }; + Command::new("pulumi") .args(["stack", "init", "test"]) .env("PULUMI_CONFIG_PASSPHRASE", " ") + .envs(github_token_env_vars.clone()) .current_dir(".") .output()?; @@ -23,11 +30,12 @@ fn test_integration() -> Result<(), anyhow::Error> { .args(["up", "-y"]) .current_dir(".") .env("PULUMI_CONFIG_PASSPHRASE", " ") + .envs(github_token_env_vars) .assert() .success(); let binding = Command::new("pulumi") - .args(["stack", "export"]) + .args(["stack", "output", "--json"]) .current_dir(".") .env("PULUMI_CONFIG_PASSPHRASE", " ") .assert() @@ -38,9 +46,9 @@ fn test_integration() -> Result<(), anyhow::Error> { let stack: Value = serde_json::from_str(str::from_utf8(stack)?)?; - let length = stack.pointer("/deployment/resources/1/inputs/length").ok_or(anyhow!("Cannot find length in stack export"))?; + let result = stack.pointer("/result").ok_or(anyhow!("Cannot find [result] in stack export"))?.as_str().ok_or(anyhow!("[result] is not a string"))?; - assert_eq!(length, &json!(36)); + assert_eq!(result.len(), 36); Ok(()) } diff --git a/providers/pulumi_wasm_provider_random/src/bindings.rs b/providers/pulumi_wasm_provider_random/src/bindings.rs index be35eae6..6631add8 100644 --- a/providers/pulumi_wasm_provider_random/src/bindings.rs +++ b/providers/pulumi_wasm_provider_random/src/bindings.rs @@ -100,24 +100,6 @@ pub mod component { _rt::bool_lift(ret as u8) } } - #[allow(unused_unsafe, clippy::all)] - pub fn combine_outputs() -> bool { - unsafe { - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "component:pulumi-wasm/output-interface@0.1.0")] - extern "C" { - #[link_name = "combine-outputs"] - fn wit_import() -> i32; - } - - #[cfg(not(target_arch = "wasm32"))] - fn wit_import() -> i32 { - unreachable!() - } - let ret = wit_import(); - _rt::bool_lift(ret as u8) - } - } impl Output { #[allow(unused_unsafe, clippy::all)] pub fn new(value: &[u8]) -> Self { @@ -407,10 +389,6 @@ pub mod exports { use super::super::super::super::_rt; pub type Output = super::super::super::super::component::pulumi_wasm::output_interface::Output; - /// variant either-u32 { - /// literal(option), - /// res(option>), - /// } pub struct RandomStringArgs<'a> { pub name: _rt::String, pub length: &'a Output, @@ -644,29 +622,28 @@ pub(crate) use __export_pulumi_provider_random_impl as export; #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.20.0:pulumi-provider-random:encoded world"] #[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1030] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xf9\x06\x01A\x02\x01\ -A\x07\x01B\x16\x04\0\x06output\x03\x01\x01p}\x01i\0\x01@\x01\x05value\x01\0\x02\x04\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1010] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe5\x06\x01A\x02\x01\ +A\x07\x01B\x15\x04\0\x06output\x03\x01\x01p}\x01i\0\x01@\x01\x05value\x01\0\x02\x04\ \0\x13[constructor]output\x01\x03\x01h\0\x01@\x02\x04self\x04\x0dfunction-names\0\ \x02\x04\0\x12[method]output.map\x01\x05\x01k\x01\x01@\x01\x04self\x04\0\x06\x04\ \0\x12[method]output.get\x01\x07\x01@\x02\x04self\x04\x05fields\0\x02\x04\0\x18[\ method]output.get-field\x01\x08\x01@\x01\x04self\x04\0s\x04\0\x17[method]output.\ get-type\x01\x09\x01@\x01\x04self\x04\0\x02\x04\0\x18[method]output.duplicate\x01\ \x0a\x01@\0\0s\x04\0\x10describe-outputs\x01\x0b\x01@\0\0\x7f\x04\0\x0fnon-done-\ -exists\x01\x0c\x04\0\x0fcombine-outputs\x01\x0c\x03\x01,component:pulumi-wasm/ou\ -tput-interface@0.1.0\x05\0\x02\x03\0\0\x06output\x01B\x0b\x02\x03\x02\x01\x01\x04\ -\0\x06output\x03\0\0\x01h\x01\x01r\x02\x04names\x05value\x02\x04\0\x0cobject-fie\ -ld\x03\0\x03\x01p\x04\x01r\x03\x04types\x04names\x06object\x05\x04\0\x19register\ --resource-request\x03\0\x06\x01i\x01\x01@\x01\x07request\x07\0\x08\x04\0\x08regi\ -ster\x01\x09\x03\x01.component:pulumi-wasm/register-interface@0.1.0\x05\x02\x01B\ -\x0c\x02\x03\x02\x01\x01\x04\0\x06output\x03\0\0\x01h\x01\x01r\x02\x04names\x06l\ -ength\x02\x04\0\x12random-string-args\x03\0\x03\x01i\x01\x01r\x01\x06result\x05\x04\ -\0\x14random-string-result\x03\0\x06\x01@\x01\x04args\x04\0\x07\x04\0\x14create-\ -random-string\x01\x08\x01@\0\x01\0\x04\0\x10handle-functions\x01\x09\x04\x01 { pub length: Output, } +pub struct RandomString { + pub result: Output, +} + pub fn create_random_string( args: RandomStringArgs, -) -> Output { +) -> RandomString { let length = clone(args.length); let args = pulumi_provider_random_interface::RandomStringArgs { name: args.name.into(), @@ -16,7 +20,9 @@ pub fn create_random_string( }; let result = pulumi_provider_random_interface::create_random_string(&args); - random_to_domain_mapper::(result.result) + RandomString { + result: random_to_domain_mapper(result.result), + } } fn random_to_domain_mapper(random: pulumi_provider_random_interface::Output) -> Output { diff --git a/pulumi_wasm/src/bindings.rs b/pulumi_wasm/src/bindings.rs index b49c1e14..9af7beea 100644 --- a/pulumi_wasm/src/bindings.rs +++ b/pulumi_wasm/src/bindings.rs @@ -241,6 +241,32 @@ pub mod component { } } #[allow(unused_unsafe, clippy::all)] + pub fn get_root_resource() -> _rt::String { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "component:pulumi-wasm/external-world@0.1.0")] + extern "C" { + #[link_name = "get-root-resource"] + fn wit_import(_: *mut u8); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8) { + unreachable!() + } + wit_import(ptr0); + let l1 = *ptr0.add(0).cast::<*mut u8>(); + let l2 = *ptr0.add(4).cast::(); + let len3 = l2; + let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); + _rt::string_lift(bytes3) + } + } + #[allow(unused_unsafe, clippy::all)] pub fn register_resource(request: &[u8]) -> _rt::Vec { unsafe { #[repr(align(4))] @@ -257,6 +283,34 @@ pub mod component { fn wit_import(_: *mut u8, _: usize, _: *mut u8); } + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8, _: usize, _: *mut u8) { + unreachable!() + } + wit_import(ptr0.cast_mut(), len0, ptr1); + let l2 = *ptr1.add(0).cast::<*mut u8>(); + let l3 = *ptr1.add(4).cast::(); + let len4 = l3; + _rt::Vec::from_raw_parts(l2.cast(), len4, len4) + } + } + #[allow(unused_unsafe, clippy::all)] + pub fn register_resource_outputs(request: &[u8]) -> _rt::Vec { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let vec0 = request; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "component:pulumi-wasm/external-world@0.1.0")] + extern "C" { + #[link_name = "register-resource-outputs"] + fn wit_import(_: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] fn wit_import(_: *mut u8, _: usize, _: *mut u8) { unreachable!() @@ -452,16 +506,6 @@ pub mod exports { } } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_combine_outputs_cabi() -> i32 { - let result0 = T::combine_outputs(); - match result0 { - true => 1, - false => 0, - } - } - #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_constructor_output_cabi( @@ -580,7 +624,6 @@ pub mod exports { type Output: GuestOutput; fn describe_outputs() -> _rt::String; fn non_done_exists() -> bool; - fn combine_outputs() -> bool; } pub trait GuestOutput: 'static { #[doc(hidden)] @@ -653,11 +696,6 @@ pub mod exports { $($path_to_types)*::_export_non_done_exists_cabi::<$ty>() } - #[export_name = "component:pulumi-wasm/output-interface@0.1.0#combine-outputs"] - unsafe extern "C" fn export_combine_outputs() -> i32 { - $($path_to_types)*::_export_combine_outputs_cabi::<$ty>() - } - #[export_name = "component:pulumi-wasm/output-interface@0.1.0#[constructor]output"] unsafe extern "C" fn export_constructor_output(arg0: *mut u8,arg1: usize,) -> i32 { $($path_to_types)*::_export_constructor_output_cabi::<<$ty as $($path_to_types)*::Guest>::Output>(arg0, arg1) @@ -973,6 +1011,66 @@ pub mod exports { struct _RetArea([::core::mem::MaybeUninit; 8]); static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 8]); } + + #[allow(clippy::all)] + pub mod stack_interface { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::super::__link_custom_section_describing_imports; + use super::super::super::super::_rt; + pub type Output = super::super::super::super::exports::component::pulumi_wasm::output_interface::Output; + pub type OutputBorrow<'a> = super::super::super::super::exports::component::pulumi_wasm::output_interface::OutputBorrow<'a>; + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_add_export_cabi( + arg0: *mut u8, + arg1: usize, + arg2: i32, + ) { + let len0 = arg1; + let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0); + T::add_export( + _rt::string_lift(bytes0), + OutputBorrow::lift(arg2 as u32 as usize), + ); + } + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_finish_cabi() -> i32 { + let result0 = T::finish(); + match result0 { + true => 1, + false => 0, + } + } + pub trait Guest { + fn add_export(name: _rt::String, value: OutputBorrow<'_>); + fn finish() -> bool; + } + #[doc(hidden)] + + macro_rules! __export_component_pulumi_wasm_stack_interface_0_1_0_cabi{ + ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { + + + #[export_name = "component:pulumi-wasm/stack-interface@0.1.0#add-export"] + unsafe extern "C" fn export_add_export(arg0: *mut u8,arg1: usize,arg2: i32,) { + $($path_to_types)*::_export_add_export_cabi::<$ty>(arg0, arg1, arg2) + } + + #[export_name = "component:pulumi-wasm/stack-interface@0.1.0#finish"] + unsafe extern "C" fn export_finish() -> i32 { + $($path_to_types)*::_export_finish_cabi::<$ty>() + } + };); +} + #[doc(hidden)] + pub(crate) use __export_component_pulumi_wasm_stack_interface_0_1_0_cabi; + } } } } @@ -1061,6 +1159,13 @@ mod _rt { ::core::mem::transmute::(val) } } + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + String::from_utf8_unchecked(bytes) + } + } use core::fmt; use core::marker; @@ -1163,13 +1268,6 @@ mod _rt { let layout = alloc::Layout::from_size_align_unchecked(size, align); alloc::dealloc(ptr as *mut u8, layout); } - pub unsafe fn string_lift(bytes: Vec) -> String { - if cfg!(debug_assertions) { - String::from_utf8(bytes).unwrap() - } else { - String::from_utf8_unchecked(bytes) - } - } extern crate alloc as alloc_crate; } @@ -1198,6 +1296,7 @@ macro_rules! __export_pulumi_wasm_impl { $($path_to_types_root)*::exports::component::pulumi_wasm::output_interface::__export_component_pulumi_wasm_output_interface_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::component::pulumi_wasm::output_interface); $($path_to_types_root)*::exports::component::pulumi_wasm::register_interface::__export_component_pulumi_wasm_register_interface_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::component::pulumi_wasm::register_interface); $($path_to_types_root)*::exports::component::pulumi_wasm::function_reverse_callback::__export_component_pulumi_wasm_function_reverse_callback_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::component::pulumi_wasm::function_reverse_callback); + $($path_to_types_root)*::exports::component::pulumi_wasm::stack_interface::__export_component_pulumi_wasm_stack_interface_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::component::pulumi_wasm::stack_interface); ) } #[doc(inline)] @@ -1206,36 +1305,39 @@ pub(crate) use __export_pulumi_wasm_impl as export; #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.20.0:pulumi-wasm:encoded world"] #[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1342] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xbc\x09\x01A\x02\x01\ -A\x0b\x01B\x0a\x01m\x05\x05TRACE\x05DEBUG\x04INFO\x04WARN\x05ERROR\x04\0\x05leve\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1499] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xd9\x0a\x01A\x02\x01\ +A\x0d\x01B\x0a\x01m\x05\x05TRACE\x05DEBUG\x04INFO\x04WARN\x05ERROR\x04\0\x05leve\ l\x03\0\0\x01ks\x01ky\x01o\x02ss\x01p\x04\x01r\x07\x05level\x01\x06targets\x04ar\ gss\x0bmodule-path\x02\x04file\x02\x04line\x03\x0akey-values\x05\x04\0\x07conten\ t\x03\0\x06\x01@\x01\x07content\x07\x01\0\x04\0\x03log\x01\x08\x03\x01\x1fcompon\ -ent:pulumi-wasm/log@0.1.0\x05\0\x01B\x05\x01@\0\0\x7f\x04\0\x0dis-in-preview\x01\ -\0\x01p}\x01@\x01\x07request\x01\0\x01\x04\0\x11register-resource\x01\x02\x03\x01\ -*component:pulumi-wasm/external-world@0.1.0\x05\x01\x01B\x16\x04\0\x06output\x03\ -\x01\x01p}\x01i\0\x01@\x01\x05value\x01\0\x02\x04\0\x13[constructor]output\x01\x03\ -\x01h\0\x01@\x02\x04self\x04\x0dfunction-names\0\x02\x04\0\x12[method]output.map\ -\x01\x05\x01k\x01\x01@\x01\x04self\x04\0\x06\x04\0\x12[method]output.get\x01\x07\ -\x01@\x02\x04self\x04\x05fields\0\x02\x04\0\x18[method]output.get-field\x01\x08\x01\ -@\x01\x04self\x04\0s\x04\0\x17[method]output.get-type\x01\x09\x01@\x01\x04self\x04\ -\0\x02\x04\0\x18[method]output.duplicate\x01\x0a\x01@\0\0s\x04\0\x10describe-out\ -puts\x01\x0b\x01@\0\0\x7f\x04\0\x0fnon-done-exists\x01\x0c\x04\0\x0fcombine-outp\ -uts\x01\x0c\x04\x01,component:pulumi-wasm/output-interface@0.1.0\x05\x02\x02\x03\ -\0\x02\x06output\x01B\x0b\x02\x03\x02\x01\x03\x04\0\x06output\x03\0\0\x01h\x01\x01\ -r\x02\x04names\x05value\x02\x04\0\x0cobject-field\x03\0\x03\x01p\x04\x01r\x03\x04\ -types\x04names\x06object\x05\x04\0\x19register-resource-request\x03\0\x06\x01i\x01\ -\x01@\x01\x07request\x07\0\x08\x04\0\x08register\x01\x09\x04\x01.component:pulum\ -i-wasm/register-interface@0.1.0\x05\x04\x01B\x0f\x02\x03\x02\x01\x03\x04\0\x06ou\ -tput\x03\0\0\x01i\x01\x01p}\x01r\x03\x02id\x02\x0bfunction-ids\x05value\x03\x04\0\ -\x1bfunction-invocation-request\x03\0\x04\x01h\x01\x01r\x02\x02id\x06\x05value\x03\ -\x04\0\x1afunction-invocation-result\x03\0\x07\x01p\x05\x01@\x01\x06sources\0\x09\ -\x04\0\x0dget-functions\x01\x0a\x01p\x08\x01@\x01\x07results\x0b\x01\0\x04\0\x0d\ -set-functions\x01\x0c\x04\x015component:pulumi-wasm/function-reverse-callback@0.\ -1.0\x05\x05\x04\x01'component:pulumi-wasm/pulumi-wasm@0.1.0\x04\0\x0b\x11\x01\0\x0b\ -pulumi-wasm\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\ -0.201.0\x10wit-bindgen-rust\x060.20.0"; +ent:pulumi-wasm/log@0.1.0\x05\0\x01B\x08\x01@\0\0\x7f\x04\0\x0dis-in-preview\x01\ +\0\x01@\0\0s\x04\0\x11get-root-resource\x01\x01\x01p}\x01@\x01\x07request\x02\0\x02\ +\x04\0\x11register-resource\x01\x03\x04\0\x19register-resource-outputs\x01\x03\x03\ +\x01*component:pulumi-wasm/external-world@0.1.0\x05\x01\x01B\x15\x04\0\x06output\ +\x03\x01\x01p}\x01i\0\x01@\x01\x05value\x01\0\x02\x04\0\x13[constructor]output\x01\ +\x03\x01h\0\x01@\x02\x04self\x04\x0dfunction-names\0\x02\x04\0\x12[method]output\ +.map\x01\x05\x01k\x01\x01@\x01\x04self\x04\0\x06\x04\0\x12[method]output.get\x01\ +\x07\x01@\x02\x04self\x04\x05fields\0\x02\x04\0\x18[method]output.get-field\x01\x08\ +\x01@\x01\x04self\x04\0s\x04\0\x17[method]output.get-type\x01\x09\x01@\x01\x04se\ +lf\x04\0\x02\x04\0\x18[method]output.duplicate\x01\x0a\x01@\0\0s\x04\0\x10descri\ +be-outputs\x01\x0b\x01@\0\0\x7f\x04\0\x0fnon-done-exists\x01\x0c\x04\x01,compone\ +nt:pulumi-wasm/output-interface@0.1.0\x05\x02\x02\x03\0\x02\x06output\x01B\x0b\x02\ +\x03\x02\x01\x03\x04\0\x06output\x03\0\0\x01h\x01\x01r\x02\x04names\x05value\x02\ +\x04\0\x0cobject-field\x03\0\x03\x01p\x04\x01r\x03\x04types\x04names\x06object\x05\ +\x04\0\x19register-resource-request\x03\0\x06\x01i\x01\x01@\x01\x07request\x07\0\ +\x08\x04\0\x08register\x01\x09\x04\x01.component:pulumi-wasm/register-interface@\ +0.1.0\x05\x04\x01B\x0f\x02\x03\x02\x01\x03\x04\0\x06output\x03\0\0\x01i\x01\x01p\ +}\x01r\x03\x02id\x02\x0bfunction-ids\x05value\x03\x04\0\x1bfunction-invocation-r\ +equest\x03\0\x04\x01h\x01\x01r\x02\x02id\x06\x05value\x03\x04\0\x1afunction-invo\ +cation-result\x03\0\x07\x01p\x05\x01@\x01\x06sources\0\x09\x04\0\x0dget-function\ +s\x01\x0a\x01p\x08\x01@\x01\x07results\x0b\x01\0\x04\0\x0dset-functions\x01\x0c\x04\ +\x015component:pulumi-wasm/function-reverse-callback@0.1.0\x05\x05\x01B\x07\x02\x03\ +\x02\x01\x03\x04\0\x06output\x03\0\0\x01h\x01\x01@\x02\x04names\x05value\x02\x01\ +\0\x04\0\x0aadd-export\x01\x03\x01@\0\0\x7f\x04\0\x06finish\x01\x04\x04\x01+comp\ +onent:pulumi-wasm/stack-interface@0.1.0\x05\x06\x04\x01'component:pulumi-wasm/pu\ +lumi-wasm@0.1.0\x04\0\x0b\x11\x01\0\x0bpulumi-wasm\x03\0\0\0G\x09producers\x01\x0c\ +processed-by\x02\x0dwit-component\x070.201.0\x10wit-bindgen-rust\x060.20.0"; #[inline(never)] #[doc(hidden)] diff --git a/pulumi_wasm/src/finalizer.rs b/pulumi_wasm/src/finalizer.rs new file mode 100644 index 00000000..6351b5a7 --- /dev/null +++ b/pulumi_wasm/src/finalizer.rs @@ -0,0 +1,101 @@ +use std::ops::Deref; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use log::info; +use prost::Message; +use rmpv::Value; +use crate::bindings::component::pulumi_wasm::external_world::{get_root_resource, is_in_preview}; +use crate::{grpc, output}; +use crate::bindings::component::pulumi_wasm::external_world; +use crate::output::{access_map, output_map, OutputContent}; + +static STACK_OUTPUT_SENT: AtomicBool = AtomicBool::new(false); + +pub(crate) fn finish() -> bool { + invoke_exporting_if_needed(); + combine_outputs() +} + +fn invoke_exporting_if_needed() { + if STACK_OUTPUT_SENT.swap(true, Relaxed) { + return; + } + let outputs = output_map(); + + if is_in_preview() { + info!("Skipping invoke_exporting because we are in preview"); + } else if outputs.is_empty() { + info!("Skipping invoke_exporting because there are no outputs") + } else { + info!("Invoking exporting"); + let outputs = output_map(); + let outputs = outputs.iter().collect::>(); + let names = outputs.iter().map(|(name, _)| name.to_string()).collect::>(); + + output::map_internal(outputs.iter().map(|(_, o)| o.output.clone()).collect(), move |values| { + let names = &names; + let object = crate::Component::create_protobuf_struct(names, &values); + info!("Resulting object: [{object:?}]"); + + let root = get_root_resource(); + info!("Root resource: [{root}]"); + + let request = grpc::RegisterResourceOutputsRequest { + urn: root, + outputs: Some(object), + }; + info!("Request: [{request:?}]"); + external_world::register_resource_outputs(request.encode_to_vec().as_slice()); + Value::Nil + }); + } +} + +fn combine_outputs() -> bool { + wasm_common::setup_logger(); + let outputs = access_map(); + let mut changed = false; + + outputs.iter().for_each(|o| { + let ref_cell = o.borrow(); + let content = ref_cell.deref(); + + let new_value = match content { + OutputContent::Func(refcells, f) => { + info!("Found func"); + let data = refcells.iter().flat_map(|r| { + let ref_cell = r.borrow(); + let content = ref_cell.deref(); + match content { + OutputContent::Done(v) => { + Some(v.clone()) + } + OutputContent::Mapped(_, _, _) | OutputContent::Func(_, _) | OutputContent::Nothing => None + } + }).collect::>(); + + if data.len() == refcells.len() { + info!("Map"); + Some(f(data)) + } else { + info!("Cannot map"); + None + } + } + OutputContent::Done(_) => None, + OutputContent::Mapped(_, _, _) => None, + OutputContent::Nothing => None + }; + + drop(ref_cell); + match new_value { + None => {} + Some(i) => { + changed = true; + o.replace(OutputContent::Done(i)); + } + }; + }); + + changed +} \ No newline at end of file diff --git a/pulumi_wasm/src/lib.rs b/pulumi_wasm/src/lib.rs index 1da70a25..fe4e61aa 100644 --- a/pulumi_wasm/src/lib.rs +++ b/pulumi_wasm/src/lib.rs @@ -2,8 +2,6 @@ use core::fmt::Debug; use std::collections::{BTreeMap}; use std::fmt::Formatter; use std::ops::Deref; - - use log::{error, info}; use prost::Message; use prost_types::Struct; @@ -11,20 +9,16 @@ use prost_types::value::Kind; use rmpv::{Utf8String, Value}; use bindings::component::pulumi_wasm::external_world; use crate::bindings::component::pulumi_wasm::external_world::is_in_preview; - - use crate::bindings::exports::component::pulumi_wasm::function_reverse_callback::{ FunctionInvocationRequest, FunctionInvocationResult, }; use crate::bindings::exports::component::pulumi_wasm::output_interface::Output as WasmOutput; - use crate::bindings::exports::component::pulumi_wasm::output_interface::{GuestOutput}; use crate::bindings::exports::component::pulumi_wasm::register_interface::{RegisterResourceRequest}; -use crate::bindings::exports::component::pulumi_wasm::{ - function_reverse_callback, output_interface, register_interface, -}; +use crate::bindings::exports::component::pulumi_wasm::{function_reverse_callback, output_interface, register_interface, stack_interface}; +use crate::bindings::exports::component::pulumi_wasm::stack_interface::OutputBorrow; -use crate::output::{access_map, FunctionId, FunctionSource, OutputContent}; +use crate::output::{access_map, FunctionId, FunctionSource, output_map, OutputContent}; bindings::export!(Component with_types_in bindings); #[allow(clippy::all)] @@ -38,9 +32,24 @@ mod grpc { tonic::include_proto!("pulumirpc"); } mod output; +mod finalizer; struct Component; +impl stack_interface::Guest for Component { + fn add_export(name: String, value: OutputBorrow<'_>) { + wasm_common::setup_logger(); + let output = value.get::().clone(); + info!("Adding export [{name}] with output [{output:?}]"); + output_map().insert(name, output); + } + + fn finish() -> bool { + wasm_common::setup_logger(); + finalizer::finish() + } +} + impl output_interface::Guest for Component { type Output = Output; @@ -74,54 +83,6 @@ impl output_interface::Guest for Component { false } - fn combine_outputs() -> bool { - wasm_common::setup_logger(); - let outputs = access_map(); - let mut changed = false; - - outputs.iter().for_each(|o| { - let ref_cell = o.borrow(); - let content = ref_cell.deref(); - - let new_value = match content { - OutputContent::Func(refcells, f) => { - info!("Found func"); - let data = refcells.iter().flat_map(|r| { - let ref_cell = r.borrow(); - let content = ref_cell.deref(); - match content { - OutputContent::Done(v) => { - Some(v.clone()) - } - OutputContent::Mapped(_, _, _) | OutputContent::Func(_, _) | OutputContent::Nothing => None - } - }).collect::>(); - - if data.len() == refcells.len() { - info!("Map"); - Some(f(data)) - } else { - info!("Cannot map"); - None - } - } - OutputContent::Done(_) => None, - OutputContent::Mapped(_, _, _) => None, - OutputContent::Nothing => None - }; - - drop(ref_cell); - match new_value { - None => {} - Some(i) => { - changed = true; - o.replace(OutputContent::Done(i)); - } - }; - }); - - changed - } } #[derive(Clone)] @@ -316,6 +277,9 @@ fn messagepack_to_protoc(v: &Value) -> prost_types::Value { Value::Integer(i) => prost_types::Value { kind: Option::from(prost_types::value::Kind::NumberValue(i.as_f64().unwrap())), }, + Value::String(s) => prost_types::Value { + kind: Option::from(prost_types::value::Kind::StringValue(s.clone().into_str().unwrap())), + }, _ => { error!("Cannot convert [{v}]"); todo!("Cannot convert [{v}]") @@ -335,14 +299,7 @@ impl register_interface::Guest for Component { let new_output = output::map_internal(values, move |v| { info!("Converting values [{v:?}] with names [{names:?}]"); - let pairs = names.iter().zip(v.iter()).map(|(name, value)| { - let v = messagepack_to_protoc(value); - (name.clone(), v) - }).collect::>(); - - let object = prost_types::Struct { - fields: BTreeMap::from_iter(pairs) - }; + let object = Self::create_protobuf_struct(&names, &v); info!("Resulting object: [{object:?}]"); @@ -404,3 +361,16 @@ impl register_interface::Guest for Component { WasmOutput::new(Output { output: new_output, tags: vec![] }) } } + +impl Component { + pub fn create_protobuf_struct(names: &[String], v: &[Value]) -> Struct { + let pairs = names.iter().zip(v.iter()).map(|(name, value)| { + let v = messagepack_to_protoc(value); + (name.clone(), v) + }).collect::>(); + + Struct { + fields: BTreeMap::from_iter(pairs) + } + } +} diff --git a/pulumi_wasm/src/output.rs b/pulumi_wasm/src/output.rs index 00465f50..f761ad77 100644 --- a/pulumi_wasm/src/output.rs +++ b/pulumi_wasm/src/output.rs @@ -1,6 +1,8 @@ use std::cell::RefCell; +use std::collections::HashMap; use rmpv::Value; use std::rc::Rc; +use crate::Output; pub(crate) type OutputContentRefCell = Rc>; @@ -35,6 +37,7 @@ impl FunctionSource { } static mut GLOBAL_MAP: Option> = None; +static mut OUTPUT_MAP: Option> = None; pub(crate) fn access_map() -> &'static mut Vec { let maybe_map = unsafe { &mut GLOBAL_MAP }; @@ -48,8 +51,20 @@ pub(crate) fn access_map() -> &'static mut Vec { } Some(m) => m, } +} + +pub(crate) fn output_map() -> &'static mut HashMap { + let maybe_map = unsafe { &mut OUTPUT_MAP }; - // unsafe { &mut GLOBAL_MAP } + match maybe_map { + None => { + unsafe { + OUTPUT_MAP = Some(HashMap::new()); + }; + output_map() + } + Some(m) => m, + } } pub(crate) enum OutputContent { diff --git a/pulumi_wasm/tests/test.rs b/pulumi_wasm/tests/test.rs index 71b0732d..576f8b24 100644 --- a/pulumi_wasm/tests/test.rs +++ b/pulumi_wasm/tests/test.rs @@ -1,7 +1,7 @@ use crate::server::component::pulumi_wasm::log; use crate::server::exports::component::pulumi_wasm::function_reverse_callback::FunctionInvocationRequest; use crate::server::PulumiWasm; -use anyhow::{Error, Ok}; +use anyhow::{Error, Ok, Result}; use std::collections::HashMap; use std::string::String; use wasmtime::component::{Component, Linker, ResourceTable}; @@ -38,23 +38,30 @@ fn create_function(f: F) -> impl Fn(Vec) -> Vec + Send } } +type Function = Box) -> Vec + Send>; + struct MyState { is_in_preview: bool, - functions: HashMap) -> Vec + Send>>, + functions: HashMap, } impl server::component::pulumi_wasm::external_world::Host for MyState { - fn is_in_preview(&mut self) -> std::result::Result { + fn is_in_preview(&mut self) -> Result { Ok(self.is_in_preview) } - fn register_resource(&mut self, _request: Vec) -> anyhow::Result> { + fn register_resource(&mut self, _request: Vec) -> Result> { + todo!() + } + fn get_root_resource(&mut self) -> Result { + todo!() + } + fn register_resource_outputs(&mut self, _request: Vec) -> Result> { todo!() } } - impl log::Host for MyState { - fn log(&mut self, s: log::Content) -> std::result::Result<(), anyhow::Error> { + fn log(&mut self, s: log::Content) -> Result<()> { println!("{:?}", s); Ok(()) } @@ -254,8 +261,8 @@ fn run_loop( ) -> Result<(), Error> { loop { let combined = plugin - .component_pulumi_wasm_output_interface() - .call_combine_outputs(store.borrow_mut())?; + .component_pulumi_wasm_stack_interface() + .call_finish(store.borrow_mut())?; if !run_all_function(store, plugin)? && !combined { return Ok(()); } diff --git a/pulumi_wasm_runner/src/main.rs b/pulumi_wasm_runner/src/main.rs index 58ca68d4..7242ceb5 100644 --- a/pulumi_wasm_runner/src/main.rs +++ b/pulumi_wasm_runner/src/main.rs @@ -89,10 +89,18 @@ async fn main() -> Result<(), Error> { } }; - let _pulumi_engine_url = std::env::var("PULUMI_ENGINE")?; + let pulumi_engine_url = std::env::var("PULUMI_ENGINE")?; let pulumi_monitor_url = std::env::var("PULUMI_MONITOR")?; - - let mut pulumi = Pulumi::create(&wasm, &Some(pulumi_monitor_url)).await?; + let pulumi_stack = std::env::var("PULUMI_STACK")?; + let pulumi_project = std::env::var("PULUMI_PROJECT")?; + + let mut pulumi = Pulumi::create(&wasm, + &Some(pulumi_monitor_url), + &Some(pulumi_engine_url), + &Some(pulumi_stack), + &Some(pulumi_project), + ).await?; + pulumi.create_root_stack().await?; pulumi.start().await?; } Command::Compile { wasm, output } => { diff --git a/pulumi_wasm_runner/src/pulumi.rs b/pulumi_wasm_runner/src/pulumi.rs index a5479645..ac318ad9 100644 --- a/pulumi_wasm_runner/src/pulumi.rs +++ b/pulumi_wasm_runner/src/pulumi.rs @@ -5,7 +5,8 @@ use wasmtime::component::{Component, Instance, Linker, ResourceTable}; use wasmtime::Store; use wasmtime_wasi::preview2::{WasiCtx, WasiCtxBuilder, WasiView}; -use crate::grpc::RegisterResourceRequest; +use crate::grpc::{GetRootResourceRequest, RegisterResourceOutputsRequest, RegisterResourceRequest, RegisterResourceResponse, SetRootResourceRequest}; +use crate::grpc::engine_client::EngineClient; use crate::grpc::resource_monitor_client::ResourceMonitorClient; use crate::pulumi::server::Main; @@ -31,16 +32,25 @@ struct SimplePluginCtx { struct MyState { pulumi_monitor_url: Option, + pulumi_engine_url: Option, + pulumi_stack: Option, + pulumi_project: Option, } #[async_trait] impl server::component::pulumi_wasm::external_world::Host for MyState { async fn is_in_preview(&mut self) -> wasmtime::Result { - Ok(std::env::var("PULUMI_DRY_RUN").is_ok()) + Ok(std::env::var("PULUMI_DRY_RUN").map(|s| s.to_ascii_lowercase() == "true").unwrap_or_else(|_| false)) } async fn register_resource(&mut self, request: Vec) -> wasmtime::Result> { Ok(self.register_async(request).await?) } + async fn register_resource_outputs(&mut self, request: Vec) -> wasmtime::Result> { + Ok(self.register_resource_outputs_async(request).await?) + } + async fn get_root_resource(&mut self) -> wasmtime::Result { + Ok(self.get_root_resource_async().await?) + } } #[async_trait] @@ -74,7 +84,7 @@ impl MyState { let engine_url = self .pulumi_monitor_url .clone() - .ok_or(Error::msg("engine_url not set"))?; + .ok_or(Error::msg("pulumi_monitor_url not set"))?; let request = RegisterResourceRequest::decode(&mut request.as_slice())?; @@ -84,6 +94,51 @@ impl MyState { Ok(result.get_ref().encode_to_vec()) } + + async fn register_resource_outputs_async(&mut self, request: Vec) -> wasmtime::Result> { + let engine_url = self + .pulumi_monitor_url + .clone() + .ok_or(Error::msg("pulumi_monitor_url not set"))?; + + let request = RegisterResourceOutputsRequest::decode(&mut request.as_slice())?; + + let mut client = ResourceMonitorClient::connect(format!("tcp://{engine_url}")).await?; + + let result = client.register_resource_outputs(request).await?; + + Ok(result.get_ref().encode_to_vec()) + } + + async fn set_root_resource_async(&mut self, urn: String) -> wasmtime::Result<()> { + let engine_url = self + .pulumi_engine_url + .clone() + .ok_or(Error::msg("pulumi_monitor_url not set"))?; + + let mut client = EngineClient::connect(format!("tcp://{engine_url}")).await?; + + let request = SetRootResourceRequest { urn }; + + let _ = client.set_root_resource(request).await?; + + Ok(()) + } + + async fn get_root_resource_async(&mut self) -> wasmtime::Result { + let engine_url = self + .pulumi_engine_url + .clone() + .ok_or(Error::msg("pulumi_monitor_url not set"))?; + + let mut client = EngineClient::connect(format!("tcp://{engine_url}")).await?; + + let request = GetRootResourceRequest {}; + + let result = client.get_root_resource(request).await?; + + Ok(result.get_ref().urn.clone()) + } } impl WasiView for SimplePluginCtx { @@ -105,6 +160,9 @@ impl Pulumi { pub async fn create( pulumi_wasm_file: &WasmFile, pulumi_monitor_url: &Option, + pulumi_engine_url: &Option, + pulumi_stack: &Option, + pulumi_project: &Option, ) -> Result { let mut engine_config = wasmtime::Config::new(); engine_config.wasm_component_model(true); @@ -136,6 +194,9 @@ impl Pulumi { context: wasi_ctx, my_state: MyState { pulumi_monitor_url: pulumi_monitor_url.clone(), + pulumi_engine_url: pulumi_engine_url.clone(), + pulumi_stack: pulumi_stack.clone(), + pulumi_project: pulumi_project.clone() }, }, ); @@ -179,6 +240,23 @@ impl Pulumi { component.serialize() } + pub async fn create_root_stack(&mut self) -> Result<(), Error> { + + let request = RegisterResourceRequest { + r#type: "pulumi:pulumi:Stack".to_string(), + name: format!("{}-{}", self.store.data_mut().my_state.pulumi_project.clone().unwrap(), self.store.data_mut().my_state.pulumi_stack.clone().unwrap()), + custom: false, + ..Default::default() + }; + + let result = self.store.data_mut().my_state.register_async(request.encode_to_vec()).await?; + + let url = RegisterResourceResponse::decode(&mut result.as_slice())?.urn; + self.store.data_mut().my_state.set_root_resource_async(url).await?; + + Ok(()) + } + pub async fn start(&mut self) -> Result<(), Error> { self.plugin .component_pulumi_wasm_pulumi_main() diff --git a/pulumi_wasm_rust/src/lib.rs b/pulumi_wasm_rust/src/lib.rs index 49800a8c..68c57c47 100644 --- a/pulumi_wasm_rust/src/lib.rs +++ b/pulumi_wasm_rust/src/lib.rs @@ -1,12 +1,5 @@ -use std::collections::HashMap; -use std::sync::Mutex; -use anyhow::{Context, Error}; -use lazy_static::lazy_static; -use log::{error, info}; - pub use pulumi_wasm_rust_macro::pulumi_main; -use crate::bindings::component::pulumi_wasm::function_reverse_callback::{FunctionInvocationRequest, FunctionInvocationResult, get_functions, set_functions}; -use crate::bindings::component::pulumi_wasm::output_interface::combine_outputs; +use crate::output::Output; #[allow(clippy::all)] #[allow(dead_code)] @@ -20,101 +13,9 @@ mod bindings { }); } -pub fn run(f: F) -> Result<(), Error> where F: Fn() -> Result<(), Error> { - let outer = || { - wasm_common::setup_logger(); - f()?; - run_loop()?; - Ok(()) - }; - - let result = outer(); - - match result { - Ok(()) => Ok(()), - Err(e) => { - error!("Error running pulumi wasm: [{e}]"); - Err(e) - } - } -} - -pub fn init() { - wasm_common::setup_logger(); -} - -pub fn close() -> Result<(), Error> { - run_loop() -} - -fn run_loop() -> Result<(), Error> { - loop { - if !run_all_function()? && !combine_outputs() { - return Ok(()); - } - } -} - -fn run_all_function( - // store: &mut Store, - // plugin: &PulumiWasm, -) -> Result { - let functions = get_functions("source"); - - if functions.is_empty() { - info!("Functions are empty"); - return Ok(false) - } - - info!("Functions are not empty"); - - let functions_map = HASHMAP.lock().unwrap(); - - let mapped: Result, _> = functions - .iter() - .map( - |FunctionInvocationRequest { - id, - function_id, - value, - }| { - info!("Invoking function [{function_id}] with value [{value:?}]"); - let v = rmpv::decode::read_value(&mut value.clone().as_slice())?; - info!("Invoking function [{function_id}] with value [{v:?}]"); - let f = functions_map.get(function_id).context(format!("Function with id {function_id} not found"))?; - Ok(FunctionInvocationResult { - id, - value: f(value.to_vec())?, - }) - }, - ) - .collect(); - - // mapped - - let mapped = match mapped { - Ok(mapped) => mapped, - Err(e) => { - error!("Failed to invoke functions due to [{e}]"); - return Err(e); - } - }; - - info!("Setting functions"); - set_functions(&mapped); - info!("run_all_function completed"); - - Ok(true) -} - - pub mod output; +pub mod runner; -type Function = Box) -> Result, Error> + Send>; - -lazy_static! { - pub static ref HASHMAP: Mutex> = { - let m = HashMap::new(); - Mutex::new(m) - }; -} +pub fn add_export(name: &str, output: Output) { + output.add_to_export(name); +} \ No newline at end of file diff --git a/pulumi_wasm_rust/src/output.rs b/pulumi_wasm_rust/src/output.rs index 4af3a5c7..a281859a 100644 --- a/pulumi_wasm_rust/src/output.rs +++ b/pulumi_wasm_rust/src/output.rs @@ -1,5 +1,9 @@ +use std::collections::HashMap; use std::fmt::Debug; use std::marker::PhantomData; +use std::sync::Mutex; +use anyhow::Error; +use lazy_static::lazy_static; use crate::bindings::component::pulumi_wasm::output_interface; use uuid::Uuid; use log::{info}; @@ -15,6 +19,15 @@ impl From for Output { } } +type Function = Box) -> Result, Error> + Send>; + +lazy_static! { + pub (crate) static ref HASHMAP: Mutex> = { + let m = HashMap::new(); + Mutex::new(m) + }; +} + impl Output { /// @@ -66,7 +79,7 @@ impl Output { }; let uuid = Uuid::now_v7().to_string(); - let mut map = crate::HASHMAP.lock().unwrap(); + let mut map = HASHMAP.lock().unwrap(); map.insert(uuid.clone(), Box::new(f)); let new_output = self.future.map(uuid.as_str()); @@ -76,4 +89,8 @@ impl Output { future: new_output, } } + + pub(crate) fn add_to_export(&self, name: &str) { + crate::bindings::component::pulumi_wasm::stack_interface::add_export(name, &self.future); + } } diff --git a/pulumi_wasm_rust/src/runner.rs b/pulumi_wasm_rust/src/runner.rs new file mode 100644 index 00000000..174ccc54 --- /dev/null +++ b/pulumi_wasm_rust/src/runner.rs @@ -0,0 +1,84 @@ +use anyhow::{Context, Error}; +use log::{error, info}; +use crate::bindings::component::pulumi_wasm::function_reverse_callback::{FunctionInvocationRequest, FunctionInvocationResult, get_functions, set_functions}; +use crate::bindings::component::pulumi_wasm::stack_interface::finish; +use crate::output::HASHMAP; + +pub fn run(f: F) -> Result<(), Error> where F: Fn() -> Result<(), Error> { + let outer = || { + wasm_common::setup_logger(); + f()?; + run_loop()?; + Ok(()) + }; + + let result = outer(); + + match result { + Ok(()) => Ok(()), + Err(e) => { + error!("Error running pulumi wasm: [{e}]"); + Err(e) + } + } +} + +fn run_loop() -> Result<(), Error> { + loop { + if !run_all_function()? && !finish() { + return Ok(()); + } + } +} + +fn run_all_function( + // store: &mut Store, + // plugin: &PulumiWasm, +) -> Result { + let functions = get_functions("source"); + + if functions.is_empty() { + info!("Functions are empty"); + return Ok(false) + } + + info!("Functions are not empty"); + + let functions_map = HASHMAP.lock().unwrap(); + + let mapped: Result, _> = functions + .iter() + .map( + |FunctionInvocationRequest { + id, + function_id, + value, + }| { + info!("Invoking function [{function_id}] with value [{value:?}]"); + let v = rmpv::decode::read_value(&mut value.clone().as_slice())?; + info!("Invoking function [{function_id}] with decoded value [{v:?}]"); + let f = functions_map.get(function_id).context(format!("Function with id {function_id} not found"))?; + Ok(FunctionInvocationResult { + id, + value: f(value.to_vec())?, + }) + }, + ) + .collect(); + + // mapped + + let mapped = match mapped { + Ok(mapped) => mapped, + Err(e) => { + error!("Failed to invoke functions due to [{e}]"); + return Err(e); + } + }; + + info!("Setting functions"); + set_functions(&mapped); + info!("run_all_function completed"); + + Ok(true) +} diff --git a/pulumi_wasm_rust_macro/src/lib.rs b/pulumi_wasm_rust_macro/src/lib.rs index e6f2fe1a..d84d4a82 100644 --- a/pulumi_wasm_rust_macro/src/lib.rs +++ b/pulumi_wasm_rust_macro/src/lib.rs @@ -19,7 +19,7 @@ pub fn pulumi_main(_attr: TokenStream, item: TokenStream) -> TokenStream { #[export_name = "component:pulumi-wasm/pulumi-main@0.1.0#main"] unsafe extern "C" fn __exported() { - pulumi_wasm_rust::run(|| { + pulumi_wasm_rust::runner::run(|| { #fn_name() }).unwrap(); } diff --git a/wits/world.wit b/wits/world.wit index fabf6706..5475e4a2 100644 --- a/wits/world.wit +++ b/wits/world.wit @@ -4,6 +4,7 @@ world pulumi-wasm { export output-interface; export register-interface; export function-reverse-callback; + export stack-interface; import log; import external-world; @@ -24,6 +25,7 @@ world main { world pulumi-wasm-rust { import output-interface; import function-reverse-callback; + import stack-interface; export pulumi-main; // Used by macro } @@ -83,7 +85,6 @@ interface output-interface { describe-outputs: func() -> string; non-done-exists: func() -> bool; - combine-outputs: func() -> bool; resource output { constructor(value: list); @@ -95,6 +96,14 @@ interface output-interface { } } +interface stack-interface { + use output-interface.{output}; + + add-export: func(name: string, value: borrow); + finish: func() -> bool; +} + + interface register-interface { use output-interface.{output}; @@ -114,18 +123,15 @@ interface register-interface { interface external-world { is-in-preview: func() -> bool; + get-root-resource: func() -> string; register-resource: func(request: list) -> list; + register-resource-outputs: func(request: list) -> list; } interface pulumi-provider-random-interface { use output-interface.{output}; -// variant either-u32 { -// literal(option), -// res(option>), -// } - record random-string-args { name: string, length: borrow,