fixes to assemble
now uses atomic move to avoid data corruption correclty cleans up temp files in every case returns backup_file info if needed validate validate before temp file gets created backup AFTER validate
This commit is contained in:
parent
6a48f2207a
commit
94e66cb108
1 changed files with 39 additions and 21 deletions
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import shutil
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -152,6 +151,16 @@ def assemble_from_fragments(src_path, delimiter=None, compiled_regexp=None, igno
|
||||||
tmp.close()
|
tmp.close()
|
||||||
return temp_path
|
return temp_path
|
||||||
|
|
||||||
|
def cleanup(path, result=None):
|
||||||
|
# cleanup just in case
|
||||||
|
if os.path.exists(path):
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except (IOError, OSError), e:
|
||||||
|
# don't error on possible race conditions, but keep warning
|
||||||
|
if result is not None:
|
||||||
|
result['warnings'] = ['Unable to remove temp file (%s): %s' % (path, str(e))]
|
||||||
|
|
||||||
# ==============================================================
|
# ==============================================================
|
||||||
# main
|
# main
|
||||||
|
|
||||||
|
@ -173,7 +182,6 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
path_md5 = None # Deprecated
|
|
||||||
path_hash = None
|
path_hash = None
|
||||||
dest_hash = None
|
dest_hash = None
|
||||||
src = os.path.expanduser(module.params['src'])
|
src = os.path.expanduser(module.params['src'])
|
||||||
|
@ -185,6 +193,7 @@ def main():
|
||||||
ignore_hidden = module.params['ignore_hidden']
|
ignore_hidden = module.params['ignore_hidden']
|
||||||
validate = module.params.get('validate', None)
|
validate = module.params.get('validate', None)
|
||||||
|
|
||||||
|
result = dict(src=src, dest=dest)
|
||||||
if not os.path.exists(src):
|
if not os.path.exists(src):
|
||||||
module.fail_json(msg="Source (%s) does not exist" % src)
|
module.fail_json(msg="Source (%s) does not exist" % src)
|
||||||
|
|
||||||
|
@ -197,37 +206,46 @@ def main():
|
||||||
except re.error, e:
|
except re.error, e:
|
||||||
module.fail_json(msg="Invalid Regexp (%s) in \"%s\"" % (e, regexp))
|
module.fail_json(msg="Invalid Regexp (%s) in \"%s\"" % (e, regexp))
|
||||||
|
|
||||||
|
if validate and "%s" not in validate:
|
||||||
|
module.fail_json(msg="validate must contain %%s: %s" % validate)
|
||||||
|
|
||||||
path = assemble_from_fragments(src, delimiter, compiled_regexp, ignore_hidden)
|
path = assemble_from_fragments(src, delimiter, compiled_regexp, ignore_hidden)
|
||||||
path_hash = module.sha1(path)
|
path_hash = module.sha1(path)
|
||||||
|
result['checksum'] = path_hash
|
||||||
if os.path.exists(dest):
|
|
||||||
dest_hash = module.sha1(dest)
|
|
||||||
|
|
||||||
if path_hash != dest_hash:
|
|
||||||
if backup and dest_hash is not None:
|
|
||||||
module.backup_local(dest)
|
|
||||||
if validate:
|
|
||||||
if "%s" not in validate:
|
|
||||||
module.fail_json(msg="validate must contain %%s: %s" % validate)
|
|
||||||
(rc, out, err) = module.run_command(validate % path)
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc, err))
|
|
||||||
|
|
||||||
shutil.copy(path, dest)
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
# Backwards compat. This won't return data if FIPS mode is active
|
# Backwards compat. This won't return data if FIPS mode is active
|
||||||
try:
|
try:
|
||||||
pathmd5 = module.md5(path)
|
pathmd5 = module.md5(path)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pathmd5 = None
|
pathmd5 = None
|
||||||
|
result['md5sum'] = pathmd5
|
||||||
|
|
||||||
os.remove(path)
|
if os.path.exists(dest):
|
||||||
|
dest_hash = module.sha1(dest)
|
||||||
|
|
||||||
|
if path_hash != dest_hash:
|
||||||
|
if validate:
|
||||||
|
(rc, out, err) = module.run_command(validate % path)
|
||||||
|
result['validation'] = dict(rc=rc, stdout=out, stderr=err)
|
||||||
|
if rc != 0:
|
||||||
|
cleanup(path)
|
||||||
|
result['msg'] = "failed to validate: rc:%s error:%s" % (rc, err)
|
||||||
|
module.fail_json(result)
|
||||||
|
if backup and dest_hash is not None:
|
||||||
|
result['backup_file'] = module.backup_local(dest)
|
||||||
|
|
||||||
|
module.atomic_move(path, dest)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
cleanup(path, result)
|
||||||
|
|
||||||
|
# handle file permissions
|
||||||
file_args = module.load_file_common_arguments(module.params)
|
file_args = module.load_file_common_arguments(module.params)
|
||||||
changed = module.set_fs_attributes_if_different(file_args, changed)
|
result['changed'] = module.set_fs_attributes_if_different(file_args, changed)
|
||||||
|
|
||||||
# Mission complete
|
# Mission complete
|
||||||
module.exit_json(src=src, dest=dest, md5sum=pathmd5, checksum=path_hash, changed=changed, msg="OK")
|
result['msg'] = "OK"
|
||||||
|
module.exit_json(result)
|
||||||
|
|
||||||
# import module snippets
|
# import module snippets
|
||||||
from ansible.module_utils.basic import *
|
from ansible.module_utils.basic import *
|
||||||
|
|
Loading…
Reference in a new issue