diff --git a/curator/actions.py b/curator/actions.py index b10066c1..97ed7d8f 100644 --- a/curator/actions.py +++ b/curator/actions.py @@ -160,7 +160,8 @@ def __init__(self, ilo, key=None, value=None, allocation_type='require', assigned to at least some of your nodes to have any effect. :arg value: An arbitrary metadata attribute value. Must correspond to values associated with `key` assigned to at least some of your nodes - to have any effect. + to have any effect. If a `None` value is provided, it will remove + any setting associated with that `key`. :arg allocation_type: Type of allocation to apply. Default is `require` :arg wait_for_completion: Wait (or not) for the operation to complete before returning. (default: `False`) @@ -174,8 +175,6 @@ def __init__(self, ilo, key=None, value=None, allocation_type='require', verify_index_list(ilo) if not key: raise MissingArgument('No value for "key" provided') - if not value: - raise MissingArgument('No value for "value" provided') if allocation_type not in ['require', 'include', 'exclude']: raise ValueError( '{0} is an invalid allocation_type. Must be one of "require", ' diff --git a/curator/validators/options.py b/curator/validators/options.py index e58c06ca..ad8deb9c 100644 --- a/curator/validators/options.py +++ b/curator/validators/options.py @@ -128,7 +128,7 @@ def timeout_override(action): } def value(): - return { Required('value'): Any(str, unicode) } + return { Required('value', default=None): Any(str, unicode, None) } def wait_for_active_shards(): return { diff --git a/docs/Changelog.rst b/docs/Changelog.rst index 35976e4b..8759b4f9 100644 --- a/docs/Changelog.rst +++ b/docs/Changelog.rst @@ -17,6 +17,8 @@ Changelog **Bug Fixes** + * Allow allocation action to unset a key/value pair by using an empty value. + Issue raised in #906. (untergeek) * Check if an index is in an alias before attempting to delete it from the alias. Issue raised in #887. (untergeek) * Fix allocation issues when using Elasticsearch 5.1+. Issue raised in #871 diff --git a/docs/asciidoc/actions.asciidoc b/docs/asciidoc/actions.asciidoc index 953c527b..c738d982 100644 --- a/docs/asciidoc/actions.asciidoc +++ b/docs/asciidoc/actions.asciidoc @@ -134,13 +134,14 @@ Required settings ~~~~~~~~~~~~~~~~~ * <> (required) -* <> (required) [float] Optional settings ~~~~~~~~~~~~~~~~~ * <> (has a default value which can optionally be changed) +* <> (has a default value which can + optionally be changed) * <> (has a default value which can optionally be changed) * <> (can override the default) diff --git a/docs/asciidoc/options.asciidoc b/docs/asciidoc/options.asciidoc index 1d0cb3b4..6109d011 100644 --- a/docs/asciidoc/options.asciidoc +++ b/docs/asciidoc/options.asciidoc @@ -455,13 +455,13 @@ from the client-defined default would be desirable. [[option_value]] == value -NOTE: This setting is required when using the <> - or the <>. +NOTE: This setting is optional when using the <> + and required when using the <>. === <> value For the <>, the value of this setting should -correspond to a node setting on one or more nodes in your cluster. +correspond to a node setting on one or more nodes in your cluster For example, you might have set @@ -476,6 +476,42 @@ the special attribute names `_ip`, `_name`, `_id`, or `_host` for <>, value can match the one of the node ip addresses, names, ids, or host names, respectively. +NOTE: To remove a routing allocation, the value of this setting should be left +empty, or the `value` setting not even included as an option. + +For example, you might have set + +[source,sh] +----------- +PUT test/_settings +{ + "index.routing.allocation.exclude.size": "small" +} +----------- + +to keep index `test` from allocating shards on nodes that have `node.tag: small`. +To remove this shard routing allocation setting, you might use an action file +similar to this: + +[source,yaml] +----------- +--- + actions: + 1: + action: allocation + description: -> + Unset 'index.routing.allocation.exclude.size' for index 'test' by + passing an empty value. + options: + key: size + value: + allocation_type: exclude + filters: + - filtertype: pattern + kind: regex + value: '^test$' +----------- + === <> value For the <>, the acceptable values for diff --git a/test/integration/test_allocation.py b/test/integration/test_allocation.py index 554605ba..f071ca33 100644 --- a/test/integration/test_allocation.py +++ b/test/integration/test_allocation.py @@ -84,6 +84,36 @@ def test_exclude(self): self.client.indices.get_settings(index='my_index')['my_index']['settings']['index']['routing']['allocation'][at][key]) self.assertNotIn('routing', self.client.indices.get_settings(index='not_my_index')['not_my_index']['settings']['index']) + def test_remove_exclude_with_none_value(self): + key = 'tag' + value = '' + at = 'exclude' + self.write_config( + self.args['configfile'], testvars.client_config.format(host, port)) + self.write_config(self.args['actionfile'], + testvars.allocation_test.format(key, value, at, False)) + self.create_index('my_index') + self.create_index('not_my_index') + # Put a setting in place before we start the test. + self.client.indices.put_settings( + index='my_index', + body={'index.routing.allocation.{0}.{1}'.format(at, key): 'bar'} + ) + # Ensure we _have_ it here first. + self.assertEquals('bar', + self.client.indices.get_settings(index='my_index')['my_index']['settings']['index']['routing']['allocation'][at][key]) + test = clicktest.CliRunner() + result = test.invoke( + curator.cli, + [ + '--config', self.args['configfile'], + self.args['actionfile'] + ], + ) + self.assertNotIn('routing', + self.client.indices.get_settings(index='my_index')['my_index']['settings']['index']) + self.assertNotIn('routing', + self.client.indices.get_settings(index='not_my_index')['not_my_index']['settings']['index']) def test_invalid_allocation_type(self): key = 'tag' value = 'value' diff --git a/test/unit/test_action_allocation.py b/test/unit/test_action_allocation.py index 221745de..d3f9a6eb 100644 --- a/test/unit/test_action_allocation.py +++ b/test/unit/test_action_allocation.py @@ -26,15 +26,6 @@ def test_create_body_no_key(self): client.indices.stats.return_value = testvars.stats_one ilo = curator.IndexList(client) self.assertRaises(curator.MissingArgument, curator.Allocation, ilo) - def test_create_body_no_value(self): - client = Mock() - client.info.return_value = {'version': {'number': '2.4.1'} } - client.indices.get_settings.return_value = testvars.settings_one - client.cluster.state.return_value = testvars.clu_state_one - client.indices.stats.return_value = testvars.stats_one - ilo = curator.IndexList(client) - self.assertRaises(curator.MissingArgument, - curator.Allocation, ilo, key='key') def test_create_body_invalid_allocation_type(self): client = Mock() client.info.return_value = {'version': {'number': '2.4.1'} }