Coverage for /Users/buh/.pyenv/versions/3.12.2/envs/es-testbed/lib/python3.12/site-packages/es_testbed/classes/entitymgrs/entitymgr.py: 86%
125 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-24 22:41 -0600
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-24 22:41 -0600
1"""Entity Class Definition"""
2import typing as t
3from dotmap import DotMap
4from elasticsearch8 import Elasticsearch
5from es_testbed.defaults import NAMEMAPPER, PLURALMAP
6from es_testbed.helpers.es_api import delete, get
7from es_testbed.helpers.utils import getlogger, uniq_values
9# pylint: disable=missing-docstring,broad-exception-caught,too-many-instance-attributes
11class EntityMgr:
12 kind = 'entity_type'
13 listname = 'entity_mgrs'
14 def __init__(
15 self,
16 client: Elasticsearch = None,
17 plan: DotMap = None,
18 autobuild: t.Optional[bool] = True,
19 ):
20 self.logger = getlogger('es_testbed.EntityMgr')
21 self.client = client
22 self.plan = plan
23 self.success = False
24 if autobuild:
25 self.setup()
27 @property
28 def entity_list(self):
29 return self.plan[self.listname]
30 @entity_list.setter
31 def entity_list(self, value: t.Sequence) -> None:
32 self.plan[self.listname] = value
33 @property
34 def entity_root(self) -> str:
35 return f'{self.plan.prefix}-{self.ident()}-{self.plan.uniq}'
36 @property
37 def failsafe(self):
38 return self.plan.failsafes[self.kind]
39 @failsafe.setter
40 def failsafe(self, value: t.Sequence) -> None:
41 self.plan.failsafes[self.kind] = value
42 @property
43 def indexlist(self) -> t.Sequence[str]:
44 return [] # Empty attribute/property waiting to be overridden
45 @property
46 def last(self) -> str:
47 return self.entity_list[-1] # Return the most recently appended item
48 @property
49 def logdisplay(self) -> str:
50 return self.kind
51 @property
52 def name(self) -> str:
53 return f'{self.entity_root}{self.suffix}'
54 @property
55 def pattern(self) -> str:
56 return f'*{self.entity_root}*'
57 @property
58 def suffix(self) -> str:
59 return f'-{len(self.entity_list) + 1:06}'
61 def appender(self, name: str) -> None:
62 self.failsafe.append(name)
63 self.entity_list.append(name)
65 def ident(self, dkey=None):
66 if not dkey:
67 dkey=self.kind
68 return NAMEMAPPER[dkey]
70 def iterate_clean(self) -> None:
71 succeed = True
72 positions = []
73 for idx, entity in enumerate(self.entity_list): # There should only be one, but we cover it
74 value = entity
75 if self.kind == 'index':
76 value = entity.name
77 self.logger.debug('Deleting %s %s', self.logdisplay, value)
78 try:
79 delete(self.client, self.kind, value)
80 except Exception as err:
81 succeed = False
82 msg = f'Unable to delete {self.logdisplay}: {value}. Error: {err}'
83 self.logger.error(msg)
84 continue
85 self.logger.debug('Deleted %s %s', self.logdisplay, value)
86 positions.append(idx)
87 positions.sort() # Sort first to ensure lowest to highest order
88 for idx in reversed(positions): # Reverse the list and iterate
89 del self.entity_list[idx] # Delete the value at position idx
90 del self.plan.failsafes[self.kind][idx] # Delete the value at position idx
91 return succeed
93 def scan(self) -> t.Sequence[str]:
94 """Find all entities matching our pattern"""
95 entities = get(self.client, self.kind, self.pattern)
96 msg = f'{self.kind} entities found matching pattern "{self.pattern}": {entities}'
97 self.logger.debug(msg)
98 self.failsafe = entities
99 return entities
101 def setup(self):
102 pass
104 def teardown(self):
105 display = PLURALMAP[self.kind] if self.kind in PLURALMAP else self.kind
106 if not self.success: 106 ↛ 107line 106 didn't jump to line 107
107 msg = (
108 f'Setup did not complete successfully. '
109 f'Manual cleanup of {display}s may be necessary.'
110 )
111 self.logger.warning(msg)
112 self.verify(correct=True) # Catch any entities that might exist but not be in entity_list
113 if self.entity_list:
114 if self.iterate_clean(): 114 ↛ exitline 114 didn't return from function 'teardown', because the condition on line 114 was never false
115 self.logger.info('Cleanup of %ss completed successfully.', display)
117 def track_index(self, name: str) -> None:
118 pass
120 def verify(self, correct: bool=False) -> t.Union[t.Sequence[str], None]:
121 retval = None
122 diffs = False
123 curr = self.scan() # This is what entity_list _should_ look like.
124 if self.kind == 'index':
125 entities = self.indexlist
126 else:
127 entities = self.entity_list
128 self.logger.debug('Getting unique values from scan output (not in self.entity_list)')
129 scan_diff = uniq_values(entities, curr)
130 self.logger.debug('Getting unique values in self.entity_list (not in scan)')
131 entity_diff = uniq_values(curr, entities)
132 if entity_diff:
133 diffs = True
134 self.logger.warning('Values in entity_list not found in scan: %s', entity_diff)
135 if scan_diff: 135 ↛ 136line 135 didn't jump to line 136, because the condition on line 135 was never true
136 diffs = True
137 self.logger.info('Values in scan not found in entity_list: %s', scan_diff)
138 if diffs:
139 if correct: 139 ↛ 148line 139 didn't jump to line 148, because the condition on line 139 was never false
140 self.logger.info('Correcting entity_list with values from scan: %s', curr)
141 if self.kind == 'index': 141 ↛ 142line 141 didn't jump to line 142, because the condition on line 141 was never true
142 self.entity_list = []
143 for index in curr:
144 self.track_index(index) # We have to re-create the tracked entities
145 else:
146 self.entity_list = curr
147 else:
148 self.logger.warning('Not correcting entity_list! Values should be: %s', curr)
149 retval = curr
150 return retval