зеркало из https://github.com/microsoft/lisa.git
342 строки
14 KiB
Python
342 строки
14 KiB
Python
# Copyright (c) Microsoft Corporation.
|
|
# Licensed under the MIT license.
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any, Dict, cast
|
|
from unittest.case import TestCase
|
|
|
|
from lisa import LisaException, constants, secret, variable
|
|
from lisa.util.logger import get_logger
|
|
|
|
|
|
class VariableTestCase(TestCase):
|
|
def setUp(self) -> None:
|
|
secret.reset()
|
|
|
|
def test_in_env(self) -> None:
|
|
os.environ["LISA_normal_value"] = "value_from_env"
|
|
os.environ["S_LISA_normal_entry"] = "s_value_from_env"
|
|
variables = self._get_default_variables()
|
|
variables.update(variable._load_from_env())
|
|
data = self._replace_and_validate(variables, {"normal_entry": "******"})
|
|
self.assertEqual("value_from_env", data["nested"]["normal_value"])
|
|
self.assertEqual("s_value_from_env", data["normal_entry"])
|
|
|
|
def test_in_pair(self) -> None:
|
|
pair1 = "normal_value:nv_from_pair"
|
|
pair2 = "S:normal_entry:s_value_from_env"
|
|
variables = self._get_default_variables()
|
|
variables.update(variable.add_secrets_from_pairs([pair1, pair2]))
|
|
data = self._replace_and_validate(variables, {"normal_entry": "******"})
|
|
self.assertEqual("nv_from_pair", data["nested"]["normal_value"])
|
|
self.assertEqual("s_value_from_env", data["normal_entry"])
|
|
|
|
def test_in_normal_file_outside_secret(self) -> None:
|
|
self._test_files(
|
|
"variable_normal.yml",
|
|
True,
|
|
{
|
|
"normal_value": "******",
|
|
"normal_entry": "******",
|
|
"secret_guid": "12345678-****-****-****-********90ab",
|
|
"secret_int": "1****0",
|
|
"secret_head_tail": "a****h",
|
|
},
|
|
)
|
|
|
|
def test_in_normal_file(self) -> None:
|
|
self._test_files(
|
|
"variable_normal.yml",
|
|
False,
|
|
{
|
|
"secret_guid": "12345678-****-****-****-********90ab",
|
|
"secret_int": "1****0",
|
|
"secret_head_tail": "a****h",
|
|
},
|
|
)
|
|
|
|
def test_in_secret_file_outside_secret(self) -> None:
|
|
self._test_files(
|
|
"variable_secret.yml",
|
|
True,
|
|
{
|
|
"normal_value": "******",
|
|
"normal_entry": "******",
|
|
"secret_guid": "12345678-****-****-****-********90ab",
|
|
"secret_int": "1****0",
|
|
"secret_head_tail": "a****h",
|
|
},
|
|
)
|
|
|
|
def test_in_secret_file(self) -> None:
|
|
self._test_files(
|
|
"variable_secret.yml",
|
|
False,
|
|
{},
|
|
)
|
|
|
|
def test_in_runbook_format_file(self) -> None:
|
|
runbook_data: Dict[str, Any] = {"variable": [{"file": "variable_normal.yml"}]}
|
|
data = self._test_runbook_file_entry(
|
|
runbook_data,
|
|
{
|
|
"secret_guid": "12345678-****-****-****-********90ab",
|
|
"secret_int": "1****0",
|
|
"secret_head_tail": "a****h",
|
|
},
|
|
{},
|
|
)
|
|
self.assertEqual("12345678-abcd-efab-cdef-1234567890ab", data["list"][0])
|
|
self.assertEqual(1234567890, data["list"][1]["dictInList"])
|
|
self.assertEqual("abcdefgh", data["headtail"])
|
|
self.assertEqual("normal_value", data["nested"]["normal_value"])
|
|
self.assertEqual("entry_value", data["normal_entry"])
|
|
|
|
def test_in_variable_path_with_variable(self) -> None:
|
|
runbook_data: Dict[str, Any] = {
|
|
"variable": [
|
|
{"file": "variable_$(var_in_var1).yml"},
|
|
{"name": "var_in_var1", "value": "$(var_in_var2)"},
|
|
{"name": "var_in_var2", "value": "normal"},
|
|
]
|
|
}
|
|
data = self._test_runbook_file_entry(
|
|
runbook_data,
|
|
{
|
|
"secret_guid": "12345678-****-****-****-********90ab",
|
|
"secret_int": "1****0",
|
|
"secret_head_tail": "a****h",
|
|
},
|
|
{},
|
|
)
|
|
self.assertEqual("12345678-abcd-efab-cdef-1234567890ab", data["list"][0])
|
|
self.assertEqual(1234567890, data["list"][1]["dictInList"])
|
|
self.assertEqual("abcdefgh", data["headtail"])
|
|
self.assertEqual("normal_value", data["nested"]["normal_value"])
|
|
self.assertEqual("entry_value", data["normal_entry"])
|
|
|
|
def test_in_runbook_path_with_variable(self) -> None:
|
|
runbook_data: Dict[str, Any] = {
|
|
"variable": [{"file": "variable_$(var_in_cmd).yml"}]
|
|
}
|
|
data = self._test_runbook_file_entry(
|
|
runbook_data,
|
|
{
|
|
"secret_guid": "12345678-****-****-****-********90ab",
|
|
"secret_int": "1****0",
|
|
"secret_head_tail": "a****h",
|
|
},
|
|
{
|
|
"var_in_cmd": variable.VariableEntry(
|
|
name="var_in_cmd", data="normal", is_used=False
|
|
)
|
|
},
|
|
)
|
|
self.assertEqual("12345678-abcd-efab-cdef-1234567890ab", data["list"][0])
|
|
self.assertEqual(1234567890, data["list"][1]["dictInList"])
|
|
self.assertEqual("abcdefgh", data["headtail"])
|
|
self.assertEqual("normal_value", data["nested"]["normal_value"])
|
|
self.assertEqual("entry_value", data["normal_entry"])
|
|
|
|
def test_in_runbook_format_variable(self) -> None:
|
|
runbook_data: Dict[str, Any] = {
|
|
"variable": [
|
|
{"name": "normal_value", "value": "normal_value"},
|
|
{"name": "normal_entry", "value": "entry_value"},
|
|
{
|
|
"name": "secret_guid",
|
|
"value": "12345678-abcd-efab-cdef-1234567890ab",
|
|
"is_secret": True,
|
|
"mask": "guid",
|
|
},
|
|
{
|
|
"name": "secret_int",
|
|
"value": 1234567890,
|
|
"is_secret": True,
|
|
"mask": "headtail",
|
|
},
|
|
{
|
|
"name": "secret_head_tail",
|
|
"value": "abcdefgh",
|
|
"is_secret": True,
|
|
"mask": "headtail",
|
|
},
|
|
]
|
|
}
|
|
data = self._test_runbook_file_entry(
|
|
runbook_data,
|
|
{
|
|
"secret_guid": "12345678-****-****-****-********90ab",
|
|
"secret_int": "1****0",
|
|
"secret_head_tail": "a****h",
|
|
},
|
|
{},
|
|
)
|
|
self.assertEqual("12345678-abcd-efab-cdef-1234567890ab", data["list"][0])
|
|
self.assertEqual(1234567890, data["list"][1]["dictInList"])
|
|
self.assertEqual("abcdefgh", data["headtail"])
|
|
self.assertEqual("normal_value", data["nested"]["normal_value"])
|
|
self.assertEqual("entry_value", data["normal_entry"])
|
|
|
|
def test_in_runbook_ordered(self) -> None:
|
|
runbook_data: Dict[str, Any] = {
|
|
"variable": [
|
|
{"file": "variable_normal.yml"},
|
|
{"name": "normal_value", "value": "normal_value1"},
|
|
{"name": "normal_entry", "value": "entry_value1"},
|
|
{
|
|
"name": "secret_guid",
|
|
"value": "12345678-abcd-efab-cdef-1234567890ac",
|
|
"is_secret": True,
|
|
"mask": "guid",
|
|
},
|
|
{
|
|
"name": "secret_int",
|
|
"value": 1234567891,
|
|
"is_secret": True,
|
|
"mask": "headtail",
|
|
},
|
|
{
|
|
"name": "secret_head_tail",
|
|
"value": "abcdefgi",
|
|
"is_secret": True,
|
|
"mask": "headtail",
|
|
},
|
|
]
|
|
}
|
|
data = self._test_runbook_file_entry(
|
|
runbook_data,
|
|
{
|
|
"secret_guid": "12345678-****-****-****-********90ac",
|
|
"secret_int": "1****1",
|
|
"secret_head_tail": "a****i",
|
|
},
|
|
{},
|
|
)
|
|
self.assertEqual("12345678-abcd-efab-cdef-1234567890ac", data["list"][0])
|
|
self.assertEqual(1234567891, data["list"][1]["dictInList"])
|
|
self.assertEqual("abcdefgi", data["headtail"])
|
|
self.assertEqual("normal_value1", data["nested"]["normal_value"])
|
|
self.assertEqual("entry_value1", data["normal_entry"])
|
|
|
|
def test_variable_not_found(self) -> None:
|
|
variables = self._get_default_variables()
|
|
with self.assertRaises(LisaException) as cm:
|
|
variable.replace_variables({"item": "$(notexists)"}, variables)
|
|
self.assertIsInstance(cm.exception, LisaException)
|
|
self.assertIn("cannot find variable", str(cm.exception))
|
|
|
|
def test_variable_not_used(self) -> None:
|
|
variables = self._get_default_variables()
|
|
variables["unused"] = variable.VariableEntry(name="unused", data="value")
|
|
self.assertFalse(variables["unused"].is_used)
|
|
self.assertFalse(variables["normal_value"].is_used)
|
|
self._replace_and_validate(variables, {"normal_entry": "original"})
|
|
self.assertFalse(variables["unused"].is_used)
|
|
self.assertTrue(variables["normal_value"].is_used)
|
|
|
|
def test_invalid_file_extension(self) -> None:
|
|
variables = self._get_default_variables()
|
|
with self.assertRaises(LisaException) as cm:
|
|
variables.update(variable._load_from_file("file.xml"))
|
|
self.assertIsInstance(cm.exception, LisaException)
|
|
self.assertIn("variable support only yaml and yml", str(cm.exception))
|
|
|
|
def _test_runbook_file_entry(
|
|
self,
|
|
data: Any,
|
|
secret_variables: Dict[str, str],
|
|
current_variables: Dict[str, variable.VariableEntry],
|
|
) -> Any:
|
|
constants.RUNBOOK_PATH = Path(__file__).parent
|
|
variables = self._get_default_variables()
|
|
variables.update(variable._load_from_runbook(data, current_variables))
|
|
data = self._replace_and_validate(variables, secret_variables)
|
|
return data
|
|
|
|
def _test_files(
|
|
self, file_name: str, all_secret: bool, secret_variables: Dict[str, str]
|
|
) -> Any:
|
|
constants.RUNBOOK_PATH = Path(__file__).parent
|
|
variables = self._get_default_variables()
|
|
variables.update(variable._load_from_file(file_name, is_secret=all_secret))
|
|
data = self._replace_and_validate(variables, secret_variables)
|
|
self.assertEqual("normal_value", data["nested"]["normal_value"])
|
|
self.assertEqual("entry_value", data["normal_entry"])
|
|
self.assertEqual("12345678-abcd-efab-cdef-1234567890ab", data["list"][0])
|
|
self.assertEqual(1234567890, data["list"][1]["dictInList"])
|
|
self.assertEqual("abcdefgh", data["headtail"])
|
|
return data
|
|
|
|
def _verify_secret(
|
|
self, variables: Dict[str, variable.VariableEntry], secrets: Dict[str, str]
|
|
) -> None:
|
|
log = get_logger()
|
|
copied_variables = dict(variables)
|
|
for secret_name, expected_value in secrets.items():
|
|
secret_name = secret_name.lower()
|
|
value = copied_variables[secret_name].data
|
|
del copied_variables[secret_name]
|
|
with self.assertLogs("lisa") as cm:
|
|
log.info(f"MUST_SECRET[{value}]")
|
|
self.assertListEqual(
|
|
[f"INFO:lisa.:MUST_SECRET[{expected_value}]"],
|
|
cm.output,
|
|
f"key: {secret_name}, value: {value}, "
|
|
f"expected: {expected_value} should be secret",
|
|
)
|
|
for key, unsecured_value in copied_variables.items():
|
|
with self.assertLogs("lisa") as cm:
|
|
log.info(f"MUST_NOT_SECRET[{unsecured_value}]")
|
|
self.assertListEqual(
|
|
[f"INFO:lisa.:MUST_NOT_SECRET[{unsecured_value}]"],
|
|
cm.output,
|
|
f"key: {key}, value: {unsecured_value} shouldn't be secret",
|
|
)
|
|
|
|
def _get_default_variables(self) -> Dict[str, variable.VariableEntry]:
|
|
data = {
|
|
"normal_value": variable.VariableEntry("normal_value", "original"),
|
|
"normal_entry": variable.VariableEntry("normal_entry", "original"),
|
|
"secret_guid": variable.VariableEntry("secret_guid", "original"),
|
|
"secret_int": variable.VariableEntry("secret_int", "original"),
|
|
"secret_head_tail": variable.VariableEntry("secret_head_tail", "original"),
|
|
}
|
|
return data
|
|
|
|
def _replace_and_validate(
|
|
self, variables: Dict[str, variable.VariableEntry], secrets: Dict[str, str]
|
|
) -> Dict[str, Any]:
|
|
data = variable.replace_variables(self._get_default_data(), variables=variables)
|
|
assert isinstance(data, dict), f"actual: {type(data)}"
|
|
self.assertDictEqual(
|
|
{
|
|
"keep": "normal",
|
|
"normal_entry": variables["normal_entry"].data,
|
|
"headtail": variables["secret_head_tail"].data,
|
|
"nested": {"normal_value": variables["normal_value"].data},
|
|
"list": [
|
|
variables["secret_guid"].data,
|
|
{"dictInList": variables["secret_int"].data},
|
|
],
|
|
"two_entries": f"1{variables['normal_entry'].data}"
|
|
f"2-$-()3{variables['normal_entry'].data}4",
|
|
},
|
|
data,
|
|
)
|
|
self._verify_secret(variables, secrets=secrets)
|
|
data = cast(Dict[str, Any], data)
|
|
return data
|
|
|
|
def _get_default_data(self) -> Dict[str, Any]:
|
|
data = {
|
|
"keep": "normal",
|
|
"normal_entry": "$(normal_entry)",
|
|
"headtail": "$(secret_head_tail)",
|
|
"nested": {"normal_value": "$(normal_value)"},
|
|
"list": ["$(secret_guid)", {"dictInList": "$(secret_int)"}],
|
|
"two_entries": "1$(normal_entry)2-$-()3$(normal_entry)4",
|
|
}
|
|
return data
|