#!/usr/bin/env python # Copyright 2016-2018, Pulumi Corporation. All rights reserved. import argparse import asyncio import logging import os import sys import traceback import runpy # The user might not have installed Pulumi yet in their environment - provide a high-quality error message in that case. try: import pulumi import pulumi.runtime except ImportError: # For whatever reason, sys.stderr.write is not picked up by the engine as a message, but 'print' is. The Python # langhost automatically flushes stdout and stderr on shutdown, so we don't need to do it here - just trust that # Python does the sane thing when printing to stderr. print(traceback.format_exc(), file=sys.stderr) print(""" It looks like the Pulumi SDK has not been installed. Have you run pip install? If you are running in a virtualenv, you must run pip install -r requirements.txt from inside the virtualenv.""", file=sys.stderr) sys.exit(1) if __name__ == "__main__": # Parse the arguments, program name, and optional arguments. ap = argparse.ArgumentParser(description='Execute a Pulumi Python program') ap.add_argument('--project', help='Set the project name') ap.add_argument('--stack', help='Set the stack name') ap.add_argument('--parallel', help='Run P resource operations in parallel (default=none)') ap.add_argument('--dry_run', help='Simulate resource changes, but without making them') ap.add_argument('--pwd', help='Change the working directory before running the program') ap.add_argument('--monitor', help='An RPC address for the resource monitor to connect to') ap.add_argument('--engine', help='An RPC address for the engine to connect to') ap.add_argument('--tracing', help='A Zipkin-compatible endpoint to send tracing data to') ap.add_argument('PROGRAM', help='The Python program to run') ap.add_argument('ARGS', help='Arguments to pass to the program', nargs='*') args = ap.parse_args() # If any config variables are present, parse and set them, so subsequent accesses are fast. config_env = pulumi.runtime.get_config_env() for k, v in config_env.items(): pulumi.runtime.set_config(k, v) # Configure the runtime so that the user program hooks up to Pulumi as appropriate. pulumi.runtime.configure( pulumi.runtime.Settings( monitor=args.monitor, engine=args.engine, project=args.project, stack=args.stack, parallel=int(args.parallel), dry_run=args.dry_run == "true" ) ) # Finally, swap in the args, chdir if needed, and run the program as if it had been executed directly. sys.argv = [args.PROGRAM] + args.ARGS if not args.pwd is None: os.chdir(args.pwd) successful = False loop = asyncio.get_event_loop() # We are (unfortunately) suppressing the log output of asyncio to avoid showing to users some of the bad things we # do in our programming model. # # Fundamentally, Pulumi is a way for users to build asynchronous dataflow graphs that, as their deployments # progress, resolve naturally and eventually result in the complete resolution of the graph. If one node in the # graph fails (i.e. a resource fails to create, there's an exception in an apply, etc.), part of the graph remains # unevaluated at the time that we exit. # # asyncio abhors this. It gets very upset if the process terminates without having observed every future that we # have resolved. If we are terminating abnormally, it is highly likely that we are not going to observe every single # future that we have created. Furthermore, it's *harmless* to do this - asyncio logs errors because it thinks it # needs to tell users that they're doing bad things (which, to their credit, they are), but we are doing this # deliberately. # # In order to paper over this for our users, we simply turn off the logger for asyncio. Users won't see any asyncio # error messages, but if they stick to the Pulumi programming model, they wouldn't be seeing any anyway. logging.getLogger("asyncio").setLevel(logging.CRITICAL) try: coro = pulumi.runtime.run_in_stack(lambda: runpy.run_path(args.PROGRAM, run_name='__main__')) loop.run_until_complete(coro) successful = True except pulumi.RunError as e: pulumi.log.error(str(e)) except Exception as e: pulumi.log.error("Program failed with an unhandled exception:") pulumi.log.error(traceback.format_exc()) finally: loop.close() sys.stdout.flush() sys.stderr.flush() exit_code = 0 if successful else 1 sys.exit(exit_code)