cleanup(inventory): Drop support for multiple connections

The support for connections to multiple clusters in the inventory
plugin is dropped to better align with user expectations and how other
inventories work. If inventories of multiple clusters are needed the
inventory can be run multiple times with different configurations.
This also helps to clean up the code and make it simpler.

For now this adds a compatibility helper so that configurations with a
single connection entry remain supported and a warning is emitted.

Signed-off-by: Felix Matouschek <fmatouschek@redhat.com>
This commit is contained in:
Felix Matouschek
2024-07-03 11:45:21 +02:00
parent 2e9d6cec15
commit fd9c30103d
12 changed files with 394 additions and 394 deletions

View File

@@ -1,7 +1,6 @@
---
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
network_name: bridge-network
label_selector: app=test
namespaces:
- default
network_name: bridge-network
label_selector: app=test

View File

@@ -1,7 +1,6 @@
---
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
network_name: bridge-network
kube_secondary_dns: true
namespaces:
- default
network_name: bridge-network
kube_secondary_dns: true

View File

@@ -1,6 +1,5 @@
---
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
use_service: true
namespaces:
- default
use_service: true

View File

@@ -17,11 +17,13 @@ author:
description:
- Fetch virtual machines from one or more namespaces with an optional label selector.
- Groups by cluster name, namespace and labels.
- Uses the M(kubernetes.core.kubectl) connection plugin to access the Kubernetes cluster.
- Groups by cluster name, namespaces and labels.
- Uses V(*.kubevirt.[yml|yaml]) YAML configuration file to set parameter values.
- By default it uses the active context in I(~/.kube/config) and will return all virtual machines
for all namespaces the active user is authorized to access.
extends_documentation_fragment:
- kubevirt.core.kubevirt_auth_options
- inventory_cache
- constructed
@@ -34,108 +36,57 @@ options:
description:
- 'Specify the format of the host in the inventory group. Available specifiers: V(name), V(namespace) and V(uid).'
default: "{namespace}-{name}"
name:
description:
- Optional name to assign to the cluster. If not provided, a name is constructed from the server
and port.
namespaces:
description:
- List of namespaces. If not specified, will fetch virtual machines from all namespaces
the user is authorized to access.
label_selector:
description:
- Define a label selector to select a subset of the fetched virtual machines.
network_name:
description:
- In case multiple networks are attached to a virtual machine, define which interface should
be returned as primary IP address.
aliases: [ interface_name ]
kube_secondary_dns:
description:
- Enable C(kubesecondarydns) derived host names when using a secondary network interface.
type: bool
default: False
use_service:
description:
- Enable the use of C(Services) to establish an SSH connection to a virtual machine.
- Services are only used if no O(network_name) was provided.
type: bool
default: True
create_groups:
description:
- Enable the creation of groups from labels on C(VirtualMachines) and C(VirtualMachineInstances).
type: bool
default: False
base_domain:
description:
- Override the base domain used to construct host names. Used in case of
C(kubesecondarydns) or C(Services) of type C(NodePort) if O(append_base_domain) is set.
append_base_domain:
description:
- Append the base domain of the cluster to host names constructed from SSH C(Services) of type C(NodePort).
type: bool
default: False
api_version:
description:
- Specify the used KubeVirt API version.
default: "kubevirt.io/v1"
connections:
description:
- Optional list of cluster connection settings. If no connections are provided, the default
I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces
the active user is authorized to access.
- Optional list of cluster connection settings.
- This parameter is deprecated. Split your connections into multiple configuration files and move
parameters of each connection to the configuration top level.
- Deprecated in version C(1.5.0), will be removed in version C(3.0.0).
type: list
elements: dict
suboptions:
name:
description:
- Optional name to assign to the cluster. If not provided, a name is constructed from the server
and port.
kubeconfig:
description:
- Path to an existing Kubernetes config file. If not provided, and no other connection
options are provided, the Kubernetes client will attempt to load the default
configuration file from I(~/.kube/config). Can also be specified via E(K8S_AUTH_KUBECONFIG)
environment variable.
context:
description:
- The name of a context found in the config file. Can also be specified via E(K8S_AUTH_CONTEXT) environment
variable.
host:
description:
- Provide a URL for accessing the API. Can also be specified via E(K8S_AUTH_HOST) environment variable.
api_key:
description:
- Token used to authenticate with the API. Can also be specified via E(K8S_AUTH_API_KEY) environment
variable.
username:
description:
- Provide a username for authenticating with the API. Can also be specified via E(K8S_AUTH_USERNAME)
environment variable.
password:
description:
- Provide a password for authenticating with the API. Can also be specified via E(K8S_AUTH_PASSWORD)
environment variable.
client_cert:
description:
- Path to a certificate used to authenticate with the API. Can also be specified via E(K8S_AUTH_CERT_FILE)
environment variable.
aliases: [ cert_file ]
client_key:
description:
- Path to a key file used to authenticate with the API. Can also be specified via E(K8S_AUTH_KEY_FILE)
environment variable.
aliases: [ key_file ]
ca_cert:
description:
- Path to a CA certificate used to authenticate with the API. Can also be specified via
E(K8S_AUTH_SSL_CA_CERT) environment variable.
aliases: [ ssl_ca_cert ]
validate_certs:
description:
- Whether or not to verify the API server's SSL certificates. Can also be specified via
E(K8S_AUTH_VERIFY_SSL) environment variable.
type: bool
aliases: [ verify_ssl ]
namespaces:
description:
- List of namespaces. If not specified, will fetch virtual machines from all namespaces
the user is authorized to access.
label_selector:
description:
- Define a label selector to select a subset of the fetched virtual machines.
network_name:
description:
- In case multiple networks are attached to a virtual machine, define which interface should
be returned as primary IP address.
aliases: [ interface_name ]
kube_secondary_dns:
description:
- Enable C(kubesecondarydns) derived host names when using a secondary network interface.
type: bool
default: False
use_service:
description:
- Enable the use of C(Services) to establish an SSH connection to a virtual machine.
- Services are only used if no O(connections.network_name) was provided.
type: bool
default: True
create_groups:
description:
- Enable the creation of groups from labels on C(VirtualMachines) and C(VirtualMachineInstances).
type: bool
default: False
base_domain:
description:
- Override the base domain used to construct host names. Used in case of
C(kubesecondarydns) or C(Services) of type C(NodePort) if O(connections.append_base_domain) is set.
append_base_domain:
description:
- Append the base domain of the cluster to host names constructed from SSH C(Services) of type C(NodePort).
type: bool
default: False
api_version:
description:
- Specify the used KubeVirt API version.
default: "kubevirt.io/v1"
requirements:
- "python >= 3.9"
@@ -146,35 +97,31 @@ requirements:
EXAMPLES = """
# Filename must end with kubevirt.[yml|yaml]
- name: Authenticate with token and return all virtual machines from all accessible namespaces
plugin: kubevirt.core.kubevirt
connections:
- host: https://192.168.64.4:8443
api_key: xxxxxxxxxxxxxxxx
validate_certs: false
# Authenticate with token and return all virtual machines from all accessible namespaces
- plugin: kubevirt.core.kubevirt
host: https://192.168.64.4:8443
api_key: xxxxxxxxxxxxxxxx
validate_certs: false
- name: Use default ~/.kube/config and return virtual machines from namespace testing connected to network bridge-network
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- testing
network_name: bridge-network
# Use default ~/.kube/config and return virtual machines from namespace testing connected to network bridge-network
- plugin: kubevirt.core.kubevirt
namespaces:
- testing
network_name: bridge-network
- name: Use default ~/.kube/config and return virtual machines from namespace testing with label app=test
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- testing
label_selector: app=test
# Use default ~/.kube/config and return virtual machines from namespace testing with label app=test
- plugin: kubevirt.core.kubevirt
namespaces:
- testing
label_selector: app=test
- name: Use a custom config file and a specific context
plugin: kubevirt.core.kubevirt
connections:
- kubeconfig: /path/to/config
context: 'awx/192-168-64-4:8443/developer'
# Use a custom config file and a specific context
- plugin: kubevirt.core.kubevirt
kubeconfig: /path/to/config
context: 'awx/192-168-64-4:8443/developer'
"""
from dataclasses import dataclass
from dataclasses import dataclass, InitVar
from json import loads
from re import compile as re_compile
from typing import (
@@ -246,21 +193,58 @@ class InventoryOptions:
base_domain: Optional[str] = None
append_base_domain: Optional[bool] = None
host_format: Optional[str] = None
config_data: InitVar[Optional[Dict]] = None
def __post_init__(self):
# Set defaults in __post_init__ to allow instatiating class with None values
if self.api_version is None:
self.api_version = "kubevirt.io/v1"
if self.kube_secondary_dns is None:
self.kube_secondary_dns = False
if self.use_service is None:
self.use_service = True
if self.create_groups is None:
self.create_groups = False
if self.append_base_domain is None:
self.append_base_domain = False
if self.host_format is None:
self.host_format = "{namespace}-{name}"
def __post_init__(self, config_data: Optional[Dict]) -> None:
if not config_data or not isinstance(config_data, dict):
config_data = {}
# Copy values from config_data and set defaults for keys not present
self.api_version = (
self.api_version
if self.api_version is not None
else config_data.get("api_version", "kubevirt.io/v1")
)
self.label_selector = (
self.label_selector
if self.label_selector is not None
else config_data.get("label_selector")
)
self.network_name = (
self.network_name
if self.network_name is not None
else config_data.get("network_name", config_data.get("interface_name"))
)
self.kube_secondary_dns = (
self.kube_secondary_dns
if self.kube_secondary_dns is not None
else config_data.get("kube_secondary_dns", False)
)
self.use_service = (
self.use_service
if self.use_service is not None
else config_data.get("use_service", True)
)
self.create_groups = (
self.create_groups
if self.create_groups is not None
else config_data.get("create_groups", False)
)
self.base_domain = (
self.base_domain
if self.base_domain is not None
else config_data.get("base_domain")
)
self.append_base_domain = (
self.append_base_domain
if self.append_base_domain is not None
else config_data.get("append_base_domain", False)
)
self.host_format = (
self.host_format
if self.host_format is not None
else config_data.get("host_format", "{namespace}-{name}")
)
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
@@ -378,7 +362,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def __init__(self) -> None:
super().__init__()
self.host_format = None
def verify_file(self, path: str) -> None:
"""
@@ -390,20 +373,15 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def parse(self, inventory: Any, loader: Any, path: str, cache: bool = True) -> None:
"""
parse runs basic setup of the inventory.
parse is the main entry point of the inventory.
It checks for availability of the Kubernetes Python client,
gets the configuration and runs fetch_objects or
if there is a cache it is used instead.
"""
super().parse(inventory, loader, path)
cache_key = self._get_cache_prefix(path)
config_data = self._read_config_data(path)
self.host_format = config_data.get("host_format")
self.setup(config_data, cache, cache_key)
def setup(self, config_data: Dict, cache: bool, cache_key: str) -> None:
"""
setup checks for availability of the Kubernetes Python client,
gets the configured connections and runs fetch_objects on them.
If there is a cache it is returned instead.
"""
if not HAS_K8S_MODULE_HELPER:
raise KubeVirtInventoryException(
"This module requires the Kubernetes Python client. "
@@ -418,61 +396,66 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
pass
if not source_data:
self.fetch_objects(config_data.get("connections"))
self.fetch_objects(config_data)
def fetch_objects(self, connections: Optional[List[Dict]]) -> None:
def fetch_objects(self, config_data: Dict) -> None:
"""
fetch_objects populates the inventory with every configured connection.
fetch_objects populates the inventory with the specified parameters.
"""
if connections:
if not config_data or not isinstance(config_data, dict):
config_data = {}
self.connections_compatibility(config_data)
client = get_api_client(**config_data)
name = config_data.get(
"name", self.get_default_host_name(client.configuration.host)
)
namespaces = (
config_data["namespaces"]
if config_data.get("namespaces")
else self.get_available_namespaces(client)
)
opts = InventoryOptions(config_data=config_data)
if opts.base_domain is None:
opts.base_domain = self.get_cluster_domain(client)
for namespace in namespaces:
self.populate_inventory_from_namespace(client, name, namespace, opts)
def connections_compatibility(self, config_data: Dict) -> None:
collection_name = "kubevirt.core"
if (connections := config_data.get("connections")) is None:
return
self.display.deprecated(
msg="The 'connections' parameter is deprecated and now supports only a single list entry.",
version="2.0.0",
collection_name=collection_name,
)
if not isinstance(connections, list):
raise KubeVirtInventoryException("Expecting connections to be a list.")
if len(connections) == 1:
if not isinstance(connections[0], dict):
raise KubeVirtInventoryException(
"Expecting connection to be a dictionary."
)
# Copy the single connections entry into the top level
for k, v in connections[0].items():
config_data[k] = v
self.display.deprecated(
msg="The 'connections' parameter is deprecated and starting with version 2.0.0 of kubevirt.core supports only a single entry.",
msg="Move all of your connection parameters to the configuration top level.",
version="3.0.0",
collection_name="kubevirt.core",
collection_name=collection_name,
)
if not isinstance(connections, list):
raise KubeVirtInventoryException("Expecting connections to be a list.")
for connection in connections:
if not isinstance(connection, dict):
raise KubeVirtInventoryException(
"Expecting connection to be a dictionary."
)
client = get_api_client(**connection)
name = connection.get(
"name", self.get_default_host_name(client.configuration.host)
)
if connection.get("namespaces"):
namespaces = connection["namespaces"]
else:
namespaces = self.get_available_namespaces(client)
opts = InventoryOptions(
connection.get("api_version"),
connection.get("label_selector"),
connection.get("network_name", connection.get("interface_name")),
connection.get("kube_secondary_dns"),
connection.get("use_service"),
connection.get("create_groups"),
connection.get("base_domain", self.get_cluster_domain(client)),
connection.get("append_base_domain"),
self.host_format,
)
for namespace in namespaces:
self.populate_inventory_from_namespace(
client, name, namespace, opts
)
else:
client = get_api_client()
name = self.get_default_host_name(client.configuration.host)
namespaces = self.get_available_namespaces(client)
opts = InventoryOptions(
host_format=self.host_format,
base_domain=self.get_cluster_domain(client),
elif len(connections) > 1:
self.display.deprecated(
msg="Split your connections into multiple configuration files.",
version="2.0.0",
collection_name=collection_name,
removed=True,
)
for namespace in namespaces:
self.populate_inventory_from_namespace(client, name, namespace, opts)
def get_cluster_domain(self, client: K8SClient) -> Optional[str]:
"""

View File

@@ -1,7 +1,6 @@
---
plugin: kubevirt.core.kubevirt
connections:
- name: test
namespaces:
- default
create_groups: true
name: test
namespaces:
- default
create_groups: true

View File

@@ -1,7 +1,6 @@
---
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
create_groups: true
label_selector: app=test
namespaces:
- default
create_groups: true
label_selector: app=test

View File

@@ -1,7 +1,6 @@
---
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
create_groups: true
network_name: bridge-network
namespaces:
- default
create_groups: true
network_name: bridge-network

View File

@@ -1,6 +1,5 @@
---
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
network_name: bridge-network
namespaces:
- default
network_name: bridge-network

View File

@@ -206,29 +206,6 @@ def test_is_windows(guest_os_info, annotations, expected):
assert InventoryModule.is_windows(guest_os_info, annotations) == expected
def test_parse(mocker, inventory):
path = "/testpath"
cache_prefix = "test-prefix"
host_format = "test-format"
config_data = {"host_format": host_format}
cache = True
get_cache_prefix = mocker.patch.object(
inventory, "_get_cache_prefix", return_value=cache_prefix
)
read_config_data = mocker.patch.object(
inventory, "_read_config_data", return_value=config_data
)
setup = mocker.patch.object(inventory, "setup")
inventory.parse(None, None, path, cache)
get_cache_prefix.assert_called_once_with(path)
read_config_data.assert_called_once_with(path)
setup.assert_called_once_with(config_data, cache, cache_prefix)
assert inventory.host_format == host_format
def test_get_cluster_domain(inventory, client):
assert inventory.get_cluster_domain(client) == DEFAULT_BASE_DOMAIN

View File

@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Copyright 2024 Red Hat, Inc.
# Apache License 2.0 (see LICENSE or http://www.apache.org/licenses/LICENSE-2.0)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import pytest
from ansible.errors import AnsibleError
from ansible_collections.kubevirt.core.plugins.inventory.kubevirt import (
KubeVirtInventoryException,
)
def test_config_data_without_connections_ignored(inventory):
config_data = {
"name": "connection-1",
"namespaces": ["default"],
"network_name": "bridge-network",
"label_selector": "app=test",
}
inventory.connections_compatibility(config_data)
assert config_data["name"] == "connection-1"
assert config_data["namespaces"] == ["default"]
assert config_data["network_name"] == "bridge-network"
assert config_data["label_selector"] == "app=test"
def test_single_connection_supported(inventory):
config_data = {
"connections": [
{
"name": "connection-1",
"namespaces": ["default"],
"network_name": "bridge-network",
"label_selector": "app=test",
}
],
"namespaces": ["something"],
"network_name": "some-network",
"label_selector": "app=something",
}
inventory.connections_compatibility(config_data)
assert config_data["name"] == "connection-1"
assert config_data["namespaces"] == ["default"]
assert config_data["network_name"] == "bridge-network"
assert config_data["label_selector"] == "app=test"
def test_multiple_connections_not_supported(inventory):
with pytest.raises(
AnsibleError, match="Split your connections into multiple configuration files."
):
inventory.connections_compatibility(
{
"connections": [
{
"name": "connection-1",
},
{
"name": "connection-2",
},
],
}
)
@pytest.mark.parametrize(
"config_data,expected",
[
({"connections": "test"}, "Expecting connections to be a list."),
(
{"connections": [["test", "test"]]},
"Expecting connection to be a dictionary.",
),
],
)
def test_connections_exceptions(inventory, config_data, expected):
with pytest.raises(KubeVirtInventoryException, match=expected):
inventory.connections_compatibility(config_data)

View File

@@ -11,7 +11,6 @@ import pytest
from ansible_collections.kubevirt.core.plugins.inventory.kubevirt import (
InventoryOptions,
KubeVirtInventoryException,
)
from ansible_collections.kubevirt.core.tests.unit.plugins.inventory.constants import (
@@ -22,135 +21,97 @@ from ansible_collections.kubevirt.core.plugins.inventory import kubevirt
@pytest.mark.parametrize(
"connections,expected",
"config_data,expected",
[
(
None,
[
{
"name": "default-hostname",
"namespace": DEFAULT_NAMESPACE,
"opts": InventoryOptions(),
},
],
{
"name": "default-hostname",
"namespaces": [DEFAULT_NAMESPACE],
"opts": InventoryOptions(),
},
),
(
[
{
"name": "test",
},
],
[
{
"name": "test",
"namespace": DEFAULT_NAMESPACE,
"opts": InventoryOptions(),
},
],
{
"name": "test",
},
{
"name": "test",
"namespaces": [DEFAULT_NAMESPACE],
"opts": InventoryOptions(),
},
),
(
[
{"name": "test", "namespaces": ["test"]},
],
[
{"name": "test", "namespace": "test", "opts": InventoryOptions()},
],
{"name": "test", "namespaces": ["test"]},
{"name": "test", "namespaces": ["test"], "opts": InventoryOptions()},
),
(
[
{
"name": "test",
"namespaces": ["test"],
"use_service": True,
"create_groups": True,
"append_base_domain": True,
"base_domain": "test-domain",
},
],
[
{
"name": "test",
"namespace": "test",
"opts": InventoryOptions(
use_service=True,
create_groups=True,
append_base_domain=True,
base_domain="test-domain",
),
},
],
{
"name": "test",
"namespaces": ["test"],
"use_service": True,
"create_groups": True,
"append_base_domain": True,
"base_domain": "test-domain",
},
{
"name": "test",
"namespaces": ["test"],
"opts": InventoryOptions(
use_service=True,
create_groups=True,
append_base_domain=True,
base_domain="test-domain",
),
},
),
(
[
{
"name": "test",
"namespaces": ["test"],
"use_service": True,
"create_groups": True,
"append_base_domain": True,
"base_domain": "test-domain",
"network_name": "test-network",
},
],
[
{
"name": "test",
"namespace": "test",
"opts": InventoryOptions(
use_service=True,
create_groups=True,
append_base_domain=True,
base_domain="test-domain",
network_name="test-network",
),
},
],
{
"name": "test",
"namespaces": ["test"],
"use_service": True,
"create_groups": True,
"append_base_domain": True,
"base_domain": "test-domain",
"network_name": "test-network",
},
{
"name": "test",
"namespaces": ["test"],
"opts": InventoryOptions(
use_service=True,
create_groups=True,
append_base_domain=True,
base_domain="test-domain",
network_name="test-network",
),
},
),
(
[
{
"name": "test",
"namespaces": ["test"],
"use_service": True,
"create_groups": True,
"append_base_domain": True,
"base_domain": "test-domain",
"interface_name": "test-interface",
},
],
[
{
"name": "test",
"namespace": "test",
"opts": InventoryOptions(
use_service=True,
create_groups=True,
append_base_domain=True,
base_domain="test-domain",
network_name="test-interface",
),
},
],
),
(
[
{
"name": "test",
},
{"name": "test", "namespaces": ["test"]},
],
[
{
"name": "test",
"namespace": DEFAULT_NAMESPACE,
"opts": InventoryOptions(),
},
{"name": "test", "namespace": "test", "opts": InventoryOptions()},
],
{
"name": "test",
"namespaces": ["test"],
"use_service": True,
"create_groups": True,
"append_base_domain": True,
"base_domain": "test-domain",
"interface_name": "test-interface",
},
{
"name": "test",
"namespaces": ["test"],
"opts": InventoryOptions(
use_service=True,
create_groups=True,
append_base_domain=True,
base_domain="test-domain",
network_name="test-interface",
),
},
),
],
)
def test_fetch_objects(mocker, inventory, connections, expected):
def test_fetch_objects(mocker, inventory, config_data, expected):
mocker.patch.object(kubevirt, "get_api_client")
mocker.patch.object(
inventory, "get_default_host_name", return_value="default-hostname"
@@ -158,8 +119,7 @@ def test_fetch_objects(mocker, inventory, connections, expected):
cluster_domain = "test.com"
mocker.patch.object(inventory, "get_cluster_domain", return_value=cluster_domain)
for e in expected:
e["opts"].base_domain = e["opts"].base_domain or cluster_domain
expected["opts"].base_domain = expected["opts"].base_domain or cluster_domain
get_available_namespaces = mocker.patch.object(
inventory, "get_available_namespaces", return_value=[DEFAULT_NAMESPACE]
@@ -168,26 +128,16 @@ def test_fetch_objects(mocker, inventory, connections, expected):
inventory, "populate_inventory_from_namespace"
)
inventory.fetch_objects(connections)
inventory.fetch_objects(config_data)
if config_data and "namespaces" in config_data:
get_available_namespaces.assert_not_called()
else:
get_available_namespaces.assert_called()
get_available_namespaces.assert_has_calls(
[mocker.call(mocker.ANY) for c in connections or [{}] if "namespaces" not in c]
)
populate_inventory_from_namespace.assert_has_calls(
[
mocker.call(mocker.ANY, e["name"], e["namespace"], e["opts"])
for e in expected
mocker.call(mocker.ANY, expected["name"], namespace, expected["opts"])
for namespace in expected["namespaces"]
]
)
@pytest.mark.parametrize(
"connections,expected",
[
("test", "Expecting connections to be a list."),
(["test", "test"], "Expecting connection to be a dictionary."),
],
)
def test_fetch_objects_exceptions(inventory, connections, expected):
with pytest.raises(KubeVirtInventoryException, match=expected):
inventory.fetch_objects(connections)

View File

@@ -24,20 +24,28 @@ from ansible_collections.kubevirt.core.plugins.inventory.kubevirt import (
False,
],
)
def test_cache_is_used(mocker, inventory, cache):
connections = [{"test-conn": {}}]
config_data = {"connections": connections}
cache_key = "test-prefix"
def test_parse(mocker, inventory, cache):
path = "/testpath"
cache_prefix = "test-prefix"
config_data = {"host_format": "test-format"}
mocker.patch.dict(inventory._cache, {cache_key: {"test-key": "test-value"}})
mocker.patch.dict(inventory._cache, {cache_prefix: {"test-key": "test-value"}})
get_cache_prefix = mocker.patch.object(
inventory, "_get_cache_prefix", return_value=cache_prefix
)
read_config_data = mocker.patch.object(
inventory, "_read_config_data", return_value=config_data
)
fetch_objects = mocker.patch.object(inventory, "fetch_objects")
inventory.setup(config_data, cache, cache_key)
inventory.parse(None, None, path, cache)
get_cache_prefix.assert_called_once_with(path)
read_config_data.assert_called_once_with(path)
if cache:
fetch_objects.assert_not_called()
else:
fetch_objects.assert_called_once_with(connections)
fetch_objects.assert_called_once_with(config_data)
@pytest.mark.parametrize(
@@ -49,15 +57,17 @@ def test_cache_is_used(mocker, inventory, cache):
)
def test_k8s_client_missing(mocker, inventory, present):
mocker.patch.object(kubevirt, "HAS_K8S_MODULE_HELPER", present)
mocker.patch.object(inventory, "_get_cache_prefix")
mocker.patch.object(inventory, "_read_config_data")
fetch_objects = mocker.patch.object(inventory, "fetch_objects")
if present:
inventory.setup({}, False, "test")
inventory.parse(None, None, "", False)
fetch_objects.assert_called_once()
else:
with pytest.raises(
KubeVirtInventoryException,
match="This module requires the Kubernetes Python client. Try `pip install kubernetes`. Detail: None",
):
inventory.setup({}, False, "test")
inventory.parse(None, None, "", False)
fetch_objects.assert_not_called()