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-25 19:21 -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 

8 

9# pylint: disable=missing-docstring,broad-exception-caught,too-many-instance-attributes 

10 

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() 

26 

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}' 

60 

61 def appender(self, name: str) -> None: 

62 self.failsafe.append(name) 

63 self.entity_list.append(name) 

64 

65 def ident(self, dkey=None): 

66 if not dkey: 

67 dkey=self.kind 

68 return NAMEMAPPER[dkey] 

69 

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 

92 

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 

100 

101 def setup(self): 

102 pass 

103 

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) 

116 

117 def track_index(self, name: str) -> None: 

118 pass 

119 

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