def _run_modules(callbacks, outdir, modules_to_run, diff, no_write, no_exec, no_git): for module_name, callback in callbacks: if module_name not in modules_to_run: logger.debug("No action: skipping %s" % module_name) else: logger.info("Running module %s..." % module_name) callback(outdir, diff, no_write, no_exec, no_git)
def _load_modules(modules_to_load, indir): config = caretakr.config.load(indir) for module_name in known_modules: if module_name in config and module_name not in modules_to_load: del config[module_name] callbacks = [] for module_name in modules_to_load: if module_name in config: module_py = importlib.import_module('caretakr.%s' % module_name) logger.info("Loading module %s..." % module_name) callback, additional_config = module_py.load(config[module_name]) config = caretakr.config.deepmerge(config, additional_config) callbacks.append((module_name, callback)) _cleanup_dictionary(config) if len(config) > 0: logger.warning("Unrecognized options left in config: %s" % config) return callbacks
def cmd(cmd, dry_run, stdin=None, expected_returncodes=None): if isinstance(stdin, list): stdin = "%s\n" % '\n'.join(stdin) if expected_returncodes is None: expected_returncodes = [0] else: expected_returncodes = flatten(expected_returncodes) if dry_run is True: logger.info("Would execute %s" % str(cmd)) else: logger.info("Executing %s" % str(cmd)) proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate(stdin) if proc.returncode not in expected_returncodes: raise Exception("Running %s failed with exit code %s: %s" % (str(cmd), proc.returncode, stderr)) return stdout, stderr
def git_commit(outdir, filenames, dry_run, module_name=None): filenames = flatten(filenames) full_filenames = [os.path.join(outdir, filename) for filename in filenames] git_dir = os.path.join(outdir, '.git') if os.path.isdir(git_dir) is False: logger.info("Skipping git actions for %s, no git dir %s" % (filenames, git_dir)) return if os.stat(git_dir).st_uid != os.geteuid(): logger.info("Skipping git actions for %s, git dir %s not owned by us" % (filenames, git_dir)) return env = os.environ.copy() git_env = { 'GIT_DIR': git_dir, 'GIT_WORK_TREE': outdir, } env.update(git_env) if dry_run is True: cmd_status = ['git', 'status', '--porcelain'] cmd_status.extend(filenames) proc_status = subprocess.Popen(cmd_status, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout_status, stderr_status = proc_status.communicate() if proc_status.returncode != 0: raise Exception("Unable to do git status in %s: %s" % (outdir, stderr_status)) logger.info("Git: changes in %s: %s" % (outdir, stdout_status.splitlines())) else: cmd_add = ['git', 'add'] cmd_add.extend(full_filenames) logger.trace("Running command %s with GIT env %s" % (cmd_add, git_env)) proc_add = subprocess.Popen(cmd_add, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) _, stderr_add = proc_add.communicate() if proc_add.returncode != 0: raise Exception("Adding changes in %s failed: %s" % (outdir, stderr_add)) logger.info("Git: commit %s" % filenames) cmd_commit = ['git', 'commit', '-m'] if module_name is not None: cmd_commit.append('commiting changes after caretakr run: %s' % module_name) else: cmd_commit.append('commiting changes after caretakr run') cmd_commit.extend(full_filenames) logger.trace("Running command %s with GIT env %s" % (cmd_commit, git_env)) proc_commit = subprocess.Popen(cmd_commit, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) _, stderr_commit = proc_commit.communicate() if proc_commit.returncode != 0 and stderr_commit != '': raise Exception("Commiting changes in %s failed: %s" % (outdir, stderr_commit))
def write_file(outdir, filename, lines, dry_run, diff=False, remove_empty=False): did_write_changes = False target_filename = os.path.join(outdir, filename) if len(lines) > 0: lines.insert(0 if not lines[0].startswith('#!') else 1, "# Managed by CARETAKR") new_content = "%s\n" % '\n'.join(lines) try: with open(target_filename) as f: current_content = f.read() except: current_content = "\n" if diff is True: if new_content != current_content: diff_lines = difflib.unified_diff(current_content.splitlines(), lines, fromfile='a/%s' % filename, tofile='b/%s' % filename, lineterm='') if sys.stdout.isatty(): print('\n'.join(map(colorize, diff_lines))) else: map(logger.info, diff_lines) if dry_run is True: if os.path.isfile(target_filename) and len(lines) == 0 and remove_empty is True: logger.info("Would attempt to remove %s, because there is no new content" % target_filename) elif ((os.path.isfile(target_filename) and os.access(target_filename, os.W_OK) is True) or (os.path.exists(target_filename) is False and os.access(os.path.dirname(target_filename), os.W_OK) is True)): if new_content != current_content: if ((os.path.isfile(target_filename) and os.stat(target_filename).st_uid != os.geteuid())): logger.error("Would refuse to overwrite %s, as it's not owned by us!" % target_filename) else: logger.info("Would write %d lines to %s" % (len(lines), target_filename)) did_write_changes = True elif not (os.path.isfile(target_filename) is False and remove_empty is True): logger.info("No changes for %s" % target_filename) else: logger.error("Would not be able to write file at %s" % target_filename) else: if len(lines) == 0 and remove_empty is True: if ((os.path.isfile(target_filename) and os.stat(target_filename).st_uid != os.geteuid())): logger.error("Refusing to remove %s, as it's not owned by us!" % target_filename) else: if os.path.isfile(target_filename): logger.info("Removing %s" % target_filename) os.remove(target_filename) did_write_changes = True else: if new_content != current_content: if ((os.path.isfile(target_filename) and os.stat(target_filename).st_uid != os.geteuid())): logger.error("Refusing to overwrite %s, as it's not owned by us!" % target_filename) else: logger.info("Writing %s (%d lines)" % (target_filename, len(lines))) try: os.makedirs(os.path.dirname(target_filename)) except: pass with open(target_filename, 'w') as f: f.write(new_content) did_write_changes = True else: logger.info("No changes for %s" % target_filename) return did_write_changes