Repository: ansible/molecule

Issue:
destroy steps do not run when tests fail and tags are used in provisioner options
# Issue Type

- Bug report

# Molecule and Ansible details

```
pipenv run ansible --version && pipenv run molecule --version
ansible 2.7.10
  config file = /Users/deric/.ansible.cfg
  configured module search path = ['/Users/deric/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/deric/.local/share/virtualenvs/molecule-destroy-cLlJrhaP/lib/python3.7/site-packages/ansible
  executable location = /Users/deric/.local/share/virtualenvs/molecule-destroy-cLlJrhaP/bin/ansible
  python version = 3.7.3 (default, Apr  7 2019, 11:57:41) [Clang 10.0.0 (clang-1000.11.45.5)]
molecule, version 2.20.1.dev18
```

Molecule installation method (one of):

- pipenv (see `Pipfile` in [example repo](https://github.com/dericcrago/molecule-destroy) and `README.md` for usage)

Ansible installation method (one of):

- pipenv (see `Pipfile` in [example repo](https://github.com/dericcrago/molecule-destroy) and `README.md` for usage)

Detail any linters or test runners used:

# Desired Behavior

Destroy steps run when tests fail and `tags` are used in `provisioner: options`.

# Actual Behaviour

Destroy steps do not run and PLAY RECAP details are missing when tests fail and `tags` are used in `provisioner: options`.

See [example repo](https://github.com/dericcrago/molecule-destroy).

```yaml
provisioner:
  name: ansible
  lint:
    name: ansible-lint
  options:
    tags: no-op
```

```
    PLAYBOOK: destroy.yml **********************************************************
    1 plays in /Users/deric/.local/share/virtualenvs/molecule-destroy-cLlJrhaP/src/molecule/molecule/provisioner/ansible/playbooks/docker/destroy.yml
    
    PLAY [Destroy] *****************************************************************
    META: ran handlers
    META: ran handlers
    META: ran handlers
    
    PLAY RECAP *********************************************************************
```


Discussion:
Hi @dericcrago, can you please provide some more detailed logs? You can run molecule with `--debug` in order to look inot what molecule is failing on. Thanks.
I cloned and used the reproducer. The destroy step runs, but the provisioner tags are applied so all of the destroy tasks appear to get filtered out, but only when destroy is run as a result of a failure in the test sequence.

Checking the debug output, the first (normal) destroy call in the test sequence runs `/path/to/bin/ansible-playbook --skip-tags=molecule-notest,notest --inventory=/tmp/molecule/molecule-destroy/default/inventory --diff /path/to/molecule/provisioner/ansible/playbooks/docker/destroy
.yml -vvv`

The second (on-error) destroy call runs `/path/to/bin/ansible-playbook --tags=no-op --skip-tags=molecule-notest,notest --inventory=/tmp/molecule/molecule-destroy/default/inventory --diff /path/to/molecule/provisioner/ansible/playbooks/docker/destroy.yml -vvv`

With a Mk. 1 eyeball diff, it looks like everything is the same with the exception of the addition of `--tags=no-op` in the on-error destroy call, which definitely shouldn't be there.
I found the problem, and am putting together a PR now. I'm unable to assign this issue to myself, but would happily accept the assignment if anyone would like to do it for me. :)
Solid stuff @seandst! 

cc @gundalow I think ^^^ is due the commit bit on this project :rocket: :rocket: :rocket: 

Resolving patch:
diff --git a/molecule/command/base.py b/molecule/command/base.py
--- a/molecule/command/base.py
+++ b/molecule/command/base.py
@@ -115,6 +115,11 @@ def execute_cmdline_scenarios(scenario_name, args, command_args):
 def execute_subcommand(config, subcommand):
     command_module = getattr(molecule.command, subcommand)
     command = getattr(command_module, util.camelize(subcommand))
+    # knowledge of the current action is used by some provisioners
+    # to ensure they behave correctly during certain sequence steps,
+    # particulary the setting of ansible options in create/destroy,
+    # and is also used for reporting in execute_cmdline_scenarios
+    config.action = subcommand
 
     return command(config).execute()
 
@@ -129,10 +134,6 @@ def execute_scenario(scenario):
     """
 
     for action in scenario.sequence:
-        # knowledge of the current action is used by some provisioners
-        # to ensure they behave correctly during certain sequence steps,
-        # and is also used for reporting in execute_cmdline_scenarios
-        scenario.config.action = action
         execute_subcommand(scenario.config, action)
 
     # pruning only if a 'destroy' step was in the sequence allows for normal


Test patch:
diff --git a/test/unit/command/test_base.py b/test/unit/command/test_base.py
--- a/test/unit/command/test_base.py
+++ b/test/unit/command/test_base.py
@@ -191,7 +191,11 @@ def test_execute_cmdline_scenarios_nodestroy(config_instance,
 
 
 def test_execute_subcommand(config_instance):
+    # scenario's config.action is mutated in-place for every sequence action,
+    # so make sure that is currently set to the executed action
+    assert config_instance.action != 'list'
     assert base.execute_subcommand(config_instance, 'list')
+    assert config_instance.action == 'list'
 
 
 def test_execute_scenario(mocker, _patched_execute_subcommand):
@@ -204,9 +208,6 @@ def test_execute_scenario(mocker, _patched_execute_subcommand):
     base.execute_scenario(scenario)
 
     assert _patched_execute_subcommand.call_count == len(scenario.sequence)
-    # scenario.config.action is mutated in-place for every sequence action,
-    # so make sure that is currently set to the last executed action
-    assert scenario.config.action == scenario.sequence[-1]
     assert not scenario.prune.called
 
 
@@ -220,9 +221,6 @@ def test_execute_scenario_destroy(mocker, _patched_execute_subcommand):
     base.execute_scenario(scenario)
 
     assert _patched_execute_subcommand.call_count == len(scenario.sequence)
-    # scenario.config.action is mutated in-place for every sequence action,
-    # so make sure that is currently set to the last executed action
-    assert scenario.config.action == scenario.sequence[-1]
     assert scenario.prune.called