mirror of https://github.com/pulumi/pulumi.git
100 lines
4.6 KiB
Python
100 lines
4.6 KiB
Python
#!/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)
|
|
|