From 2f85d62989deaf24f451c0986ecc9952dadcc066 Mon Sep 17 00:00:00 2001 From: Ari Stark <46357470+ari-stark@users.noreply.github.com> Date: Tue, 12 Feb 2019 03:49:53 +0100 Subject: [PATCH] Add state option to git_config module (#50578) * Add state option to git_config module State present/absent option works like --set/--unset option for 'git config'. * Change git_config to avoid useless parameter passed to git command When unsetting value, command was : git config --unset foo ''. Now command is : git config --unset foo. * Add some integration tests to git_config module * Add missing aliases file * Change set up method Using git command seems to cause troubles on some OS : changing config by changing '.gitconfig' file. * Remove some distros from tests pool Git is not installed or is out of date on these distros. * Fix aliases to skip tests on centos6 * Refactor tests of the git_config module * Add use case when state=absent and value is defined --- .../modules/source_control/git_config.py | 37 ++++++++++++++++--- test/integration/targets/git_config/aliases | 2 + .../targets/git_config/files/gitconfig | 2 + .../tasks/exclusion_state_list-all.yml | 16 ++++++++ .../git_config/tasks/get_set_no_state.yml | 25 +++++++++++++ .../tasks/get_set_state_present.yml | 27 ++++++++++++++ .../targets/git_config/tasks/main.yml | 23 ++++++++++++ .../precedence_between_unset_and_value.yml | 25 +++++++++++++ .../targets/git_config/tasks/setup.yml | 11 ++++++ .../git_config/tasks/setup_no_value.yml | 8 ++++ .../targets/git_config/tasks/setup_value.yml | 8 ++++ .../git_config/tasks/unset_check_mode.yml | 25 +++++++++++++ .../git_config/tasks/unset_no_value.yml | 23 ++++++++++++ .../targets/git_config/tasks/unset_value.yml | 24 ++++++++++++ .../targets/git_config/vars/main.yml | 6 +++ 15 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 test/integration/targets/git_config/aliases create mode 100644 test/integration/targets/git_config/files/gitconfig create mode 100644 test/integration/targets/git_config/tasks/exclusion_state_list-all.yml create mode 100644 test/integration/targets/git_config/tasks/get_set_no_state.yml create mode 100644 test/integration/targets/git_config/tasks/get_set_state_present.yml create mode 100644 test/integration/targets/git_config/tasks/main.yml create mode 100644 test/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml create mode 100644 test/integration/targets/git_config/tasks/setup.yml create mode 100644 test/integration/targets/git_config/tasks/setup_no_value.yml create mode 100644 test/integration/targets/git_config/tasks/setup_value.yml create mode 100644 test/integration/targets/git_config/tasks/unset_check_mode.yml create mode 100644 test/integration/targets/git_config/tasks/unset_no_value.yml create mode 100644 test/integration/targets/git_config/tasks/unset_value.yml create mode 100644 test/integration/targets/git_config/vars/main.yml diff --git a/lib/ansible/modules/source_control/git_config.py b/lib/ansible/modules/source_control/git_config.py index 620a885b60a..da8fd8697b0 100644 --- a/lib/ansible/modules/source_control/git_config.py +++ b/lib/ansible/modules/source_control/git_config.py @@ -51,6 +51,14 @@ options: also specify the repo parameter. It defaults to system only when not using I(list_all)=yes. choices: [ "local", "global", "system" ] + state: + description: + - "Indicates the setting should be set/unset. + This parameter has higher precedence than I(value) parameter: + when I(state)=absent and I(value) is defined, I(value) is discarded." + choices: [ 'present', 'absent' ] + default: 'present' + version_added: '2.8' value: description: - When specifying the name of a single setting, supply a value to @@ -69,6 +77,12 @@ EXAMPLES = ''' scope: global value: status +# Unset some settings in ~/.gitconfig +- git_config: + name: alias.ci + scope: global + state: absent + # Or system-wide: - git_config: name: alias.remotev @@ -149,9 +163,10 @@ def main(): name=dict(type='str'), repo=dict(type='path'), scope=dict(required=False, type='str', choices=['local', 'global', 'system']), + state=dict(required=False, type='str', default='present', choices=['present', 'absent']), value=dict(required=False) ), - mutually_exclusive=[['list_all', 'name'], ['list_all', 'value']], + mutually_exclusive=[['list_all', 'name'], ['list_all', 'value'], ['list_all', 'state']], required_if=[('scope', 'local', ['repo'])], required_one_of=[['list_all', 'name']], supports_check_mode=True, @@ -175,6 +190,12 @@ def main(): else: scope = 'system' + if params['state'] == 'absent': + unset = 'unset' + params['value'] = None + else: + unset = None + if params['value']: new_value = params['value'] else: @@ -212,16 +233,22 @@ def main(): k, v = value.split('=', 1) config_values[k] = v module.exit_json(changed=False, msg='', config_values=config_values) - elif not new_value: + elif not new_value and not unset: module.exit_json(changed=False, msg='', config_value=out.rstrip()) + elif unset and not out: + module.exit_json(changed=False, msg='no setting to unset') else: old_value = out.rstrip() if old_value == new_value: module.exit_json(changed=False, msg="") if not module.check_mode: - new_value_quoted = shlex_quote(new_value) - cmd = ' '.join(args + [new_value_quoted]) + if unset: + args.insert(len(args) - 1, "--" + unset) + cmd = ' '.join(args) + else: + new_value_quoted = shlex_quote(new_value) + cmd = ' '.join(args + [new_value_quoted]) (rc, out, err) = module.run_command(cmd, cwd=dir) if err: module.fail_json(rc=rc, msg=err, cmd=cmd) @@ -232,7 +259,7 @@ def main(): before_header=' '.join(args), before=old_value + "\n", after_header=' '.join(args), - after=new_value + "\n" + after=(new_value or '') + "\n" ), changed=True ) diff --git a/test/integration/targets/git_config/aliases b/test/integration/targets/git_config/aliases new file mode 100644 index 00000000000..dc0ba495eab --- /dev/null +++ b/test/integration/targets/git_config/aliases @@ -0,0 +1,2 @@ +shippable/posix/group3 + diff --git a/test/integration/targets/git_config/files/gitconfig b/test/integration/targets/git_config/files/gitconfig new file mode 100644 index 00000000000..989aa1c8bfa --- /dev/null +++ b/test/integration/targets/git_config/files/gitconfig @@ -0,0 +1,2 @@ +[http] + proxy = foo \ No newline at end of file diff --git a/test/integration/targets/git_config/tasks/exclusion_state_list-all.yml b/test/integration/targets/git_config/tasks/exclusion_state_list-all.yml new file mode 100644 index 00000000000..fe8b2fd239a --- /dev/null +++ b/test/integration/targets/git_config/tasks/exclusion_state_list-all.yml @@ -0,0 +1,16 @@ +--- +- import_tasks: setup_no_value.yml + +- name: testing exclusion between state and list_all parameters + git_config: + list_all: true + state: absent + register: result + ignore_errors: yes + +- name: assert git_config failed + assert: + that: + - result is failed + - "result.msg == 'parameters are mutually exclusive: list_all, state'" +... diff --git a/test/integration/targets/git_config/tasks/get_set_no_state.yml b/test/integration/targets/git_config/tasks/get_set_no_state.yml new file mode 100644 index 00000000000..149a9b2d93e --- /dev/null +++ b/test/integration/targets/git_config/tasks/get_set_no_state.yml @@ -0,0 +1,25 @@ +--- +- import_tasks: setup_no_value.yml + +- name: setting value without state + git_config: + name: "{{ option_name }}" + value: "{{ option_value }}" + scope: "{{ option_scope }}" + register: set_result + +- name: getting value without state + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert set changed and value is correct + assert: + that: + - set_result.changed == true + - set_result.diff.before == "\n" + - set_result.diff.after == option_value + "\n" + - get_result.changed == false + - get_result.config_value == option_value +... diff --git a/test/integration/targets/git_config/tasks/get_set_state_present.yml b/test/integration/targets/git_config/tasks/get_set_state_present.yml new file mode 100644 index 00000000000..59f3c9c0ee3 --- /dev/null +++ b/test/integration/targets/git_config/tasks/get_set_state_present.yml @@ -0,0 +1,27 @@ +--- +- import_tasks: setup_no_value.yml + +- name: setting value with state=present + git_config: + name: "{{ option_name }}" + value: "{{ option_value }}" + scope: "{{ option_scope }}" + state: present + register: result + +- name: getting value with state=present + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: present + register: get_result + +- name: assert set changed and value is correct with state=present + assert: + that: + - set_result.changed == true + - set_result.diff.before == "\n" + - set_result.diff.after == option_value + "\n" + - get_result.changed == false + - get_result.config_value == option_value +... diff --git a/test/integration/targets/git_config/tasks/main.yml b/test/integration/targets/git_config/tasks/main.yml new file mode 100644 index 00000000000..53f62edb446 --- /dev/null +++ b/test/integration/targets/git_config/tasks/main.yml @@ -0,0 +1,23 @@ +--- +# test code for the git_config module + +- name: setup + import_tasks: setup.yml + +- block: + # testing parameters exclusion: state and list_all + - import_tasks: exclusion_state_list-all.yml + # testing get/set option without state + - import_tasks: get_set_no_state.yml + # testing get/set option with state=present + - import_tasks: get_set_state_present.yml + # testing state=absent without value to delete + - import_tasks: unset_no_value.yml + # testing state=absent with value to delete + - import_tasks: unset_value.yml + # testing state=absent with value to delete and a defined value parameter + - import_tasks: precedence_between_unset_and_value.yml + # testing state=absent with check mode + - import_tasks: unset_check_mode.yml + when: git_installed is succeeded and git_version.stdout is version(git_version_supporting_includes, ">=") +... diff --git a/test/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml b/test/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml new file mode 100644 index 00000000000..24ef2920153 --- /dev/null +++ b/test/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml @@ -0,0 +1,25 @@ +--- +- import_tasks: setup_value.yml + +- name: unsetting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + value: bar + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unset changed and deleted value + assert: + that: + - unset_result.changed == true + - unset_result.diff.before == option_value + "\n" + - unset_result.diff.after == "\n" + - get_result.config_value == '' +... diff --git a/test/integration/targets/git_config/tasks/setup.yml b/test/integration/targets/git_config/tasks/setup.yml new file mode 100644 index 00000000000..85e168fe65d --- /dev/null +++ b/test/integration/targets/git_config/tasks/setup.yml @@ -0,0 +1,11 @@ +--- +- name: verify that git is installed so this test can continue + command: which git + register: git_installed + ignore_errors: yes + +- name: get git version, only newer than {{git_version_supporting_includes}} has includes option + shell: "git --version | grep 'git version' | sed 's/git version //'" + register: git_version + ignore_errors: yes +... \ No newline at end of file diff --git a/test/integration/targets/git_config/tasks/setup_no_value.yml b/test/integration/targets/git_config/tasks/setup_no_value.yml new file mode 100644 index 00000000000..01a2c9735ed --- /dev/null +++ b/test/integration/targets/git_config/tasks/setup_no_value.yml @@ -0,0 +1,8 @@ +--- +# ------ +# set up : deleting gitconfig file +- name: set up without value + file: + path: ~/.gitconfig + state: absent +... \ No newline at end of file diff --git a/test/integration/targets/git_config/tasks/setup_value.yml b/test/integration/targets/git_config/tasks/setup_value.yml new file mode 100644 index 00000000000..f5e0565441b --- /dev/null +++ b/test/integration/targets/git_config/tasks/setup_value.yml @@ -0,0 +1,8 @@ +--- +# ------ +# set up : set gitconfig with value +- name: set up with value + copy: + src: gitconfig + dest: ~/.gitconfig +... \ No newline at end of file diff --git a/test/integration/targets/git_config/tasks/unset_check_mode.yml b/test/integration/targets/git_config/tasks/unset_check_mode.yml new file mode 100644 index 00000000000..c8fe00c0b7d --- /dev/null +++ b/test/integration/targets/git_config/tasks/unset_check_mode.yml @@ -0,0 +1,25 @@ +--- +- import_tasks: setup_value.yml + +- name: unsetting value with check mode + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + check_mode: yes + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unset changed but dit not delete value + assert: + that: + - unset_result.changed == true + - unset_result.diff.before == option_value + "\n" + - unset_result.diff.after == "\n" + - get_result.config_value == option_value +... diff --git a/test/integration/targets/git_config/tasks/unset_no_value.yml b/test/integration/targets/git_config/tasks/unset_no_value.yml new file mode 100644 index 00000000000..71568e3aa46 --- /dev/null +++ b/test/integration/targets/git_config/tasks/unset_no_value.yml @@ -0,0 +1,23 @@ +--- +- import_tasks: setup_no_value.yml + +- name: unsetting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unsetting didn't change + assert: + that: + - unset_result.changed == false + - unset_result.msg == 'no setting to unset' + - get_result.config_value == '' +... diff --git a/test/integration/targets/git_config/tasks/unset_value.yml b/test/integration/targets/git_config/tasks/unset_value.yml new file mode 100644 index 00000000000..a2308156aa1 --- /dev/null +++ b/test/integration/targets/git_config/tasks/unset_value.yml @@ -0,0 +1,24 @@ +--- +- import_tasks: setup_value.yml + +- name: unsetting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unset changed and deleted value + assert: + that: + - unset_result.changed == true + - unset_result.diff.before == option_value + "\n" + - unset_result.diff.after == "\n" + - get_result.config_value == '' +... diff --git a/test/integration/targets/git_config/vars/main.yml b/test/integration/targets/git_config/vars/main.yml new file mode 100644 index 00000000000..545110a5bff --- /dev/null +++ b/test/integration/targets/git_config/vars/main.yml @@ -0,0 +1,6 @@ +--- +git_version_supporting_includes: 1.7.10 +option_name: http.proxy +option_value: 'foo' +option_scope: global +... \ No newline at end of file