Refactor set_configs.py

This refactor brings the logging in line with the rest of Kolla.
The fucntion names were updated to reflect thier new role.

Additionally, it fixes several issues with the permissions which
currently break all containers that use set_configs.py

It will also work with source being a directory or a file now.

Change-Id: I4a197a343e3baf3bd31532debdff5972adb8aefa
Partially-Implements: blueprint replace-config-external
This commit is contained in:
Sam Yaple 2015-09-17 09:53:31 +00:00
parent 1b33345fb8
commit 77800984bc
2 changed files with 121 additions and 146 deletions

View File

@ -26,186 +26,161 @@ LOG = logging.getLogger(__name__)
LOG.setLevel(logging.INFO)
def json_key_validation(json_file):
valid_keys = ['source', 'dest', 'owner', 'perm']
def validate_config(config):
required_keys = {'source', 'dest', 'owner', 'perm'}
# 'command' is not present in the json file
if json_file.get('command') is None:
LOG.error('command was never specified in your json file. Command '
'is what your container will execute upon start.')
if 'command' not in config:
LOG.error('Config is missing required "command" key')
sys.exit(1)
# Check for valid keys
for data in json_file.get('config_files'):
key_not_found = ''
for valid_key in valid_keys:
if valid_key not in data.keys():
key_not_found += valid_key + ' '
if key_not_found is not '':
LOG.error('JSON data "%s" is missing keys "%s"'
% (data.keys(), key_not_found))
# Validate config sections
for data in config.get('config_files', list()):
# Verify required keys exist. Only 'source' and 'dest' are
# required. 'owner' and 'perm' should user system defaults if not
# specified
if not data.viewkeys() >= required_keys:
LOG.error('Config is missing required keys: {}'.format(data))
sys.exit(1)
for key in data.keys():
# Invalid key in json file
if key not in valid_keys:
LOG.error('Unexpected JSON key "%s". This value is currently '
'not supported.' % key)
sys.exit(1)
def copy_files(data):
dest = data.get('dest')
source = data.get('source')
# The command option will be built up and written to '/command_options'.
# which will be added to the end of $CMD in start.sh as $ARGS.
def write_command_options(args):
with open('/command_options', 'w+') as f:
f.write(args)
if not os.path.exists(source):
LOG.error('The source to copy does not exist: {}'.format(source))
sys.exit(1)
def copy_configurations():
json_path = '/opt/kolla/config_files/config.json'
LOG.info('Loading config json file "%s".' % json_path)
# If JSON file is empty don't move any configs.
# It's required there always be at least 'command' in the json file
with open(json_path) as conf:
try:
config = json.load(conf)
except ValueError:
LOG.error('Empty config json file present. There are no config '
'files being moved.')
sys.exit(1)
json_key_validation(config)
# Save the 'command' specified in the json file so start.sh can
# consume it.
cmd = config.get('command')
write_command_options(cmd)
for data in config.get('config_files'):
dest_path = data.get('dest')
source_path = data.get('source')
config_owner = data.get('owner')
LOG.info('The command being run is "%s"' % cmd)
# Make sure all the proper config dirs are in place.
if os.path.isdir(dest_path):
# The destination is a dir
LOG.info('Checking if parent directories for "%s" exist.'
% dest_path)
if os.path.exists(dest):
LOG.info('Removing existing destination: {}'.format(dest))
if os.path.isdir(dest):
shutil.rmtree(dest)
else:
# The destination is a file
dest_path = os.path.dirname(data.get('dest'))
LOG.info('Checking if parent directories for "%s" exist.'
% dest_path)
os.remove(dest)
if os.path.exists(dest_path):
LOG.info('Config destination "%s" has the proper directories '
'in place.' % dest_path)
else:
os.makedirs(dest_path)
LOG.info('Creating directory "%s" because it was not found.'
% dest_path)
if os.path.isdir(source):
source_path = source
dest_path = dest
else:
source_path = os.path.dirname(source)
dest_path = os.path.dirname(dest)
# Copy over the config file(s).
if os.path.isdir(source_path):
# The source is a dir
LOG.info('Checking if there are any config files mounted '
'in "%s".' % source_path)
config_files = os.listdir(source_path)
if config_files == []:
LOG.warning('The source directory "%s" is empty. No '
'config files will be copied.'
% source_path)
if not os.path.exists(dest_path):
LOG.info('Creating dest parent directory: {}'.format(dest_path))
os.makedirs(dest_path)
if source != source_path:
# Source is file
LOG.info('Copying {} to {}'.format(source, dest))
shutil.copy(source, dest)
else:
# Source is a directory
for src in os.listdir(source_path):
LOG.info('Copying {} to {}'.format(src, dest_path))
if os.path.isdir(src):
shutil.copytree(src, dest_path)
else:
# Source and dest need to either both be dirs or files
if os.path.isdir(dest_path):
for config in config_files:
shutil.copy(config, dest_path)
LOG.info('Config file found. Copying config file '
'"%s" to "%s".'
% (config, dest_path))
else:
LOG.error('If you specify the config source as a '
'directory, then the destination also needs '
'to be a directory')
sys.exit(1)
else:
# The source is a file
LOG.info('Checking if there is a config file mounted in "%s".'
% (source_path))
if os.path.exists(source_path):
shutil.copy(source_path, dest_path)
LOG.info('Config file found. Copying config file "%s" to '
'"%s".' % (source_path, dest_path))
shutil.copy(src, dest_path)
if dest_path in cmd:
LOG.info('Using config file: "%s" to start the %s '
'service'
% (source_path, config_owner))
else:
LOG.warning('The config file "%s" is present, but you '
'are not using it when starting %s. '
% (source_path, config_owner))
else:
LOG.warning('Skipping config "%s" because it was not '
'mounted at the expected location: "%s".'
% (dest_path, source_path))
# Check for user and group id in the environment.
try:
uid = getpwnam(config_owner).pw_uid
except KeyError:
LOG.error('The user "%s" does not exist.'
% config_owner)
sys.exit(1)
try:
gid = getpwnam(config_owner).pw_gid
except KeyError:
LOG.error('The group "%s" doesn\'t exist.'
% config_owner)
sys.exit(1)
def set_permissions(data):
def set_perms(file_, uid, guid, perm):
LOG.info('Setting permissions for {}'.format(file_))
# Give config file proper perms.
try:
os.chown(dest_path, uid, gid)
os.chown(file_, uid, gid)
except OSError as e:
LOG.error("Couldn't chown file %s because of"
"os error %s." % (dest_path, e))
LOG.error('While trying to chown {} received error: {}'.format(
file_, e))
sys.exit(1)
try:
os.chmod(dest_path, int(data.get('perm')))
os.chmod(file_, perm)
except OSError as e:
LOG.error("Couldn't chown file %s because of"
"os error %s." % (dest_path, e))
LOG.error('While trying to chmod {} received error: {}'.format(
file_, e))
sys.exit(1)
dest = data.get('dest')
owner = data.get('owner')
perm = int(data.get('perm'), 0)
# Check for user and group id in the environment.
try:
uid = getpwnam(owner).pw_uid
except KeyError:
LOG.error('The specified user does not exist: {}'.format(owner))
sys.exit(1)
try:
gid = getpwnam(owner).pw_gid
except KeyError:
LOG.error('The specified group does not exist: {}'.format(owner))
sys.exit(1)
# Set permissions on the top level dir or file
set_perms(dest, uid, gid, perm)
if os.path.isdir(dest):
# Recursively set permissions
for root, dirs, files in os.walk(dest):
for dir_ in dirs:
set_perms(os.path.join(root, dir_), uid, gid, perm)
for file_ in files:
set_perms(os.path.join(root, file_), uid, gid, perm)
def load_config():
config_file = '/opt/kolla/config_files/config.json'
LOG.info('Loading config file at {}'.format(config_file))
# Attempt to read config file
with open(config_file) as f:
try:
config = json.load(f)
except ValueError:
LOG.error('Invalid json file found at {}'.format(config_file))
sys.exit(1)
except IOError as e:
LOG.error('Could not read file {}. Failed with error {}'.format(
config_file, e))
sys.exit(1)
LOG.info('Validating config file')
validate_config(config)
if 'config_files' in config:
LOG.info('Copying service configuration files')
for data in config['config_files']:
copy_files(data)
set_permissions(data)
else:
LOG.debug('No files to copy found in config')
LOG.info('Writing out command to execute')
LOG.debug('Command is: {}'.format(config['command']))
# The value from the 'command' key will be written to '/run_command'
with open('/run_command', 'w+') as f:
f.write(config['command'])
def execute_config_strategy():
try:
kolla_config_strategy = os.environ.get("KOLLA_CONFIG_STRATEGY")
config_strategy = os.environ.get("KOLLA_CONFIG_STRATEGY")
LOG.info('Kolla config strategy set to: {}'.format(config_strategy))
except KeyError:
LOG.error("KOLLA_CONFIG_STRATEGY is not set properly.")
sys.exit(1)
if kolla_config_strategy == "COPY_ALWAYS":
# Read all existing json files.
copy_configurations()
elif kolla_config_strategy == "COPY_ONCE":
if config_strategy == "COPY_ALWAYS":
load_config()
elif config_strategy == "COPY_ONCE":
if os.path.exists('/configured'):
LOG.info("This container has already been configured; "
"Refusing to copy new configs.")
LOG.info("The config strategy prevents copying new configs")
sys.exit(0)
else:
copy_configurations()
load_config()
f = open('/configured', 'w+')
f.close()
else:
LOG.error("KOLLA_CONFIG_STRATEGY is not set properly: %s."
% kolla_config_strategy)
LOG.error('KOLLA_CONFIG_STRATEGY is not set properly')
sys.exit(1)

View File

@ -7,7 +7,7 @@ source /opt/kolla/kolla-common.sh
# Generate run command
python /opt/kolla/set_configs.py
CMD=$(cat /command_options)
CMD=$(cat /run_command)
# Loading functions
source /opt/kolla/config/config-galera.sh