diff --git a/docker/base/set_configs.py b/docker/base/set_configs.py index 0c86c0621d..7b57735506 100644 --- a/docker/base/set_configs.py +++ b/docker/base/set_configs.py @@ -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) diff --git a/docker/mariadb/start.sh b/docker/mariadb/start.sh index f33dc74938..69f40818c8 100755 --- a/docker/mariadb/start.sh +++ b/docker/mariadb/start.sh @@ -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