def test_compilation_error_dotnet(self): stack_name = stack_namer(compilation_error_project) project_dir = test_path("errors", compilation_error_project, "dotnet") stack = create_stack(stack_name, work_dir=project_dir) try: self.assertRaises(CompilationError, stack.up) self.assertRaisesRegex(CompilationError, "Build FAILED.", stack.up) finally: stack.workspace.remove_stack(stack_name)
def test_project_settings_respected(self): project_name = "correct_project" stack_name = stack_namer(project_name) stack = create_stack(stack_name, program=pulumi_program, project_name=project_name, opts=LocalWorkspaceOptions(work_dir=test_path("data", project_name))) project_settings = stack.workspace.project_settings() self.assertEqual(project_settings.description, "This is a description") stack.workspace.remove_stack(stack_name)
def test_compilation_error_go(self): stack_name = stack_namer(compilation_error_project) project_dir = test_path("errors", compilation_error_project, "go") stack = create_stack(stack_name, work_dir=project_dir) try: self.assertRaises(CompilationError, stack.up) self.assertRaisesRegex(CompilationError, ": syntax error:|: undefined:", stack.up) finally: stack.workspace.remove_stack(stack_name)
def test_structured_events(self): project_name = "structured_events" stack_name = stack_namer(project_name) stack = create_stack(stack_name, program=pulumi_program, project_name=project_name) stack_config: ConfigMap = { "bar": ConfigValue(value="abc"), "buzz": ConfigValue(value="secret", secret=True) } try: stack.set_all_config(stack_config) # can't mutate a bool from the callback, so use a single-item list seen_summary_event = [False] def find_summary_event(event: EngineEvent): if event.summary_event: seen_summary_event[0] = True # pulumi up up_res = stack.up(on_event=find_summary_event) self.assertEqual(seen_summary_event[0], True, "No SummaryEvent for `up`") self.assertEqual(up_res.summary.kind, "update") self.assertEqual(up_res.summary.result, "succeeded") # pulumi preview seen_summary_event[0] = False pre_res = stack.preview(on_event=find_summary_event) self.assertEqual(seen_summary_event[0], True, "No SummaryEvent for `preview`") self.assertEqual(pre_res.change_summary.get(OpType.SAME), 1) # pulumi refresh seen_summary_event[0] = False refresh_res = stack.refresh(on_event=find_summary_event) self.assertEqual(seen_summary_event[0], True, "No SummaryEvent for `refresh`") self.assertEqual(refresh_res.summary.kind, "refresh") self.assertEqual(refresh_res.summary.result, "succeeded") # pulumi destroy seen_summary_event[0] = False destroy_res = stack.destroy(on_event=find_summary_event) self.assertEqual(seen_summary_event[0], True, "No SummaryEvent for `destroy`") self.assertEqual(destroy_res.summary.kind, "destroy") self.assertEqual(destroy_res.summary.result, "succeeded") finally: stack.workspace.remove_stack(stack_name)
def test_compilation_error_typescript(self): stack_name = stack_namer(compilation_error_project) project_dir = test_path("errors", compilation_error_project, "typescript") subprocess.run(["npm", "install"], check=True, cwd=project_dir, capture_output=True) stack = create_stack(stack_name, work_dir=project_dir) try: self.assertRaises(CompilationError, stack.up) self.assertRaisesRegex(CompilationError, "Unable to compile TypeScript", stack.up) finally: stack.workspace.remove_stack(stack_name)
def test_inline_runtime_error_python(self): project_name = "inline_runtime_error_python" stack_name = stack_namer(project_name) stack = create_stack(stack_name, program=failing_program, project_name=project_name) inline_error_text = "python inline source runtime error" try: self.assertRaises(InlineSourceRuntimeError, stack.up) self.assertRaisesRegex(InlineSourceRuntimeError, inline_error_text, stack.up) self.assertRaises(InlineSourceRuntimeError, stack.preview) self.assertRaisesRegex(InlineSourceRuntimeError, inline_error_text, stack.preview) finally: stack.workspace.remove_stack(stack_name)
def test_supports_stack_outputs(self): project_name = "inline_python" stack_name = stack_namer(project_name) stack = create_stack(stack_name, program=pulumi_program, project_name=project_name) stack_config: ConfigMap = { "bar": ConfigValue(value="abc"), "buzz": ConfigValue(value="secret", secret=True) } def assert_outputs(outputs): self.assertEqual(len(outputs), 3) self.assertEqual(outputs["exp_static"].value, "foo") self.assertFalse(outputs["exp_static"].secret) self.assertEqual(outputs["exp_cfg"].value, "abc") self.assertFalse(outputs["exp_cfg"].secret) self.assertEqual(outputs["exp_secret"].value, "secret") self.assertTrue(outputs["exp_secret"].secret) try: stack.set_all_config(stack_config) initial_outputs = stack.outputs() self.assertEqual(len(initial_outputs), 0) # pulumi up up_res = stack.up() self.assertEqual(up_res.summary.kind, "update") self.assertEqual(up_res.summary.result, "succeeded") assert_outputs(up_res.outputs) outputs_after_up = stack.outputs() assert_outputs(outputs_after_up) # pulumi destroy destroy_res = stack.destroy() self.assertEqual(destroy_res.summary.kind, "destroy") self.assertEqual(destroy_res.summary.result, "succeeded") outputs_after_destroy = stack.outputs() self.assertEqual(len(outputs_after_destroy), 0) finally: stack.workspace.remove_stack(stack_name)
def test_stack_lifecycle_inline_program(self): project_name = "inline_python" stack_name = stack_namer(project_name) stack = create_stack(stack_name, program=pulumi_program, project_name=project_name) stack_config: ConfigMap = { "bar": ConfigValue(value="abc"), "buzz": ConfigValue(value="secret", secret=True) } try: stack.set_all_config(stack_config) # pulumi up up_res = stack.up() self.assertEqual(len(up_res.outputs), 3) self.assertEqual(up_res.outputs["exp_static"].value, "foo") self.assertFalse(up_res.outputs["exp_static"].secret) self.assertEqual(up_res.outputs["exp_cfg"].value, "abc") self.assertFalse(up_res.outputs["exp_cfg"].secret) self.assertEqual(up_res.outputs["exp_secret"].value, "secret") self.assertTrue(up_res.outputs["exp_secret"].secret) self.assertEqual(up_res.summary.kind, "update") self.assertEqual(up_res.summary.result, "succeeded") # pulumi preview preview_result = stack.preview() self.assertEqual(preview_result.change_summary.get(OpType.SAME), 1) # pulumi refresh refresh_res = stack.refresh() self.assertEqual(refresh_res.summary.kind, "refresh") self.assertEqual(refresh_res.summary.result, "succeeded") # pulumi destroy destroy_res = stack.destroy() self.assertEqual(destroy_res.summary.kind, "destroy") self.assertEqual(destroy_res.summary.result, "succeeded") finally: stack.workspace.remove_stack(stack_name)
def check_isolation(minimal=False): stack_name = f'isolation-test-{uuid.uuid4()}' stack = automation.create_stack(stack_name=stack_name, project_name='isolation-test', program=program) with pytest.raises(automation.errors.CommandError): stack.set_config('bad', automation.ConfigValue('1')) stack.up(on_output=ignore) if not minimal: stack.set_config('bad', automation.ConfigValue('0')) stack.up(on_output=ignore) destroy_res = stack.destroy() assert destroy_res.summary.kind == "destroy" assert destroy_res.summary.result == "succeeded" stack.workspace.remove_stack(stack_name)
def create_handler(): """creates new sites""" stack_name = request.json.get('id') content = request.json.get('content') try: def pulumi_program(): return create_pulumi_program(content) # create a new stack, generating our pulumi program on the fly from the POST body stack = auto.create_stack(stack_name=stack_name, project_name=project_name, program=pulumi_program) stack.set_config("aws:region", auto.ConfigValue("us-west-2")) # deploy the stack, tailing the logs to stdout up_res = stack.up(on_output=print) return jsonify(id=stack_name, url=up_res.outputs['website_url'].value) except auto.StackAlreadyExistsError: return make_response(f"stack '{stack_name}' already exists", 409) except Exception as exn: return make_response(str(exn), 500)
async def test_parallel_updates(): first_stack_name = f"stack-{uuid.uuid4()}" second_stack_name = f"stack-{uuid.uuid4()}" stacks = [ automation.create_stack(stack_name, project_name='test-parallel', program=program) for stack_name in {first_stack_name, second_stack_name} ] stack_up_responses = await asyncio.gather( *[async_stack_up(stack) for stack in stacks]) assert all({ stack_response.summary.result == "succeeded" for stack_response in stack_up_responses }) stack_destroy_responses = await asyncio.gather( *[async_stack_destroy(stack) for stack in stacks]) assert all({ stack_response.summary.result == "succeeded" for stack_response in stack_destroy_responses })
def test_stack_lifecycle_local_program(self): project_name = "testproj" stack_name = stack_namer(project_name) work_dir = test_path("data", project_name) stack = create_stack(stack_name, work_dir=work_dir) self.assertIsNone(print(stack)) config: ConfigMap = { "bar": ConfigValue(value="abc"), "buzz": ConfigValue(value="secret", secret=True) } stack.set_all_config(config) # pulumi up up_res = stack.up() self.assertEqual(len(up_res.outputs), 3) self.assertEqual(up_res.outputs["exp_static"].value, "foo") self.assertFalse(up_res.outputs["exp_static"].secret) self.assertEqual(up_res.outputs["exp_cfg"].value, "abc") self.assertFalse(up_res.outputs["exp_cfg"].secret) self.assertEqual(up_res.outputs["exp_secret"].value, "secret") self.assertTrue(up_res.outputs["exp_secret"].secret) self.assertEqual(up_res.summary.kind, "update") self.assertEqual(up_res.summary.result, "succeeded") # pulumi preview preview_result = stack.preview() self.assertEqual(preview_result.change_summary.get(OpType.SAME), 1) # pulumi refresh refresh_res = stack.refresh() self.assertEqual(refresh_res.summary.kind, "refresh") self.assertEqual(refresh_res.summary.result, "succeeded") # pulumi destroy destroy_res = stack.destroy() self.assertEqual(destroy_res.summary.kind, "destroy") self.assertEqual(destroy_res.summary.result, "succeeded") stack.workspace.remove_stack(stack_name)
def test_runtime_errors(self): for lang in ["python", "go", "dotnet", "javascript", "typescript"]: stack_name = stack_namer(runtime_error_project) project_dir = test_path("errors", runtime_error_project, lang) if lang in ["javascript", "typescript"]: subprocess.run(["npm", "install"], check=True, cwd=project_dir, capture_output=True) if lang == "python": subprocess.run(["python3", "-m", "venv", "venv"], check=True, cwd=project_dir, capture_output=True) subprocess.run([ os.path.join("venv", "bin", "pip"), "install", "-r", "requirements.txt" ], check=True, cwd=project_dir, capture_output=True) stack = create_stack(stack_name, work_dir=project_dir) try: self.assertRaises(RuntimeError, stack.up) if lang == "go": self.assertRaisesRegex(RuntimeError, "panic: runtime error", stack.up) else: self.assertRaisesRegex( RuntimeError, "failed with an unhandled exception", stack.up) finally: stack.workspace.remove_stack(stack_name)
def test_secret_config_warnings(self): def program(): config = Config() config.get("plainstr1") config.require("plainstr2") config.get_secret("plainstr3") config.require_secret("plainstr4") config.get_bool("plainbool1") config.require_bool("plainbool2") config.get_secret_bool("plainbool3") config.require_secret_bool("plainbool4") config.get_int("plainint1") config.require_int("plainint2") config.get_secret_int("plainint3") config.require_secret_int("plainint4") config.get_float("plainfloat1") config.require_float("plainfloat2") config.get_secret_float("plainfloat3") config.require_secret_float("plainfloat4") config.get_object("plainobj1") config.require_object("plainobj2") config.get_secret_object("plainobj3") config.require_secret_object("plainobj4") config.get("str1") config.require("str2") config.get_secret("str3") config.require_secret("str4") config.get_bool("bool1") config.require_bool("bool2") config.get_secret_bool("bool3") config.require_secret_bool("bool4") config.get_int("int1") config.require_int("int2") config.get_secret_int("int3") config.require_secret_int("int4") config.get_float("float1") config.require_float("float2") config.get_secret_float("float3") config.require_secret_float("float4") config.get_object("obj1") config.require_object("obj2") config.get_secret_object("obj3") config.require_secret_object("obj4") project_name = "inline_python" stack_name = stack_namer(project_name) stack = create_stack(stack_name, program=program, project_name=project_name) stack_config: ConfigMap = { "plainstr1": ConfigValue(value="1"), "plainstr2": ConfigValue(value="2"), "plainstr3": ConfigValue(value="3"), "plainstr4": ConfigValue(value="4"), "plainbool1": ConfigValue(value="true"), "plainbool2": ConfigValue(value="true"), "plainbool3": ConfigValue(value="true"), "plainbool4": ConfigValue(value="true"), "plainint1": ConfigValue(value="1"), "plainint2": ConfigValue(value="2"), "plainint3": ConfigValue(value="3"), "plainint4": ConfigValue(value="4"), "plainfloat1": ConfigValue(value="1.1"), "plainfloat2": ConfigValue(value="2.2"), "plainfloat3": ConfigValue(value="3.3"), "plainfloat4": ConfigValue(value="4.3"), "plainobj1": ConfigValue(value="{}"), "plainobj2": ConfigValue(value="{}"), "plainobj3": ConfigValue(value="{}"), "plainobj4": ConfigValue(value="{}"), "str1": ConfigValue(value="1", secret=True), "str2": ConfigValue(value="2", secret=True), "str3": ConfigValue(value="3", secret=True), "str4": ConfigValue(value="4", secret=True), "bool1": ConfigValue(value="true", secret=True), "bool2": ConfigValue(value="true", secret=True), "bool3": ConfigValue(value="true", secret=True), "bool4": ConfigValue(value="true", secret=True), "int1": ConfigValue(value="1", secret=True), "int2": ConfigValue(value="2", secret=True), "int3": ConfigValue(value="3", secret=True), "int4": ConfigValue(value="4", secret=True), "float1": ConfigValue(value="1.1", secret=True), "float2": ConfigValue(value="2.2", secret=True), "float3": ConfigValue(value="3.3", secret=True), "float4": ConfigValue(value="4.4", secret=True), "obj1": ConfigValue(value="{}", secret=True), "obj2": ConfigValue(value="{}", secret=True), "obj3": ConfigValue(value="{}", secret=True), "obj4": ConfigValue(value="{}", secret=True), } try: stack.set_all_config(stack_config) events: List[str] = [] def find_diagnostic_events(event: EngineEvent): if event.diagnostic_event and event.diagnostic_event.severity == "warning": events.append(event.diagnostic_event.message) expected_warnings = [ "Configuration 'inline_python:str1' value is a secret; use `get_secret` instead of `get`", "Configuration 'inline_python:str2' value is a secret; use `require_secret` instead of `require`", "Configuration 'inline_python:bool1' value is a secret; use `get_secret_bool` instead of `get_bool`", "Configuration 'inline_python:bool2' value is a secret; use `require_secret_bool` instead of `require_bool`", "Configuration 'inline_python:int1' value is a secret; use `get_secret_int` instead of `get_int`", "Configuration 'inline_python:int2' value is a secret; use `require_secret_int` instead of `require_int`", "Configuration 'inline_python:float1' value is a secret; use `get_secret_float` instead of `get_float`", "Configuration 'inline_python:float2' value is a secret; use `require_secret_float` instead of `require_float`", "Configuration 'inline_python:obj1' value is a secret; use `get_secret_object` instead of `get_object`", "Configuration 'inline_python:obj2' value is a secret; use `require_secret_object` instead of `require_object`", ] # These keys should not be in any warning messages. unexpected_warnings = [ "plainstr1", "plainstr2", "plainstr3", "plainstr4", "plainbool1", "plainbool2", "plainbool3", "plainbool4", "plainint1", "plainint2", "plainint3", "plainint4", "plainfloat1", "plainfloat2", "plainfloat3", "plainfloat4", "plainobj1", "plainobj2", "plainobj3", "plainobj4", "str3", "str4", "bool3", "bool4", "int3", "int4", "float3", "float4", "obj3", "obj4", ] def validate(warnings: List[str]): for expected in expected_warnings: found = False for warning in warnings: if expected in warning: found = True break self.assertTrue(found, "expected warning not found") for unexpected in unexpected_warnings: for warning in warnings: self.assertFalse( unexpected in warning, f"Unexpected ${unexpected}' found in warning") # pulumi preview stack.preview(on_event=find_diagnostic_events) validate(events) # pulumi up events = [] stack.up(on_event=find_diagnostic_events) validate(events) finally: stack.workspace.remove_stack(stack_name)