#!/usr/bin/python2.7
#==============================================================================
# Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#==============================================================================
import cfnbootstrap
from cfnbootstrap.cfn_client import CloudFormationClient
from cfnbootstrap import util
from optparse import OptionParser
from optparse import OptionGroup
import base64
import re
from cfnbootstrap.packages import requests
import socket
import sys
try:
import simplejson as json
except ImportError:
import json
default_id = util.get_instance_id()
if not default_id:
default_id = socket.getfqdn()
parser = OptionParser(usage="usage: %prog [options] [WaitConditionHandle URL]")
parser.add_option_group(util.get_cred_options(parser))
parser.add_option_group(util.get_proxy_options(parser))
parser.add_option("-s", "--success", help="If true, signal success to CloudFormation; if false, signal failure. Default: true",
dest="success", type="choice", choices=["true", "false"], action="store", default="true")
parser.add_option("-i", "--id", help="A unique ID to send with the signal",
dest="id", type="string", action="store", default=default_id)
parser.add_option("-e", "--exit-code", help="Derive success or failure from specified exit code",
dest="exit_code", type="int", action="store")
wch_group = OptionGroup(parser, "WaitConditionHandle Signal Options")
wch_group.add_option("-r", "--reason", help="The reason for success/failure",
dest="reason", type="string", action="store", default="")
wch_group.add_option("-d", "--data", help="Data to include with the WaitCondition signal",
dest="data", type="string", action="store", default="")
parser.add_option_group(wch_group)
rs_group = OptionGroup(parser, "Resource Signal Options")
rs_group.add_option("", "--stack", help="A CloudFormation stack",
dest="stack_name", type="string", action="store")
rs_group.add_option("", "--resource", help="A CloudFormation logical resource ID",
dest="logical_resource_id", type="string", action="store")
rs_group.add_option("", "--url", help="The CloudFormation service URL. The endpoint URL must match the region option. Use of this parameter is discouraged.",
dest="endpoint", type="string", action="store")
rs_group.add_option("", "--region", help="The CloudFormation region. Default: us-east-1.",
dest="region", type="string", default="us-east-1")
parser.add_option_group(rs_group)
(options, args) = parser.parse_args()
signal_success = True
if options.exit_code:
signal_success = False
elif options.success != "true":
signal_success = False
cfnbootstrap.configureLogging("DEBUG")
def get_status(signal_success):
return 'SUCCESS' if signal_success else 'FAILURE'
@util.retry_on_failure()
@util.timeout()
def send(url, data):
requests.put(url,
data=json.dumps(data),
headers={"Content-Type" : ""},
verify=True,
proxies=util.get_proxyinfo(options)).raise_for_status()
def signal_wch(options, args):
if not options.reason and not signal_success:
options.reason="Configuration failed."
data = { 'Status': get_status(signal_success),
'Reason' : options.reason,
'Data' : options.data,
'UniqueId' : options.id }
try:
url = args[0] if re.match(r'https?://.*', args[0]) else base64.b64decode(args[0])
except TypeError:
print >> sys.stderr, "Error: Invalid WaitConditionHandle URL specified: %s" % args[0]
sys.exit(1)
if not re.match(r'https?://.*', url):
print >> sys.stderr, "Error: Invalid WaitConditionHandle URL specified: %s" % args[0]
sys.exit(1)
try:
send(url, data)
print 'CloudFormation signaled successfully with %s.' % data['Status']
sys.exit(0)
except IOError, e:
print >> sys.stderr, 'Error signaling CloudFormation: %s' % str(e)
sys.exit(1)
def signal_resource(options, args):
if not options.stack_name:
print >> sys.stderr, "Error: You must specify a stack name when signaling a resource"
parser.print_help(sys.stderr)
sys.exit(1)
creds = util.get_creds_or_die(options)
url = CloudFormationClient.endpointForRegion(options.region)
if options.endpoint:
url = options.endpoint
proxyinfo = util.get_proxyinfo(options)
status = get_status(signal_success)
try:
CloudFormationClient(creds, url=url, region=options.region, proxyinfo=proxyinfo).signal_resource(options.logical_resource_id, options.stack_name, options.id, status)
except IOError, e:
if e.strerror:
print >> sys.stderr, e.strerror
else:
print >> sys.stderr, "Unknown error signaling %s" % options.logical_resource_id
sys.exit(1)
is_wch_signal = args and args[0]
is_resource_signal = options.logical_resource_id is not None
if is_wch_signal and is_resource_signal:
print >> sys.stderr, "Error: Cannot specify both a WaitConditionHandle URL and a logical resource id"
sys.exit(1)
if is_wch_signal:
signal_wch(options, args)
elif is_resource_signal:
signal_resource(options, args)
else:
print >> sys.stderr, "Error: No WaitConditionHandle URL or logical resource id specified"
parser.print_help(sys.stderr)
sys.exit(1)
@KyuuKazami