1import types
2
3import pytest
4from salt.cloud.clouds import azurearm as azure
5from tests.support.mock import MagicMock, create_autospec, patch
6
7
8def copy_func(func, globals=None):
9    # I do not know that this is complete, but it's sufficient for now.
10    # The key to "moving" the function to another module (or stubbed module)
11    # is to update __globals__.
12
13    copied_func = types.FunctionType(
14        func.__code__, globals, func.__name__, func.__defaults__, func.__closure__
15    )
16    copied_func.__module__ = func.__module__
17    copied_func.__doc__ = func.__doc__
18    copied_func.__kwdefaults__ = func.__kwdefaults__
19    copied_func.__dict__.update(func.__dict__)
20    return copied_func
21
22
23def mock_module(mod, sut=None):
24    if sut is None:
25        sut = [None]
26
27    mock = create_autospec(mod)
28
29    # we need to provide a '__globals__' so functions being tested behave correctly.
30    mock_globals = {}
31
32    # exclude the system under test
33    for name in sut:
34        attr = getattr(mod, name)
35        if isinstance(attr, types.FunctionType):
36            attr = copy_func(attr, mock_globals)
37        setattr(mock, name, attr)
38
39    # fully populate our mock_globals
40    for name in mod.__dict__:
41        if name in mock.__dict__:
42            mock_globals[name] = mock.__dict__[name]
43        elif type(getattr(mod, name)) is type(types):  # is a module
44            mock_globals[name] = getattr(mock, name)
45        else:
46            mock_globals[name] = mod.__dict__[name]
47
48    return mock
49
50
51@pytest.fixture
52def configure_loader_modules():
53    return {azure: {"__opts__": {}, "__active_provider_name__": None}}
54
55
56@pytest.mark.skipif(not azure.HAS_LIBS, reason="azure not available")
57def test_function_signatures():
58    mock_azure = mock_module(azure, sut=["request_instance", "__opts__", "__utils__"])
59    mock_azure.create_network_interface.return_value = [
60        MagicMock(),
61        MagicMock(),
62        MagicMock(),
63    ]
64    mock_azure.salt.utils.stringutils.to_str.return_value = "P4ssw0rd"
65    mock_azure.salt.utils.cloud.gen_keys.return_value = [MagicMock(), MagicMock()]
66    mock_azure.__opts__["pki_dir"] = None
67
68    mock_azure.request_instance.__globals__[
69        "__builtins__"
70    ] = mock_azure.request_instance.__globals__["__builtins__"].copy()
71    mock_azure.request_instance.__globals__["__builtins__"]["getattr"] = MagicMock()
72
73    mock_azure.__utils__["cloud.fire_event"] = mock_azure.salt.utils.cloud.fire_event
74    mock_azure.__utils__[
75        "cloud.filter_event"
76    ] = mock_azure.salt.utils.cloud.filter_event
77    mock_azure.__opts__["sock_dir"] = MagicMock()
78    mock_azure.__opts__["transport"] = MagicMock()
79
80    mock_azure.request_instance(
81        {"image": "http://img", "storage_account": "blah", "size": ""}
82    )
83
84    # we literally only check that a final creation call occurred.
85    mock_azure.get_conn.return_value.virtual_machines.create_or_update.assert_called_once()
86
87
88def test_get_configured_provider():
89    mock_azure = mock_module(
90        azure, sut=["get_configured_provider", "__opts__", "__utils__"]
91    )
92
93    good_combos = [
94        {
95            "subscription_id": "3287abc8-f98a-c678-3bde-326766fd3617",
96            "tenant": "ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF",
97            "client_id": "ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF",
98            "secret": "XXXXXXXXXXXXXXXXXXXXXXXX",
99        },
100        {
101            "subscription_id": "3287abc8-f98a-c678-3bde-326766fd3617",
102            "username": "larry",
103            "password": "123pass",
104        },
105        {"subscription_id": "3287abc8-f98a-c678-3bde-326766fd3617"},
106    ]
107
108    for combo in good_combos:
109        mock_azure.__opts__["providers"] = {"azure_test": {"azurearm": combo}}
110        assert azure.get_configured_provider() == combo
111
112    bad_combos = [
113        {"subscrption": "3287abc8-f98a-c678-3bde-326766fd3617"},
114        {},
115    ]
116
117    for combo in bad_combos:
118        mock_azure.__opts__["providers"] = {"azure_test": {"azurearm": combo}}
119        assert not azure.get_configured_provider()
120
121
122def test_get_conn():
123    mock_azure = mock_module(azure, sut=["get_conn", "__opts__", "__utils__"])
124
125    mock_azure.__opts__["providers"] = {
126        "azure_test": {
127            "azurearm": {
128                "subscription_id": "3287abc8-f98a-c678-3bde-326766fd3617",
129                "driver": "azurearm",
130                "password": "monkeydonkey",
131            }
132        }
133    }
134    # password is stripped if username not provided
135    expected = {"subscription_id": "3287abc8-f98a-c678-3bde-326766fd3617"}
136    with patch(
137        "salt.utils.azurearm.get_client", side_effect=lambda client_type, **kw: kw
138    ):
139        assert azure.get_conn(client_type="compute") == expected
140
141    mock_azure.__opts__["providers"] = {
142        "azure_test": {
143            "azurearm": {
144                "subscription_id": "3287abc8-f98a-c678-3bde-326766fd3617",
145                "driver": "azurearm",
146                "username": "donkeymonkey",
147                "password": "monkeydonkey",
148            }
149        }
150    }
151    # username and password via provider config
152    expected = {
153        "subscription_id": "3287abc8-f98a-c678-3bde-326766fd3617",
154        "username": "donkeymonkey",
155        "password": "monkeydonkey",
156    }
157    with patch(
158        "salt.utils.azurearm.get_client", side_effect=lambda client_type, **kw: kw
159    ):
160        assert azure.get_conn(client_type="compute") == expected
161