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:
parent
1b33345fb8
commit
77800984bc
@ -26,186 +26,161 @@ LOG = logging.getLogger(__name__)
|
|||||||
LOG.setLevel(logging.INFO)
|
LOG.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
def json_key_validation(json_file):
|
def validate_config(config):
|
||||||
valid_keys = ['source', 'dest', 'owner', 'perm']
|
required_keys = {'source', 'dest', 'owner', 'perm'}
|
||||||
|
|
||||||
# 'command' is not present in the json file
|
if 'command' not in config:
|
||||||
if json_file.get('command') is None:
|
LOG.error('Config is missing required "command" key')
|
||||||
LOG.error('command was never specified in your json file. Command '
|
|
||||||
'is what your container will execute upon start.')
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Check for valid keys
|
# Validate config sections
|
||||||
for data in json_file.get('config_files'):
|
for data in config.get('config_files', list()):
|
||||||
key_not_found = ''
|
# Verify required keys exist. Only 'source' and 'dest' are
|
||||||
for valid_key in valid_keys:
|
# required. 'owner' and 'perm' should user system defaults if not
|
||||||
if valid_key not in data.keys():
|
# specified
|
||||||
key_not_found += valid_key + ' '
|
if not data.viewkeys() >= required_keys:
|
||||||
|
LOG.error('Config is missing required keys: {}'.format(data))
|
||||||
if key_not_found is not '':
|
|
||||||
LOG.error('JSON data "%s" is missing keys "%s"'
|
|
||||||
% (data.keys(), key_not_found))
|
|
||||||
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)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
# The command option will be built up and written to '/command_options'.
|
def copy_files(data):
|
||||||
# which will be added to the end of $CMD in start.sh as $ARGS.
|
dest = data.get('dest')
|
||||||
def write_command_options(args):
|
source = data.get('source')
|
||||||
with open('/command_options', 'w+') as f:
|
|
||||||
f.write(args)
|
|
||||||
|
|
||||||
|
if not os.path.exists(source):
|
||||||
def copy_configurations():
|
LOG.error('The source to copy does not exist: {}'.format(source))
|
||||||
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)
|
sys.exit(1)
|
||||||
|
|
||||||
json_key_validation(config)
|
if os.path.exists(dest):
|
||||||
|
LOG.info('Removing existing destination: {}'.format(dest))
|
||||||
# Save the 'command' specified in the json file so start.sh can
|
if os.path.isdir(dest):
|
||||||
# consume it.
|
shutil.rmtree(dest)
|
||||||
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)
|
|
||||||
else:
|
else:
|
||||||
# The destination is a file
|
os.remove(dest)
|
||||||
dest_path = os.path.dirname(data.get('dest'))
|
|
||||||
LOG.info('Checking if parent directories for "%s" exist.'
|
|
||||||
% dest_path)
|
|
||||||
|
|
||||||
if os.path.exists(dest_path):
|
if os.path.isdir(source):
|
||||||
LOG.info('Config destination "%s" has the proper directories '
|
source_path = source
|
||||||
'in place.' % dest_path)
|
dest_path = dest
|
||||||
else:
|
else:
|
||||||
|
source_path = os.path.dirname(source)
|
||||||
|
dest_path = os.path.dirname(dest)
|
||||||
|
|
||||||
|
if not os.path.exists(dest_path):
|
||||||
|
LOG.info('Creating dest parent directory: {}'.format(dest_path))
|
||||||
os.makedirs(dest_path)
|
os.makedirs(dest_path)
|
||||||
LOG.info('Creating directory "%s" because it was not found.'
|
|
||||||
% dest_path)
|
|
||||||
|
|
||||||
# Copy over the config file(s).
|
if source != source_path:
|
||||||
if os.path.isdir(source_path):
|
# Source is file
|
||||||
# The source is a dir
|
LOG.info('Copying {} to {}'.format(source, dest))
|
||||||
LOG.info('Checking if there are any config files mounted '
|
shutil.copy(source, dest)
|
||||||
'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)
|
|
||||||
else:
|
else:
|
||||||
# Source and dest need to either both be dirs or files
|
# Source is a directory
|
||||||
if os.path.isdir(dest_path):
|
for src in os.listdir(source_path):
|
||||||
for config in config_files:
|
LOG.info('Copying {} to {}'.format(src, dest_path))
|
||||||
shutil.copy(config, dest_path)
|
if os.path.isdir(src):
|
||||||
LOG.info('Config file found. Copying config file '
|
shutil.copytree(src, dest_path)
|
||||||
'"%s" to "%s".'
|
|
||||||
% (config, dest_path))
|
|
||||||
else:
|
else:
|
||||||
LOG.error('If you specify the config source as a '
|
shutil.copy(src, dest_path)
|
||||||
'directory, then the destination also needs '
|
|
||||||
'to be a directory')
|
|
||||||
|
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(file_, uid, gid)
|
||||||
|
except OSError as e:
|
||||||
|
LOG.error('While trying to chown {} received error: {}'.format(
|
||||||
|
file_, e))
|
||||||
|
sys.exit(1)
|
||||||
|
try:
|
||||||
|
os.chmod(file_, perm)
|
||||||
|
except OSError as e:
|
||||||
|
LOG.error('While trying to chmod {} received error: {}'.format(
|
||||||
|
file_, e))
|
||||||
sys.exit(1)
|
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))
|
|
||||||
|
|
||||||
if dest_path in cmd:
|
dest = data.get('dest')
|
||||||
LOG.info('Using config file: "%s" to start the %s '
|
owner = data.get('owner')
|
||||||
'service'
|
perm = int(data.get('perm'), 0)
|
||||||
% (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.
|
# Check for user and group id in the environment.
|
||||||
try:
|
try:
|
||||||
uid = getpwnam(config_owner).pw_uid
|
uid = getpwnam(owner).pw_uid
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOG.error('The user "%s" does not exist.'
|
LOG.error('The specified user does not exist: {}'.format(owner))
|
||||||
% config_owner)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
gid = getpwnam(config_owner).pw_gid
|
gid = getpwnam(owner).pw_gid
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOG.error('The group "%s" doesn\'t exist.'
|
LOG.error('The specified group does not exist: {}'.format(owner))
|
||||||
% config_owner)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Give config file proper perms.
|
# 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:
|
try:
|
||||||
os.chown(dest_path, uid, gid)
|
config = json.load(f)
|
||||||
except OSError as e:
|
except ValueError:
|
||||||
LOG.error("Couldn't chown file %s because of"
|
LOG.error('Invalid json file found at {}'.format(config_file))
|
||||||
"os error %s." % (dest_path, e))
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
except IOError as e:
|
||||||
os.chmod(dest_path, int(data.get('perm')))
|
LOG.error('Could not read file {}. Failed with error {}'.format(
|
||||||
except OSError as e:
|
config_file, e))
|
||||||
LOG.error("Couldn't chown file %s because of"
|
|
||||||
"os error %s." % (dest_path, e))
|
|
||||||
sys.exit(1)
|
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():
|
def execute_config_strategy():
|
||||||
try:
|
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:
|
except KeyError:
|
||||||
LOG.error("KOLLA_CONFIG_STRATEGY is not set properly.")
|
LOG.error("KOLLA_CONFIG_STRATEGY is not set properly.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if kolla_config_strategy == "COPY_ALWAYS":
|
if config_strategy == "COPY_ALWAYS":
|
||||||
# Read all existing json files.
|
load_config()
|
||||||
copy_configurations()
|
elif config_strategy == "COPY_ONCE":
|
||||||
elif kolla_config_strategy == "COPY_ONCE":
|
|
||||||
if os.path.exists('/configured'):
|
if os.path.exists('/configured'):
|
||||||
LOG.info("This container has already been configured; "
|
LOG.info("The config strategy prevents copying new configs")
|
||||||
"Refusing to copy new configs.")
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
copy_configurations()
|
load_config()
|
||||||
f = open('/configured', 'w+')
|
f = open('/configured', 'w+')
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
LOG.error("KOLLA_CONFIG_STRATEGY is not set properly: %s."
|
LOG.error('KOLLA_CONFIG_STRATEGY is not set properly')
|
||||||
% kolla_config_strategy)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ source /opt/kolla/kolla-common.sh
|
|||||||
|
|
||||||
# Generate run command
|
# Generate run command
|
||||||
python /opt/kolla/set_configs.py
|
python /opt/kolla/set_configs.py
|
||||||
CMD=$(cat /command_options)
|
CMD=$(cat /run_command)
|
||||||
|
|
||||||
# Loading functions
|
# Loading functions
|
||||||
source /opt/kolla/config/config-galera.sh
|
source /opt/kolla/config/config-galera.sh
|
||||||
|
Loading…
Reference in New Issue
Block a user