outerbounds 0.3.180rc5__py3-none-any.whl → 0.3.181__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.
outerbounds/__init__.py CHANGED
@@ -1,3 +1 @@
1
- from . import apps
2
-
3
- __all__ = ["apps"]
1
+ from .command_groups import cli
@@ -8,7 +8,6 @@ import shutil
8
8
  import subprocess
9
9
 
10
10
  from ..utils import metaflowconfig
11
- from ..apps.app_cli import app
12
11
 
13
12
  APP_READY_POLL_TIMEOUT_SECONDS = 300
14
13
  # Even after our backend validates that the app routes are ready, it takes a few seconds for
@@ -21,6 +20,11 @@ def cli(**kwargs):
21
20
  pass
22
21
 
23
22
 
23
+ @click.group(help="Manage apps")
24
+ def app(**kwargs):
25
+ pass
26
+
27
+
24
28
  @app.command(help="Start an app using a port and a name")
25
29
  @click.option(
26
30
  "-d",
@@ -410,7 +414,7 @@ def kill_process(config_dir=None, profile=None, port=-1, name=""):
410
414
  default=os.environ.get("METAFLOW_PROFILE", ""),
411
415
  help="The named metaflow profile in which your workstation exists",
412
416
  )
413
- def list_local(config_dir=None, profile=None):
417
+ def list(config_dir=None, profile=None):
414
418
  if "WORKSTATION_ID" not in os.environ:
415
419
  click.secho(
416
420
  "All outerbounds app commands can only be run from a workstation.",
@@ -7,6 +7,7 @@ from . import (
7
7
  tutorials_cli,
8
8
  fast_bakery_cli,
9
9
  secrets_cli,
10
+ kubernetes_cli,
10
11
  )
11
12
 
12
13
 
@@ -20,6 +21,7 @@ from . import (
20
21
  tutorials_cli.cli,
21
22
  fast_bakery_cli.cli,
22
23
  secrets_cli.cli,
24
+ kubernetes_cli.cli,
23
25
  ],
24
26
  )
25
27
  def cli(**kwargs):
@@ -0,0 +1,479 @@
1
+ import json
2
+ import base64
3
+ import boto3
4
+ import requests
5
+ import os
6
+ import time
7
+ from datetime import datetime
8
+ from functools import partial
9
+
10
+ from os import path, environ
11
+ from sys import exit
12
+ from outerbounds._vendor import click
13
+
14
+ from ..utils import metaflowconfig
15
+
16
+
17
+ def _logger(
18
+ body="", system_msg=False, head="", bad=False, timestamp=True, nl=True, color=None
19
+ ):
20
+ if timestamp:
21
+ if timestamp is True:
22
+ dt = datetime.now()
23
+ else:
24
+ dt = timestamp
25
+ tstamp = dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
26
+ click.secho(tstamp + " ", fg=ColorTheme.TIMESTAMP, nl=False)
27
+ if head:
28
+ click.secho(head, fg=ColorTheme.INFO_COLOR, nl=False)
29
+ click.secho(
30
+ body,
31
+ bold=system_msg,
32
+ fg=ColorTheme.BAD_COLOR if bad else color if color is not None else None,
33
+ nl=nl,
34
+ )
35
+
36
+
37
+ class ColorTheme:
38
+ TIMESTAMP = "magenta"
39
+ LOADING_COLOR = "cyan"
40
+ BAD_COLOR = "red"
41
+ INFO_COLOR = "green"
42
+
43
+ TL_HEADER_COLOR = "magenta"
44
+ ROW_COLOR = "bright_white"
45
+
46
+ INFO_KEY_COLOR = "green"
47
+ INFO_VALUE_COLOR = "bright_white"
48
+
49
+
50
+ def print_table(data, headers):
51
+ """Print data in a formatted table."""
52
+
53
+ if not data:
54
+ return
55
+
56
+ # Calculate column widths
57
+ col_widths = [len(h) for h in headers]
58
+
59
+ # Calculate actual widths based on data
60
+ for row in data:
61
+ for i, cell in enumerate(row):
62
+ col_widths[i] = max(col_widths[i], len(str(cell)))
63
+
64
+ # Print header
65
+ header_row = " | ".join(
66
+ [headers[i].ljust(col_widths[i]) for i in range(len(headers))]
67
+ )
68
+ click.secho("-" * len(header_row), fg=ColorTheme.TL_HEADER_COLOR)
69
+ click.secho(header_row, fg=ColorTheme.TL_HEADER_COLOR, bold=True)
70
+ click.secho("-" * len(header_row), fg=ColorTheme.TL_HEADER_COLOR)
71
+
72
+ # Print data rows
73
+ for row in data:
74
+ formatted_row = " | ".join(
75
+ [str(row[i]).ljust(col_widths[i]) for i in range(len(row))]
76
+ )
77
+ click.secho(formatted_row, fg=ColorTheme.ROW_COLOR, bold=True)
78
+ click.secho("-" * len(header_row), fg=ColorTheme.TL_HEADER_COLOR)
79
+
80
+
81
+ def _get_kubernetes_client():
82
+ """Get kubernetes client from metaflow configuration."""
83
+ from metaflow.plugins.kubernetes.kubernetes_client import KubernetesClient
84
+
85
+ return KubernetesClient()
86
+
87
+
88
+ def _get_current_user():
89
+ """Get current user from environment or metaflow config."""
90
+ # Try to get user from metaflow config first
91
+ try:
92
+ from metaflow.util import get_username
93
+
94
+ user = get_username()
95
+ if user:
96
+ return user
97
+ except:
98
+ pass
99
+
100
+ # Fallback to environment variables
101
+ raise click.ClickException("Failed to get current user")
102
+
103
+
104
+ def _format_jobs_and_jobsets_table(
105
+ jobs_with_outcomes, jobsets_with_outcomes, filter_unchanged=True
106
+ ):
107
+ """Format jobs and jobsets into a table for display."""
108
+ headers = [
109
+ "Type",
110
+ "Name",
111
+ "Namespace",
112
+ "Status",
113
+ "Outcome",
114
+ "Created",
115
+ "Flow",
116
+ "Run ID",
117
+ "User",
118
+ ]
119
+ table_data = []
120
+
121
+ # Add jobs to table
122
+ for job, outcome in jobs_with_outcomes:
123
+ # Filter out unchanged resources if requested
124
+ if filter_unchanged and outcome == "leave_unchanged":
125
+ continue
126
+
127
+ annotations = job.metadata.annotations or {}
128
+
129
+ # Format creation timestamp
130
+ created_time = "N/A"
131
+ if job.metadata.creation_timestamp:
132
+ created_time = job.metadata.creation_timestamp.strftime("%Y-%m-%d %H:%M:%S")
133
+
134
+ table_data.append(
135
+ [
136
+ "Job",
137
+ job.metadata.name,
138
+ job.metadata.namespace,
139
+ str(job.status.active or 0) + " active"
140
+ if job.status.active
141
+ else "inactive",
142
+ outcome,
143
+ created_time,
144
+ annotations.get("metaflow/flow_name", "N/A"),
145
+ annotations.get("metaflow/run_id", "N/A"),
146
+ annotations.get("metaflow/user", "N/A"),
147
+ ]
148
+ )
149
+
150
+ # Add jobsets to table
151
+ for jobset, outcome in jobsets_with_outcomes:
152
+ # Filter out unchanged resources if requested
153
+ if filter_unchanged and outcome == "leave_unchanged":
154
+ continue
155
+
156
+ metadata = jobset.get("metadata", {})
157
+ annotations = metadata.get("annotations", {})
158
+ status = jobset.get("status", {})
159
+
160
+ # Format creation timestamp
161
+ created_time = "N/A"
162
+ creation_timestamp = metadata.get("creationTimestamp")
163
+ if creation_timestamp:
164
+ try:
165
+ from datetime import datetime
166
+
167
+ # Parse ISO timestamp
168
+ dt = datetime.fromisoformat(creation_timestamp.replace("Z", "+00:00"))
169
+ created_time = dt.strftime("%Y-%m-%d %H:%M:%S")
170
+ except:
171
+ created_time = (
172
+ creation_timestamp[:19]
173
+ if len(creation_timestamp) >= 19
174
+ else creation_timestamp
175
+ )
176
+
177
+ table_data.append(
178
+ [
179
+ "JobSet",
180
+ metadata.get("name", "N/A"),
181
+ metadata.get("namespace", "N/A"),
182
+ "terminal" if status.get("terminalState") else "running",
183
+ outcome,
184
+ created_time,
185
+ annotations.get("metaflow/flow_name", "N/A"),
186
+ annotations.get("metaflow/run_id", "N/A"),
187
+ annotations.get("metaflow/user", "N/A"),
188
+ ]
189
+ )
190
+
191
+ return headers, table_data
192
+
193
+
194
+ @click.group()
195
+ def cli(**kwargs):
196
+ pass
197
+
198
+
199
+ @click.group(help="Commands for interacting with Kubernetes.")
200
+ def kubernetes(**kwargs):
201
+ pass
202
+
203
+
204
+ @kubernetes.command(help="Kill pods/jobs/jobsets for a specific flow.")
205
+ @click.option("--flow-name", required=True, help="Flow name to kill pods for")
206
+ @click.option("--run-id", help="Specific run ID to kill pods for")
207
+ @click.option("--my-runs", is_flag=True, help="Only kill runs by current user")
208
+ @click.option(
209
+ "--dry-run", is_flag=True, help="Show what would be killed without actually killing"
210
+ )
211
+ @click.option("--auto-approve", is_flag=True, help="Skip confirmation prompt")
212
+ @click.option(
213
+ "--clear-everything",
214
+ is_flag=True,
215
+ help="Force delete ALL matching resources regardless of their status (including terminal/completed ones)",
216
+ )
217
+ def kill(flow_name, run_id, my_runs, dry_run, auto_approve, clear_everything):
218
+ """Kill pods/jobs/jobsets for a specific flow."""
219
+ import warnings
220
+ from metaflow.ob_internal import PodKiller # type: ignore
221
+
222
+ warnings.filterwarnings("ignore")
223
+
224
+ logger = partial(_logger, timestamp=True)
225
+
226
+ # Get kubernetes client
227
+ kubernetes_client = _get_kubernetes_client()
228
+
229
+ # Determine user filter
230
+ user = None
231
+ if my_runs:
232
+ user = _get_current_user()
233
+ logger(f"🔍 Filtering for runs by user: {user}", color=ColorTheme.INFO_COLOR)
234
+
235
+ pod_killer = PodKiller(
236
+ kubernetes_client=kubernetes_client.get(),
237
+ echo_func=lambda x: None,
238
+ namespace=kubernetes_client._namespace,
239
+ )
240
+
241
+ # Find matching jobs and jobsets
242
+ logger(
243
+ f"🔍 Searching for jobs and jobsets matching flow: {flow_name}",
244
+ color=ColorTheme.INFO_COLOR,
245
+ )
246
+ if run_id:
247
+ logger(f"🔍 Filtering by run ID: {run_id}", color=ColorTheme.INFO_COLOR)
248
+
249
+ try:
250
+ (
251
+ jobs_with_outcomes,
252
+ jobsets_with_outcomes,
253
+ ) = pod_killer.extract_matching_jobs_and_jobsets(
254
+ flow_name=flow_name, run_id=run_id, user=user
255
+ )
256
+ except Exception as e:
257
+ logger(f"Error finding matching resources: {e}", bad=True, system_msg=True)
258
+ exit(1)
259
+
260
+ # Check if anything was found
261
+ total_resources_found = len(jobs_with_outcomes) + len(jobsets_with_outcomes)
262
+ if total_resources_found == 0:
263
+ logger("✅ No matching jobs or jobsets found.", color=ColorTheme.INFO_COLOR)
264
+ return
265
+
266
+ # Calculate resources that will be processed
267
+ if clear_everything:
268
+ # Process ALL resources regardless of status
269
+ jobs_to_process = len(jobs_with_outcomes)
270
+ jobsets_to_process = len(jobsets_with_outcomes)
271
+ total_to_process = jobs_to_process + jobsets_to_process
272
+ filter_table = False
273
+
274
+ # Show warning for clear-everything mode
275
+ logger(
276
+ "⚠️ CLEAR EVERYTHING MODE: All matching resources will be force deleted regardless of status!",
277
+ color=ColorTheme.BAD_COLOR,
278
+ system_msg=True,
279
+ )
280
+ else:
281
+ # Normal mode: only process resources not in terminal state
282
+ jobs_to_process = len(
283
+ [j for j, outcome in jobs_with_outcomes if outcome != "leave_unchanged"]
284
+ )
285
+ jobsets_to_process = len(
286
+ [j for j, outcome in jobsets_with_outcomes if outcome != "leave_unchanged"]
287
+ )
288
+ total_to_process = jobs_to_process + jobsets_to_process
289
+ filter_table = True
290
+
291
+ # Display what will be affected
292
+ headers, table_data = _format_jobs_and_jobsets_table(
293
+ jobs_with_outcomes, jobsets_with_outcomes, filter_unchanged=filter_table
294
+ )
295
+
296
+ if total_to_process == 0:
297
+ logger(
298
+ "✅ All matching resources are already in terminal state. Nothing to do.",
299
+ color=ColorTheme.INFO_COLOR,
300
+ )
301
+ return
302
+
303
+ if dry_run:
304
+ logger(
305
+ "=== DRY RUN - The following resources would be affected ===",
306
+ color=ColorTheme.INFO_COLOR,
307
+ system_msg=True,
308
+ timestamp=False,
309
+ )
310
+ else:
311
+ logger(
312
+ "=== The following resources will be killed/deleted ===",
313
+ color=ColorTheme.BAD_COLOR,
314
+ system_msg=True,
315
+ timestamp=False,
316
+ )
317
+
318
+ print_table(table_data, headers)
319
+
320
+ # Show summary
321
+ logger(
322
+ "📊 Summary:",
323
+ )
324
+ logger(
325
+ f" • Total resources found: {total_resources_found}",
326
+ )
327
+ logger(
328
+ f" • Jobs to process: {jobs_to_process}",
329
+ )
330
+ logger(
331
+ f" • JobSets to process: {jobsets_to_process}",
332
+ )
333
+ logger(
334
+ f" • Resources to process: {total_to_process}",
335
+ )
336
+
337
+ if clear_everything:
338
+ logger(
339
+ " • Mode: CLEAR EVERYTHING (forcing deletion of ALL resources)",
340
+ color=ColorTheme.BAD_COLOR,
341
+ )
342
+ else:
343
+ # Show how many are being skipped in normal mode
344
+ skipped_resources = total_resources_found - total_to_process
345
+ if skipped_resources > 0:
346
+ logger(
347
+ f" • Resources already in terminal state (skipped): {skipped_resources}",
348
+ color=ColorTheme.INFO_COLOR,
349
+ )
350
+
351
+ if dry_run:
352
+ logger(
353
+ "🔍 Dry run completed. No resources were actually killed.",
354
+ color=ColorTheme.INFO_COLOR,
355
+ system_msg=True,
356
+ )
357
+ return
358
+
359
+ # Confirm before proceeding (unless auto-approve is set)
360
+ if not auto_approve:
361
+ confirm = click.prompt(
362
+ click.style(
363
+ f"⚠️ Are you sure you want to kill/delete {total_to_process} resources?",
364
+ fg=ColorTheme.BAD_COLOR,
365
+ bold=True,
366
+ ),
367
+ default="no",
368
+ type=click.Choice(["yes", "no"]),
369
+ )
370
+ if confirm == "no":
371
+ logger("❌ Operation cancelled.", color=ColorTheme.BAD_COLOR)
372
+ exit(1)
373
+
374
+ # Execute the kills/deletions
375
+ logger(
376
+ f"🚀 Processing {total_to_process} resources...",
377
+ color=ColorTheme.INFO_COLOR,
378
+ system_msg=True,
379
+ )
380
+
381
+ try:
382
+ progress_label = (
383
+ f"⚰️ Coffin: Deleting jobs and jobsets matching flow: {flow_name}"
384
+ )
385
+ if clear_everything:
386
+ progress_label = (
387
+ f"🔥 CLEAR ALL: Force deleting ALL resources for flow: {flow_name}"
388
+ )
389
+
390
+ __progress_bar = click.progressbar(
391
+ length=total_to_process,
392
+ label=click.style(
393
+ progress_label,
394
+ fg=ColorTheme.BAD_COLOR if clear_everything else ColorTheme.INFO_COLOR,
395
+ bold=True,
396
+ ),
397
+ fill_char=click.style(
398
+ "█",
399
+ fg=ColorTheme.BAD_COLOR if clear_everything else ColorTheme.INFO_COLOR,
400
+ bold=True,
401
+ ),
402
+ empty_char=click.style(
403
+ "░",
404
+ fg=ColorTheme.BAD_COLOR if clear_everything else ColorTheme.INFO_COLOR,
405
+ bold=True,
406
+ ),
407
+ item_show_func=lambda x: click.style(
408
+ x,
409
+ fg=ColorTheme.BAD_COLOR,
410
+ bold=True,
411
+ ),
412
+ )
413
+
414
+ pod_killer = PodKiller(
415
+ kubernetes_client=kubernetes_client.get(),
416
+ echo_func=lambda x: None,
417
+ namespace=kubernetes_client._namespace,
418
+ progress_bar=__progress_bar,
419
+ )
420
+
421
+ if clear_everything:
422
+ # Force delete everything mode
423
+ (
424
+ results,
425
+ jobs_processed,
426
+ jobsets_processed,
427
+ ) = pod_killer.process_matching_jobs_and_jobsets_force_all(
428
+ flow_name=flow_name, run_id=run_id, user=user
429
+ )
430
+ else:
431
+ # Normal mode
432
+ (
433
+ results,
434
+ jobs_processed,
435
+ jobsets_processed,
436
+ ) = pod_killer.process_matching_jobs_and_jobsets(
437
+ flow_name=flow_name, run_id=run_id, user=user
438
+ )
439
+
440
+ # Report results
441
+ successful_operations = sum(1 for r in results if r is True)
442
+ failed_operations = sum(1 for r in results if r is False)
443
+
444
+ logger(
445
+ "📊 Operation completed:",
446
+ )
447
+ logger(
448
+ f" • Jobs processed: {jobs_processed}",
449
+ )
450
+ logger(
451
+ f" • JobSets processed: {jobsets_processed}",
452
+ )
453
+ logger(
454
+ f" • Successful operations: {successful_operations}",
455
+ )
456
+
457
+ if failed_operations > 0:
458
+ logger(
459
+ f" • Failed operations: {failed_operations}",
460
+ color=ColorTheme.BAD_COLOR,
461
+ )
462
+ logger(
463
+ "⚠️ Some operations failed. Check the logs above for details.",
464
+ color=ColorTheme.BAD_COLOR,
465
+ system_msg=True,
466
+ )
467
+ else:
468
+ logger(
469
+ "✅ All operations completed successfully!",
470
+ color=ColorTheme.INFO_COLOR,
471
+ system_msg=True,
472
+ )
473
+
474
+ except Exception as e:
475
+ logger(f"Error during kill operation: {e}", bad=True, system_msg=True)
476
+ raise e
477
+
478
+
479
+ cli.add_command(kubernetes, name="kubernetes")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: outerbounds
3
- Version: 0.3.180rc5
3
+ Version: 0.3.181
4
4
  Summary: More Data Science, Less Administration
5
5
  License: Proprietary
6
6
  Keywords: data science,machine learning,MLOps
@@ -29,8 +29,8 @@ Requires-Dist: google-cloud-secret-manager (>=2.20.0,<3.0.0) ; extra == "gcp"
29
29
  Requires-Dist: google-cloud-storage (>=2.14.0,<3.0.0) ; extra == "gcp"
30
30
  Requires-Dist: metaflow-checkpoint (==0.2.1)
31
31
  Requires-Dist: ob-metaflow (==2.15.17.1)
32
- Requires-Dist: ob-metaflow-extensions (==1.1.168rc5)
33
- Requires-Dist: ob-metaflow-stubs (==6.0.3.180rc5)
32
+ Requires-Dist: ob-metaflow-extensions (==1.1.169)
33
+ Requires-Dist: ob-metaflow-stubs (==6.0.3.181)
34
34
  Requires-Dist: opentelemetry-distro (>=0.41b0) ; extra == "otel"
35
35
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.20.0) ; extra == "otel"
36
36
  Requires-Dist: opentelemetry-instrumentation-requests (>=0.41b0) ; extra == "otel"
@@ -1,4 +1,4 @@
1
- outerbounds/__init__.py,sha256=8kikk4NZ0M0ymHuZN2mzw4LpJKFk0rvjYZ1uRefM7PE,39
1
+ outerbounds/__init__.py,sha256=GPdaubvAYF8pOFWJ3b-sPMKCpyfpteWVMZWkmaYhxRw,32
2
2
  outerbounds/_vendor/PyYAML.LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
3
3
  outerbounds/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  outerbounds/_vendor/_yaml/__init__.py,sha256=nD985-g4Mrx97PhtSzI2L53o8sCHUJ4ZoBWcUd7o0PQ,1449
@@ -20,9 +20,6 @@ outerbounds/_vendor/click/testing.py,sha256=ptpMYgRY7dVfE3UDgkgwayu9ePw98sQI3D7z
20
20
  outerbounds/_vendor/click/types.py,sha256=u8LK2CRcVw3jWDutzP_wgFV478TXhsjL8gYSFPjGKkE,35865
21
21
  outerbounds/_vendor/click/utils.py,sha256=33D6E7poH_nrKB-xr-UyDEXnxOcCiQqxuRLtrqeVv6o,18682
22
22
  outerbounds/_vendor/click.LICENSE,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475
23
- outerbounds/_vendor/spinner/__init__.py,sha256=gTm0NdQGTRxmghye1CYkhRtodji0MezsqAWs9OrLLRc,102
24
- outerbounds/_vendor/spinner/spinners.py,sha256=0Hl1dskTzfibHPM5dfHV3no1maFduOCJ48r8mr3tgr0,15786
25
- outerbounds/_vendor/spinner.LICENSE,sha256=R2t7cIDZ6-VroHO5M_FWET8adqmdI-PGv0AnhyvCo4A,1069
26
23
  outerbounds/_vendor/vendor_any.txt,sha256=9vi_h2zSBIx0sFM9i69xNEwe-HyeX0_sdtMjImmvgXo,27
27
24
  outerbounds/_vendor/yaml/__init__.py,sha256=lPcXUknB0EUNfCL8MJOgq26y70nOQR_Eajqzycmtnhg,12569
28
25
  outerbounds/_vendor/yaml/_yaml.cpython-311-darwin.so,sha256=YiF55JiadfOvw_mUH-lONNnsiMHj6C6o1SBfTCvvW54,362008
@@ -42,29 +39,12 @@ outerbounds/_vendor/yaml/resolver.py,sha256=dPhU1d7G1JCMktPFvNhyqwj2oNvx1yf_Jfa3
42
39
  outerbounds/_vendor/yaml/scanner.py,sha256=ZcI8IngR56PaQ0m27WU2vxCqmDCuRjz-hr7pirbMPuw,52982
43
40
  outerbounds/_vendor/yaml/serializer.py,sha256=8wFZRy9SsQSktF_f9OOroroqsh4qVUe53ry07P9UgCc,4368
44
41
  outerbounds/_vendor/yaml/tokens.py,sha256=JBSu38wihGr4l73JwbfMA7Ks1-X84g8-NskTz7KwPmA,2578
45
- outerbounds/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- outerbounds/apps/_state_machine.py,sha256=3hQF5O2zJdtQWdy9e5w393O85u6UjGApqTMlRU3UhFk,12964
47
- outerbounds/apps/app_cli.py,sha256=j_0vEQlsKGgvlPidog4bSZrY-_D1ne3JmHVafijL-iI,45354
48
- outerbounds/apps/app_config.py,sha256=KBmW9grhiuG9XZG-R0GZkM-024cjj6ztGzOX_2wZW34,11291
49
- outerbounds/apps/artifacts.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
- outerbounds/apps/capsule.py,sha256=YoOkUYGPrbXPaSUwS3teui1XPJDZCIxn-9tPpm1X0GM,30927
51
- outerbounds/apps/cli_to_config.py,sha256=Thc5jXRxoU6Pr8kAVVOX-5Es5ha6y6Vh_GBzL__oI7Q,3299
52
- outerbounds/apps/code_package/__init__.py,sha256=8McF7pgx8ghvjRnazp2Qktlxi9yYwNiwESSQrk-2oW8,68
53
- outerbounds/apps/code_package/code_packager.py,sha256=RWvM5BKjgLhu7icsO_n5SSYC57dwyST0dWpoWF88ovU,22881
54
- outerbounds/apps/code_package/examples.py,sha256=aF8qKIJxCVv_ugcShQjqUsXKKKMsm1oMkQIl8w3QKuw,4016
55
- outerbounds/apps/config_schema.yaml,sha256=j_mysTAPkIMSocItTg3aduMDfBs2teIhAErvpF0Elus,8826
56
- outerbounds/apps/dependencies.py,sha256=UucyQYZ5VjUPBb3XzAARa4fuiLHXuV7iMZ8OZ_nAuE8,3949
57
- outerbounds/apps/deployer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
- outerbounds/apps/experimental/__init__.py,sha256=ExPFIJSF8FcE1pKVyiNQnX8aBi3Rz8YFQ2_s5NVeU7I,3056
59
- outerbounds/apps/perimeters.py,sha256=DKWKMbkSx8WObf4yA74UolleT417B9c_6POioztMHxY,1766
60
- outerbounds/apps/secrets.py,sha256=sBMJzwjsARhtcuxgn18i66lagllpYOlh2oXR_hVAb1Y,6110
61
- outerbounds/apps/utils.py,sha256=NbEpSrzk-l95EBFoWuxil_0xjpIuFt0jjIXD2r1w71M,12118
62
- outerbounds/apps/validations.py,sha256=kR2eXckx0XJ4kUOOLkMRepbTh0INtL1Z8aV4-fZpfc8,678
63
42
  outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
64
43
  outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
65
- outerbounds/command_groups/apps_cli.py,sha256=ecXyLhGxjbct62iqviP9qBX8s4d-XG56ICpTM2h2etk,20821
66
- outerbounds/command_groups/cli.py,sha256=de4_QY1UeoKX6y-IXIbmklAi6bz0DsdBSmAoCg6lq1o,482
44
+ outerbounds/command_groups/apps_cli.py,sha256=v9OlQ1b4BGB-cBZiHB6W5gDocDoMmrQ7zdK11QiJ-B8,20847
45
+ outerbounds/command_groups/cli.py,sha256=FTeeDrvyBb-qcs2xklTiCyVTN5I0tBPyBReqDIE4oWU,530
67
46
  outerbounds/command_groups/fast_bakery_cli.py,sha256=5kja7v6C651XAY6dsP_IkBPJQgfU4hA4S9yTOiVPhW0,6213
47
+ outerbounds/command_groups/kubernetes_cli.py,sha256=2bxPKUp5g_gdwVo4lT-IeWvHxz6Jmj1KxG70nXNgX_M,14758
68
48
  outerbounds/command_groups/local_setup_cli.py,sha256=tuuqJRXQ_guEwOuQSIf9wkUU0yg8yAs31myGViAK15s,36364
69
49
  outerbounds/command_groups/perimeters_cli.py,sha256=iF_Uw7ROiSctf6FgoJEy30iDBLVE1j9FKuR3shgJRmc,19050
70
50
  outerbounds/command_groups/secrets_cli.py,sha256=Vgn_aiTo76a0s5hCJhNWEOrCVhyYeivD08ooQxz0y7c,2952
@@ -76,7 +56,7 @@ outerbounds/utils/metaflowconfig.py,sha256=l2vJbgPkLISU-XPGZFaC8ZKmYFyJemlD6bwB-
76
56
  outerbounds/utils/schema.py,sha256=lMUr9kNgn9wy-sO_t_Tlxmbt63yLeN4b0xQXbDUDj4A,2331
77
57
  outerbounds/utils/utils.py,sha256=4Z8cszNob_8kDYCLNTrP-wWads_S_MdL3Uj3ju4mEsk,501
78
58
  outerbounds/vendor.py,sha256=gRLRJNXtZBeUpPEog0LOeIsl6GosaFFbCxUvR4bW6IQ,5093
79
- outerbounds-0.3.180rc5.dist-info/METADATA,sha256=bhULcwevS8zjmFWtUdm_NqRqF1vYNqx1z-NGfBmKpb8,1846
80
- outerbounds-0.3.180rc5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
81
- outerbounds-0.3.180rc5.dist-info/entry_points.txt,sha256=AP6rZg7y5SK9e9a9iVq0Fi9Q2KPjPZSwtZ6R98rLw-8,56
82
- outerbounds-0.3.180rc5.dist-info/RECORD,,
59
+ outerbounds-0.3.181.dist-info/METADATA,sha256=XyEanDRdzyNArTKojXDq1HKZtEC6iPvJwv_7VrPFb6I,1837
60
+ outerbounds-0.3.181.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
61
+ outerbounds-0.3.181.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
62
+ outerbounds-0.3.181.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ outerbounds=outerbounds:cli
3
+
@@ -1,4 +0,0 @@
1
- __author__ = "Manraj Singh"
2
- __email__ = "manrajsinghgrover@gmail.com"
3
-
4
- from .spinners import Spinners