heat-cfntools/bin/cfn-signal
Anant Patil 2710bba2cb Convert all internal commands to list
Make all internal commands as list to avoid any possibility of command
line injection. Commands supplied as string are susceptible to
substitution.

All the internal commands are supplied as list to CommandRunner. As a
convention, all the commands must be given as list to subprocess except
the commands read from file, like in case of cfn hooks and commands
section in metadata.

Few internal commands require shell redirects and they will be
implemented in another patch.

Change-Id: Ifabaf44e341144bc85508dc05c76b1d83e41ae44
Partial-Bug: #1312246
2015-09-22 10:37:11 +05:30

119 lines
3.7 KiB
Python
Executable File

#!/usr/bin/env python
#
# 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.
"""
Implements cfn-signal CloudFormation functionality
"""
import argparse
import logging
import sys
from heat_cfntools.cfntools import cfn_helper
description = " "
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-s', '--success',
dest="success",
help="signal status to report",
default='true',
required=False)
parser.add_argument('-r', '--reason',
dest="reason",
help="The reason for the failure",
default="Configuration Complete",
required=False)
parser.add_argument('-d', '--data',
dest="data",
default="Application has completed configuration.",
help="The data to send",
required=False)
parser.add_argument('-i', '--id',
dest="unique_id",
help="the unique id to send back to the WaitCondition",
default=None,
required=False)
parser.add_argument('-e', '--exit-code',
dest="exit_code",
help="The exit code from a process to interpret",
default=None,
required=False)
parser.add_argument('--exit',
dest="exit",
help="DEPRECATED! Use -e or --exit-code instead.",
default=None,
required=False)
parser.add_argument('url',
help='the url to post to')
parser.add_argument('-k', '--insecure',
help="This will make insecure https request to cfn-api.",
action='store_true')
args = parser.parse_args()
log_format = '%(levelname)s [%(asctime)s] %(message)s'
log_file_name = "/var/log/cfn-signal.log"
logging.basicConfig(filename=log_file_name,
format=log_format,
level=logging.DEBUG)
LOG = logging.getLogger('cfntools')
LOG.debug('cfn-signal called %s ' % (str(args)))
if args.exit:
LOG.warning('--exit DEPRECATED! Use -e or --exit-code instead.')
status = 'FAILURE'
exit_code = args.exit_code or args.exit
if exit_code:
# "exit_code" takes precedence over "success".
if exit_code == '0':
status = 'SUCCESS'
else:
if args.success == 'true':
status = 'SUCCESS'
unique_id = args.unique_id
if unique_id is None:
LOG.debug('No id passed from the command line')
md = cfn_helper.Metadata('not-used', None)
unique_id = md.get_instance_id()
if unique_id is None:
LOG.error('Could not get the instance id from metadata!')
import socket
unique_id = socket.getfqdn()
LOG.debug('id: %s' % (unique_id))
body = {
"Status": status,
"Reason": args.reason,
"UniqueId": unique_id,
"Data": args.data
}
data = cfn_helper.json.dumps(body)
cmd = ['curl']
if args.insecure:
cmd.append('--insecure')
cmd.extend([
'-X', 'PUT',
'-H', 'Content-Type:',
'--data-binary', data,
args.url
])
command = cfn_helper.CommandRunner(cmd).run()
if command.status != 0:
LOG.error(command.stderr)
sys.exit(command.status)