1
1
import os
2
+ from multiprocessing import Lock
3
+ from pickle import Pickler , Unpickler
2
4
from typing import Any , Dict , List , Optional
3
5
4
6
import yaml
14
16
assert _JOB_DEFINITIONS
15
17
16
18
_INSTANCE_ID_FORMAT : str = "instance-00000000-0000-0000-0000-{id:012d}"
19
+ _TASK_ID_FORMAT : str = "task-00000000-0000-0000-0000-{id:012d}"
17
20
_WORKFLOW_DEFINITION_ID_FORMAT : str = "workflow-00000000-0000-0000-0000-{id:012d}"
18
21
_RUNNING_WORKFLOW_ID_FORMAT : str = "r-workflow-00000000-0000-0000-0000-{id:012d}"
19
22
_RUNNING_WORKFLOW_STEP_ID_FORMAT : str = (
20
23
"r-workflow-step-00000000-0000-0000-0000-{id:012d}"
21
24
)
22
25
26
+ _WORKFLOW_PICKLE_FILE : str = "workflow.pickle"
27
+ _RUNNING_WORKFLOW_PICKLE_FILE : str = "running-workflow.pickle"
28
+ _RUNNING_WORKFLOW_STEP_PICKLE_FILE : str = "running-workflow-step.pickle"
29
+ _INSTANCE_PICKLE_FILE : str = "instance.pickle"
30
+ _TASK_PICKLE_FILE : str = "task.pickle"
31
+
23
32
24
33
class UnitTestAPIAdapter (APIAdapter ):
25
34
"""A minimal API adapter. It serves-up Job Definitions
26
35
from the job-definitions/job-definitions.yaml file and provides basic
27
36
(in-memory) storage for Workflow Definitions and related tables."""
28
37
38
+ mp_lock = Lock ()
39
+
29
40
def __init__ (self ):
30
41
super ().__init__ ()
31
- # A map of workflow definitions, keyed by workflow definition ID.
32
- self ._workflow_definitions : Dict [str , Dict [str , Any ]] = {}
33
- self ._running_workflow : Dict [str , Dict [str , Any ]] = {}
34
- self ._running_workflow_steps : Dict [str , Dict [str , Any ]] = {}
35
- self ._instances : Dict [str , Dict [str , Any ]] = {}
36
- self ._tasks : Dict [str , Dict [str , Any ]] = {}
42
+ # Safely initialise the pickle files
43
+ UnitTestAPIAdapter .mp_lock .acquire ()
44
+ with open (f"tests/{ _WORKFLOW_PICKLE_FILE } " , "wb" ) as pickle_file :
45
+ Pickler (pickle_file ).dump ({})
46
+ with open (f"tests/{ _RUNNING_WORKFLOW_PICKLE_FILE } " , "wb" ) as pickle_file :
47
+ Pickler (pickle_file ).dump ({})
48
+ with open (f"tests/{ _RUNNING_WORKFLOW_STEP_PICKLE_FILE } " , "wb" ) as pickle_file :
49
+ Pickler (pickle_file ).dump ({})
50
+ with open (f"tests/{ _INSTANCE_PICKLE_FILE } " , "wb" ) as pickle_file :
51
+ Pickler (pickle_file ).dump ({})
52
+ with open (f"tests/{ _TASK_PICKLE_FILE } " , "wb" ) as pickle_file :
53
+ Pickler (pickle_file ).dump ({})
54
+ UnitTestAPIAdapter .mp_lock .release ()
37
55
38
56
def create_workflow (self , * , workflow_definition : Dict [str , Any ]) -> str :
39
- next_id : int = len (self ._workflow_definitions ) + 1
57
+ UnitTestAPIAdapter .mp_lock .acquire ()
58
+ with open (f"tests/{ _WORKFLOW_PICKLE_FILE } " , "rb" ) as pickle_file :
59
+ workflow = Unpickler (pickle_file ).load ()
60
+
61
+ next_id : int = len (workflow ) + 1
40
62
workflow_definition_id : str = _WORKFLOW_DEFINITION_ID_FORMAT .format (id = next_id )
41
- self ._workflow_definitions [workflow_definition_id ] = workflow_definition
63
+ workflow [workflow_definition_id ] = workflow_definition
64
+
65
+ with open (f"tests/{ _WORKFLOW_PICKLE_FILE } " , "wb" ) as pickle_file :
66
+ Pickler (pickle_file ).dump (workflow )
67
+ UnitTestAPIAdapter .mp_lock .release ()
68
+
42
69
return {"id" : workflow_definition_id }
43
70
44
71
def get_workflow (self , * , workflow_definition_id : str ) -> Dict [str , Any ]:
45
- if workflow_definition_id not in self ._workflow_definitions :
72
+ UnitTestAPIAdapter .mp_lock .acquire ()
73
+ with open (f"tests/{ _WORKFLOW_PICKLE_FILE } " , "rb" ) as pickle_file :
74
+ workflow = Unpickler (pickle_file ).load ()
75
+ UnitTestAPIAdapter .mp_lock .release ()
76
+
77
+ if workflow_definition_id not in workflow :
46
78
return {}
47
- return {"workflow" : self . _workflow_definitions [workflow_definition_id ]}
79
+ return {"workflow" : workflow [workflow_definition_id ]}
48
80
49
81
def get_workflow_by_name (self , * , name : str , version : str ) -> Dict [str , Any ]:
82
+ UnitTestAPIAdapter .mp_lock .acquire ()
83
+ with open (f"tests/{ _WORKFLOW_PICKLE_FILE } " , "rb" ) as pickle_file :
84
+ workflow = Unpickler (pickle_file ).load ()
85
+ UnitTestAPIAdapter .mp_lock .release ()
86
+
50
87
item = {}
51
- for wfid , value in self . _workflow_definitions .items ():
88
+ for wfid , value in workflow .items ():
52
89
if value ["name" ] == name :
53
90
item = {"id" : wfid , "workflow" : value }
54
91
return item
55
92
56
93
def create_running_workflow (self , * , workflow_definition_id : str ) -> str :
57
- next_id : int = len (self ._running_workflow ) + 1
94
+ UnitTestAPIAdapter .mp_lock .acquire ()
95
+ with open (f"tests/{ _RUNNING_WORKFLOW_PICKLE_FILE } " , "rb" ) as pickle_file :
96
+ running_workflow = Unpickler (pickle_file ).load ()
97
+
98
+ next_id : int = len (running_workflow ) + 1
58
99
running_workflow_id : str = _RUNNING_WORKFLOW_ID_FORMAT .format (id = next_id )
59
100
record = {"done" : False , "success" : False , "workflow" : workflow_definition_id }
60
- self ._running_workflow [running_workflow_id ] = record
101
+ running_workflow [running_workflow_id ] = record
102
+
103
+ with open (f"tests/{ _RUNNING_WORKFLOW_PICKLE_FILE } " , "wb" ) as pickle_file :
104
+ Pickler (pickle_file ).dump (running_workflow )
105
+ UnitTestAPIAdapter .mp_lock .release ()
106
+
61
107
return {"id" : running_workflow_id }
62
108
63
109
def get_running_workflow (self , * , running_workflow_id : str ) -> Dict [str , Any ]:
64
- if running_workflow_id not in self ._running_workflow :
110
+ UnitTestAPIAdapter .mp_lock .acquire ()
111
+ with open (f"tests/{ _RUNNING_WORKFLOW_PICKLE_FILE } " , "rb" ) as pickle_file :
112
+ running_workflow = Unpickler (pickle_file ).load ()
113
+ UnitTestAPIAdapter .mp_lock .release ()
114
+
115
+ if running_workflow_id not in running_workflow :
65
116
return {}
66
- return {"running_workflow" : self . _running_workflow [running_workflow_id ]}
117
+ return {"running_workflow" : running_workflow [running_workflow_id ]}
67
118
68
119
def create_running_workflow_step (
69
120
self , * , running_workflow_id : str , step : str
70
121
) -> str :
71
- next_id : int = len (self ._running_workflow_steps ) + 1
122
+ UnitTestAPIAdapter .mp_lock .acquire ()
123
+ with open (f"tests/{ _RUNNING_WORKFLOW_STEP_PICKLE_FILE } " , "rb" ) as pickle_file :
124
+ running_workflow_step = Unpickler (pickle_file ).load ()
125
+
126
+ next_id : int = len (running_workflow_step ) + 1
72
127
running_workflow_step_id : str = _RUNNING_WORKFLOW_STEP_ID_FORMAT .format (
73
128
id = next_id
74
129
)
@@ -78,58 +133,99 @@ def create_running_workflow_step(
78
133
"success" : False ,
79
134
"running_workflow" : running_workflow_id ,
80
135
}
81
- self ._running_workflow_steps [running_workflow_step_id ] = record
136
+ running_workflow_step [running_workflow_step_id ] = record
137
+
138
+ with open (f"tests/{ _RUNNING_WORKFLOW_STEP_PICKLE_FILE } " , "wb" ) as pickle_file :
139
+ Pickler (pickle_file ).dump (running_workflow_step )
140
+ UnitTestAPIAdapter .mp_lock .release ()
141
+
82
142
return {"id" : running_workflow_step_id }
83
143
84
144
def get_running_workflow_step (
85
145
self , * , running_workflow_step_id : str
86
146
) -> Dict [str , Any ]:
87
- if running_workflow_step_id not in self ._running_workflow_steps :
147
+ UnitTestAPIAdapter .mp_lock .acquire ()
148
+ with open (f"tests/{ _RUNNING_WORKFLOW_STEP_PICKLE_FILE } " , "rb" ) as pickle_file :
149
+ running_workflow_step = Unpickler (pickle_file ).load ()
150
+ UnitTestAPIAdapter .mp_lock .release ()
151
+
152
+ if running_workflow_step_id not in running_workflow_step :
88
153
return {}
89
154
return {
90
- "running_workflow_step" : self ._running_workflow_steps [
91
- running_workflow_step_id
92
- ]
155
+ "running_workflow_step" : running_workflow_step [running_workflow_step_id ]
93
156
}
94
157
95
158
def get_running_workflow_steps (
96
159
self , * , running_workflow_id : str
97
160
) -> List [Dict [str , Any ]]:
161
+ UnitTestAPIAdapter .mp_lock .acquire ()
162
+ with open (f"tests/{ _RUNNING_WORKFLOW_STEP_PICKLE_FILE } " , "rb" ) as pickle_file :
163
+ running_workflow_step = Unpickler (pickle_file ).load ()
164
+ UnitTestAPIAdapter .mp_lock .release ()
165
+
98
166
steps = []
99
- for key , value in self . _running_workflow_steps .items ():
167
+ for key , value in running_workflow_step .items ():
100
168
if value ["running_workflow" ] == running_workflow_id :
101
169
item = {"running_workflow_step" : value , "id" : key }
102
170
steps .append (item )
103
171
return {"count" : len (steps ), "running_workflow_steps" : steps }
104
172
105
173
def create_instance (self , * , running_workflow_step_id : str ) -> Dict [str , Any ]:
106
- next_id : int = len (self ._instances ) + 1
174
+ UnitTestAPIAdapter .mp_lock .acquire ()
175
+ with open (f"tests/{ _INSTANCE_PICKLE_FILE } " , "rb" ) as pickle_file :
176
+ instances = Unpickler (pickle_file ).load ()
177
+
178
+ next_id : int = len (instances ) + 1
107
179
instance_id : str = _INSTANCE_ID_FORMAT .format (id = next_id )
108
180
record = {
109
181
"running_workflow_step" : running_workflow_step_id ,
110
182
}
111
- self ._instances [instance_id ] = record
183
+ instances [instance_id ] = record
184
+
185
+ with open (f"tests/{ _INSTANCE_PICKLE_FILE } " , "wb" ) as pickle_file :
186
+ Pickler (pickle_file ).dump (instances )
187
+ UnitTestAPIAdapter .mp_lock .release ()
188
+
112
189
return {"id" : instance_id }
113
190
114
191
def get_instance (self , * , instance_id : str ) -> Dict [str , Any ]:
115
- if instance_id not in self ._instances :
192
+ UnitTestAPIAdapter .mp_lock .acquire ()
193
+ with open (f"tests/{ _INSTANCE_PICKLE_FILE } " , "rb" ) as pickle_file :
194
+ instances = Unpickler (pickle_file ).load ()
195
+ UnitTestAPIAdapter .mp_lock .release ()
196
+
197
+ if instance_id not in instances :
116
198
return {}
117
- return self . _instances [instance_id ]
199
+ return instances [instance_id ]
118
200
119
201
def create_task (self , * , instance_id : str ) -> Dict [str , Any ]:
120
- next_id : int = len (self ._instances ) + 1
121
- task_id : str = _INSTANCE_ID_FORMAT .format (id = next_id )
202
+ UnitTestAPIAdapter .mp_lock .acquire ()
203
+ with open (f"tests/{ _TASK_PICKLE_FILE } " , "rb" ) as pickle_file :
204
+ tasks = Unpickler (pickle_file ).load ()
205
+
206
+ next_id : int = len (tasks ) + 1
207
+ task_id : str = _TASK_ID_FORMAT .format (id = next_id )
122
208
record = {
123
209
"done" : False ,
124
210
"exit_code" : 0 ,
125
211
}
126
- self ._tasks [task_id ] = record
212
+ tasks [task_id ] = record
213
+
214
+ with open (f"tests/{ _TASK_PICKLE_FILE } " , "wb" ) as pickle_file :
215
+ Pickler (pickle_file ).dump (tasks )
216
+ UnitTestAPIAdapter .mp_lock .release ()
217
+
127
218
return {"id" : task_id }
128
219
129
220
def get_task (self , * , task_id : str ) -> Dict [str , Any ]:
130
- if task_id not in self ._tasks :
221
+ UnitTestAPIAdapter .mp_lock .acquire ()
222
+ with open (f"tests/{ _TASK_PICKLE_FILE } " , "rb" ) as pickle_file :
223
+ tasks = Unpickler (pickle_file ).load ()
224
+ UnitTestAPIAdapter .mp_lock .release ()
225
+
226
+ if task_id not in tasks :
131
227
return {}
132
- return self . _tasks [task_id ]
228
+ return tasks [task_id ]
133
229
134
230
def get_job (
135
231
self , * , collection : str , job : str , version : str
0 commit comments