Coverage for src/es_testbed/classes/testplan.py: 66%
68 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-23 13:32 -0600
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-23 13:32 -0600
1"""TestPlan Class Definition"""
2import typing as t
3from es_testbed.exceptions import TestPlanMisconfig
4from es_testbed.defaults import TESTPLAN
5from es_testbed.helpers.utils import randomstr, getlogger
6from .args import Args
7from .ilm import IlmBuilder
8# pylint: disable=missing-docstring
10class TestPlan(Args):
11 __test__ = False
12 def __init__(
13 self,
14 settings: t.Dict[str, t.Any] = None,
15 defaults: t.Dict[str, t.Any] = None,
16 default_entities: bool = True,
17 ):
18 if defaults is None: 18 ↛ 20line 18 didn't jump to line 20, because the condition on line 18 was never false
19 defaults = TESTPLAN
20 super().__init__(settings=settings, defaults=defaults)
21 self.logger = getlogger('es_testbed.TestPlan')
22 self.entities = []
23 self.ilm = False
24 self.prefix = None
25 self.uniq = randomstr(length=8, lowercase=True)
26 self.update_settings(settings)
27 self.logger.debug('settings = %s', self.asdict)
28 self.update_ilm()
29 if not self.entities: 29 ↛ exitline 29 didn't return from function '__init__', because the condition on line 29 was never false
30 if default_entities: 30 ↛ exitline 30 didn't return from function '__init__', because the condition on line 30 was never false
31 self.make_default_entities()
33 ### Example settings
34 # settings={
35 # 'type': 'indices', # Default is indices? Or should it be data_streams?
36 # 'prefix': 'es-testbed', # Provide this value as a default
37 # 'rollover_alias': True, # Only respected if 'type' == 'indices'.
38 # # Will rollover after creation and filling
39 # 'uniq': 'my-unique-str', # If not provided, randomstr(length=8, lowercase=True)
40 # 'ilm': { # All of these ILM values are defaults
41 # 'tiers': ['hot', 'delete'],
42 # 'forcemerge': False,
43 # 'max_num_segments': 1,
44 # 'repository': None,
45 # }
46 #
47 # # If these keys aren't specified per entity, then all entities will get this treatment
48 # # EXCEPT for the is_write_index for aliases and data_streams
49 #
50 # 'defaults': {
51 # 'entity_count': 3,
52 # 'docs': 10,
53 # 'match': True,
54 # 'searchable': tier...
55 # }
57 # # Manually specifying entities makes sense for individual indices, but not so much for
58 # # alias-backed indices or data_streams
59 # 'entities': [
60 # {
61 # 'docs': 10,
62 # 'match': True,
63 # 'searchable': 'frozen'
64 # },
65 # {
66 # 'docs': 10,
67 # 'match': False,
68 # 'searchable': 'cold',
69 # },
70 # {
71 # 'docs': 10,
72 # 'match': True,
73 # 'searchable': 'frozen'
74 # },
75 # ]
76 # }
77 def add_entity(
78 self,
79 docs: t.Optional[int] = 10,
80 match: t.Optional[bool] = True,
81 searchable: t.Optional[str] = None
82 ) -> None:
83 entity = {'docs': docs, 'match': match}
84 if searchable: 84 ↛ 85line 84 didn't jump to line 85, because the condition on line 84 was never true
85 entity['searchable'] = searchable
86 self.entities.append(entity)
88 def make_default_entities(self) -> None:
89 if self.settings and isinstance(self.settings, dict): 89 ↛ 92line 89 didn't jump to line 92, because the condition on line 89 was never false
90 if 'defaults' in self.settings: 90 ↛ 92line 90 didn't jump to line 92, because the condition on line 90 was never false
91 defs = self.settings['defaults']
92 kwargs = {'docs': defs['docs'], 'match': defs['match'], 'searchable': defs['searchable']}
93 for _ in range(0, defs['entity_count']):
94 self.add_entity(**kwargs)
95 self.logger.debug('Created %s entities', len(self.entities))
97 def update_ilm(self) -> None:
98 if self.use_ilm():
99 self.ilm = IlmBuilder() # Set defaults
100 for k,v in self.settings['ilm'].items(): 100 ↛ 101line 100 didn't jump to line 101, because the loop on line 100 never started
101 self.logger.debug('IlmBuilder.%s = %s', k, v)
102 setattr(self.ilm, k, v)
103 # If cold or frozen tiers weren't included in settings['ilm']['tiers']
104 # we manually correct here
105 for entity in self.entities: 105 ↛ 106line 105 didn't jump to line 106, because the loop on line 105 never started
106 if 'searchable' in entity and entity['searchable'] is not None:
107 if not entity['searchable'] in self.ilm.tiers:
108 self.ilm.tiers.append(entity['searchable'])
109 self.logger.debug('ILM settings = %s',
110 self.ilm.asdict if isinstance(self.ilm, IlmBuilder) else self.ilm)
113 def use_ilm(self) -> bool:
114 use_ilm = False
115 self.logger.debug('INIT: use_ilm = %s', use_ilm)
116 if 'ilm' in self.settings: 116 ↛ 126line 116 didn't jump to line 126, because the condition on line 116 was never false
117 if isinstance(self.settings['ilm'], dict): # It's a dictionary, not a bool
118 self.logger.debug('settings["ilm"] is a dictionary')
119 use_ilm = True
120 elif isinstance(self.settings['ilm'], bool): 120 ↛ 123line 120 didn't jump to line 123, because the condition on line 120 was never false
121 self.logger.debug('settings["ilm"] is a bool')
122 use_ilm = self.settings['ilm'] # Accept whatever the boolean value is
123 elif self.settings['ilm'] is None: # Empty dict is truthy, but is not None
124 self.logger.debug('settings["ilm"] is None')
125 use_ilm = False
126 if self.entities: 126 ↛ 128line 126 didn't jump to line 128, because the condition on line 126 was never true
127 # Detect if cold or frozen tiers were included in settings['ilm']['tiers']
128 for entity in self.entities:
129 if 'searchable' in entity and entity['searchable'] is not None:
130 self.logger.debug('Test entities contain searchable snapshots')
131 if not use_ilm:
132 raise TestPlanMisconfig(
133 'Searchable entities were found, but ILM is disabled')
134 self.logger.debug('FINAL: use_ilm = %s', use_ilm)
135 return use_ilm