nextmv 1.0.0__py3-none-any.whl → 1.0.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/__entrypoint__.py +2 -1
  3. nextmv/__init__.py +4 -0
  4. nextmv/cli/CONTRIBUTING.md +40 -112
  5. nextmv/cli/cloud/__init__.py +0 -4
  6. nextmv/cli/cloud/acceptance/create.py +22 -20
  7. nextmv/cli/cloud/acceptance/delete.py +12 -8
  8. nextmv/cli/cloud/acceptance/get.py +10 -9
  9. nextmv/cli/cloud/acceptance/list.py +3 -3
  10. nextmv/cli/cloud/acceptance/update.py +6 -6
  11. nextmv/cli/cloud/account/__init__.py +3 -3
  12. nextmv/cli/cloud/account/create.py +11 -11
  13. nextmv/cli/cloud/account/delete.py +8 -7
  14. nextmv/cli/cloud/account/get.py +3 -3
  15. nextmv/cli/cloud/account/update.py +5 -5
  16. nextmv/cli/cloud/app/create.py +26 -25
  17. nextmv/cli/cloud/app/delete.py +7 -6
  18. nextmv/cli/cloud/app/exists.py +2 -2
  19. nextmv/cli/cloud/app/get.py +2 -2
  20. nextmv/cli/cloud/app/list.py +3 -3
  21. nextmv/cli/cloud/app/push.py +54 -349
  22. nextmv/cli/cloud/app/update.py +12 -12
  23. nextmv/cli/cloud/batch/create.py +28 -26
  24. nextmv/cli/cloud/batch/delete.py +10 -6
  25. nextmv/cli/cloud/batch/get.py +9 -9
  26. nextmv/cli/cloud/batch/list.py +3 -3
  27. nextmv/cli/cloud/batch/metadata.py +4 -4
  28. nextmv/cli/cloud/batch/update.py +6 -6
  29. nextmv/cli/cloud/data/__init__.py +1 -1
  30. nextmv/cli/cloud/data/upload.py +15 -15
  31. nextmv/cli/cloud/ensemble/__init__.py +0 -2
  32. nextmv/cli/cloud/ensemble/create.py +22 -21
  33. nextmv/cli/cloud/ensemble/delete.py +10 -6
  34. nextmv/cli/cloud/ensemble/get.py +4 -4
  35. nextmv/cli/cloud/ensemble/update.py +9 -9
  36. nextmv/cli/cloud/input_set/__init__.py +0 -2
  37. nextmv/cli/cloud/input_set/create.py +22 -22
  38. nextmv/cli/cloud/input_set/get.py +3 -3
  39. nextmv/cli/cloud/input_set/list.py +3 -3
  40. nextmv/cli/cloud/input_set/update.py +24 -24
  41. nextmv/cli/cloud/instance/create.py +15 -14
  42. nextmv/cli/cloud/instance/delete.py +7 -6
  43. nextmv/cli/cloud/instance/exists.py +2 -2
  44. nextmv/cli/cloud/instance/get.py +2 -2
  45. nextmv/cli/cloud/instance/list.py +3 -3
  46. nextmv/cli/cloud/instance/update.py +14 -14
  47. nextmv/cli/cloud/managed_input/create.py +16 -14
  48. nextmv/cli/cloud/managed_input/delete.py +8 -7
  49. nextmv/cli/cloud/managed_input/get.py +3 -3
  50. nextmv/cli/cloud/managed_input/list.py +3 -3
  51. nextmv/cli/cloud/managed_input/update.py +9 -9
  52. nextmv/cli/cloud/run/cancel.py +2 -2
  53. nextmv/cli/cloud/run/create.py +40 -34
  54. nextmv/cli/cloud/run/get.py +8 -8
  55. nextmv/cli/cloud/run/input.py +4 -4
  56. nextmv/cli/cloud/run/list.py +6 -6
  57. nextmv/cli/cloud/run/logs.py +10 -9
  58. nextmv/cli/cloud/run/metadata.py +4 -4
  59. nextmv/cli/cloud/run/track.py +33 -32
  60. nextmv/cli/cloud/scenario/create.py +21 -21
  61. nextmv/cli/cloud/scenario/delete.py +10 -6
  62. nextmv/cli/cloud/scenario/get.py +9 -9
  63. nextmv/cli/cloud/scenario/list.py +3 -3
  64. nextmv/cli/cloud/scenario/metadata.py +4 -4
  65. nextmv/cli/cloud/scenario/update.py +6 -6
  66. nextmv/cli/cloud/secrets/create.py +17 -17
  67. nextmv/cli/cloud/secrets/delete.py +10 -6
  68. nextmv/cli/cloud/secrets/get.py +4 -4
  69. nextmv/cli/cloud/secrets/list.py +3 -3
  70. nextmv/cli/cloud/secrets/update.py +20 -17
  71. nextmv/cli/cloud/upload/create.py +2 -2
  72. nextmv/cli/cloud/version/create.py +10 -9
  73. nextmv/cli/cloud/version/delete.py +7 -6
  74. nextmv/cli/cloud/version/exists.py +2 -2
  75. nextmv/cli/cloud/version/get.py +2 -2
  76. nextmv/cli/cloud/version/list.py +3 -3
  77. nextmv/cli/cloud/version/update.py +8 -8
  78. nextmv/cli/community/__init__.py +1 -1
  79. nextmv/cli/community/clone.py +204 -20
  80. nextmv/cli/community/list.py +125 -60
  81. nextmv/cli/configuration/config.py +10 -43
  82. nextmv/cli/configuration/create.py +7 -7
  83. nextmv/cli/configuration/delete.py +8 -8
  84. nextmv/cli/configuration/list.py +3 -3
  85. nextmv/cli/main.py +36 -26
  86. nextmv/cli/message.py +54 -71
  87. nextmv/cli/options.py +0 -28
  88. nextmv/cli/version.py +1 -1
  89. nextmv/cloud/__init__.py +38 -14
  90. nextmv/cloud/acceptance_test.py +65 -1
  91. nextmv/cloud/account.py +6 -1
  92. nextmv/cloud/application/__init__.py +75 -18
  93. nextmv/cloud/application/_acceptance.py +8 -13
  94. nextmv/cloud/application/_batch_scenario.py +19 -4
  95. nextmv/cloud/application/_input_set.py +6 -42
  96. nextmv/cloud/application/_instance.py +3 -3
  97. nextmv/cloud/application/_managed_input.py +2 -2
  98. nextmv/cloud/application/_version.py +3 -4
  99. nextmv/cloud/batch_experiment.py +1 -3
  100. nextmv/cloud/integration.py +4 -7
  101. nextmv/deprecated.py +3 -5
  102. nextmv/input.py +52 -0
  103. nextmv/local/runner.py +1 -1
  104. nextmv/model.py +11 -50
  105. nextmv/options.py +256 -11
  106. nextmv/output.py +62 -0
  107. nextmv/run.py +10 -1
  108. nextmv/status.py +51 -1
  109. {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/METADATA +4 -5
  110. nextmv-1.0.0.dev0.dist-info/RECORD +158 -0
  111. nextmv/cli/cloud/ensemble/list.py +0 -63
  112. nextmv/cli/cloud/input_set/delete.py +0 -64
  113. nextmv/cli/cloud/shadow/__init__.py +0 -33
  114. nextmv/cli/cloud/shadow/create.py +0 -184
  115. nextmv/cli/cloud/shadow/delete.py +0 -64
  116. nextmv/cli/cloud/shadow/get.py +0 -61
  117. nextmv/cli/cloud/shadow/list.py +0 -63
  118. nextmv/cli/cloud/shadow/metadata.py +0 -66
  119. nextmv/cli/cloud/shadow/start.py +0 -43
  120. nextmv/cli/cloud/shadow/stop.py +0 -53
  121. nextmv/cli/cloud/shadow/update.py +0 -96
  122. nextmv/cli/cloud/switchback/__init__.py +0 -33
  123. nextmv/cli/cloud/switchback/create.py +0 -151
  124. nextmv/cli/cloud/switchback/delete.py +0 -64
  125. nextmv/cli/cloud/switchback/get.py +0 -62
  126. nextmv/cli/cloud/switchback/list.py +0 -63
  127. nextmv/cli/cloud/switchback/metadata.py +0 -68
  128. nextmv/cli/cloud/switchback/start.py +0 -43
  129. nextmv/cli/cloud/switchback/stop.py +0 -53
  130. nextmv/cli/cloud/switchback/update.py +0 -96
  131. nextmv/cli/confirm.py +0 -34
  132. nextmv/cloud/application/_shadow.py +0 -320
  133. nextmv/cloud/application/_switchback.py +0 -332
  134. nextmv/cloud/community.py +0 -446
  135. nextmv/cloud/shadow.py +0 -254
  136. nextmv/cloud/switchback.py +0 -228
  137. nextmv-1.0.0.dist-info/RECORD +0 -185
  138. nextmv-1.0.0.dist-info/entry_points.txt +0 -2
  139. {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/WHEEL +0 -0
  140. {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/licenses/LICENSE +0 -0
@@ -1,332 +0,0 @@
1
- """
2
- Application mixin for managing switchback tests.
3
- """
4
-
5
- from datetime import datetime
6
- from typing import TYPE_CHECKING
7
-
8
- from nextmv.cloud.shadow import StopIntent
9
- from nextmv.cloud.switchback import SwitchbackTest, SwitchbackTestMetadata, TestComparisonSingle
10
- from nextmv.run import Run
11
- from nextmv.safe import safe_id
12
-
13
- if TYPE_CHECKING:
14
- from . import Application
15
-
16
-
17
- class ApplicationSwitchbackMixin:
18
- """
19
- Mixin class for managing switchback tests within an application.
20
- """
21
-
22
- def switchback_test(self: "Application", switchback_test_id: str) -> SwitchbackTest:
23
- """
24
- Get a switchback test. This method also returns the runs of the switchback
25
- test under the `.runs` attribute.
26
-
27
- Parameters
28
- ----------
29
- switchback_test_id : str
30
- ID of the switchback test.
31
-
32
- Returns
33
- -------
34
- SwitchbackTest
35
- The requested switchback test details.
36
-
37
- Raises
38
- ------
39
- requests.HTTPError
40
- If the response status code is not 2xx.
41
-
42
- Examples
43
- --------
44
- >>> switchback_test = app.switchback_test("switchback-123")
45
- >>> print(switchback_test.name)
46
- 'My Switchback Test'
47
- """
48
-
49
- response = self.client.request(
50
- method="GET",
51
- endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}",
52
- )
53
-
54
- exp = SwitchbackTest.from_dict(response.json())
55
-
56
- runs_response = self.client.request(
57
- method="GET",
58
- endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/runs",
59
- )
60
-
61
- runs = [Run.from_dict(run) for run in runs_response.json().get("runs", [])]
62
- exp.runs = runs
63
-
64
- return exp
65
-
66
- def switchback_test_metadata(self: "Application", switchback_test_id: str) -> SwitchbackTestMetadata:
67
- """
68
- Get metadata for a switchback test.
69
-
70
- Parameters
71
- ----------
72
- switchback_test_id : str
73
- ID of the switchback test.
74
-
75
- Returns
76
- -------
77
- SwitchbackTestMetadata
78
- The requested switchback test metadata.
79
-
80
- Raises
81
- ------
82
- requests.HTTPError
83
- If the response status code is not 2xx.
84
-
85
- Examples
86
- --------
87
- >>> metadata = app.switchback_test_metadata("switchback-123")
88
- >>> print(metadata.name)
89
- 'My Switchback Test'
90
- """
91
-
92
- response = self.client.request(
93
- method="GET",
94
- endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/metadata",
95
- )
96
-
97
- return SwitchbackTestMetadata.from_dict(response.json())
98
-
99
- def delete_switchback_test(self: "Application", switchback_test_id: str) -> None:
100
- """
101
- Delete a switchback test.
102
-
103
- Deletes a switchback test along with all the associated information,
104
- such as its runs.
105
-
106
- Parameters
107
- ----------
108
- switchback_test_id : str
109
- ID of the switchback test to delete.
110
-
111
- Raises
112
- ------
113
- requests.HTTPError
114
- If the response status code is not 2xx.
115
-
116
- Examples
117
- --------
118
- >>> app.delete_switchback_test("switchback-123")
119
- """
120
-
121
- _ = self.client.request(
122
- method="DELETE",
123
- endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}",
124
- )
125
-
126
- def list_switchback_tests(self: "Application") -> list[SwitchbackTest]:
127
- """
128
- List all switchback tests.
129
-
130
- Returns
131
- -------
132
- list[SwitchbackTest]
133
- List of switchback tests.
134
-
135
- Raises
136
- ------
137
- requests.HTTPError
138
- If the response status code is not 2xx.
139
- """
140
-
141
- response = self.client.request(
142
- method="GET",
143
- endpoint=f"{self.experiments_endpoint}/switchback",
144
- )
145
-
146
- return [SwitchbackTest.from_dict(switchback_test) for switchback_test in response.json().get("items", [])]
147
-
148
- def new_switchback_test(
149
- self: "Application",
150
- comparison: TestComparisonSingle,
151
- unit_duration_minutes: float,
152
- units: int,
153
- switchback_test_id: str | None = None,
154
- name: str | None = None,
155
- description: str | None = None,
156
- start: datetime | None = None,
157
- ) -> SwitchbackTest:
158
- """
159
- Create a new switchback test in draft mode. Switchback tests are
160
- experiments that alternate between different instances over specified
161
- time intervals.
162
-
163
- Use the `comparison` parameter to define how to set up the instance
164
- comparison. The test will alternate between the baseline and candidate
165
- instances defined in the comparison.
166
-
167
- You may specify `start` to make the switchback test start at a
168
- specific time. Alternatively, you may use the `start_switchback_test`
169
- method to start the test.
170
-
171
- Parameters
172
- ----------
173
- comparison : TestComparisonSingle
174
- Comparison defining the baseline and candidate instances.
175
- unit_duration_minutes : float
176
- Duration of each interval in minutes. The value must be between 1
177
- and 10080.
178
- units : int
179
- Total number of intervals in the switchback test. The value must be
180
- between 1 and 1000.
181
- switchback_test_id : Optional[str], default=None
182
- Optional ID for the switchback test. Will be generated if not
183
- provided.
184
- name : Optional[str], default=None
185
- Optional name of the switchback test. If not provided, the ID will
186
- be used as the name.
187
- description : Optional[str], default=None
188
- Optional description of the switchback test.
189
- start : Optional[datetime], default=None
190
- Optional scheduled start time for the switchback test.
191
-
192
- Returns
193
- -------
194
- SwitchbackTest
195
- The created switchback test.
196
-
197
- Raises
198
- ------
199
- requests.HTTPError
200
- If the response status code is not 2xx.
201
- """
202
-
203
- if unit_duration_minutes < 1 or unit_duration_minutes > 10080:
204
- raise ValueError("unit_duration_minutes must be between 1 and 10080")
205
-
206
- if units < 1 or units > 1000:
207
- raise ValueError("units must be between 1 and 1000")
208
-
209
- # Generate ID if not provided
210
- if switchback_test_id is None:
211
- switchback_test_id = safe_id("switchback")
212
-
213
- # Use ID as name if name not provided
214
- if name is None or name == "":
215
- name = switchback_test_id
216
-
217
- payload = {
218
- "id": switchback_test_id,
219
- "name": name,
220
- "comparison": comparison.to_dict(),
221
- "generate_random_plan": {
222
- "unit_duration_minutes": unit_duration_minutes,
223
- "units": units,
224
- },
225
- }
226
-
227
- if description is not None:
228
- payload["description"] = description
229
- if start is not None:
230
- payload["generate_random_plan"]["start"] = start.isoformat()
231
-
232
- response = self.client.request(
233
- method="POST",
234
- endpoint=f"{self.experiments_endpoint}/switchback",
235
- payload=payload,
236
- )
237
-
238
- return SwitchbackTest.from_dict(response.json())
239
-
240
- def start_switchback_test(self: "Application", switchback_test_id: str) -> None:
241
- """
242
- Start a switchback test. Create a switchback test in draft mode using the
243
- `new_switchback_test` method, then use this method to start the test.
244
-
245
- Parameters
246
- ----------
247
- switchback_test_id : str
248
- ID of the switchback test to start.
249
-
250
- Raises
251
- ------
252
- requests.HTTPError
253
- If the response status code is not 2xx.
254
- """
255
-
256
- _ = self.client.request(
257
- method="PUT",
258
- endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/start",
259
- )
260
-
261
- def stop_switchback_test(self: "Application", switchback_test_id: str, intent: StopIntent) -> None:
262
- """
263
- Stop a switchback test. The test should already have started before using
264
- this method.
265
-
266
- Parameters
267
- ----------
268
- switchback_test_id : str
269
- ID of the switchback test to stop.
270
-
271
- intent : StopIntent
272
- Intent for stopping the switchback test.
273
-
274
- Raises
275
- ------
276
- requests.HTTPError
277
- If the response status code is not 2xx.
278
- """
279
-
280
- payload = {
281
- "intent": intent.value,
282
- }
283
-
284
- _ = self.client.request(
285
- method="PUT",
286
- endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/stop",
287
- payload=payload,
288
- )
289
-
290
- def update_switchback_test(
291
- self: "Application",
292
- switchback_test_id: str,
293
- name: str | None = None,
294
- description: str | None = None,
295
- ) -> SwitchbackTest:
296
- """
297
- Update a switchback test.
298
-
299
- Parameters
300
- ----------
301
- switchback_test_id : str
302
- ID of the switchback test to update.
303
- name : Optional[str], default=None
304
- Optional name of the switchback test.
305
- description : Optional[str], default=None
306
- Optional description of the switchback test.
307
-
308
- Returns
309
- -------
310
- SwitchbackTest
311
- The information with the updated switchback test.
312
-
313
- Raises
314
- ------
315
- requests.HTTPError
316
- If the response status code is not 2xx.
317
- """
318
-
319
- payload = {}
320
-
321
- if name is not None:
322
- payload["name"] = name
323
- if description is not None:
324
- payload["description"] = description
325
-
326
- response = self.client.request(
327
- method="PATCH",
328
- endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}",
329
- payload=payload,
330
- )
331
-
332
- return SwitchbackTest.from_dict(response.json())