hcs-core 0.1.307__tar.gz → 0.1.309__tar.gz

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 (71) hide show
  1. {hcs_core-0.1.307 → hcs_core-0.1.309}/PKG-INFO +2 -2
  2. hcs_core-0.1.309/hcs_core/__init__.py +1 -0
  3. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/cli_options.py +19 -3
  4. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/cli_processor.py +10 -8
  5. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/util.py +48 -1
  6. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/cli_options.py +2 -0
  7. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/client_util.py +28 -15
  8. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/login_support.py +1 -1
  9. {hcs_core-0.1.307 → hcs_core-0.1.309}/pyproject.toml +1 -1
  10. hcs_core-0.1.307/hcs_core/__init__.py +0 -1
  11. {hcs_core-0.1.307 → hcs_core-0.1.309}/.gitignore +0 -0
  12. {hcs_core-0.1.307 → hcs_core-0.1.309}/README.md +0 -0
  13. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/__init__.py +0 -0
  14. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/_init.py +0 -0
  15. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/built_in_cmds/__init__.py +0 -0
  16. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/built_in_cmds/_ut.py +0 -0
  17. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/built_in_cmds/context.py +0 -0
  18. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/built_in_cmds/profile.py +0 -0
  19. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/cmd_util.py +0 -0
  20. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/config.py +0 -0
  21. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/context.py +0 -0
  22. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/data_util.py +0 -0
  23. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/dispatcher.py +0 -0
  24. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/duration.py +0 -0
  25. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/extension.py +0 -0
  26. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/fn_util.py +0 -0
  27. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/fstore.py +0 -0
  28. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/jsondot.py +0 -0
  29. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/logger.py +0 -0
  30. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/profile.py +0 -0
  31. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/profile_store.py +0 -0
  32. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/recent.py +0 -0
  33. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/state.py +0 -0
  34. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/task_schd.py +0 -0
  35. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/telemetry.py +0 -0
  36. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/template_util.py +0 -0
  37. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/timeutil.py +0 -0
  38. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/ctxp/var_template.py +0 -0
  39. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/__init__.py +0 -0
  40. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/actions.py +0 -0
  41. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/base_provider.py +0 -0
  42. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/context.py +0 -0
  43. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/core.py +0 -0
  44. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/dag.py +0 -0
  45. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/helper.py +0 -0
  46. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/kop.py +0 -0
  47. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/provider/__init__.py +0 -0
  48. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/provider/dev/__init__.py +0 -0
  49. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/provider/dev/_prepare.py +0 -0
  50. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/provider/dev/dummy.py +0 -0
  51. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/plan/provider/dev/fibonacci.py +0 -0
  52. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/__init__.py +0 -0
  53. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/auth.py +0 -0
  54. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/csp.py +0 -0
  55. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/ez_client.py +0 -0
  56. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/hcs_client.py +0 -0
  57. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/init.py +0 -0
  58. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/payload_util.py +0 -0
  59. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/requtil.py +0 -0
  60. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/sglib/utils.py +0 -0
  61. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/__init__.py +0 -0
  62. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/check_license.py +0 -0
  63. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/duration.py +0 -0
  64. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/exit.py +0 -0
  65. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/hcs_constants.py +0 -0
  66. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/job_view.py +0 -0
  67. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/pki_util.py +0 -0
  68. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/query_util.py +0 -0
  69. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/scheduler.py +0 -0
  70. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/ssl_util.py +0 -0
  71. {hcs_core-0.1.307 → hcs_core-0.1.309}/hcs_core/util/versions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hcs-core
3
- Version: 0.1.307
3
+ Version: 0.1.309
4
4
  Summary: Horizon Cloud Service CLI module.
5
5
  Project-URL: Homepage, https://github.com/euc-eng/hcs-cli
6
6
  Project-URL: Bug Tracker, https://github.com/euc-eng/hcs-cli/issues
@@ -36,7 +36,7 @@ Requires-Dist: schedule>=1.1.0
36
36
  Requires-Dist: setuptools>=70.0.0
37
37
  Requires-Dist: tabulate>=0.9.0
38
38
  Requires-Dist: websocket-client>=1.2.3
39
- Requires-Dist: yumako>=0.1.31
39
+ Requires-Dist: yumako>=0.1.36
40
40
  Provides-Extra: dev
41
41
  Requires-Dist: bandit; extra == 'dev'
42
42
  Requires-Dist: black; extra == 'dev'
@@ -0,0 +1 @@
1
+ __version__ = "0.1.309"
@@ -13,6 +13,8 @@ See the License for the specific language governing permissions and
13
13
  limitations under the License.
14
14
  """
15
15
 
16
+ import os
17
+
16
18
  import click
17
19
 
18
20
  verbose = click.option(
@@ -27,7 +29,7 @@ verbose = click.option(
27
29
  output = click.option(
28
30
  "--output",
29
31
  "-o",
30
- type=click.Choice(["json", "json-compact", "yaml", "yml", "text", "table"], case_sensitive=False),
32
+ type=click.Choice(["json", "json-compact", "yaml", "yml", "table", "t", "text"], case_sensitive=False),
31
33
  default=None,
32
34
  hidden=True,
33
35
  help="Specify output format",
@@ -101,10 +103,24 @@ force = click.option("--force/--grace", type=bool, default=True, help="Specify d
101
103
 
102
104
  confirm = click.option("--confirm/--prompt", "-y", type=bool, default=False, help="Confirm the operation without prompt.")
103
105
 
106
+ env = click.option(
107
+ "--env",
108
+ multiple=True,
109
+ type=str,
110
+ required=False,
111
+ help="Alternative explicit in-line environment variable override in KEY=VALUE format.",
112
+ )
113
+
114
+
115
+ def apply_env(envs):
116
+ for kv in envs:
117
+ k, v = kv.split("=", 1)
118
+ os.environ[k.strip()] = v.strip()
119
+
104
120
 
105
- def formatter(custom_fn):
121
+ def formatter(formatter=None, columns=None):
106
122
  def decorator(f):
107
- f.formatter = custom_fn
123
+ f.formatter = formatter if formatter else columns
108
124
  return f
109
125
 
110
126
  return decorator
@@ -24,7 +24,7 @@ from pathlib import Path
24
24
  import click
25
25
  from click.core import Group
26
26
 
27
- from .util import avoid_trace_for_ctrl_c, print_error, print_output, validate_error_return
27
+ from .util import avoid_trace_for_ctrl_c, default_table_formatter, print_error, print_output, validate_error_return
28
28
 
29
29
  _eager_loading = os.environ.get("_CTXP_EAGER_LOAD")
30
30
  if _eager_loading:
@@ -192,15 +192,17 @@ def _default_io(cmd: click.Command):
192
192
 
193
193
  telemetry_update(ctx.command_path, ctx.params)
194
194
 
195
- if io_args["output"] == "table":
195
+ if io_args["output"] == "table" or io_args["output"] == "t":
196
196
 
197
197
  def _format(data):
198
- if not hasattr(callback, "formatter"):
199
- from .util import CtxpException
200
-
201
- raise CtxpException("Table output is specified, but no custom formatter specified on the command.")
202
- formatter = callback.__getattribute__("formatter")
203
- return formatter(data)
198
+ if hasattr(callback, "formatter"):
199
+ formatter = callback.__getattribute__("formatter")
200
+ else:
201
+ formatter = default_table_formatter
202
+ if isinstance(formatter, dict):
203
+ return default_table_formatter(data, formatter)
204
+ else:
205
+ return formatter(data)
204
206
 
205
207
  io_args["format"] = _format
206
208
  ret = callback(*args, **kwargs)
@@ -13,6 +13,7 @@ See the License for the specific language governing permissions and
13
13
  limitations under the License.
14
14
  """
15
15
 
16
+ import datetime
16
17
  import json
17
18
  import os
18
19
  import re
@@ -26,6 +27,7 @@ import click
26
27
  import httpx
27
28
  import questionary
28
29
  import yaml
30
+ import yumako
29
31
 
30
32
 
31
33
  class CtxpException(Exception):
@@ -112,7 +114,7 @@ def print_output(data: Any, args: dict, file=sys.stdout):
112
114
  text = data
113
115
  else:
114
116
  text = json.dumps(data, indent=4)
115
- elif output == "table":
117
+ elif output == "table" or output == "t":
116
118
  formatter = args["format"]
117
119
  text = formatter(data)
118
120
  else:
@@ -411,3 +413,48 @@ def colorize(data: dict, name: str, mapping: dict):
411
413
  data[name] = click.style(s, fg=c)
412
414
  else:
413
415
  raise Exception(f"Unexpected mapping type: {type(mapping)} {mapping}")
416
+
417
+
418
+ def default_table_formatter(data: Any, mapping: dict = None):
419
+ if not isinstance(data, list):
420
+ return data
421
+
422
+ def _restrict_readable_length(data: dict, name: str, length: int):
423
+ text = data.get(name)
424
+ if not text:
425
+ return
426
+ if len(text) > length:
427
+ data[name] = text[: length - 3] + "..."
428
+
429
+ field_mapping = {}
430
+ for d in data:
431
+ if "id" in d:
432
+ field_mapping["id"] = "Id"
433
+ if "name" in d:
434
+ field_mapping["name"] = "Name"
435
+ if "location" in d:
436
+ field_mapping["location"] = "Location"
437
+ if "type" in d:
438
+ field_mapping["type"] = "Type"
439
+ if "status" in d:
440
+ field_mapping["status"] = "Status"
441
+ if "createdAt" in d:
442
+ d["_createdStale"] = yumako.time.stale(d["createdAt"], datetime.timezone.utc)
443
+ field_mapping["_createdStale"] = "Created At"
444
+ if "updatedAt" in d:
445
+ d["_updatedStale"] = yumako.time.stale(d["updatedAt"], datetime.timezone.utc)
446
+ field_mapping["_updatedStale"] = "Updated At"
447
+
448
+ colorize(
449
+ d,
450
+ "status",
451
+ {
452
+ "READY": "green",
453
+ "SUCCESS": "green",
454
+ "ERROR": "red",
455
+ },
456
+ )
457
+ _restrict_readable_length(d, "name", 60)
458
+ if mapping:
459
+ field_mapping.update(mapping)
460
+ return format_table(data, fields_mapping=field_mapping)
@@ -18,7 +18,9 @@ import os
18
18
  import click
19
19
 
20
20
  from hcs_core.ctxp import CtxpException, recent
21
+ from hcs_core.ctxp.cli_options import apply_env as apply_env
21
22
  from hcs_core.ctxp.cli_options import confirm as confirm
23
+ from hcs_core.ctxp.cli_options import env as env
22
24
  from hcs_core.ctxp.cli_options import exclude_field as exclude_field
23
25
  from hcs_core.ctxp.cli_options import field as field
24
26
  from hcs_core.ctxp.cli_options import first as first
@@ -288,10 +288,13 @@ def wait_for_res_deleted(
288
288
  resource_name: str,
289
289
  fn_get: Callable,
290
290
  timeout: str,
291
- polling_interval_seconds: int = 10,
291
+ polling_interval: int = 10,
292
292
  fn_is_error: Callable = None,
293
293
  ):
294
294
  timeout_seconds = _parse_timeout(timeout)
295
+ polling_interval_seconds = _parse_timeout(polling_interval)
296
+ if polling_interval_seconds < 3:
297
+ polling_interval_seconds = 3
295
298
  start = time.time()
296
299
  while True:
297
300
  t = fn_get()
@@ -310,7 +313,7 @@ def wait_for_res_deleted(
310
313
  sleep_seconds = remaining_seconds
311
314
  if sleep_seconds > polling_interval_seconds:
312
315
  sleep_seconds = polling_interval_seconds
313
- exit.sleep(sleep_seconds)
316
+ time.sleep(sleep_seconds)
314
317
 
315
318
 
316
319
  # flake8: noqa=E731
@@ -337,21 +340,25 @@ def wait_for_res_status(
337
340
  field_name = get_status
338
341
  get_status = lambda t: t[field_name]
339
342
  if status_map:
340
- if isinstance(status_map["ready"], str):
341
- status_map["ready"] = [status_map["ready"]]
342
- if isinstance(status_map["transition"], str):
343
- status_map["transition"] = [status_map["transition"]]
344
- if isinstance(status_map["error"], str):
345
- status_map["error"] = [status_map["error"]]
346
343
  if is_ready:
347
344
  raise CtxpException("Can not specify is_ready when status_map is provided.")
348
345
  if is_error:
349
346
  raise CtxpException("Can not specify is_error when status_map is provided.")
350
347
  if is_transition:
351
348
  raise CtxpException("Can not specify is_transition when status_map is provided.")
352
- is_ready = lambda s: s in status_map["ready"]
353
- is_error = lambda s: s in status_map["error"]
354
- is_transition = lambda s: s in status_map["transition"]
349
+
350
+ ready_status = status_map["ready"]
351
+ error_status = status_map["error"]
352
+ transition_status = status_map["transition"]
353
+ if isinstance(ready_status, str):
354
+ ready_status = [ready_status]
355
+ if isinstance(error_status, str):
356
+ error_status = [error_status]
357
+ if isinstance(transition_status, str):
358
+ transition_status = [transition_status]
359
+ is_ready = lambda s: s in ready_status
360
+ is_error = lambda s: error_status is None or s in error_status
361
+ is_transition = lambda s: transition_status is None or s in transition_status
355
362
  else:
356
363
  if not is_ready:
357
364
  raise CtxpException("Either status_map or is_ready must be specified.")
@@ -359,6 +366,9 @@ def wait_for_res_status(
359
366
  raise CtxpException("Either status_map or is_error must be specified.")
360
367
  if not is_transition:
361
368
  raise CtxpException("Either status_map or is_transition must be specified.")
369
+ ready_status = None
370
+ error_status = None
371
+ transition_status = None
362
372
 
363
373
  while True:
364
374
  t = fn_get()
@@ -368,9 +378,9 @@ def wait_for_res_status(
368
378
  raise CtxpException(prefix + "Not found.")
369
379
  status = get_status(t)
370
380
  if is_error(status):
371
- msg = prefix + f"Status error. Actual={status}"
372
- if status_map:
373
- msg += f", expected={status_map['ready']}"
381
+ msg = prefix + f"Unexpected terminal state. Actual={status}"
382
+ if ready_status:
383
+ msg += f", expected={ready_status}"
374
384
  print("-- DUMP START --", file=sys.stderr)
375
385
  print(json.dumps(t, indent=4), file=sys.stderr)
376
386
  print("-- DUMP END --", file=sys.stderr)
@@ -383,7 +393,10 @@ def wait_for_res_status(
383
393
  now = time.time()
384
394
  remaining_seconds = timeout_seconds - (now - start)
385
395
  if remaining_seconds < 1:
386
- raise TimeoutError(prefix + "Timeout.")
396
+ msg = prefix + f"Timeout. Current: {status}."
397
+ if ready_status:
398
+ msg += f" Expected: {ready_status}."
399
+ raise TimeoutError(msg)
387
400
  sleep_seconds = remaining_seconds
388
401
  if sleep_seconds > polling_interval_seconds:
389
402
  sleep_seconds = polling_interval_seconds
@@ -61,7 +61,7 @@ _auth_success_html = """
61
61
  </style>
62
62
  </head>
63
63
  <body>
64
- <h3>You have successfully logged into VMware Horizon Cloud Service.</h3>
64
+ <h3>You have successfully logged into Omnissa Horizon Cloud Service.</h3>
65
65
  <p>You can close this window, and return to the terminal.</p>
66
66
  <br/>
67
67
  <p><a href="https://github.com/euc-eng/hcs-cli/blob/dev/README.md">HCS CLI</a> is in beta. <a href="https://github.com/euc-eng/hcs-cli/blob/main/doc/hcs-cli-cheatsheet.md">Cheatsheet</a>:</p>
@@ -43,7 +43,7 @@ dependencies = [
43
43
  "websocket_client>=1.2.3",
44
44
  "psutil>=7.0.0",
45
45
  "schedule>=1.1.0",
46
- "yumako>=0.1.31",
46
+ "yumako>=0.1.36",
47
47
  "pydantic>=2.11.7",
48
48
  "python-dotenv>=1.1.1",
49
49
  ]
@@ -1 +0,0 @@
1
- __version__ = "0.1.307"
File without changes
File without changes