|
@@ -34,6 +34,7 @@
|
|
from __future__ import print_function
|
|
from __future__ import print_function
|
|
import atexit
|
|
import atexit
|
|
import copy
|
|
import copy
|
|
|
|
+import fcntl
|
|
import json
|
|
import json
|
|
import os
|
|
import os
|
|
import re
|
|
import re
|
|
@@ -199,14 +200,35 @@ class Yedit(object): # pragma: no cover
|
|
|
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
+ # pylint: disable=too-many-return-statements,too-many-branches
|
|
@staticmethod
|
|
@staticmethod
|
|
- def remove_entry(data, key, sep='.'):
|
|
|
|
|
|
+ def remove_entry(data, key, index=None, value=None, sep='.'):
|
|
''' remove data at location key '''
|
|
''' remove data at location key '''
|
|
if key == '' and isinstance(data, dict):
|
|
if key == '' and isinstance(data, dict):
|
|
- data.clear()
|
|
|
|
|
|
+ if value is not None:
|
|
|
|
+ data.pop(value)
|
|
|
|
+ elif index is not None:
|
|
|
|
+ raise YeditException("remove_entry for a dictionary does not have an index {}".format(index))
|
|
|
|
+ else:
|
|
|
|
+ data.clear()
|
|
|
|
+
|
|
return True
|
|
return True
|
|
|
|
+
|
|
elif key == '' and isinstance(data, list):
|
|
elif key == '' and isinstance(data, list):
|
|
- del data[:]
|
|
|
|
|
|
+ ind = None
|
|
|
|
+ if value is not None:
|
|
|
|
+ try:
|
|
|
|
+ ind = data.index(value)
|
|
|
|
+ except ValueError:
|
|
|
|
+ return False
|
|
|
|
+ elif index is not None:
|
|
|
|
+ ind = index
|
|
|
|
+ else:
|
|
|
|
+ del data[:]
|
|
|
|
+
|
|
|
|
+ if ind is not None:
|
|
|
|
+ data.pop(ind)
|
|
|
|
+
|
|
return True
|
|
return True
|
|
|
|
|
|
if not (key and Yedit.valid_key(key, sep)) and \
|
|
if not (key and Yedit.valid_key(key, sep)) and \
|
|
@@ -321,7 +343,9 @@ class Yedit(object): # pragma: no cover
|
|
tmp_filename = filename + '.yedit'
|
|
tmp_filename = filename + '.yedit'
|
|
|
|
|
|
with open(tmp_filename, 'w') as yfd:
|
|
with open(tmp_filename, 'w') as yfd:
|
|
|
|
+ fcntl.flock(yfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
yfd.write(contents)
|
|
yfd.write(contents)
|
|
|
|
+ fcntl.flock(yfd, fcntl.LOCK_UN)
|
|
|
|
|
|
os.rename(tmp_filename, filename)
|
|
os.rename(tmp_filename, filename)
|
|
|
|
|
|
@@ -340,10 +364,16 @@ class Yedit(object): # pragma: no cover
|
|
pass
|
|
pass
|
|
|
|
|
|
# Try to use RoundTripDumper if supported.
|
|
# Try to use RoundTripDumper if supported.
|
|
- try:
|
|
|
|
- Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper))
|
|
|
|
- except AttributeError:
|
|
|
|
- Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False))
|
|
|
|
|
|
+ if self.content_type == 'yaml':
|
|
|
|
+ try:
|
|
|
|
+ Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper))
|
|
|
|
+ except AttributeError:
|
|
|
|
+ Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False))
|
|
|
|
+ elif self.content_type == 'json':
|
|
|
|
+ Yedit._write(self.filename, json.dumps(self.yaml_dict, indent=4, sort_keys=True))
|
|
|
|
+ else:
|
|
|
|
+ raise YeditException('Unsupported content_type: {}.'.format(self.content_type) +
|
|
|
|
+ 'Please specify a content_type of yaml or json.')
|
|
|
|
|
|
return (True, self.yaml_dict)
|
|
return (True, self.yaml_dict)
|
|
|
|
|
|
@@ -391,7 +421,7 @@ class Yedit(object): # pragma: no cover
|
|
|
|
|
|
# Try to use RoundTripLoader if supported.
|
|
# Try to use RoundTripLoader if supported.
|
|
try:
|
|
try:
|
|
- self.yaml_dict = yaml.safe_load(contents, yaml.RoundTripLoader)
|
|
|
|
|
|
+ self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader)
|
|
except AttributeError:
|
|
except AttributeError:
|
|
self.yaml_dict = yaml.safe_load(contents)
|
|
self.yaml_dict = yaml.safe_load(contents)
|
|
|
|
|
|
@@ -450,7 +480,7 @@ class Yedit(object): # pragma: no cover
|
|
|
|
|
|
return (False, self.yaml_dict)
|
|
return (False, self.yaml_dict)
|
|
|
|
|
|
- def delete(self, path):
|
|
|
|
|
|
+ def delete(self, path, index=None, value=None):
|
|
''' remove path from a dict'''
|
|
''' remove path from a dict'''
|
|
try:
|
|
try:
|
|
entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
|
|
entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
|
|
@@ -460,7 +490,7 @@ class Yedit(object): # pragma: no cover
|
|
if entry is None:
|
|
if entry is None:
|
|
return (False, self.yaml_dict)
|
|
return (False, self.yaml_dict)
|
|
|
|
|
|
- result = Yedit.remove_entry(self.yaml_dict, path, self.separator)
|
|
|
|
|
|
+ result = Yedit.remove_entry(self.yaml_dict, path, index, value, self.separator)
|
|
if not result:
|
|
if not result:
|
|
return (False, self.yaml_dict)
|
|
return (False, self.yaml_dict)
|
|
|
|
|
|
@@ -636,7 +666,12 @@ class Yedit(object): # pragma: no cover
|
|
|
|
|
|
curr_value = invalue
|
|
curr_value = invalue
|
|
if val_type == 'yaml':
|
|
if val_type == 'yaml':
|
|
- curr_value = yaml.load(invalue)
|
|
|
|
|
|
+ try:
|
|
|
|
+ # AUDIT:maybe-no-member makes sense due to different yaml libraries
|
|
|
|
+ # pylint: disable=maybe-no-member
|
|
|
|
+ curr_value = yaml.safe_load(invalue, Loader=yaml.RoundTripLoader)
|
|
|
|
+ except AttributeError:
|
|
|
|
+ curr_value = yaml.safe_load(invalue)
|
|
elif val_type == 'json':
|
|
elif val_type == 'json':
|
|
curr_value = json.loads(invalue)
|
|
curr_value = json.loads(invalue)
|
|
|
|
|
|
@@ -705,6 +740,7 @@ class Yedit(object): # pragma: no cover
|
|
'''perform the idempotent crud operations'''
|
|
'''perform the idempotent crud operations'''
|
|
yamlfile = Yedit(filename=params['src'],
|
|
yamlfile = Yedit(filename=params['src'],
|
|
backup=params['backup'],
|
|
backup=params['backup'],
|
|
|
|
+ content_type=params['content_type'],
|
|
separator=params['separator'])
|
|
separator=params['separator'])
|
|
|
|
|
|
state = params['state']
|
|
state = params['state']
|
|
@@ -735,7 +771,7 @@ class Yedit(object): # pragma: no cover
|
|
if params['update']:
|
|
if params['update']:
|
|
rval = yamlfile.pop(params['key'], params['value'])
|
|
rval = yamlfile.pop(params['key'], params['value'])
|
|
else:
|
|
else:
|
|
- rval = yamlfile.delete(params['key'])
|
|
|
|
|
|
+ rval = yamlfile.delete(params['key'], params['index'], params['value'])
|
|
|
|
|
|
if rval[0] and params['src']:
|
|
if rval[0] and params['src']:
|
|
yamlfile.write()
|
|
yamlfile.write()
|
|
@@ -1639,6 +1675,7 @@ def main():
|
|
timeout=dict(default=30, type='int'),
|
|
timeout=dict(default=30, type='int'),
|
|
approve_all=dict(default=False, type='bool'),
|
|
approve_all=dict(default=False, type='bool'),
|
|
service_account=dict(default='node-bootstrapper', type='str'),
|
|
service_account=dict(default='node-bootstrapper', type='str'),
|
|
|
|
+ fail_on_timeout=dict(default=False, type='bool'),
|
|
),
|
|
),
|
|
supports_check_mode=True,
|
|
supports_check_mode=True,
|
|
mutually_exclusive=[['approve_all', 'nodes']],
|
|
mutually_exclusive=[['approve_all', 'nodes']],
|
|
@@ -1649,6 +1686,12 @@ def main():
|
|
|
|
|
|
rval = OCcsr.run_ansible(module.params, module.check_mode)
|
|
rval = OCcsr.run_ansible(module.params, module.check_mode)
|
|
|
|
|
|
|
|
+ # If we timed out then we weren't finished. Fail if user requested to fail.
|
|
|
|
+ if (module.params['timeout'] > 0 and
|
|
|
|
+ module.params['fail_on_timeout'] and
|
|
|
|
+ rval['timeout']):
|
|
|
|
+ return module.fail_json(msg='Timed out accepting certificate signing requests. Failing as requested.', **rval)
|
|
|
|
+
|
|
if 'failed' in rval:
|
|
if 'failed' in rval:
|
|
return module.fail_json(**rval)
|
|
return module.fail_json(**rval)
|
|
|
|
|