Repository: ansible-collections/community.general

Issue:
consul_token runtime error on simple run
### Summary

community.general.consul_token raises runtime error: invalid arguments passed.

### Issue Type

Bug Report

### Component Name

consul_token

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.15.4]
  config file = None
  configured module search path = ['/home/lanson/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/lanson/.venvs/ansible/lib/python3.10/site-packages/ansible
  ansible collection location = /home/lanson/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/lanson/.venvs/ansible/bin/ansible
  python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/home/lanson/.venvs/ansible/bin/python3)
  jinja version = 3.1.2
  libyaml = True
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
# /home/lanson/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 8.4.0
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
CONFIG_FILE() = None
```


### OS / Environment

Tested in ubuntu 22.04,20.04, Debian 11,12

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
        - name: "Create consul agents token"  # noqa: run-once[task] no-handler
          run_once: true
          delegate_to: localhost
          block:
            - name: "Create consul agent policy"
              community.general.consul_policy:
                host: "192.168.1.100"
                token: "b9af57a6-2553-4e85-a2cd-5e647332b5c5"
                port: 8500
                scheme: http
                state: present
                name: agents-policy
                rules: |
                  agent_prefix "" {
                       policy = "write"
                   }
                   node_prefix "" {
                        policy = "write"
                    }
                    service_prefix "" {
                      policy = "read"
                    }
              register: _consul_agent_policy

            - name: "Create consul agents token"
              community.general.consul_token:
                host: "192.168.1.100"
                token: "b9af57a6-2553-4e85-a2cd-5e647332b5c5"
                port: 8500
                scheme: http
                state: present
                local: true
                policies:
                  - id: _consul_agent_policy.policy.ID
              register: _consul_agent_token
```
This will raise a runtime error while trying to create the token

### Expected Results

The token module should create the token.

### Actual Results

```console (paste below)
The full traceback is:
Traceback (most recent call last):
  File "/tmp/.ansible/ansible-tmp-1710283853.4198768-180272-111872570833078/AnsiballZ_consul_token.py", line 107, in <module>
    _ansiballz_main()
  File "/tmp/.ansible/ansible-tmp-1710283853.4198768-180272-111872570833078/AnsiballZ_consul_token.py", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/tmp/.ansible/ansible-tmp-1710283853.4198768-180272-111872570833078/AnsiballZ_consul_token.py", line 47, in invoke_module
    runpy.run_module(mod_name='ansible_collections.community.general.plugins.modules.consul_token', init_globals=dict(_module_fqn='ansible_collections.community.general.plugins.modules.consul_token', _modlib_path=modlib_path),
  File "/usr/lib/python3.10/runpy.py", line 224, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/lib/python3.10/runpy.py", line 96, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/tmp/ansible_community.general.consul_token_payload_8mgv7ej6/ansible_community.general.consul_token_payload.zip/ansible_collections/community/general/plugins/modules/consul_token.py", line 325, in <module>
  File "/tmp/ansible_community.general.consul_token_payload_8mgv7ej6/ansible_community.general.consul_token_payload.zip/ansible_collections/community/general/plugins/modules/consul_token.py", line 321, in main
  File "/tmp/ansible_community.general.consul_token_payload_8mgv7ej6/ansible_community.general.consul_token_payload.zip/ansible_collections/community/general/plugins/module_utils/consul.py", line 123, in execute
  File "/tmp/ansible_community.general.consul_token_payload_8mgv7ej6/ansible_community.general.consul_token_payload.zip/ansible_collections/community/general/plugins/module_utils/consul.py", line 222, in read_object
  File "/tmp/ansible_community.general.consul_token_payload_8mgv7ej6/ansible_community.general.consul_token_payload.zip/ansible_collections/community/general/plugins/module_utils/consul.py", line 219, in endpoint_url
RuntimeError: invalid arguments passed
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/consul_token.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/consul_token.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @apollo13 @sgargan
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Hi @ednxzu, #8091 should fix this. In the meantime you could work around this by supplying an `accessor_id`. This way the module can also operate in a idempotent fashion. If you just want to create new tokens and don't want a static `accessor_id` you can use https://docs.ansible.com/ansible/latest/collections/ansible/builtin/to_uuid_filter.html#ansible-collections-ansible-builtin-to-uuid-filter to generate a uuid for it (combined with `random` it would change every run)

Resolving patch:
diff --git a/plugins/module_utils/consul.py b/plugins/module_utils/consul.py
--- a/plugins/module_utils/consul.py
+++ b/plugins/module_utils/consul.py
@@ -277,7 +277,7 @@ def _request(self, method, url_parts, data=None, params=None):
             headers["X-Consul-Token"] = token
 
         try:
-            if data:
+            if data is not None:
                 data = json.dumps(data)
                 headers["Content-Type"] = "application/json"
             if params:
diff --git a/plugins/modules/consul_token.py b/plugins/modules/consul_token.py
--- a/plugins/modules/consul_token.py
+++ b/plugins/modules/consul_token.py
@@ -237,6 +237,12 @@ class ConsulTokenModule(_ConsulModule):
 
     create_only_fields = {"expiration_ttl"}
 
+    def read_object(self):
+        # if `accessor_id` is not supplied we can only create objects and are not idempotent
+        if not self.params.get(self.unique_identifier):
+            return None
+        return super(ConsulTokenModule, self).read_object()
+
     def needs_update(self, api_obj, module_obj):
         # SecretID is usually not supplied
         if "SecretID" not in module_obj and "SecretID" in api_obj:


Test patch:
diff --git a/tests/integration/targets/consul/tasks/consul_token.yml b/tests/integration/targets/consul/tasks/consul_token.yml
--- a/tests/integration/targets/consul/tasks/consul_token.yml
+++ b/tests/integration/targets/consul/tasks/consul_token.yml
@@ -14,6 +14,17 @@
     - foo-access
     - foo-access2
 
+- name: Create token without accessor
+  community.general.consul_token:
+    state: present
+  register: simple_create_result
+
+- assert:
+    that:
+      - simple_create_result is changed
+      - simple_create_result.token.AccessorID
+      - simple_create_result.operation == 'create'
+
 - name: Create token
   community.general.consul_token:
     state: present