hcs-core 0.1.291__tar.gz → 0.1.292__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.
- {hcs_core-0.1.291 → hcs_core-0.1.292}/PKG-INFO +16 -16
- hcs_core-0.1.292/hcs_core/__init__.py +1 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/built_in_cmds/profile.py +1 -3
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/cli_options.py +2 -6
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/data_util.py +2 -6
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/duration.py +1 -3
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/logger.py +4 -4
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/profile.py +1 -3
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/recent.py +1 -1
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/telemetry.py +16 -2
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/util.py +1 -1
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/core.py +3 -11
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/auth.py +16 -7
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/client_util.py +27 -13
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/csp.py +1 -3
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/login_support.py +1 -1
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/job_view.py +1 -3
- {hcs_core-0.1.291 → hcs_core-0.1.292}/pyproject.toml +16 -16
- hcs_core-0.1.291/hcs_core/__init__.py +0 -1
- {hcs_core-0.1.291 → hcs_core-0.1.292}/.gitignore +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/README.md +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/__init__.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/_init.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/built_in_cmds/__init__.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/built_in_cmds/_ut.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/built_in_cmds/context.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/cli_processor.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/cmd_util.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/config.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/context.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/dispatcher.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/extension.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/fn_util.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/fstore.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/jsondot.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/profile_store.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/state.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/task_schd.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/template_util.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/timeutil.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/ctxp/var_template.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/__init__.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/actions.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/base_provider.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/context.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/dag.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/helper.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/kop.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/provider/__init__.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/provider/dev/__init__.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/provider/dev/_prepare.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/provider/dev/dummy.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/plan/provider/dev/fibonacci.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/__init__.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/cli_options.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/ez_client.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/hcs_client.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/init.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/payload_util.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/requtil.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/sglib/utils.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/__init__.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/check_license.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/duration.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/exit.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/hcs_constants.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/pki_util.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/query_util.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/scheduler.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/hcs_core/util/ssl_util.py +0 -0
- {hcs_core-0.1.291 → hcs_core-0.1.292}/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.
|
|
3
|
+
Version: 0.1.292
|
|
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
|
|
@@ -14,29 +14,29 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Requires-Python: >=3.9
|
|
17
|
-
Requires-Dist: authlib>=1.
|
|
18
|
-
Requires-Dist: click>=8.1
|
|
17
|
+
Requires-Dist: authlib>=1.6.0
|
|
18
|
+
Requires-Dist: click>=8.2.1
|
|
19
19
|
Requires-Dist: coloredlogs>=15.0.1
|
|
20
|
-
Requires-Dist: cryptography>=
|
|
21
|
-
Requires-Dist: graphviz>=0.
|
|
22
|
-
Requires-Dist: httpx>=0.
|
|
23
|
-
Requires-Dist: packaging>=
|
|
24
|
-
Requires-Dist: portalocker>=
|
|
25
|
-
Requires-Dist: psutil>=
|
|
26
|
-
Requires-Dist: pydantic>=2.
|
|
27
|
-
Requires-Dist: pyjwt>=2.
|
|
28
|
-
Requires-Dist: pyopenssl>=
|
|
29
|
-
Requires-Dist: python-dotenv>=1.1.
|
|
20
|
+
Requires-Dist: cryptography>=45.0.5
|
|
21
|
+
Requires-Dist: graphviz>=0.21
|
|
22
|
+
Requires-Dist: httpx>=0.28.0
|
|
23
|
+
Requires-Dist: packaging>=25.0
|
|
24
|
+
Requires-Dist: portalocker>=3.0.0
|
|
25
|
+
Requires-Dist: psutil>=7.0.0
|
|
26
|
+
Requires-Dist: pydantic>=2.11.7
|
|
27
|
+
Requires-Dist: pyjwt>=2.10.1
|
|
28
|
+
Requires-Dist: pyopenssl>=25.1.0
|
|
29
|
+
Requires-Dist: python-dotenv>=1.1.1
|
|
30
30
|
Requires-Dist: pyyaml>=6.0.1
|
|
31
31
|
Requires-Dist: questionary>=2.0.1
|
|
32
|
-
Requires-Dist: rel>=0.4.
|
|
32
|
+
Requires-Dist: rel>=0.4.9.20
|
|
33
33
|
Requires-Dist: retry>=0.9.2
|
|
34
|
-
Requires-Dist: rich>=
|
|
34
|
+
Requires-Dist: rich>=14.0.0
|
|
35
35
|
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.
|
|
39
|
+
Requires-Dist: yumako>=0.1.27
|
|
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.292"
|
|
@@ -89,9 +89,7 @@ def get(name: str):
|
|
|
89
89
|
if name:
|
|
90
90
|
data = profile.get(name)
|
|
91
91
|
if data is None:
|
|
92
|
-
panic(
|
|
93
|
-
"Profile not found. Use 'hcs profile list' to show available profiles, or 'hcs profile init' to create one."
|
|
94
|
-
)
|
|
92
|
+
panic("Profile not found. Use 'hcs profile list' to show available profiles, or 'hcs profile init' to create one.")
|
|
95
93
|
else:
|
|
96
94
|
data = profile.current()
|
|
97
95
|
if data is None:
|
|
@@ -74,9 +74,7 @@ sort = click.option(
|
|
|
74
74
|
help="Ascending/Descending. Format is property,{asc|desc} and default is ascending",
|
|
75
75
|
)
|
|
76
76
|
|
|
77
|
-
limit = click.option(
|
|
78
|
-
"--limit", "-l", type=int, required=False, default=100, help="Optionally, specify the number of records to fetch."
|
|
79
|
-
)
|
|
77
|
+
limit = click.option("--limit", "-l", type=int, required=False, default=100, help="Optionally, specify the number of records to fetch.")
|
|
80
78
|
|
|
81
79
|
ids = click.option(
|
|
82
80
|
"--ids",
|
|
@@ -101,9 +99,7 @@ first = click.option(
|
|
|
101
99
|
|
|
102
100
|
force = click.option("--force/--grace", type=bool, default=True, help="Specify deletion mode: forceful, or graceful.")
|
|
103
101
|
|
|
104
|
-
confirm = click.option(
|
|
105
|
-
"--confirm/--prompt", "-y", type=bool, default=False, help="Confirm the operation without prompt."
|
|
106
|
-
)
|
|
102
|
+
confirm = click.option("--confirm/--prompt", "-y", type=bool, default=False, help="Confirm the operation without prompt.")
|
|
107
103
|
|
|
108
104
|
|
|
109
105
|
def formatter(custom_fn):
|
|
@@ -100,9 +100,7 @@ def strict_dict_to_class(data: dict, class_type):
|
|
|
100
100
|
value = strict_dict_to_class(value, field_type)
|
|
101
101
|
setattr(inst, field_name, value)
|
|
102
102
|
continue
|
|
103
|
-
raise ValueError(
|
|
104
|
-
f"Field '{class_type.__name__}.{field_name}' has an incorrect type. Declared: {field_type}, actual: {type(value)}"
|
|
105
|
-
)
|
|
103
|
+
raise ValueError(f"Field '{class_type.__name__}.{field_name}' has an incorrect type. Declared: {field_type}, actual: {type(value)}")
|
|
106
104
|
return inst
|
|
107
105
|
|
|
108
106
|
|
|
@@ -341,9 +339,7 @@ def resolve_expression(expr, fn_get_value, referencing_attr_path) -> Tuple[Any,
|
|
|
341
339
|
f"Invalid variable value for expression. Expect list, actual {type(target_value).__name__}. attr_path={referencing_attr_path}, src_var_name={src_var_name}"
|
|
342
340
|
)
|
|
343
341
|
if not mapped_value.startswith(tmp_var_name + "."):
|
|
344
|
-
raise CtxpException(
|
|
345
|
-
f"Unsupported expression. attr_path={referencing_attr_path}, src_var_name={src_var_name}"
|
|
346
|
-
)
|
|
342
|
+
raise CtxpException(f"Unsupported expression. attr_path={referencing_attr_path}, src_var_name={src_var_name}")
|
|
347
343
|
new_attr_path = mapped_value[len(tmp_var_name) + 1 :]
|
|
348
344
|
ret = []
|
|
349
345
|
for i in target_value:
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
PATTERN = (
|
|
6
|
-
"([-+]?)P(?:([-+]?[0-9]+)D)?(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?"
|
|
7
|
-
)
|
|
5
|
+
PATTERN = "([-+]?)P(?:([-+]?[0-9]+)D)?(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?"
|
|
8
6
|
|
|
9
7
|
# Examples
|
|
10
8
|
|
|
@@ -21,7 +21,9 @@ from logging.handlers import RotatingFileHandler
|
|
|
21
21
|
import coloredlogs
|
|
22
22
|
|
|
23
23
|
LOG_FORMAT_SIMPLE = "%(levelname).4s %(asctime)s %(name)-16s %(message)s"
|
|
24
|
-
LOG_FORMAT_LONG =
|
|
24
|
+
LOG_FORMAT_LONG = (
|
|
25
|
+
"%(color_on)s%(asctime)s.%(msecs)03d [%(process)-5d:%(threadName)-12s] %(levelname)-7s [%(name)-16s] %(message)s%(color_off)s"
|
|
26
|
+
)
|
|
25
27
|
DATE_FORMAT_SIMPLE = "%H:%M:%S"
|
|
26
28
|
|
|
27
29
|
|
|
@@ -133,9 +135,7 @@ def setup(
|
|
|
133
135
|
)
|
|
134
136
|
|
|
135
137
|
|
|
136
|
-
def setup_console_output(
|
|
137
|
-
logger, console_log_output, console_log_level, console_log_color, console_log_mask, log_line_template, date_fmt
|
|
138
|
-
):
|
|
138
|
+
def setup_console_output(logger, console_log_output, console_log_level, console_log_color, console_log_mask, log_line_template, date_fmt):
|
|
139
139
|
if not date_fmt:
|
|
140
140
|
date_fmt = coloredlogs.DEFAULT_DATE_FORMAT
|
|
141
141
|
coloredlogs.install(
|
|
@@ -82,9 +82,7 @@ def current(reload: bool = False, exit_on_failure: bool = True, exclude_secret:
|
|
|
82
82
|
data = get(profile_name, reload)
|
|
83
83
|
|
|
84
84
|
if data is None and exit_on_failure:
|
|
85
|
-
panic(
|
|
86
|
-
"Profile not set. Use 'hcs profile use [profile-name]' to choose one, or use 'hcs profile init' to create default profiles."
|
|
87
|
-
)
|
|
85
|
+
panic("Profile not set. Use 'hcs profile use [profile-name]' to choose one, or use 'hcs profile init' to create default profiles.")
|
|
88
86
|
|
|
89
87
|
if exclude_secret:
|
|
90
88
|
data = dotdict(dict(data))
|
|
@@ -12,6 +12,7 @@ log = logging.getLogger(__name__)
|
|
|
12
12
|
|
|
13
13
|
_record = None
|
|
14
14
|
_enabled = None
|
|
15
|
+
_version = None
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def disable():
|
|
@@ -26,6 +27,19 @@ def _is_disabled():
|
|
|
26
27
|
return not _enabled
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def _get_version():
|
|
31
|
+
global _version
|
|
32
|
+
if _version is None:
|
|
33
|
+
try:
|
|
34
|
+
from importlib.metadata import version
|
|
35
|
+
|
|
36
|
+
_version = version("hcs-cli")
|
|
37
|
+
except Exception as e:
|
|
38
|
+
log.debug(f"Failed to get hcs-cli version: {e}")
|
|
39
|
+
_version = "unknown"
|
|
40
|
+
return _version
|
|
41
|
+
|
|
42
|
+
|
|
29
43
|
def start(cmd_path: str, params: dict):
|
|
30
44
|
if _is_disabled():
|
|
31
45
|
return
|
|
@@ -38,6 +52,7 @@ def start(cmd_path: str, params: dict):
|
|
|
38
52
|
"return": -1,
|
|
39
53
|
"error": None,
|
|
40
54
|
"time_ms": -1,
|
|
55
|
+
"version": _get_version(),
|
|
41
56
|
"env": {
|
|
42
57
|
"python_version": sys.version,
|
|
43
58
|
"platform": sys.platform,
|
|
@@ -79,10 +94,9 @@ def _injest(doc):
|
|
|
79
94
|
headers={"Content-Type": "application/json"},
|
|
80
95
|
content=json.dumps(doc),
|
|
81
96
|
timeout=4,
|
|
97
|
+
verify=False,
|
|
82
98
|
)
|
|
83
99
|
response.raise_for_status()
|
|
84
100
|
except Exception as e:
|
|
85
101
|
log.debug(f"Telemetry ingestion failed: {e}", exc_info=True)
|
|
86
102
|
return
|
|
87
|
-
|
|
88
|
-
log.debug(f"Telemetry ingestion successful: {response.status_code}")
|
|
@@ -125,7 +125,7 @@ def print_output(data: Any, args: dict, file=sys.stdout):
|
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
def print_error(error):
|
|
128
|
-
critical_errors = [KeyError, TypeError]
|
|
128
|
+
critical_errors = [KeyError, TypeError, AttributeError, ValueError]
|
|
129
129
|
for ex in critical_errors:
|
|
130
130
|
if isinstance(error, ex):
|
|
131
131
|
traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
|
|
@@ -41,19 +41,13 @@ def _prepare_data(data: dict, additional_context: dict, target_resource_name: st
|
|
|
41
41
|
data.update(additional_context)
|
|
42
42
|
blueprint, pending = process_template(data)
|
|
43
43
|
|
|
44
|
-
if
|
|
45
|
-
target_resource_name
|
|
46
|
-
and target_resource_name not in blueprint["resource"]
|
|
47
|
-
and target_resource_name not in blueprint["runtime"]
|
|
48
|
-
):
|
|
44
|
+
if target_resource_name and target_resource_name not in blueprint["resource"] and target_resource_name not in blueprint["runtime"]:
|
|
49
45
|
raise PlanException("Target resource or runtime not found: " + target_resource_name)
|
|
50
46
|
|
|
51
47
|
for k, v in pending.items():
|
|
52
48
|
if v.startswith("default.") or v.startswith("var."):
|
|
53
49
|
if not k.find(".conditions."):
|
|
54
|
-
raise PlanException(
|
|
55
|
-
f"Invalid blueprint. Unresolved static references. Variable not found: {v}. Required by {k}"
|
|
56
|
-
)
|
|
50
|
+
raise PlanException(f"Invalid blueprint. Unresolved static references. Variable not found: {v}. Required by {k}")
|
|
57
51
|
deployment_id = blueprint["deploymentId"]
|
|
58
52
|
state_file = deployment_id + ".state.yml"
|
|
59
53
|
prev = data_util.load_data_file(state_file, default={})
|
|
@@ -291,9 +285,7 @@ def _deploy_res(name, res, state):
|
|
|
291
285
|
else:
|
|
292
286
|
new_state = handler.update(res_data, res_state)
|
|
293
287
|
else:
|
|
294
|
-
raise PlanException(
|
|
295
|
-
f"Unknown action. This is a problem of the concrete plugin.decide function. Plugin={name}, action={action}"
|
|
296
|
-
)
|
|
288
|
+
raise PlanException(f"Unknown action. This is a problem of the concrete plugin.decide function. Plugin={name}, action={action}")
|
|
297
289
|
|
|
298
290
|
if new_state:
|
|
299
291
|
fn_set_state(new_state)
|
|
@@ -38,9 +38,9 @@ def _is_auth_valid(auth_data):
|
|
|
38
38
|
_login_lock = threading.Lock()
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
def login(force_refresh: bool = False):
|
|
41
|
+
def login(force_refresh: bool = False, verbose: bool = False):
|
|
42
42
|
"""Ensure login state, using credentials from the current profile. Return oauth token."""
|
|
43
|
-
return _populate_token_with_cache(profile.current().csp, force_refresh)
|
|
43
|
+
return _populate_token_with_cache(profile.current().csp, force_refresh, verbose)
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def refresh_oauth_token(old_oauth_token: dict, csp_url: str):
|
|
@@ -98,17 +98,22 @@ def save_auth_cache(auth_config: dict, token: dict):
|
|
|
98
98
|
profile.auth.set(cache)
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
def _populate_token_with_cache(auth_config: dict, force_refresh: bool = False):
|
|
102
|
-
|
|
101
|
+
def _populate_token_with_cache(auth_config: dict, force_refresh: bool = False, verbose: bool = False):
|
|
102
|
+
if verbose:
|
|
103
|
+
print("_populate_token_with_cache")
|
|
103
104
|
with _login_lock:
|
|
104
105
|
cache, hash, token = _get_auth_cache(auth_config)
|
|
105
106
|
if token and not force_refresh:
|
|
107
|
+
if verbose:
|
|
108
|
+
print("Using cached auth token.")
|
|
106
109
|
return token
|
|
107
110
|
|
|
108
111
|
# invalid token. Refresh or recreate it.
|
|
109
112
|
if token:
|
|
110
113
|
# try using refresh token if possible
|
|
111
114
|
if auth_config.get("provider", "vmwarecsp") == "vmwarecsp":
|
|
115
|
+
if verbose:
|
|
116
|
+
print("Provider: vmwarecsp.")
|
|
112
117
|
try:
|
|
113
118
|
token = refresh_oauth_token(token, auth_config.url)
|
|
114
119
|
except Exception as e:
|
|
@@ -116,11 +121,17 @@ def _populate_token_with_cache(auth_config: dict, force_refresh: bool = False):
|
|
|
116
121
|
token = None
|
|
117
122
|
else:
|
|
118
123
|
# hcs auth-service. Does not support refresh token.
|
|
124
|
+
if verbose:
|
|
125
|
+
print("Provider: hcs auth-service.")
|
|
119
126
|
token = None
|
|
120
127
|
|
|
121
128
|
if not token:
|
|
122
129
|
if _has_credential(auth_config):
|
|
130
|
+
if verbose:
|
|
131
|
+
print("Config:", auth_config)
|
|
123
132
|
token = CspClient.create(**auth_config).oauth_token()
|
|
133
|
+
if verbose:
|
|
134
|
+
print("Token:", json.dumps(token, indent=4))
|
|
124
135
|
else:
|
|
125
136
|
if auth_config.get("browser"):
|
|
126
137
|
from .login_support import login_via_browser
|
|
@@ -129,9 +140,7 @@ def _populate_token_with_cache(auth_config: dict, force_refresh: bool = False):
|
|
|
129
140
|
if not token:
|
|
130
141
|
raise CtxpException("Browser auth failed.")
|
|
131
142
|
else:
|
|
132
|
-
raise CtxpException(
|
|
133
|
-
"Browser auth was never attempted and no client credentials or API token provided."
|
|
134
|
-
)
|
|
143
|
+
raise CtxpException("Browser auth was never attempted and no client credentials or API token provided.")
|
|
135
144
|
|
|
136
145
|
if not token.get("expires_at"):
|
|
137
146
|
token["expires_at"] = int(time.time() + token["expires_in"])
|
|
@@ -32,19 +32,31 @@ _caches = {}
|
|
|
32
32
|
_client_instance_lock = threading.RLock()
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def
|
|
36
|
-
service_name: str, hdc: str = None, region: str = None
|
|
37
|
-
): # make it deferred so no need to initialize profile
|
|
38
|
-
if region and hdc:
|
|
39
|
-
raise Exception("region and hdc cannot be specified at the same time.")
|
|
40
|
-
|
|
41
|
-
# check per-service override in profile
|
|
35
|
+
def _get_service_override(service_name: str):
|
|
42
36
|
profile_data = profile.current()
|
|
43
37
|
override = profile_data.get("override", {})
|
|
44
38
|
service_override = {}
|
|
45
39
|
for k, v in override.items():
|
|
46
40
|
if service_name == k.lower():
|
|
47
41
|
service_override = v
|
|
42
|
+
|
|
43
|
+
if not service_override:
|
|
44
|
+
if service_name == "org-service":
|
|
45
|
+
service_override = override.get("org", {})
|
|
46
|
+
elif service_name.find("-") >= 0:
|
|
47
|
+
camel_name = "".join(word.capitalize() for word in service_name.split("-"))
|
|
48
|
+
camel_name = camel_name[0].lower() + camel_name[1:]
|
|
49
|
+
service_override = override.get(camel_name, {})
|
|
50
|
+
return service_override
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _lazy_init(service_name: str, hdc: str = None, region: str = None): # make it deferred so no need to initialize profile
|
|
54
|
+
if region and hdc:
|
|
55
|
+
raise Exception("region and hdc cannot be specified at the same time.")
|
|
56
|
+
|
|
57
|
+
profile_data = profile.current()
|
|
58
|
+
|
|
59
|
+
service_override = _get_service_override(service_name)
|
|
48
60
|
service_override_url = service_override.get("url")
|
|
49
61
|
if service_override_url:
|
|
50
62
|
log.debug(f"Using per-service override for {service_name}: {service_override_url}")
|
|
@@ -83,7 +95,7 @@ def _lazy_init(
|
|
|
83
95
|
if provider == "vmwarecsp":
|
|
84
96
|
token_url = profile_data.csp.url
|
|
85
97
|
elif provider == "auth-service":
|
|
86
|
-
token_url = profile_data.auth
|
|
98
|
+
token_url = profile_data.auth["tokenUrl"]
|
|
87
99
|
else:
|
|
88
100
|
raise CtxpException(f"Unknown provider: {provider}. Supported providers: vmwarecsp, auth-service.")
|
|
89
101
|
|
|
@@ -164,9 +176,13 @@ def regional_service_client(service_name: str, region: str = None):
|
|
|
164
176
|
return instance
|
|
165
177
|
|
|
166
178
|
|
|
167
|
-
def
|
|
179
|
+
def is_regional_service(service_name: str):
|
|
168
180
|
regional_services = ["vmhub", "connection-service"]
|
|
169
|
-
|
|
181
|
+
return service_name in regional_services
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def service_client(service_name: str, region: str = None, hdc: str = None):
|
|
185
|
+
if is_regional_service(service_name):
|
|
170
186
|
return regional_service_client(service_name, region)
|
|
171
187
|
else:
|
|
172
188
|
return hdc_service_client(service_name, hdc)
|
|
@@ -358,9 +374,7 @@ def wait_for_res_status(
|
|
|
358
374
|
if is_ready(status):
|
|
359
375
|
return t
|
|
360
376
|
if not is_transition(status):
|
|
361
|
-
raise CtxpException(
|
|
362
|
-
prefix + f"Unexpected status: {status}. If this is a transition, add it to status_map['transition']."
|
|
363
|
-
)
|
|
377
|
+
raise CtxpException(prefix + f"Unexpected status: {status}. If this is a transition, add it to status_map['transition'].")
|
|
364
378
|
|
|
365
379
|
now = time.time()
|
|
366
380
|
remaining_seconds = timeout_seconds - (now - start)
|
|
@@ -112,9 +112,7 @@ class CspClient:
|
|
|
112
112
|
"Accept": "application/json",
|
|
113
113
|
}
|
|
114
114
|
# <no org id for this API>
|
|
115
|
-
resp = self._client.post(
|
|
116
|
-
"/csp/gateway/am/api/auth/api-tokens/authorize", headers=headers, data=f"api_token={api_token}"
|
|
117
|
-
)
|
|
115
|
+
resp = self._client.post("/csp/gateway/am/api/auth/api-tokens/authorize", headers=headers, data=f"api_token={api_token}")
|
|
118
116
|
self._oauth_token = resp.json()
|
|
119
117
|
return self._oauth_token
|
|
120
118
|
|
|
@@ -64,7 +64,7 @@ _auth_success_html = """
|
|
|
64
64
|
<h3>You have successfully logged into VMware Horizon Cloud Service.</h3>
|
|
65
65
|
<p>You can close this window, and return to the terminal.</p>
|
|
66
66
|
<br/>
|
|
67
|
-
<p><a href="https://github.com/euc-eng/hcs-cli">HCS CLI</a> is in beta
|
|
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>
|
|
68
68
|
<code>
|
|
69
69
|
# To get the login details: <br/>
|
|
70
70
|
hcs login -d <br/><br/>
|
|
@@ -153,9 +153,7 @@ class JobView:
|
|
|
153
153
|
_MyPlainBarColumn(),
|
|
154
154
|
# TextColumn("[white][progress.percentage][white]{task.percentage:>3.0f}%"),
|
|
155
155
|
# TaskProgressColumn(show_speed=True),
|
|
156
|
-
TimeRemainingColumn(
|
|
157
|
-
compact=True, elapsed_when_finished=True, table_column=Column(style="white", min_width=5)
|
|
158
|
-
),
|
|
156
|
+
TimeRemainingColumn(compact=True, elapsed_when_finished=True, table_column=Column(style="white", min_width=5)),
|
|
159
157
|
TextColumn("{task.fields[details]}", table_column=Column(max_width=msg_width, no_wrap=True)),
|
|
160
158
|
get_time=monotonic,
|
|
161
159
|
)
|
|
@@ -23,29 +23,29 @@ keywords = [
|
|
|
23
23
|
dynamic = ["version"]
|
|
24
24
|
|
|
25
25
|
dependencies = [
|
|
26
|
-
"Authlib>=1.
|
|
27
|
-
"click>=8.1
|
|
26
|
+
"Authlib>=1.6.0",
|
|
27
|
+
"click>=8.2.1",
|
|
28
28
|
"coloredlogs>=15.0.1",
|
|
29
|
-
"cryptography>=
|
|
30
|
-
"graphviz>=0.
|
|
31
|
-
"httpx>=0.
|
|
32
|
-
"packaging>=
|
|
33
|
-
"portalocker>=
|
|
34
|
-
"PyJWT>=2.
|
|
35
|
-
"pyOpenSSL>=
|
|
29
|
+
"cryptography>=45.0.5",
|
|
30
|
+
"graphviz>=0.21",
|
|
31
|
+
"httpx>=0.28.0",
|
|
32
|
+
"packaging>=25.0",
|
|
33
|
+
"portalocker>=3.0.0",
|
|
34
|
+
"PyJWT>=2.10.1",
|
|
35
|
+
"pyOpenSSL>=25.1.0",
|
|
36
36
|
"PyYAML>=6.0.1",
|
|
37
37
|
"questionary>=2.0.1",
|
|
38
|
-
"rich>=
|
|
38
|
+
"rich>=14.0.0",
|
|
39
39
|
"setuptools>=70.0.0",
|
|
40
40
|
"retry>=0.9.2",
|
|
41
41
|
"tabulate>=0.9.0",
|
|
42
|
-
"rel>=0.4.
|
|
42
|
+
"rel>=0.4.9.20",
|
|
43
43
|
"websocket_client>=1.2.3",
|
|
44
|
-
"psutil>=
|
|
44
|
+
"psutil>=7.0.0",
|
|
45
45
|
"schedule>=1.1.0",
|
|
46
|
-
"yumako>=0.1.
|
|
47
|
-
"pydantic>=2.
|
|
48
|
-
"python-dotenv>=1.1.
|
|
46
|
+
"yumako>=0.1.27",
|
|
47
|
+
"pydantic>=2.11.7",
|
|
48
|
+
"python-dotenv>=1.1.1",
|
|
49
49
|
]
|
|
50
50
|
|
|
51
51
|
[project.optional-dependencies]
|
|
@@ -80,7 +80,7 @@ exclude = [
|
|
|
80
80
|
]
|
|
81
81
|
|
|
82
82
|
[tool.black]
|
|
83
|
-
line-length =
|
|
83
|
+
line-length = 140
|
|
84
84
|
target-version = ['py39']
|
|
85
85
|
include = '\.pyi?$'
|
|
86
86
|
extend-exclude = '''
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.291"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|