From 5907c6b6b09b69acdd5960594632a4072cc11efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Sat, 5 Oct 2024 23:52:56 +0200 Subject: [PATCH] Better optional values handling (#430) Closes #414 --- examples/simple/src/lib.rs | 12 + examples/simple/tests/test.rs | 14 + .../src/lib.rs | 1 + .../pulumi_wasm_provider_docker/src/lib.rs | 1 + .../pulumi_wasm_provider_random/src/lib.rs | 1 + pulumi_wasm/src/bindings.rs | 50 +++- pulumi_wasm/src/lib.rs | 22 +- pulumi_wasm_core/src/engine.rs | 261 +++++++++++++++++- pulumi_wasm_core/src/nodes.rs | 11 +- pulumi_wasm_core/src/pulumi/service.rs | 2 +- pulumi_wasm_core/src/pulumi/service_impl.rs | 2 +- .../src/output/provider/lib.rs.handlebars | 1 + .../functions-secrets/provider/src/lib.rs | 1 + pulumi_wasm_runner/src/main.rs | 6 + pulumi_wasm_runner/src/pulumi.rs | 7 + .../src/lib.rs | 9 + .../tests/create_final_component_test.rs | 20 +- .../wit/deps/pulumi-wasm-external.wit | 4 + pulumi_wasm_wit/wit/world.wit | 2 + 19 files changed, 394 insertions(+), 33 deletions(-) diff --git a/examples/simple/src/lib.rs b/examples/simple/src/lib.rs index a3cd30b06..2fd28ac77 100644 --- a/examples/simple/src/lib.rs +++ b/examples/simple/src/lib.rs @@ -19,6 +19,9 @@ fn test_main() -> Result<(), Error> { // Tests number mapping let number = random_string.min_upper.map(|i| i * 2); + // Optional values are deserialized as None + let keepers = random_string.keepers.map(|map| format!("Keepers: {map:?}")); + let val1 = Output::new(&1); let val2 = Output::new(&"abc".to_string()); @@ -29,10 +32,19 @@ fn test_main() -> Result<(), Error> { let combined_string = combined.map(|values| format!("Values: {values:?}")); let combined_2_string = combined_2.map(|values| format!("Values: {values:?}")); + let random_string_2 = random_string::create( + "test_2", + RandomStringArgs::builder() + .length(keepers.map(|s| s.len() as i32)) + .build_struct(), + ); + add_export("result", &random_string.result); add_export("transformed_result", &t); add_export("number", &number); add_export("combined_string", &combined_string); add_export("combined_2_string", &combined_2_string); + add_export("keepers", &keepers); + add_export("result_2", &random_string_2.result); Ok(()) } diff --git a/examples/simple/tests/test.rs b/examples/simple/tests/test.rs index bae38584f..e05961a40 100644 --- a/examples/simple/tests/test.rs +++ b/examples/simple/tests/test.rs @@ -73,11 +73,25 @@ fn test_integration() -> Result<(), anyhow::Error> { .as_str() .ok_or(anyhow!("[combined_2_string] is not a string"))?; + let keepers = stack + .pointer("/keepers") + .ok_or(anyhow!("Cannot find [keepers] in stack export"))? + .as_str() + .ok_or(anyhow!("[keepers] is not a string"))?; + + let result_2 = stack + .pointer("/result_2") + .ok_or(anyhow!("Cannot find [result_2] in stack export"))? + .as_str() + .ok_or(anyhow!("[result_2] is not a string"))?; + assert_eq!(result.len(), 36); assert_eq!(transformed_result, format!("Result: {}", result)); assert_eq!(number, 0); assert_eq!(combined_string, "Values: (1, \"abc\")"); assert_eq!(combined_2_string, "Values: (1, \"abc\")"); + assert_eq!(keepers, "Keepers: None"); + assert_eq!(result_2.len(), 13); Ok(()) } diff --git a/providers/pulumi_wasm_provider_cloudflare/src/lib.rs b/providers/pulumi_wasm_provider_cloudflare/src/lib.rs index 74e86404d..04bc47418 100644 --- a/providers/pulumi_wasm_provider_cloudflare/src/lib.rs +++ b/providers/pulumi_wasm_provider_cloudflare/src/lib.rs @@ -2,6 +2,7 @@ mod resource; mod function; #[allow(unused_braces)] +#[allow(unused_imports)] mod bindings; bindings::export!(Component with_types_in bindings); diff --git a/providers/pulumi_wasm_provider_docker/src/lib.rs b/providers/pulumi_wasm_provider_docker/src/lib.rs index 74e86404d..04bc47418 100644 --- a/providers/pulumi_wasm_provider_docker/src/lib.rs +++ b/providers/pulumi_wasm_provider_docker/src/lib.rs @@ -2,6 +2,7 @@ mod resource; mod function; #[allow(unused_braces)] +#[allow(unused_imports)] mod bindings; bindings::export!(Component with_types_in bindings); diff --git a/providers/pulumi_wasm_provider_random/src/lib.rs b/providers/pulumi_wasm_provider_random/src/lib.rs index 58d0a13bc..5d036ff0a 100644 --- a/providers/pulumi_wasm_provider_random/src/lib.rs +++ b/providers/pulumi_wasm_provider_random/src/lib.rs @@ -1,6 +1,7 @@ mod resource; #[allow(unused_braces)] +#[allow(unused_imports)] mod bindings; bindings::export!(Component with_types_in bindings); diff --git a/pulumi_wasm/src/bindings.rs b/pulumi_wasm/src/bindings.rs index 56187bdf4..3ec7c8428 100644 --- a/pulumi_wasm/src/bindings.rs +++ b/pulumi_wasm/src/bindings.rs @@ -1271,6 +1271,37 @@ pub mod exports { ); } } + #[allow(dead_code)] + pub mod pulumi_wasm_external { + #[allow(dead_code, clippy::all)] + pub mod pulumi_settings { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports; + use super::super::super::super::_rt; + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_set_in_preview_cabi(arg0: i32) { + #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); + T::set_in_preview(_rt::bool_lift(arg0 as u8)); + } + pub trait Guest { + fn set_in_preview(in_preview: bool); + } + #[doc(hidden)] + macro_rules! __export_component_pulumi_wasm_external_pulumi_settings_0_0_0_stable_dev_cabi { + ($ty:ident with_types_in $($path_to_types:tt)*) => { + const _ : () = { #[export_name = + "component:pulumi-wasm-external/pulumi-settings@0.0.0-STABLE-DEV#set-in-preview"] + unsafe extern "C" fn export_set_in_preview(arg0 : i32,) { + $($path_to_types)*:: _export_set_in_preview_cabi::<$ty > (arg0) } + }; + }; + } + #[doc(hidden)] + pub(crate) use __export_component_pulumi_wasm_external_pulumi_settings_0_0_0_stable_dev_cabi; + } + } } } mod _rt { @@ -1474,7 +1505,10 @@ macro_rules! __export_pulumi_wasm_impl { exports::component::pulumi_wasm::register_interface); $($path_to_types_root)*:: exports::component::pulumi_wasm::stack_interface::__export_component_pulumi_wasm_stack_interface_0_0_0_dev_cabi!($ty with_types_in $($path_to_types_root)*:: - exports::component::pulumi_wasm::stack_interface); + exports::component::pulumi_wasm::stack_interface); $($path_to_types_root)*:: + exports::component::pulumi_wasm_external::pulumi_settings::__export_component_pulumi_wasm_external_pulumi_settings_0_0_0_stable_dev_cabi!($ty + with_types_in $($path_to_types_root)*:: + exports::component::pulumi_wasm_external::pulumi_settings); }; } #[doc(inline)] @@ -1482,9 +1516,9 @@ pub(crate) use __export_pulumi_wasm_impl as export; #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.30.0:pulumi-wasm:encoded world"] #[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1775] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xed\x0c\x01A\x02\x01\ -A\x0b\x01B\x0a\x01m\x05\x05TRACE\x05DEBUG\x04INFO\x04WARN\x05ERROR\x04\0\x05leve\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1882] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xd8\x0d\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\x013component\ @@ -1517,9 +1551,11 @@ d\x02\x0bfunction-ids\x05values\x04\0\x1bfunction-invocation-request\x03\0\x03\x h\x01\x01r\x02\x02id\x05\x05values\x04\0\x1afunction-invocation-result\x03\0\x06\ \x01@\x02\x04names\x05value\x05\x01\0\x04\0\x0aadd-export\x01\x08\x01p\x07\x01p\x04\ \x01@\x01\x09functions\x09\0\x0a\x04\0\x06finish\x01\x0b\x04\x01/component:pulum\ -i-wasm/stack-interface@0.0.0-DEV\x05\x05\x04\x01+component:pulumi-wasm/pulumi-wa\ -sm@0.0.0-DEV\x04\0\x0b\x11\x01\0\x0bpulumi-wasm\x03\0\0\0G\x09producers\x01\x0cp\ -rocessed-by\x02\x0dwit-component\x070.215.0\x10wit-bindgen-rust\x060.30.0"; +i-wasm/stack-interface@0.0.0-DEV\x05\x05\x01B\x02\x01@\x01\x0ain-preview\x7f\x01\ +\0\x04\0\x0eset-in-preview\x01\0\x04\x01?component:pulumi-wasm-external/pulumi-s\ +ettings@0.0.0-STABLE-DEV\x05\x06\x04\x01+component:pulumi-wasm/pulumi-wasm@0.0.0\ +-DEV\x04\0\x0b\x11\x01\0\x0bpulumi-wasm\x03\0\0\0G\x09producers\x01\x0cprocessed\ +-by\x02\x0dwit-component\x070.215.0\x10wit-bindgen-rust\x060.30.0"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { diff --git a/pulumi_wasm/src/lib.rs b/pulumi_wasm/src/lib.rs index 80ec36a78..cc86f2d2a 100644 --- a/pulumi_wasm/src/lib.rs +++ b/pulumi_wasm/src/lib.rs @@ -16,6 +16,8 @@ use crate::bindings::exports::component::pulumi_wasm::stack_interface::{ use crate::bindings::exports::component::pulumi_wasm::{ output_interface, register_interface, stack_interface, }; +use crate::bindings::exports::component::pulumi_wasm_external::pulumi_settings; +use std::sync::atomic::{AtomicBool, Ordering}; bindings::export!(Component with_types_in bindings); @@ -74,11 +76,14 @@ impl stack_interface::Guest for Component { } }) .collect() + } +} - // vec![] +static GLOBAL_BOOL: AtomicBool = AtomicBool::new(false); - // true - // finalizer::finish() +impl pulumi_settings::Guest for Component { + fn set_in_preview(in_preview: bool) { + GLOBAL_BOOL.store(in_preview, Ordering::SeqCst); } } @@ -122,6 +127,7 @@ impl register_interface::Guest for Component { request.name.to_string(), object, outputs, + GLOBAL_BOOL.load(Ordering::SeqCst), ); RegisterResourceResult { @@ -153,10 +159,12 @@ impl register_interface::Guest for Component { }) .collect::>(); - let (_, field_outputs) = - refcell - .borrow_mut() - .create_resource_invoke_node(request.token, object, outputs); + let (_, field_outputs) = refcell.borrow_mut().create_resource_invoke_node( + request.token, + object, + outputs, + GLOBAL_BOOL.load(Ordering::SeqCst), + ); ResourceInvokeResult { fields: field_outputs diff --git a/pulumi_wasm_core/src/engine.rs b/pulumi_wasm_core/src/engine.rs index be8c2a24a..bc408123b 100644 --- a/pulumi_wasm_core/src/engine.rs +++ b/pulumi_wasm_core/src/engine.rs @@ -228,7 +228,7 @@ impl Engine { fn handle_creating_resources(&mut self) { let output = self .pulumi - .register_resource_poll(&self.register_resource_ids); + .poll_resource_operations(&self.register_resource_ids); for (output_id, response) in output { self.register_resource_ids.remove(&output_id); @@ -811,10 +811,11 @@ impl Engine { token: String, inputs: HashMap, outputs: HashSet, + in_preview: bool, ) -> (OutputId, HashMap) { let operation = ResourceRequestOperation::Invoke(ResourceInvokeRequestOperation::new(token)); - self.create_register_or_read_resource_node(operation, inputs, outputs) + self.create_register_or_read_resource_node(operation, inputs, outputs, in_preview) } pub fn create_register_resource_node( @@ -823,10 +824,11 @@ impl Engine { name: String, inputs: HashMap, outputs: HashSet, + in_preview: bool, ) -> (OutputId, HashMap) { let operation = ResourceRequestOperation::Register(RegisterResourceRequestOperation::new(r#type, name)); - self.create_register_or_read_resource_node(operation, inputs, outputs) + self.create_register_or_read_resource_node(operation, inputs, outputs, in_preview) } fn create_register_or_read_resource_node( @@ -834,6 +836,7 @@ impl Engine { operation: ResourceRequestOperation, inputs: HashMap, outputs: HashSet, + in_preview: bool, ) -> (OutputId, HashMap) { let output_id = Uuid::now_v7().into(); let node = @@ -848,7 +851,8 @@ impl Engine { let mut output_nodes = HashMap::new(); outputs.iter().for_each(|field_name| { - let extract_field_output = self.create_extract_field(field_name.clone(), output_id); + let extract_field_output = + self.create_extract_field(field_name.clone(), output_id, in_preview); output_nodes.insert(field_name.clone(), extract_field_output); }); @@ -859,9 +863,10 @@ impl Engine { &mut self, field_name: FieldName, source_output_id: OutputId, + in_preview: bool, ) -> OutputId { let output_id = Uuid::now_v7().into(); - let node = ExtractFieldNode::new(field_name); + let node = ExtractFieldNode::new(field_name, in_preview); let callback = Callback::extract_field(output_id); self.nodes .insert(output_id, EngineNode::ExtractField(node).into()); @@ -1046,7 +1051,7 @@ mod tests { let value = Value::Object([("key".into(), 1.into())].into_iter().collect()); let done_node_output_id = engine.create_done_node(value.clone()); let extract_field_node_output_id = - engine.create_extract_field("key".into(), done_node_output_id); + engine.create_extract_field("key".into(), done_node_output_id, true); let native_function_node_output_id = engine.create_native_function_node("func".into(), extract_field_node_output_id); @@ -1072,6 +1077,7 @@ mod tests { Set(Exists(1.into())), "key".into(), vec![NativeFunction(native_function_node_output_id)], + true, ) ); } @@ -1096,7 +1102,7 @@ mod tests { }; #[test] - fn should_create_required_nodes() { + fn should_create_required_nodes_during_preview() { let mut engine = Engine::new(MockPulumiService::new()); let done_node_output_id = engine.create_done_node(1.into()); let (register_resource_node_output_id, output_fields) = engine @@ -1105,6 +1111,7 @@ mod tests { "name".into(), HashMap::from([("input".into(), done_node_output_id)]), ["output".into()].into(), + true, ); assert_eq!(output_fields.len(), 1); @@ -1140,12 +1147,62 @@ mod tests { engine .get_extract_field(*output_fields.get(&"output".into()).unwrap()) .deref(), - &ExtractFieldNode::create(NotYetCalculated, "output".into(), vec![]) + &ExtractFieldNode::create(NotYetCalculated, "output".into(), vec![], true) ); } #[test] - fn should_handle_create_resource_node() { + fn should_create_required_nodes_during_execution() { + let mut engine = Engine::new(MockPulumiService::new()); + let done_node_output_id = engine.create_done_node(1.into()); + let (register_resource_node_output_id, output_fields) = engine + .create_register_resource_node( + "type".into(), + "name".into(), + HashMap::from([("input".into(), done_node_output_id)]), + ["output".into()].into(), + false, + ); + + assert_eq!(output_fields.len(), 1); + assert_eq!( + engine.get_done(done_node_output_id).deref(), + &DoneNode::create( + 1.into(), + vec![Callback::create_resource( + register_resource_node_output_id, + "input".into(), + )], + ) + ); + assert_eq!( + engine + .get_create_resource(register_resource_node_output_id) + .deref(), + &AbstractResourceNode::create( + NotYetCalculated, + ResourceRequestOperation::Register(RegisterResourceRequestOperation::new( + "type".into(), + "name".into() + )), + HashSet::from(["input".into()]), + HashMap::new(), + HashSet::from(["output".into()]), + vec![Callback::extract_field( + *output_fields.get(&"output".into()).unwrap() + )], + ) + ); + assert_eq!( + engine + .get_extract_field(*output_fields.get(&"output".into()).unwrap()) + .deref(), + &ExtractFieldNode::create(NotYetCalculated, "output".into(), vec![], false) + ); + } + + #[test] + fn should_create_resource_when_all_inputs_are_set() { let mut mock = MockPulumiService::new(); let register_resource_node_output_id_once_cell = Arc::new(OnceLock::new()); @@ -1174,7 +1231,7 @@ mod tests { let register_resource_node_output_id_once_cell_2 = register_resource_node_output_id_once_cell.clone(); - mock.expect_register_resource_poll() + mock.expect_poll_resource_operations() .times(1) .with(function(move |output_ids| { output_ids @@ -1202,6 +1259,7 @@ mod tests { "name".into(), HashMap::from([("input".into(), done_node_output_id)]), ["output".into()].into(), + true, ); register_resource_node_output_id_once_cell .set(register_resource_node_output_id) @@ -1213,6 +1271,189 @@ mod tests { assert_eq!(output_node.get_value(), &Value::Bool(true).into()); } } + mod invoke_resource { + use std::collections::{HashMap, HashSet}; + use std::ops::Deref; + use std::sync::{Arc, OnceLock}; + + use mockall::predicate::{eq, function}; + use serde_json::Value; + + use crate::engine::Engine; + use crate::model::MaybeNodeValue::NotYetCalculated; + use crate::nodes::{ + AbstractResourceNode, Callback, DoneNode, ExtractFieldNode, + ResourceInvokeRequestOperation, ResourceRequestOperation, + }; + use crate::pulumi::service::{ + MockPulumiService, PerformResourceRequest, RegisterResourceResponse, + }; + + #[test] + fn should_create_required_nodes_during_preview() { + let mut engine = Engine::new(MockPulumiService::new()); + let done_node_output_id = engine.create_done_node(1.into()); + let (invoke_resource_node_output_id, output_fields) = engine + .create_resource_invoke_node( + "token".into(), + HashMap::from([("input".into(), done_node_output_id)]), + ["output".into()].into(), + true, + ); + + assert_eq!(output_fields.len(), 1); + assert_eq!( + engine.get_done(done_node_output_id).deref(), + &DoneNode::create( + 1.into(), + vec![Callback::create_resource( + invoke_resource_node_output_id, + "input".into(), + )], + ) + ); + assert_eq!( + engine + .get_create_resource(invoke_resource_node_output_id) + .deref(), + &AbstractResourceNode::create( + NotYetCalculated, + ResourceRequestOperation::Invoke(ResourceInvokeRequestOperation::new( + "token".into(), + )), + HashSet::from(["input".into()]), + HashMap::new(), + HashSet::from(["output".into()]), + vec![Callback::extract_field( + *output_fields.get(&"output".into()).unwrap() + )], + ) + ); + assert_eq!( + engine + .get_extract_field(*output_fields.get(&"output".into()).unwrap()) + .deref(), + &ExtractFieldNode::create(NotYetCalculated, "output".into(), vec![], true) + ); + } + + #[test] + fn should_create_required_nodes_during_execution() { + let mut engine = Engine::new(MockPulumiService::new()); + let done_node_output_id = engine.create_done_node(1.into()); + let (invoke_resource_node_output_id, output_fields) = engine + .create_resource_invoke_node( + "token".into(), + HashMap::from([("input".into(), done_node_output_id)]), + ["output".into()].into(), + false, + ); + + assert_eq!(output_fields.len(), 1); + assert_eq!( + engine.get_done(done_node_output_id).deref(), + &DoneNode::create( + 1.into(), + vec![Callback::create_resource( + invoke_resource_node_output_id, + "input".into(), + )], + ) + ); + assert_eq!( + engine + .get_create_resource(invoke_resource_node_output_id) + .deref(), + &AbstractResourceNode::create( + NotYetCalculated, + ResourceRequestOperation::Invoke(ResourceInvokeRequestOperation::new( + "token".into(), + )), + HashSet::from(["input".into()]), + HashMap::new(), + HashSet::from(["output".into()]), + vec![Callback::extract_field( + *output_fields.get(&"output".into()).unwrap() + )], + ) + ); + assert_eq!( + engine + .get_extract_field(*output_fields.get(&"output".into()).unwrap()) + .deref(), + &ExtractFieldNode::create(NotYetCalculated, "output".into(), vec![], false) + ); + } + + #[test] + fn should_create_resource_when_all_inputs_are_set() { + let mut mock = MockPulumiService::new(); + + let invoke_resource_node_output_id_once_cell = Arc::new(OnceLock::new()); + + let invoke_resource_node_output_id_once_cell_2 = + invoke_resource_node_output_id_once_cell.clone(); + mock.expect_perform_resource_operation() + .times(1) + .with( + function(move |output_id| { + output_id + == invoke_resource_node_output_id_once_cell_2 + .deref() + .get() + .unwrap() + }), + eq(PerformResourceRequest { + operation: ResourceRequestOperation::Invoke( + ResourceInvokeRequestOperation::new("token".into()), + ), + object: HashMap::from([("input".into(), Some(1.into()))]), + expected_results: HashSet::from(["output".into()]), + }), + ) + .returning(|_, _| ()); + + let invoke_resource_node_output_id_once_cell_2 = + invoke_resource_node_output_id_once_cell.clone(); + mock.expect_poll_resource_operations() + .times(1) + .with(function(move |output_ids| { + output_ids + == &([*invoke_resource_node_output_id_once_cell_2 + .deref() + .get() + .unwrap()] + .into()) + })) + .returning(|output_ids| { + let output_id = output_ids.iter().next().unwrap(); + + HashMap::from([( + *output_id, + RegisterResourceResponse { + outputs: HashMap::from([("output".into(), true.into())]), + }, + )]) + }); + + let mut engine = Engine::new(mock); + let done_node_output_id = engine.create_done_node(1.into()); + let (invoke_resource_node_output_id, outputs) = engine.create_resource_invoke_node( + "token".into(), + HashMap::from([("input".into(), done_node_output_id)]), + ["output".into()].into(), + true, + ); + invoke_resource_node_output_id_once_cell + .set(invoke_resource_node_output_id) + .unwrap(); + let result = engine.run(HashMap::new()); + assert_eq!(result, None); + + let output_node = engine.get_extract_field(*outputs.get(&"output".into()).unwrap()); + assert_eq!(output_node.get_value(), &Value::Bool(true).into()); + } + } mod combine_outputs { use super::*; diff --git a/pulumi_wasm_core/src/nodes.rs b/pulumi_wasm_core/src/nodes.rs index 80470b202..5d8d3df36 100644 --- a/pulumi_wasm_core/src/nodes.rs +++ b/pulumi_wasm_core/src/nodes.rs @@ -261,6 +261,7 @@ pub(crate) struct ExtractFieldNode { value: MaybeNodeValue, field_name: FieldName, callbacks: Vec, + in_preview: bool, } impl ExtractFieldNode { @@ -268,16 +269,18 @@ impl ExtractFieldNode { value: MaybeNodeValue, field_name: FieldName, callbacks: Vec, + in_preview: bool, ) -> Self { Self { value, field_name, callbacks, + in_preview, } } - pub(crate) fn new(field_name: FieldName) -> ExtractFieldNode { - Self::create(NotYetCalculated, field_name, Vec::new()) + pub(crate) fn new(field_name: FieldName, in_preview: bool) -> ExtractFieldNode { + Self::create(NotYetCalculated, field_name, Vec::new(), in_preview) } pub(crate) fn add_callback(&mut self, callback: Callback) { @@ -303,8 +306,10 @@ impl ExtractFieldNode { NodeValue::Exists(Value::Object(map)) => { let key: Value = self.field_name.as_string().clone().into(); let value = map.iter().find(|(k, _)| *k == &key).map(|(_, v)| v.clone()); + let in_preview = self.in_preview; let new_node_value = match value { - None => NodeValue::Nothing, + None if in_preview => NodeValue::Nothing, + None => NodeValue::Exists(Value::Null), Some(v) => NodeValue::Exists(v), }; self.value = Set(new_node_value.clone()); diff --git a/pulumi_wasm_core/src/pulumi/service.rs b/pulumi_wasm_core/src/pulumi/service.rs index 512769906..e4cb122f9 100644 --- a/pulumi_wasm_core/src/pulumi/service.rs +++ b/pulumi_wasm_core/src/pulumi/service.rs @@ -11,7 +11,7 @@ pub trait PulumiService { fn get_root_resource(&self) -> String; fn register_outputs(&self, outputs: HashMap); fn perform_resource_operation(&self, output_id: OutputId, request: PerformResourceRequest); - fn register_resource_poll( + fn poll_resource_operations( &self, register_ids: &HashSet, ) -> HashMap; diff --git a/pulumi_wasm_core/src/pulumi/service_impl.rs b/pulumi_wasm_core/src/pulumi/service_impl.rs index 099411039..a58053b64 100644 --- a/pulumi_wasm_core/src/pulumi/service_impl.rs +++ b/pulumi_wasm_core/src/pulumi/service_impl.rs @@ -148,7 +148,7 @@ impl PulumiService for PulumiServiceImpl { } } - fn register_resource_poll( + fn poll_resource_operations( &self, _register_ids: &HashSet, ) -> HashMap { diff --git a/pulumi_wasm_generator_lib/src/output/provider/lib.rs.handlebars b/pulumi_wasm_generator_lib/src/output/provider/lib.rs.handlebars index e854d56a2..88774a054 100644 --- a/pulumi_wasm_generator_lib/src/output/provider/lib.rs.handlebars +++ b/pulumi_wasm_generator_lib/src/output/provider/lib.rs.handlebars @@ -7,6 +7,7 @@ mod function; {{#if package.contains_resources_or_functions}} #[allow(unused_braces)] +#[allow(unused_imports)] mod bindings; bindings::export!(Component with_types_in bindings); {{/if}} diff --git a/pulumi_wasm_generator_lib/tests/output/functions-secrets/provider/src/lib.rs b/pulumi_wasm_generator_lib/tests/output/functions-secrets/provider/src/lib.rs index 74e86404d..04bc47418 100644 --- a/pulumi_wasm_generator_lib/tests/output/functions-secrets/provider/src/lib.rs +++ b/pulumi_wasm_generator_lib/tests/output/functions-secrets/provider/src/lib.rs @@ -2,6 +2,7 @@ mod resource; mod function; #[allow(unused_braces)] +#[allow(unused_imports)] mod bindings; bindings::export!(Component with_types_in bindings); diff --git a/pulumi_wasm_runner/src/main.rs b/pulumi_wasm_runner/src/main.rs index 2c595a00b..691153523 100644 --- a/pulumi_wasm_runner/src/main.rs +++ b/pulumi_wasm_runner/src/main.rs @@ -125,6 +125,11 @@ async fn main() -> Result<(), Error> { let pulumi_monitor_url = std::env::var("PULUMI_MONITOR")?; let pulumi_stack = std::env::var("PULUMI_STACK")?; let pulumi_project = std::env::var("PULUMI_PROJECT")?; + let pulumi_preview = match std::env::var("PULUMI_DRY_RUN") { + Ok(preview) if preview == "true" => true, + Ok(preview) if preview == "false" => false, + _ => false, + }; let mut pulumi = Pulumi::create( wasm, @@ -134,6 +139,7 @@ async fn main() -> Result<(), Error> { pulumi_project, ) .await?; + pulumi.set_preview(pulumi_preview).await?; log::info!("Creating root stack"); pulumi.create_root_stack().await?; log::info!("Created root stack. Invoking main"); diff --git a/pulumi_wasm_runner/src/pulumi.rs b/pulumi_wasm_runner/src/pulumi.rs index 3d162cec0..220216814 100644 --- a/pulumi_wasm_runner/src/pulumi.rs +++ b/pulumi_wasm_runner/src/pulumi.rs @@ -280,6 +280,13 @@ impl Pulumi { component.serialize() } + pub async fn set_preview(&mut self, in_preview: bool) -> Result<(), Error> { + self.plugin + .component_pulumi_wasm_external_pulumi_settings() + .call_set_in_preview(&mut self.store, in_preview) + .await + } + pub async fn create_root_stack(&mut self) -> Result<(), Error> { let request = RegisterResourceRequest { r#type: "pulumi:pulumi:Stack".to_string(), diff --git a/pulumi_wasm_runner_component_creator/src/lib.rs b/pulumi_wasm_runner_component_creator/src/lib.rs index 677ab036d..c63994c55 100644 --- a/pulumi_wasm_runner_component_creator/src/lib.rs +++ b/pulumi_wasm_runner_component_creator/src/lib.rs @@ -171,6 +171,15 @@ pub async fn create( .export(pulumi_main_export, pulumi_main_component_name) .unwrap(); + let pulumi_main_component_name = + "component:pulumi-wasm-external/pulumi-settings@0.0.0-STABLE-DEV"; + let pulumi_main_export = graph + .alias_instance_export(pulumi_wasm_instance, pulumi_main_component_name) + .unwrap(); + graph + .export(pulumi_main_export, pulumi_main_component_name) + .unwrap(); + Ok(graph.encode(EncodeOptions::default()).unwrap()) } diff --git a/pulumi_wasm_runner_component_creator/tests/create_final_component_test.rs b/pulumi_wasm_runner_component_creator/tests/create_final_component_test.rs index 270b041bc..73f31746e 100644 --- a/pulumi_wasm_runner_component_creator/tests/create_final_component_test.rs +++ b/pulumi_wasm_runner_component_creator/tests/create_final_component_test.rs @@ -25,6 +25,7 @@ async fn should_combine_wasm_components() -> Result<()> { world root { import component:pulumi-wasm/output-interface@0.0.0-DEV; export component:pulumi-wasm-external/pulumi-main@0.0.0-STABLE-DEV; + import component:pulumi-wasm-external/pulumi-settings@0.0.0-STABLE-DEV; import pulumi:docker/container@4.5.3--0.0.0-DEV; } "#, @@ -54,7 +55,7 @@ async fn should_combine_wasm_components() -> Result<()> { .await .unwrap(); - assert_component_only_exports_main(&result)?; + assert_component_only_exports_main_and_settings(&result)?; Ok(()) } @@ -230,7 +231,7 @@ async fn return_error_when_multiple_versions_of_pulumi_wasm_in_providers_is_foun Ok(()) } -fn assert_component_only_exports_main(result: &[u8]) -> Result<()> { +fn assert_component_only_exports_main_and_settings(result: &[u8]) -> Result<()> { let mut graph = CompositionGraph::new(); let main = Package::from_bytes("main", None, result, graph.types_mut()).unwrap(); let main_package_id = graph.register_package(main.clone()).unwrap(); @@ -243,7 +244,10 @@ fn assert_component_only_exports_main(result: &[u8]) -> Result<()> { assert_eq!( exports_names, - vec!["component:pulumi-wasm-external/pulumi-main@0.0.0-STABLE-DEV".to_string()] + vec![ + "component:pulumi-wasm-external/pulumi-main@0.0.0-STABLE-DEV".to_string(), + "component:pulumi-wasm-external/pulumi-settings@0.0.0-STABLE-DEV".to_string() + ] ); let imports_names: Vec<_> = graph.types()[graph[main_package_id].ty()] @@ -262,6 +266,7 @@ struct TestProgramSource {} impl PulumiWasmSource for TestProgramSource { async fn get(&self, version: &str, debug: bool) -> Result> { let mut resolve = Resolve::new(); + resolve.add_pulumi_wasm_stable().unwrap(); let pkg = resolve.add_pulumi_wasm(version).unwrap(); let world = resolve.select_world(pkg, None).unwrap(); @@ -290,9 +295,10 @@ impl DefaultProviderSource for TestDefaultProviderSource { provider_name: &str, provider_version: &str, pulumi_wasm_version: &str, - debug: bool, + _debug: bool, ) -> Result> { let mut resolve = Resolve::new(); + resolve.add_pulumi_wasm_stable().unwrap(); resolve.add_pulumi_wasm(pulumi_wasm_version).unwrap(); let pkg = resolve .add_provider(provider_name, provider_version, pulumi_wasm_version) @@ -365,6 +371,7 @@ impl ResolveExt for Resolve { package component:pulumi-wasm@{pulumi_wasm_version}; world root {{ + export component:pulumi-wasm-external/pulumi-settings@0.0.0-STABLE-DEV; export output-interface; }} @@ -396,6 +403,11 @@ impl ResolveExt for Resolve { interface pulumi-main { main: func(); } + + interface pulumi-settings { + set-in-preview: func(in-preview: bool); + } + "#, )?; diff --git a/pulumi_wasm_wit/wit/deps/pulumi-wasm-external.wit b/pulumi_wasm_wit/wit/deps/pulumi-wasm-external.wit index 0f674392e..1a8495ac7 100644 --- a/pulumi_wasm_wit/wit/deps/pulumi-wasm-external.wit +++ b/pulumi_wasm_wit/wit/deps/pulumi-wasm-external.wit @@ -48,6 +48,10 @@ interface log { log: func(content: content); } +interface pulumi-settings { + set-in-preview: func(in-preview: bool); +} + interface pulumi-main { main: func(); } diff --git a/pulumi_wasm_wit/wit/world.wit b/pulumi_wasm_wit/wit/world.wit index 259a11691..f2978d22a 100644 --- a/pulumi_wasm_wit/wit/world.wit +++ b/pulumi_wasm_wit/wit/world.wit @@ -4,6 +4,7 @@ world pulumi-wasm { export output-interface; export register-interface; export stack-interface; + export component:pulumi-wasm-external/pulumi-settings@0.0.0-STABLE-DEV; import component:pulumi-wasm-external/log@0.0.0-STABLE-DEV; import component:pulumi-wasm-external/external-world@0.0.0-STABLE-DEV; @@ -21,6 +22,7 @@ world logger { world runner { export component:pulumi-wasm-external/pulumi-main@0.0.0-STABLE-DEV; + export component:pulumi-wasm-external/pulumi-settings@0.0.0-STABLE-DEV; import component:pulumi-wasm-external/external-world@0.0.0-STABLE-DEV; import component:pulumi-wasm-external/log@0.0.0-STABLE-DEV; }