1
+ # Copyright 2024 Cloudbase Solutions Srl
2
+ # All Rights Reserved.
3
+ import argparse
4
+ import copy
5
+ import os
6
+ from unittest import mock
7
+
8
+ from coriolisclient .cli import deployments
9
+ from coriolisclient .tests import test_base
10
+ from coriolisclient .v1 import common
11
+ from coriolisclient .v1 import deployments as v1_deployments
12
+
13
+ DEPLOYMENT_ID = "depl_id"
14
+ DEPLOYMENT_DATA = {
15
+ "storage_mappings" : None ,
16
+ "id" : DEPLOYMENT_ID ,
17
+ "last_execution_status" : "COMPLETED" ,
18
+ "created_at" : None ,
19
+ "updated_at" : None ,
20
+ "transfer_id" : "transfer_1" ,
21
+ "transfer_scenario_type" : "replica" ,
22
+ "reservation_id" : "res1" ,
23
+ "instances" : ["instance1" ],
24
+ "notes" : "" ,
25
+ "origin_endpoint_id" : "source1" ,
26
+ "origin_minion_pool_id" : None ,
27
+ "destination_endpoint_id" : "dest1" ,
28
+ "destination_minion_pool_id" : None ,
29
+ "instance_osmorphing_minion_pool_mappings" : None ,
30
+ "shutdown_instances" : False ,
31
+ "destination_environment" : {"opt1" : "env1" },
32
+ "source_environment" : None ,
33
+ "network_map" : {"net_source" : "net_dest" },
34
+ "user_scripts" : None ,
35
+ "tasks" : [],
36
+ "transfer_result" : None ,
37
+ }
38
+ DEPLOYMENT_FORMATTED_DATA = [
39
+ "depl_id" , "COMPLETED" , None , None , "transfer_1" , "replica" , "res1" ,
40
+ "instance1" , "" , "source1" , None , "dest1" , None , '{}' , False ,
41
+ '{\n "opt1": "env1"\n }' , '{}' , '{\n "net_source": "net_dest"\n }' , "" , "" ,
42
+ None , '{}' , "" , '{}' ]
43
+
44
+ DEPLOYMENT_LIST_DATA = {
45
+ "id" : "1" ,
46
+ "transfer_id" : "2" ,
47
+ "last_execution_status" : "RUNNING" ,
48
+ "instances" : ["instance1" , "instance2" ],
49
+ "notes" : "test_notes" ,
50
+ "created_at" : "2024-11-28T15:18:25.000000" ,
51
+ }
52
+ DEPLOYMENT_LIST_FORMATTED_DATA = (
53
+ "1" , "2" , "RUNNING" , "instance1\n instance2" , "test_notes" ,
54
+ "2024-11-28T15:18:25.000000" )
55
+ APP_MOCK = mock .MagicMock ()
56
+
57
+ class DeploymentFormatterTestCase (test_base .CoriolisBaseTestCase ):
58
+
59
+ def setUp (self ):
60
+ super (DeploymentFormatterTestCase , self ).setUp ()
61
+ self .formatter = deployments .DeploymentFormatter ()
62
+
63
+ def test__get_sorted_list (self ):
64
+ obj1 = mock .Mock (created_at = "2024-10-24T18:51:32.000000" )
65
+ obj2 = mock .Mock (created_at = "2024-11-18T15:18:25.000000" )
66
+ obj3 = mock .Mock (created_at = "2024-11-28T15:18:25.000000" )
67
+ obj_list = [obj1 , obj3 , obj2 ]
68
+ self .assertEqual (
69
+ [obj1 , obj2 , obj3 ], self .formatter ._get_sorted_list (obj_list ))
70
+
71
+ def test__get_formatted_data (self ):
72
+ obj = mock .Mock (** DEPLOYMENT_LIST_DATA )
73
+ self .assertEqual (
74
+ DEPLOYMENT_LIST_FORMATTED_DATA ,
75
+ self .formatter ._get_formatted_data (obj ))
76
+
77
+
78
+ class DeploymentDetailFormatterTestCase (test_base .CoriolisBaseTestCase ):
79
+
80
+ def setUp (self ):
81
+ super (DeploymentDetailFormatterTestCase , self ).setUp ()
82
+ self .formatter = deployments .DeploymentDetailFormatter (
83
+ show_instances_data = True )
84
+ self .progress_updates = [
85
+ {"created_at" : "2024-10-24T18:51:32.000000" ,
86
+ "index" : 0 ,
87
+ "message" : "message 0" },
88
+ {"created_at" : "2024-11-18T15:18:25.000000" ,
89
+ "index" : 2 ,
90
+ "message" : "message 2" },
91
+ {"created_at" : "2024-11-28T15:18:25.000000" ,
92
+ "index" : 1 ,
93
+ "message" : "message 1" },
94
+ ]
95
+ self .formatted_progress_updates = (
96
+ f"2024-10-24T18:51:32.000000 message 0{ os .linesep } "
97
+ f"2024-11-28T15:18:25.000000 message 1{ os .linesep } "
98
+ f"2024-11-18T15:18:25.000000 message 2" )
99
+ self .tasks_data = [
100
+ {
101
+ "id" : "1" ,
102
+ "task_type" : "type1" ,
103
+ "instance" : "instance1" ,
104
+ "status" : "COMPLETED" ,
105
+ "depends_on" : ["task0" ],
106
+ "exception_details" : None ,
107
+ "progress_updates" : self .progress_updates ,
108
+ },
109
+ {
110
+ "id" : "2" ,
111
+ "task_type" : "type2" ,
112
+ "instance" : "instance1" ,
113
+ "status" : "COMPLETED" ,
114
+ "depends_on" : ["task0" ],
115
+ "exception_details" : None ,
116
+ "progress_updates" : self .progress_updates ,
117
+ }
118
+ ]
119
+ self .formatted_tasks = [
120
+ f'id: 1{ os .linesep } '
121
+ f'task_type: type1{ os .linesep } '
122
+ f'instance: instance1{ os .linesep } '
123
+ f'status: COMPLETED{ os .linesep } '
124
+ f'depends_on: task0{ os .linesep } '
125
+ f'exception_details: { os .linesep } '
126
+ f'progress_updates:{ os .linesep } '
127
+ f'{ self .formatted_progress_updates } ' ,
128
+
129
+ f'id: 2{ os .linesep } '
130
+ f'task_type: type2{ os .linesep } '
131
+ f'instance: instance1{ os .linesep } '
132
+ f'status: COMPLETED{ os .linesep } '
133
+ f'depends_on: task0{ os .linesep } '
134
+ f'exception_details: { os .linesep } '
135
+ f'progress_updates:{ os .linesep } '
136
+ f'{ self .formatted_progress_updates } '
137
+ ]
138
+ self .manager_mock = mock .MagicMock ()
139
+
140
+ def test_init_no_instances_data (self ):
141
+ formatter = deployments .DeploymentDetailFormatter (
142
+ show_instances_data = False )
143
+ self .assertNotIn ('instances_data' , formatter .columns )
144
+
145
+ def test__format_instances (self ):
146
+ obj = mock .Mock (instances = ["instance2" , "instance3" , "instance1" ])
147
+ self .assertEqual (
148
+ f"instance1{ os .linesep } instance2{ os .linesep } instance3" ,
149
+ self .formatter ._format_instances (obj ))
150
+
151
+ def test__format_progress_updates (self ):
152
+ task_dict = {
153
+ "progress_updates" : self .progress_updates }
154
+ self .assertEqual (
155
+ self .formatted_progress_updates ,
156
+ self .formatter ._format_progress_updates (task_dict ))
157
+
158
+ def test__format_task (self ):
159
+ task_data = self .tasks_data [0 ]
160
+
161
+ task = common .Task (self .manager_mock , task_data )
162
+
163
+ self .assertEqual (
164
+ self .formatted_tasks [0 ], self .formatter ._format_task (task ))
165
+
166
+ def test__format_tasks (self ):
167
+ obj = mock .Mock (
168
+ tasks = [common .Task (self .manager_mock , task_data )
169
+ for task_data in self .tasks_data ])
170
+ expected_result = (f'{ self .formatted_tasks [0 ]} { os .linesep } { os .linesep } '
171
+ f'{ self .formatted_tasks [1 ]} ' )
172
+ self .assertEqual (expected_result , self .formatter ._format_tasks (obj ))
173
+
174
+ def test__get_formatted_data (self ):
175
+ obj_data = {
176
+ ** DEPLOYMENT_DATA ,
177
+ "info" : {"depl" : "info" },
178
+ }
179
+ obj = mock .Mock (** obj_data )
180
+ obj .to_dict .return_value = obj_data
181
+ expected_result = copy .copy (DEPLOYMENT_FORMATTED_DATA )
182
+ expected_result .append (obj_data ['info' ])
183
+
184
+ self .assertEqual (
185
+ expected_result , self .formatter ._get_formatted_data (obj ))
186
+
187
+
188
+ class CreateDeploymentTestCase (test_base .CoriolisBaseTestCase ):
189
+
190
+ def setUp (self ):
191
+ super (CreateDeploymentTestCase , self ).setUp ()
192
+ self .mock_app = APP_MOCK
193
+ self .cli = deployments .CreateDeployment (self .mock_app , 'app_arg' )
194
+
195
+ def test_get_parser (self ):
196
+ parser = self .cli .get_parser ('coriolis' )
197
+ global_script = "linux=/linux/path"
198
+ instance_script = "instance1=/instance1/path"
199
+ args = parser .parse_args ([
200
+ 'transfer_id' , '--force' , '--dont-clone-disks' ,
201
+ '--skip-os-morphing' , '--user-script-global' , global_script ,
202
+ '--user-script-instance' , instance_script ])
203
+ self .assertEqual (
204
+ ('transfer_id' , True , False , True , [global_script ],
205
+ [instance_script ]),
206
+ (args .transfer , args .force , args .clone_disks ,
207
+ args .skip_os_morphing , args .global_scripts ,
208
+ args .instance_scripts ))
209
+
210
+ def test_take_action (self ):
211
+ args = mock .Mock (global_scripts = None , instance_scripts = None )
212
+ args .instance_osmorphing_minion_pool_mappings = [
213
+ {"instance_id" : "instance1" , "pool_id" : "pool1" }]
214
+ mock_fun = (self .mock_app .client_manager .coriolis .deployments .
215
+ create_from_transfer )
216
+ mock_fun .return_value = (
217
+ v1_deployments .Deployment (mock .MagicMock (), DEPLOYMENT_DATA ))
218
+ expected_pool_mappings = {"instance1" : "pool1" }
219
+ expected_user_scripts = {"global" : {}, "instances" : {}}
220
+ expected_data = copy .copy (DEPLOYMENT_FORMATTED_DATA )
221
+
222
+ columns , data = self .cli .take_action (args )
223
+
224
+ self .assertEqual (
225
+ deployments .DeploymentDetailFormatter ().columns , columns )
226
+ self .assertEqual (expected_data , data )
227
+ mock_fun .assert_called_once_with (
228
+ args .transfer , args .clone_disks , args .force , args .skip_os_morphing ,
229
+ user_scripts = expected_user_scripts ,
230
+ instance_osmorphing_minion_pool_mappings = expected_pool_mappings )
231
+
232
+
233
+ class ShowDeploymentTestCase (test_base .CoriolisBaseTestCase ):
234
+
235
+ def setUp (self ):
236
+ super (ShowDeploymentTestCase , self ).setUp ()
237
+ self .mock_app = APP_MOCK
238
+ self .cli = deployments .ShowDeployment (self .mock_app , 'app_args' )
239
+
240
+
241
+ def test_get_parser (self ):
242
+ parser = self .cli .get_parser ('coriolis' )
243
+ args = parser .parse_args ([DEPLOYMENT_ID , '--show-instances-data' ])
244
+ self .assertEqual (
245
+ (DEPLOYMENT_ID , True ),
246
+ (args .id , args .show_instances_data ))
247
+
248
+ def test_take_action (self ):
249
+ show_instances_data = False
250
+ args = mock .Mock (
251
+ id = DEPLOYMENT_ID , show_instances_data = show_instances_data )
252
+ mock_fun = self .mock_app .client_manager .coriolis .deployments .get
253
+ mock_fun .return_value = v1_deployments .Deployment (
254
+ mock .MagicMock (), DEPLOYMENT_DATA )
255
+ expected_data = DEPLOYMENT_FORMATTED_DATA
256
+
257
+ columns , data = self .cli .take_action (args )
258
+
259
+ self .assertEqual (
260
+ deployments .DeploymentDetailFormatter (
261
+ show_instances_data = show_instances_data ).columns , columns )
262
+ self .assertEqual (expected_data , data )
263
+ mock_fun .assert_called_once_with (DEPLOYMENT_ID )
264
+
265
+
266
+ class CancelDeploymentTestCase (test_base .CoriolisBaseTestCase ):
267
+
268
+ def setUp (self ):
269
+ super (CancelDeploymentTestCase , self ).setUp ()
270
+ self .mock_app = APP_MOCK
271
+ self .cli = deployments .CancelDeployment (self .mock_app , 'app_args' )
272
+
273
+ def test_get_parser (self ):
274
+ parser = self .cli .get_parser ('coriolis' )
275
+ args = parser .parse_args ([DEPLOYMENT_ID , '--force' ])
276
+ self .assertEqual ((DEPLOYMENT_ID , True ), (args .id , args .force ))
277
+
278
+ def test_take_action (self ):
279
+ force = True
280
+ args = mock .Mock (id = DEPLOYMENT_ID , force = force )
281
+ mock_fun = self .mock_app .client_manager .coriolis .deployments .cancel
282
+
283
+ self .cli .take_action (args )
284
+
285
+ mock_fun .assert_called_once_with (DEPLOYMENT_ID , force )
286
+
287
+
288
+ class DeleteDeploymentTestCase (test_base .CoriolisBaseTestCase ):
289
+
290
+ def setUp (self ):
291
+ super (DeleteDeploymentTestCase , self ).setUp ()
292
+ self .mock_app = APP_MOCK
293
+ self .cli = deployments .DeleteDeployment (self .mock_app , 'app_args' )
294
+
295
+ def test_get_parser (self ):
296
+ parser = self .cli .get_parser ('coriolis' )
297
+ args = parser .parse_args ([DEPLOYMENT_ID ])
298
+ self .assertEqual (DEPLOYMENT_ID , args .id )
299
+
300
+ def test_take_action (self ):
301
+ args = mock .Mock (id = DEPLOYMENT_ID )
302
+ mock_fun = self .mock_app .client_manager .coriolis .deployments .delete
303
+
304
+ self .cli .take_action (args )
305
+ mock_fun .assert_called_once_with (DEPLOYMENT_ID )
306
+
307
+
308
+ class ListDeploymentTestCase (test_base .CoriolisBaseTestCase ):
309
+
310
+ def setUp (self ):
311
+ super (ListDeploymentTestCase , self ).setUp ()
312
+ self .mock_app = APP_MOCK
313
+ self .cli = deployments .ListDeployment (self .mock_app , 'app_args' )
314
+
315
+ def test_get_parser (self ):
316
+ parser = self .cli .get_parser ('coriolis' )
317
+ self .assertIsInstance (parser , argparse .ArgumentParser )
318
+
319
+ def test_take_action (self ):
320
+ mock_fun = self .mock_app .client_manager .coriolis .deployments .list
321
+ mock_fun .return_value = [
322
+ v1_deployments .Deployment (mock .MagicMock (), DEPLOYMENT_LIST_DATA )]
323
+
324
+ columns , data = self .cli .take_action (mock .ANY )
325
+
326
+ self .assertEqual (deployments .DeploymentFormatter ().columns , columns )
327
+ self .assertEqual ([DEPLOYMENT_LIST_FORMATTED_DATA ], list (data ))
0 commit comments