ob-metaflow 2.15.3.1__py2.py3-none-any.whl → 2.15.5.1__py2.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.
Potentially problematic release.
This version of ob-metaflow might be problematic. Click here for more details.
- metaflow/plugins/argo/argo_client.py +9 -2
- metaflow/plugins/argo/argo_workflows.py +79 -28
- metaflow/plugins/argo/argo_workflows_cli.py +16 -25
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +5 -2
- metaflow/plugins/cards/card_modules/main.js +52 -50
- metaflow/plugins/metadata_providers/service.py +16 -7
- metaflow/runner/click_api.py +5 -1
- metaflow/runner/deployer.py +3 -2
- metaflow/version.py +1 -1
- {ob_metaflow-2.15.3.1.data → ob_metaflow-2.15.5.1.data}/data/share/metaflow/devtools/Makefile +14 -4
- {ob_metaflow-2.15.3.1.data → ob_metaflow-2.15.5.1.data}/data/share/metaflow/devtools/Tiltfile +4 -4
- ob_metaflow-2.15.5.1.dist-info/METADATA +87 -0
- {ob_metaflow-2.15.3.1.dist-info → ob_metaflow-2.15.5.1.dist-info}/RECORD +18 -18
- {ob_metaflow-2.15.3.1.dist-info → ob_metaflow-2.15.5.1.dist-info}/WHEEL +1 -1
- ob_metaflow-2.15.3.1.dist-info/METADATA +0 -94
- {ob_metaflow-2.15.3.1.data → ob_metaflow-2.15.5.1.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {ob_metaflow-2.15.3.1.dist-info → ob_metaflow-2.15.5.1.dist-info}/LICENSE +0 -0
- {ob_metaflow-2.15.3.1.dist-info → ob_metaflow-2.15.5.1.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.15.3.1.dist-info → ob_metaflow-2.15.5.1.dist-info}/top_level.txt +0 -0
|
@@ -256,12 +256,19 @@ class ArgoClient(object):
|
|
|
256
256
|
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
257
257
|
)
|
|
258
258
|
|
|
259
|
-
def trigger_workflow_template(self, name, parameters={}):
|
|
259
|
+
def trigger_workflow_template(self, name, usertype, username, parameters={}):
|
|
260
260
|
client = self._client.get()
|
|
261
261
|
body = {
|
|
262
262
|
"apiVersion": "argoproj.io/v1alpha1",
|
|
263
263
|
"kind": "Workflow",
|
|
264
|
-
"metadata": {
|
|
264
|
+
"metadata": {
|
|
265
|
+
"generateName": name + "-",
|
|
266
|
+
"annotations": {
|
|
267
|
+
"metaflow/triggered_by_user": json.dumps(
|
|
268
|
+
{"type": usertype, "name": username}
|
|
269
|
+
)
|
|
270
|
+
},
|
|
271
|
+
},
|
|
265
272
|
"spec": {
|
|
266
273
|
"workflowTemplateRef": {"name": name},
|
|
267
274
|
"arguments": {
|
|
@@ -65,6 +65,7 @@ from metaflow.util import (
|
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
from .argo_client import ArgoClient
|
|
68
|
+
from metaflow.util import resolve_identity
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
class ArgoWorkflowsException(MetaflowException):
|
|
@@ -109,8 +110,7 @@ class ArgoWorkflows(object):
|
|
|
109
110
|
notify_slack_webhook_url=None,
|
|
110
111
|
notify_pager_duty_integration_key=None,
|
|
111
112
|
notify_incident_io_api_key=None,
|
|
112
|
-
|
|
113
|
-
incident_io_error_severity_id=None,
|
|
113
|
+
incident_io_alert_source_config_id=None,
|
|
114
114
|
enable_heartbeat_daemon=True,
|
|
115
115
|
enable_error_msg_capture=False,
|
|
116
116
|
):
|
|
@@ -161,8 +161,7 @@ class ArgoWorkflows(object):
|
|
|
161
161
|
self.notify_slack_webhook_url = notify_slack_webhook_url
|
|
162
162
|
self.notify_pager_duty_integration_key = notify_pager_duty_integration_key
|
|
163
163
|
self.notify_incident_io_api_key = notify_incident_io_api_key
|
|
164
|
-
self.
|
|
165
|
-
self.incident_io_error_severity_id = incident_io_error_severity_id
|
|
164
|
+
self.incident_io_alert_source_config_id = incident_io_alert_source_config_id
|
|
166
165
|
self.enable_heartbeat_daemon = enable_heartbeat_daemon
|
|
167
166
|
self.enable_error_msg_capture = enable_error_msg_capture
|
|
168
167
|
self.parameters = self._process_parameters()
|
|
@@ -316,8 +315,16 @@ class ArgoWorkflows(object):
|
|
|
316
315
|
"Workflows before proceeding." % name
|
|
317
316
|
)
|
|
318
317
|
try:
|
|
318
|
+
id_parts = resolve_identity().split(":")
|
|
319
|
+
parts_size = len(id_parts)
|
|
320
|
+
usertype = id_parts[0] if parts_size > 0 else "unknown"
|
|
321
|
+
username = id_parts[1] if parts_size > 1 else "unknown"
|
|
322
|
+
|
|
319
323
|
return ArgoClient(namespace=KUBERNETES_NAMESPACE).trigger_workflow_template(
|
|
320
|
-
name,
|
|
324
|
+
name,
|
|
325
|
+
usertype,
|
|
326
|
+
username,
|
|
327
|
+
parameters,
|
|
321
328
|
)
|
|
322
329
|
except Exception as e:
|
|
323
330
|
raise ArgoWorkflowsException(str(e))
|
|
@@ -2528,25 +2535,49 @@ class ArgoWorkflows(object):
|
|
|
2528
2535
|
def _incident_io_alert_template(self):
|
|
2529
2536
|
if self.notify_incident_io_api_key is None:
|
|
2530
2537
|
return None
|
|
2531
|
-
if self.
|
|
2538
|
+
if self.incident_io_alert_source_config_id is None:
|
|
2532
2539
|
raise MetaflowException(
|
|
2533
|
-
"Creating
|
|
2540
|
+
"Creating alerts for errors requires a alert source config ID."
|
|
2534
2541
|
)
|
|
2542
|
+
ui_links = self._incident_io_ui_urls_for_run()
|
|
2535
2543
|
return Template("notify-incident-io-on-error").http(
|
|
2536
2544
|
Http("POST")
|
|
2537
|
-
.url(
|
|
2545
|
+
.url(
|
|
2546
|
+
"https://api.incident.io/v2/alert_events/http/%s"
|
|
2547
|
+
% self.incident_io_alert_source_config_id
|
|
2548
|
+
)
|
|
2538
2549
|
.header("Content-Type", "application/json")
|
|
2539
2550
|
.header("Authorization", "Bearer %s" % self.notify_incident_io_api_key)
|
|
2540
2551
|
.body(
|
|
2541
2552
|
json.dumps(
|
|
2542
2553
|
{
|
|
2543
2554
|
"idempotency_key": "argo-{{workflow.name}}", # use run id to deduplicate alerts.
|
|
2544
|
-
"
|
|
2545
|
-
"
|
|
2546
|
-
"
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2555
|
+
"status": "firing",
|
|
2556
|
+
"title": "Flow %s has failed." % self.flow.name,
|
|
2557
|
+
"description": "Metaflow run {run_pathspec} failed!{urls}".format(
|
|
2558
|
+
run_pathspec="%s/argo-{{workflow.name}}" % self.flow.name,
|
|
2559
|
+
urls=(
|
|
2560
|
+
"\n\nSee details for the run at:\n\n"
|
|
2561
|
+
+ "\n\n".join(ui_links)
|
|
2562
|
+
if ui_links
|
|
2563
|
+
else ""
|
|
2564
|
+
),
|
|
2565
|
+
),
|
|
2566
|
+
"source_url": (
|
|
2567
|
+
"%s/%s/%s"
|
|
2568
|
+
% (
|
|
2569
|
+
UI_URL.rstrip("/"),
|
|
2570
|
+
self.flow.name,
|
|
2571
|
+
"argo-{{workflow.name}}",
|
|
2572
|
+
)
|
|
2573
|
+
if UI_URL
|
|
2574
|
+
else None
|
|
2575
|
+
),
|
|
2576
|
+
"metadata": {
|
|
2577
|
+
"run_status": "failed",
|
|
2578
|
+
"flow_name": self.flow.name,
|
|
2579
|
+
"run_id": "argo-{{workflow.name}}",
|
|
2580
|
+
},
|
|
2550
2581
|
}
|
|
2551
2582
|
)
|
|
2552
2583
|
)
|
|
@@ -2555,27 +2586,49 @@ class ArgoWorkflows(object):
|
|
|
2555
2586
|
def _incident_io_change_template(self):
|
|
2556
2587
|
if self.notify_incident_io_api_key is None:
|
|
2557
2588
|
return None
|
|
2558
|
-
if self.
|
|
2589
|
+
if self.incident_io_alert_source_config_id is None:
|
|
2559
2590
|
raise MetaflowException(
|
|
2560
|
-
"Creating
|
|
2591
|
+
"Creating alerts for successes requires an alert source config ID."
|
|
2561
2592
|
)
|
|
2593
|
+
ui_links = self._incident_io_ui_urls_for_run()
|
|
2562
2594
|
return Template("notify-incident-io-on-success").http(
|
|
2563
2595
|
Http("POST")
|
|
2564
|
-
.url(
|
|
2596
|
+
.url(
|
|
2597
|
+
"https://api.incident.io/v2/alert_events/http/%s"
|
|
2598
|
+
% self.incident_io_alert_source_config_id
|
|
2599
|
+
)
|
|
2565
2600
|
.header("Content-Type", "application/json")
|
|
2566
2601
|
.header("Authorization", "Bearer %s" % self.notify_incident_io_api_key)
|
|
2567
2602
|
.body(
|
|
2568
2603
|
json.dumps(
|
|
2569
2604
|
{
|
|
2570
2605
|
"idempotency_key": "argo-{{workflow.name}}", # use run id to deduplicate alerts.
|
|
2571
|
-
"
|
|
2572
|
-
"
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2606
|
+
"status": "firing",
|
|
2607
|
+
"title": "Flow %s has succeeded." % self.flow.name,
|
|
2608
|
+
"description": "Metaflow run {run_pathspec} succeeded!{urls}".format(
|
|
2609
|
+
run_pathspec="%s/argo-{{workflow.name}}" % self.flow.name,
|
|
2610
|
+
urls=(
|
|
2611
|
+
"\n\nSee details for the run at:\n\n"
|
|
2612
|
+
+ "\n\n".join(ui_links)
|
|
2613
|
+
if ui_links
|
|
2614
|
+
else ""
|
|
2615
|
+
),
|
|
2616
|
+
),
|
|
2617
|
+
"source_url": (
|
|
2618
|
+
"%s/%s/%s"
|
|
2619
|
+
% (
|
|
2620
|
+
UI_URL.rstrip("/"),
|
|
2621
|
+
self.flow.name,
|
|
2622
|
+
"argo-{{workflow.name}}",
|
|
2623
|
+
)
|
|
2624
|
+
if UI_URL
|
|
2625
|
+
else None
|
|
2626
|
+
),
|
|
2627
|
+
"metadata": {
|
|
2628
|
+
"run_status": "succeeded",
|
|
2629
|
+
"flow_name": self.flow.name,
|
|
2630
|
+
"run_id": "argo-{{workflow.name}}",
|
|
2631
|
+
},
|
|
2579
2632
|
}
|
|
2580
2633
|
)
|
|
2581
2634
|
)
|
|
@@ -2597,9 +2650,7 @@ class ArgoWorkflows(object):
|
|
|
2597
2650
|
"{{workflow.name}}",
|
|
2598
2651
|
)
|
|
2599
2652
|
links.append(url)
|
|
2600
|
-
|
|
2601
|
-
links = ["See details for the run at: ", *links]
|
|
2602
|
-
return "\n\n".join(links)
|
|
2653
|
+
return links
|
|
2603
2654
|
|
|
2604
2655
|
def _pager_duty_change_template(self):
|
|
2605
2656
|
# https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgy-send-a-change-event
|
|
@@ -130,6 +130,7 @@ def argo_workflows(obj, name=None):
|
|
|
130
130
|
is_flag=True,
|
|
131
131
|
default=False,
|
|
132
132
|
help="Only print out JSON sent to Argo Workflows. Do not deploy anything.",
|
|
133
|
+
hidden=True,
|
|
133
134
|
)
|
|
134
135
|
@click.option(
|
|
135
136
|
"--max-workers",
|
|
@@ -182,14 +183,9 @@ def argo_workflows(obj, name=None):
|
|
|
182
183
|
help="Incident.io API V2 key for workflow success/failure notifications.",
|
|
183
184
|
)
|
|
184
185
|
@click.option(
|
|
185
|
-
"--incident-io-
|
|
186
|
-
default=None,
|
|
187
|
-
help="Incident.io severity id for success alerts.",
|
|
188
|
-
)
|
|
189
|
-
@click.option(
|
|
190
|
-
"--incident-io-error-severity-id",
|
|
186
|
+
"--incident-io-alert-source-config-id",
|
|
191
187
|
default=None,
|
|
192
|
-
help="Incident.io
|
|
188
|
+
help="Incident.io Alert source config ID. Example '01GW2G3V0S59R238FAHPDS1R66'",
|
|
193
189
|
)
|
|
194
190
|
@click.option(
|
|
195
191
|
"--enable-heartbeat-daemon/--no-enable-heartbeat-daemon",
|
|
@@ -229,8 +225,7 @@ def create(
|
|
|
229
225
|
notify_slack_webhook_url=None,
|
|
230
226
|
notify_pager_duty_integration_key=None,
|
|
231
227
|
notify_incident_io_api_key=None,
|
|
232
|
-
|
|
233
|
-
incident_io_error_severity_id=None,
|
|
228
|
+
incident_io_alert_source_config_id=None,
|
|
234
229
|
enable_heartbeat_daemon=True,
|
|
235
230
|
deployer_attribute_file=None,
|
|
236
231
|
enable_error_msg_capture=False,
|
|
@@ -287,8 +282,7 @@ def create(
|
|
|
287
282
|
notify_slack_webhook_url,
|
|
288
283
|
notify_pager_duty_integration_key,
|
|
289
284
|
notify_incident_io_api_key,
|
|
290
|
-
|
|
291
|
-
incident_io_error_severity_id,
|
|
285
|
+
incident_io_alert_source_config_id,
|
|
292
286
|
enable_heartbeat_daemon,
|
|
293
287
|
enable_error_msg_capture,
|
|
294
288
|
)
|
|
@@ -464,8 +458,7 @@ def make_flow(
|
|
|
464
458
|
notify_slack_webhook_url,
|
|
465
459
|
notify_pager_duty_integration_key,
|
|
466
460
|
notify_incident_io_api_key,
|
|
467
|
-
|
|
468
|
-
incident_io_error_severity_id,
|
|
461
|
+
incident_io_alert_source_config_id,
|
|
469
462
|
enable_heartbeat_daemon,
|
|
470
463
|
enable_error_msg_capture,
|
|
471
464
|
):
|
|
@@ -488,19 +481,18 @@ def make_flow(
|
|
|
488
481
|
"https://api.slack.com/messaging/webhooks to generate a webhook url.\n"
|
|
489
482
|
" For notifications through PagerDuty, generate an integration key by following the instructions at "
|
|
490
483
|
"https://support.pagerduty.com/docs/services-and-integrations#create-a-generic-events-api-integration\n"
|
|
491
|
-
" For notifications through Incident.io, generate an
|
|
484
|
+
" For notifications through Incident.io, generate an alert source config."
|
|
492
485
|
)
|
|
493
486
|
|
|
494
|
-
if
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
487
|
+
if (
|
|
488
|
+
(notify_on_error or notify_on_success)
|
|
489
|
+
and notify_incident_io_api_key
|
|
490
|
+
and incident_io_alert_source_config_id is None
|
|
491
|
+
):
|
|
492
|
+
raise MetaflowException(
|
|
493
|
+
"Incident.io alerts require an alert source configuration ID. Please set one with --incident-io-alert-source-config-id"
|
|
494
|
+
)
|
|
499
495
|
|
|
500
|
-
if notify_on_success and incident_io_success_severity_id is None:
|
|
501
|
-
raise MetaflowException(
|
|
502
|
-
"Incident.io success notifications require a severity id. Please set one with --incident-io-success-severity-id"
|
|
503
|
-
)
|
|
504
496
|
# Attach @kubernetes and @environment decorator to the flow to
|
|
505
497
|
# ensure that the related decorator hooks are invoked.
|
|
506
498
|
decorators._attach_decorators(
|
|
@@ -545,8 +537,7 @@ def make_flow(
|
|
|
545
537
|
notify_slack_webhook_url=notify_slack_webhook_url,
|
|
546
538
|
notify_pager_duty_integration_key=notify_pager_duty_integration_key,
|
|
547
539
|
notify_incident_io_api_key=notify_incident_io_api_key,
|
|
548
|
-
|
|
549
|
-
incident_io_error_severity_id=incident_io_error_severity_id,
|
|
540
|
+
incident_io_alert_source_config_id=incident_io_alert_source_config_id,
|
|
550
541
|
enable_heartbeat_daemon=enable_heartbeat_daemon,
|
|
551
542
|
enable_error_msg_capture=enable_error_msg_capture,
|
|
552
543
|
)
|
|
@@ -171,12 +171,16 @@ class ArgoWorkflowsTriggeredRun(TriggeredRun):
|
|
|
171
171
|
command_obj.sync_wait()
|
|
172
172
|
return command_obj.process.returncode == 0
|
|
173
173
|
|
|
174
|
-
def wait_for_completion(
|
|
174
|
+
def wait_for_completion(
|
|
175
|
+
self, check_interval: int = 5, timeout: Optional[int] = None
|
|
176
|
+
):
|
|
175
177
|
"""
|
|
176
178
|
Wait for the workflow to complete or timeout.
|
|
177
179
|
|
|
178
180
|
Parameters
|
|
179
181
|
----------
|
|
182
|
+
check_interval: int, default: 5
|
|
183
|
+
Frequency of checking for workflow completion, in seconds.
|
|
180
184
|
timeout : int, optional, default None
|
|
181
185
|
Maximum time in seconds to wait for workflow completion.
|
|
182
186
|
If None, waits indefinitely.
|
|
@@ -187,7 +191,6 @@ class ArgoWorkflowsTriggeredRun(TriggeredRun):
|
|
|
187
191
|
If the workflow does not complete within the specified timeout period.
|
|
188
192
|
"""
|
|
189
193
|
start_time = time.time()
|
|
190
|
-
check_interval = 5
|
|
191
194
|
while self.is_running:
|
|
192
195
|
if timeout is not None and (time.time() - start_time) > timeout:
|
|
193
196
|
raise TimeoutError(
|