8000 Fix connection option handling for delegated driver by apatard · Pull Request #2988 · ansible/molecule · GitHub
[go: up one dir, main page]

Skip to content

Fix connection option handling for delegated driver - #2988

Merged
ssbarnea merged 1 commit into
ansible:masterfrom
apatard:delegate_connection
Nov 25, 2020
Merged

ssbarnea merged 1 commit into
ansible:masterfrom
apatard:delegate_connection

Conversation

@apatard
@apatard apatard commented Nov 20, 2020 •
Copy link
Copy Markdown
Contributor

The documentation and the code tends to suggest that in order to configure the ansible connection plugin, the option to use is 'connection' but the test handling this option is testing 'ansible_connection', so fix that.

The documentation and the code tends to suggest that in order to
configure the ansible connection plugin, the option to use is
'connection' but the test handling this option is testing
'ansible_connection', so fix that.

Signed-off-by: Arnaud Patard <apatard@hupstream.com>
@ssbarnea ssbarnea added the bug label Nov 22, 2020
@ssbarnea ssbarnea changed the title src/molecule/driver/delegated.py: Fix connection option handling Fix connection option handling for delegated driver Nov 22, 2020
@ssbarnea ssbarnea left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly the opposite, we should always follow Ansible and adapt to its changes, so we avoid confusing users. Based on https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html we need to assure we use ansible_connection and not just connection.

OK to update the docs, ok even if it breaks some users config, we will mark it as major and explain it that we follow ansible.

@apatard
apatard commented Nov 23, 2020
Copy link
Copy Markdown
Contributor Author

hm. not sure to follow. The change is only for the case of managed instances and here, the options are already not following ansible. See https://github.com/ansible-community/molecule/blob/master/src/molecule/driver/delegated.py#L185. If the idea is to get ansible variables by adding 'ansible_' as prefix, then my change is fine (and the identity option has possibly to be renamed). If not, then all options has to be renamed.
Am I missing something ?

@ssbarnea
ssbarnea merged commit 11d09c8 into ansible:master Nov 25, 2020
shvenkat-rh pushed a commit that referenced this pull request Sep 17, 2026
Fixes #4696.

## Problem

On the default driver with Molecule managing the instances, every host
with an instance-config entry gets `ansible_connection: smart` in the
generated inventory unless the create playbook recorded a `connection`.
If the create playbook recorded no `user` or `port`, the host also gets
`ansible_user: null` and `ansible_port: null`.

A host variable beats the same variable set at group or all level, so a
user's own `ansible_connection`, `ansible_user` or `ansible_port` in the
scenario's inventory is silently discarded.

Scenario, with a create playbook that records only `instance` and
`address`:

```yaml
# molecule/default/molecule.yml
driver:
  name: default
platforms:
  - name: probe
    groups:
      - probes
provisioner:
  name: ansible
  inventory:
    group_vars:
      probes:
        ansible_connection: local
        ansible_user: groupuser
        ansible_port: 2222
```

On `main` (`20b71bda`), the host entry Molecule generates and the
effective host vars:

```yaml
# <ephemeral directory>/inventory/ansible_inventory.yml
    probe: &id001
      ansible_connection: smart
      ansible_host: 127.0.0.1
      ansible_port: null
      ansible_ssh_common_args: -o UserKnownHostsFile=/dev/null -o ControlMaster=auto
        -o ControlPersist=60s -o ForwardX11=no -o LogLevel=ERROR -o IdentitiesOnly=yes
        -o StrictHostKeyChecking=no
      ansible_user: null
```

```json
// ansible-inventory --host probe
    "ansible_connection
9B78
": "smart",
    "ansible_port": null,
    "ansible_user": null,
```

## What changes

- Molecule writes `ansible_connection`, `ansible_user`, `ansible_host`
and `ansible_port` only when the create playbook recorded the matching
value. No `smart` fallback, no `null`.
- A scenario that sets no transport anywhere connects as before on every
supported core.
- A scenario that set its own value now gets it.

The same scenario on this branch (`fb42ac7d`). The `ansible_connection:
smart`, `ansible_port: null` and `ansible_user: null` lines are gone
from the host entry, and the group values apply:

```yaml
# <ephemeral directory>/inventory/ansible_inventory.yml
    probe: &id001
      ansible_host: 127.0.0.1
      ansible_ssh_common_args: -o UserKnownHostsFile=/dev/null -o ControlMaster=auto
        -o ControlPersist=60s -o ForwardX11=no -o LogLevel=ERROR -o IdentitiesOnly=yes
        -o StrictHostKeyChecking=no
```

```json
// ansible-inventory --host probe
    "ansible_connection": "local",
    "ansible_port": 2222,
    "ansible_user": "groupuser",
```

The reporter's scenario records no `connection`, so it now gets no
`ansible_connection` and connects over `ssh` without the `connection:
ssh` workaround. `driver.options.ansible_connection_options` still
applies only with `managed: False`, and `provisioner.connection_options`
is the override for the generated host vars. Both are unchanged.
`ansible_ssh_common_args` is still written for every managed host.

## Risk / Impact

A managed scenario that set `ansible_connection`, `ansible_user` or
`ansible_port` in its own inventory and depended on Molecule's
host-level `smart` or `null` winning now gets its own value.

## Root cause

`Delegated.ansible_connection_options()` in
`src/molecule/driver/delegated.py` builds the managed host vars from
`instance_config.yml`. The `instance_params` table's last entry is
`("connection", "smart")` under a truthy guard, so the fallback is
emitted on every host unless the instance config carries its own
`connection`. Below the table, `ansible_user`, `ansible_host` and
`ansible_port` are assigned unconditionally from `d.get(...)`, so an
unrecorded key lands as `None` and is dumped as `null`.

The `smart` fallback is a regression. #1674 (`0e07804b`, 2019)
introduced it. #2811 (`4efdb48a`, 2020-09) and #2988 (`11d09c81`,
2020-11) put it behind a guard, so `ansible_connection` was written only
when recorded. #3932 (`5b750ff4`, 2023-06) folded the keys into the
`instance_params` table and reintroduced an unconditional write, as
`null`. #3957 (`99a95f87`, 2023-07) made it `smart` because
`ansible_connection: null` crashed `meta: reset_connection`. This PR
leaves the key absent rather than `null`, so that crash does not return.
The `null` writes for user, host and port also date from #1674, so
dropping them is new behavior.

Ansible's own default transport has been `ssh` since ansible-core 2.16,
and `smart` resolved to `ssh` on any controller with a
ControlPersist-capable OpenSSH, so removing the host var does not change
the plugin for a scenario that sets nothing.

The fix changes the table entry to `("connection", None)` and drops
`None` values from the result in one comprehension at the return.

## Changes

- The default driver writes the four host vars only when the create
playbook recorded a value for them.
- `docs/configuration.md`, one paragraph after the instance-config API
block: the four host vars are written only when recorded, where a
managed scenario sets its own values, and that
`driver.options.ansible_connection_options` supplies host vars only with
`managed: False`.

## Tests

`tests/unit/driver/test_delegated.py`:

- `test_ansible_connection_options_when_managed`, ssh case: the
`"ansible_connection": "smart"` expectation is removed and `assert
"ansible_connection" not in ssh_result` is added. The winrm case, which
records `connection: winrm`, is unchanged and still expects it.
- New
`test_ansible_connection_options_omits_unrecorded_keys_when_managed`:
the instance config records `instance`, `address` and `user: null`.
Asserts `ansible_host` is present, the other three are absent, and no
value is `None`, which is the shape #3957 fixed.

## Test plan

- [x] `tox -e lint` passes
- [x] `tox -e pkg` passes
- [x] `tox -e py` passes, 910 passed, 7 skipped, same skips as `main`
- [x] Coverage unchanged, 90% total, `delegated.py` 93%
- [x] New test fails on `main` (2 failed, 44 passed), passes on this
branch (46 passed)
- [x] Scenario above run on `main` and this branch,
`ansible_inventory.yml` and `ansible-inventory --host` captured, outputs
are the blocks in this body
- [x] Scenario recording `address`, `user`, `port` and no `connection`,
only the `ansible_connection: smart` line differs
- [x] Scenario with no `instance_config.yml`, host entry `{}` on both
sides
- [x] Docs updated


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Delegated driver inventory no longer forces the `smart` connection
type when no connection is recorded.
- Connection settings from inventory, configuration files, environment
variables, and other supported options now apply correctly.
- Unrecorded connection values no longer override existing host or
inventory settings.
- SSH common arguments continue to be applied for both managed and
unmanaged instances.

- **Documentation**
- Updated delegated driver documentation to clarify connection and
inventory behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Jeff Pullen <9343691+jeffcpullen@users.noreply.github.com>
Co-authored-by: Jeff Pullen <9343691+jeffcpullen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0