fdai-deployment-cli 0.1.2__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 (203) hide show
  1. fdai_deployment_cli/__about__.py +3 -0
  2. fdai_deployment_cli/__init__.py +5 -0
  3. fdai_deployment_cli/__main__.py +5 -0
  4. fdai_deployment_cli/acr_publish.py +585 -0
  5. fdai_deployment_cli/aks_catalog_review.py +137 -0
  6. fdai_deployment_cli/aks_historical_reconciliation.py +732 -0
  7. fdai_deployment_cli/aks_job_environment.py +59 -0
  8. fdai_deployment_cli/aks_job_execution.py +794 -0
  9. fdai_deployment_cli/aks_kubernetes_defaults.py +66 -0
  10. fdai_deployment_cli/aks_preflight.py +422 -0
  11. fdai_deployment_cli/aks_readiness.py +220 -0
  12. fdai_deployment_cli/aks_service_update.py +180 -0
  13. fdai_deployment_cli/aks_workload_jobs.py +281 -0
  14. fdai_deployment_cli/application_state_adoption.py +347 -0
  15. fdai_deployment_cli/azure_actor.py +61 -0
  16. fdai_deployment_cli/azure_naming.py +252 -0
  17. fdai_deployment_cli/azure_retail_prices.py +173 -0
  18. fdai_deployment_cli/bootstrap_approval.py +99 -0
  19. fdai_deployment_cli/bootstrap_channel.py +321 -0
  20. fdai_deployment_cli/bootstrap_executor.py +311 -0
  21. fdai_deployment_cli/bootstrap_plan.py +237 -0
  22. fdai_deployment_cli/bootstrap_reconcile.py +402 -0
  23. fdai_deployment_cli/bootstrap_support.py +138 -0
  24. fdai_deployment_cli/bootstrap_worker.py +448 -0
  25. fdai_deployment_cli/bundle.py +352 -0
  26. fdai_deployment_cli/capacity.py +143 -0
  27. fdai_deployment_cli/catalog_review_profile.py +306 -0
  28. fdai_deployment_cli/cli.py +798 -0
  29. fdai_deployment_cli/cli_help.py +214 -0
  30. fdai_deployment_cli/cli_parser.py +791 -0
  31. fdai_deployment_cli/cli_plan.py +450 -0
  32. fdai_deployment_cli/compiler.py +99 -0
  33. fdai_deployment_cli/console_artifact.py +218 -0
  34. fdai_deployment_cli/console_config.py +214 -0
  35. fdai_deployment_cli/console_update.py +832 -0
  36. fdai_deployment_cli/contracts.py +307 -0
  37. fdai_deployment_cli/control_package.py +140 -0
  38. fdai_deployment_cli/deadline_transport.py +70 -0
  39. fdai_deployment_cli/deployment_cost.py +131 -0
  40. fdai_deployment_cli/deployment_deadline.py +28 -0
  41. fdai_deployment_cli/deployment_kit.py +790 -0
  42. fdai_deployment_cli/deployment_kit_cache.py +191 -0
  43. fdai_deployment_cli/deployment_progress.py +648 -0
  44. fdai_deployment_cli/development_authority_cli.py +56 -0
  45. fdai_deployment_cli/development_authority_profile.py +319 -0
  46. fdai_deployment_cli/doctor.py +136 -0
  47. fdai_deployment_cli/entitlement_preflight.py +68 -0
  48. fdai_deployment_cli/entitlement_transport.py +49 -0
  49. fdai_deployment_cli/entra_bootstrap.py +579 -0
  50. fdai_deployment_cli/entra_control_profile.py +214 -0
  51. fdai_deployment_cli/entra_graph.py +349 -0
  52. fdai_deployment_cli/entra_profiles.py +215 -0
  53. fdai_deployment_cli/entra_source.py +244 -0
  54. fdai_deployment_cli/execution_bundle.py +478 -0
  55. fdai_deployment_cli/execution_copies.py +63 -0
  56. fdai_deployment_cli/foundation_adoption.py +438 -0
  57. fdai_deployment_cli/foundation_adoption_evidence.py +245 -0
  58. fdai_deployment_cli/foundation_adoption_host.py +157 -0
  59. fdai_deployment_cli/foundation_adoption_transport.py +51 -0
  60. fdai_deployment_cli/foundation_failure.py +72 -0
  61. fdai_deployment_cli/foundation_image.py +168 -0
  62. fdai_deployment_cli/foundation_input.py +235 -0
  63. fdai_deployment_cli/foundation_output.py +154 -0
  64. fdai_deployment_cli/foundation_plan.py +381 -0
  65. fdai_deployment_cli/foundation_process.py +64 -0
  66. fdai_deployment_cli/foundation_progress.py +231 -0
  67. fdai_deployment_cli/github_actions.py +171 -0
  68. fdai_deployment_cli/github_apply_artifact.py +259 -0
  69. fdai_deployment_cli/github_artifact_io.py +48 -0
  70. fdai_deployment_cli/github_dispatch.py +248 -0
  71. fdai_deployment_cli/github_plan_artifact.py +181 -0
  72. fdai_deployment_cli/github_provider_artifact.py +170 -0
  73. fdai_deployment_cli/github_transport.py +46 -0
  74. fdai_deployment_cli/github_workflow_values.py +312 -0
  75. fdai_deployment_cli/hub_image_deploy.py +354 -0
  76. fdai_deployment_cli/hub_image_stage.py +162 -0
  77. fdai_deployment_cli/hub_image_trust.py +281 -0
  78. fdai_deployment_cli/hub_ownership.py +161 -0
  79. fdai_deployment_cli/hub_workload_handoff.py +264 -0
  80. fdai_deployment_cli/hub_workload_handoff_inputs.py +227 -0
  81. fdai_deployment_cli/identity_profile.py +227 -0
  82. fdai_deployment_cli/image_targets.py +197 -0
  83. fdai_deployment_cli/installation_scope.py +280 -0
  84. fdai_deployment_cli/license.py +311 -0
  85. fdai_deployment_cli/license_issue.py +239 -0
  86. fdai_deployment_cli/lifecycle_authorization.py +252 -0
  87. fdai_deployment_cli/lifecycle_commands.py +173 -0
  88. fdai_deployment_cli/lifecycle_configuration.py +722 -0
  89. fdai_deployment_cli/lifecycle_configuration_artifact.py +45 -0
  90. fdai_deployment_cli/lifecycle_configuration_cli.py +324 -0
  91. fdai_deployment_cli/lifecycle_configuration_document.py +74 -0
  92. fdai_deployment_cli/lifecycle_configuration_entities.py +146 -0
  93. fdai_deployment_cli/lifecycle_configuration_package.py +492 -0
  94. fdai_deployment_cli/lifecycle_configuration_publication.py +277 -0
  95. fdai_deployment_cli/lifecycle_configuration_publication_cli.py +191 -0
  96. fdai_deployment_cli/lifecycle_configuration_sealing.py +127 -0
  97. fdai_deployment_cli/lifecycle_enrollment.py +45 -0
  98. fdai_deployment_cli/lifecycle_infrastructure_agent.py +174 -0
  99. fdai_deployment_cli/lifecycle_installation_binding.py +65 -0
  100. fdai_deployment_cli/lifecycle_native.py +247 -0
  101. fdai_deployment_cli/lifecycle_native_reconciliation.py +568 -0
  102. fdai_deployment_cli/lifecycle_native_transition.py +339 -0
  103. fdai_deployment_cli/lifecycle_ownership_exclusion.py +78 -0
  104. fdai_deployment_cli/lifecycle_plan.py +800 -0
  105. fdai_deployment_cli/lifecycle_promotion.py +185 -0
  106. fdai_deployment_cli/lifecycle_release.py +308 -0
  107. fdai_deployment_cli/lifecycle_self_upgrade.py +202 -0
  108. fdai_deployment_cli/lifecycle_upgrade_bundle.py +176 -0
  109. fdai_deployment_cli/notification_recipients_input.py +222 -0
  110. fdai_deployment_cli/observer_artifact.py +106 -0
  111. fdai_deployment_cli/oci_archive.py +353 -0
  112. fdai_deployment_cli/offline_kit.py +797 -0
  113. fdai_deployment_cli/offline_prepare.py +135 -0
  114. fdai_deployment_cli/operational_evidence_verifier_input.py +575 -0
  115. fdai_deployment_cli/plan_input.py +101 -0
  116. fdai_deployment_cli/private_output.py +192 -0
  117. fdai_deployment_cli/profile.py +70 -0
  118. fdai_deployment_cli/progress.py +183 -0
  119. fdai_deployment_cli/readiness.py +286 -0
  120. fdai_deployment_cli/run_command_receiver.py +418 -0
  121. fdai_deployment_cli/runtime_build.py +386 -0
  122. fdai_deployment_cli/runtime_profile.py +287 -0
  123. fdai_deployment_cli/runtime_release.py +745 -0
  124. fdai_deployment_cli/runtime_stage.py +82 -0
  125. fdai_deployment_cli/runtime_support_installation.py +215 -0
  126. fdai_deployment_cli/simulation.py +80 -0
  127. fdai_deployment_cli/source_application_inputs.py +397 -0
  128. fdai_deployment_cli/source_azure.py +586 -0
  129. fdai_deployment_cli/source_change_cli.py +107 -0
  130. fdai_deployment_cli/source_change_parser.py +75 -0
  131. fdai_deployment_cli/source_change_plan.py +181 -0
  132. fdai_deployment_cli/source_changed_update.py +196 -0
  133. fdai_deployment_cli/source_deploy.py +170 -0
  134. fdai_deployment_cli/source_foundation.py +174 -0
  135. fdai_deployment_cli/source_host_tools.py +163 -0
  136. fdai_deployment_cli/source_image_stage.py +400 -0
  137. fdai_deployment_cli/source_input.py +173 -0
  138. fdai_deployment_cli/source_projection.py +164 -0
  139. fdai_deployment_cli/source_receiver.py +76 -0
  140. fdai_deployment_cli/source_recovery.py +264 -0
  141. fdai_deployment_cli/source_runtime.py +81 -0
  142. fdai_deployment_cli/source_runtime_support.py +368 -0
  143. fdai_deployment_cli/source_service_update.py +726 -0
  144. fdai_deployment_cli/source_service_update_cli.py +47 -0
  145. fdai_deployment_cli/source_snapshot.py +178 -0
  146. fdai_deployment_cli/source_teardown.py +574 -0
  147. fdai_deployment_cli/source_terraform.py +85 -0
  148. fdai_deployment_cli/source_transport.py +399 -0
  149. fdai_deployment_cli/standalone_aks_inventory.py +558 -0
  150. fdai_deployment_cli/standalone_aks_job_execution.py +769 -0
  151. fdai_deployment_cli/standalone_aks_nodepool_guard.py +141 -0
  152. fdai_deployment_cli/standalone_application.py +977 -0
  153. fdai_deployment_cli/standalone_application_completion.py +151 -0
  154. fdai_deployment_cli/standalone_application_input_recovery.py +107 -0
  155. fdai_deployment_cli/standalone_application_recovery.py +116 -0
  156. fdai_deployment_cli/standalone_approval_input.py +24 -0
  157. fdai_deployment_cli/standalone_catalog_checkpoint.py +122 -0
  158. fdai_deployment_cli/standalone_catalog_review.py +251 -0
  159. fdai_deployment_cli/standalone_checkpoint_failure.py +127 -0
  160. fdai_deployment_cli/standalone_console_publish.py +174 -0
  161. fdai_deployment_cli/standalone_database_principals.py +41 -0
  162. fdai_deployment_cli/standalone_deploy.py +638 -0
  163. fdai_deployment_cli/standalone_development_authority.py +166 -0
  164. fdai_deployment_cli/standalone_foundation_adoption.py +87 -0
  165. fdai_deployment_cli/standalone_foundation_transition.py +767 -0
  166. fdai_deployment_cli/standalone_host.py +4608 -0
  167. fdai_deployment_cli/standalone_host_rebind.py +226 -0
  168. fdai_deployment_cli/standalone_host_residual.py +104 -0
  169. fdai_deployment_cli/standalone_host_state.py +145 -0
  170. fdai_deployment_cli/standalone_host_values.py +563 -0
  171. fdai_deployment_cli/standalone_kubernetes_tools.py +100 -0
  172. fdai_deployment_cli/standalone_license_installation.py +177 -0
  173. fdai_deployment_cli/standalone_management_egress.py +83 -0
  174. fdai_deployment_cli/standalone_migration_evidence.py +38 -0
  175. fdai_deployment_cli/standalone_operational_evidence.py +356 -0
  176. fdai_deployment_cli/standalone_planned_outputs.py +147 -0
  177. fdai_deployment_cli/standalone_private_access_probe.py +100 -0
  178. fdai_deployment_cli/standalone_product_profile.py +110 -0
  179. fdai_deployment_cli/standalone_remote_prepare.py +442 -0
  180. fdai_deployment_cli/standalone_residual_apply.py +540 -0
  181. fdai_deployment_cli/standalone_residual_successor.py +431 -0
  182. fdai_deployment_cli/standalone_review.py +266 -0
  183. fdai_deployment_cli/standalone_service_update_finalization.py +116 -0
  184. fdai_deployment_cli/standalone_service_update_recovery.py +277 -0
  185. fdai_deployment_cli/standalone_stage_targets.py +325 -0
  186. fdai_deployment_cli/standalone_status.py +67 -0
  187. fdai_deployment_cli/standalone_terraform_environment.py +237 -0
  188. fdai_deployment_cli/standalone_transfer_cleanup.py +237 -0
  189. fdai_deployment_cli/standalone_trial_activation.py +193 -0
  190. fdai_deployment_cli/state.py +434 -0
  191. fdai_deployment_cli/state_handoff.py +253 -0
  192. fdai_deployment_cli/status_projection.py +97 -0
  193. fdai_deployment_cli/support_install.py +257 -0
  194. fdai_deployment_cli/target.py +46 -0
  195. fdai_deployment_cli/trust/capabilities.json +62 -0
  196. fdai_deployment_cli/trust/deployment-bundle-root.pub +3 -0
  197. fdai_deployment_cli/trust/deployment-release-root.pub +3 -0
  198. fdai_deployment_cli/trust/upstream-signing-key.pub +3 -0
  199. fdai_deployment_cli/trust_roots.py +50 -0
  200. fdai_deployment_cli-0.1.2.dist-info/METADATA +112 -0
  201. fdai_deployment_cli-0.1.2.dist-info/RECORD +203 -0
  202. fdai_deployment_cli-0.1.2.dist-info/WHEEL +4 -0
  203. fdai_deployment_cli-0.1.2.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,191 @@
1
+ """Revalidate retained kit inputs; cache metadata never grants trust or deployment authority."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import fcntl
6
+ import hashlib
7
+ import json
8
+ import os
9
+ import stat
10
+ from collections.abc import Iterator
11
+ from contextlib import contextmanager
12
+ from pathlib import Path
13
+
14
+ from fdai_deployment_cli.contracts import load_json_object
15
+ from fdai_deployment_cli.execution_copies import new_execution_copy
16
+ from fdai_deployment_cli.offline_kit import (
17
+ MANIFEST_NAME,
18
+ SIGNATURE_NAME,
19
+ MaterializedOfflineArtifacts,
20
+ OfflineKitVerification,
21
+ _scan_tree,
22
+ )
23
+ from fdai_deployment_cli.private_output import read_private_bytes, write_private_output
24
+
25
+ _MAX_CACHE_ENTRIES = 40_000
26
+
27
+
28
+ def path_present(path: Path) -> bool:
29
+ """Detect even dangling links so exclusive destinations are never silently reused."""
30
+
31
+ return os.path.lexists(path)
32
+
33
+
34
+ @contextmanager
35
+ def acquisition_lock(work_dir: Path) -> Iterator[None]:
36
+ """Serialize local acquisition only, never replacing target or execution locks."""
37
+
38
+ descriptor = os.open(work_dir, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
39
+ try:
40
+ details = os.fstat(descriptor)
41
+ if stat.S_IMODE(details.st_mode) != 0o700 or details.st_uid != os.geteuid():
42
+ raise ValueError("deployment kit work directory must be current-UID mode 0700")
43
+ try:
44
+ fcntl.flock(descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
45
+ except BlockingIOError as exc:
46
+ raise ValueError(
47
+ "deployment kit acquisition is already active in this work directory"
48
+ ) from exc
49
+ yield
50
+ finally:
51
+ os.close(descriptor)
52
+
53
+
54
+ def bind_online_source(work_dir: Path, *, url: str, default_url: str) -> bool:
55
+ """Pin the requested URL digest, not its remote provenance or authenticity.
56
+
57
+ Unbound retained inputs are candidates only for the default versioned source.
58
+ The returned legacy flag requires version checking after full signature verification.
59
+ """
60
+
61
+ marker = work_dir / "online-source.json"
62
+ source_digest = hashlib.sha256(url.encode("utf-8")).hexdigest()
63
+ if path_present(marker):
64
+ value = load_json_object(
65
+ read_private_bytes(marker, max_bytes=4096),
66
+ label="online kit source record",
67
+ max_bytes=4096,
68
+ )
69
+ kind = value.get("kind")
70
+ if (
71
+ set(value) != {"schema_version", "source_sha256", "kind"}
72
+ or value.get("schema_version") != "fdai.online-kit-source.v1"
73
+ or not isinstance(kind, str)
74
+ or kind not in ("request", "legacy-retained")
75
+ or value.get("source_sha256") != source_digest
76
+ ):
77
+ raise ValueError(
78
+ "retained deployment kit source differs or is invalid; preserve the work directory for review"
79
+ )
80
+ return kind == "legacy-retained"
81
+ legacy = any(
82
+ path_present(work_dir / name)
83
+ for name in ("downloaded-kit.tar.gz", "kit", "verified", "bundle")
84
+ )
85
+ if legacy and url != default_url:
86
+ raise ValueError(
87
+ "retained deployment kit source is unbound; an online URL override cannot reuse it"
88
+ )
89
+ write_private_output(
90
+ marker,
91
+ json.dumps(
92
+ {
93
+ "schema_version": "fdai.online-kit-source.v1",
94
+ "source_sha256": source_digest,
95
+ "kind": "legacy-retained" if legacy else "request",
96
+ },
97
+ sort_keys=True,
98
+ separators=(",", ":"),
99
+ )
100
+ + "\n",
101
+ )
102
+ return legacy
103
+
104
+
105
+ def validate_retained_archive(path: Path, *, max_bytes: int) -> None:
106
+ """Reject unsafe cached archive metadata before reading; signatures still decide trust."""
107
+
108
+ details = path.lstat()
109
+ if (
110
+ not stat.S_ISREG(details.st_mode)
111
+ or stat.S_IMODE(details.st_mode) != 0o600
112
+ or details.st_uid != os.geteuid()
113
+ or details.st_nlink != 1
114
+ or not 0 < details.st_size <= max_bytes
115
+ ):
116
+ raise ValueError(
117
+ "retained deployment kit archive is unsafe or empty; preserve it for review"
118
+ )
119
+
120
+
121
+ def validate_cached_tree(
122
+ root: Path, *, executable: Path | None = None, tool_directory: Path | None = None
123
+ ) -> None:
124
+ """Require private, owned, non-linked cached files; content is checked separately."""
125
+
126
+ directories = [root]
127
+ entries = 0
128
+ while directories:
129
+ directory = directories.pop()
130
+ details = directory.lstat()
131
+ mode = stat.S_IMODE(details.st_mode)
132
+ # mkdir(parents=True, mode=0700) uses the umask for intermediate parents.
133
+ # The enclosing 0700 root still prevents external traversal; no descendant
134
+ # may be writable by another principal or owned by one.
135
+ if (
136
+ not stat.S_ISDIR(details.st_mode)
137
+ or (directory == root and mode != 0o700)
138
+ or mode & 0o022
139
+ or details.st_uid != os.geteuid()
140
+ ):
141
+ raise ValueError("retained deployment kit directory is unsafe; preserve it for review")
142
+ for path in directory.iterdir():
143
+ entries += 1
144
+ if entries > _MAX_CACHE_ENTRIES:
145
+ raise ValueError("retained deployment kit exceeds its entry limit")
146
+ details = path.lstat()
147
+ if stat.S_ISDIR(details.st_mode):
148
+ directories.append(path)
149
+ continue
150
+ is_tool = tool_directory is not None and path.parent == tool_directory
151
+ expected_mode = 0o700 if path == executable or is_tool else 0o600
152
+ if (
153
+ not stat.S_ISREG(details.st_mode)
154
+ or stat.S_IMODE(details.st_mode) != expected_mode
155
+ or details.st_uid != os.geteuid()
156
+ or details.st_nlink != 1
157
+ ):
158
+ raise ValueError("retained deployment kit file is unsafe; preserve it for review")
159
+
160
+
161
+ def verify_retained_artifacts(
162
+ destination: Path, verification: OfflineKitVerification
163
+ ) -> MaterializedOfflineArtifacts:
164
+ """Recheck the entire existing snapshot against newly authenticated source digests."""
165
+
166
+ terraform = destination / verification.terraform_binary
167
+ validate_cached_tree(destination, executable=terraform, tool_directory=destination / "bin")
168
+ files, sizes, _total = _scan_tree(destination)
169
+ if (
170
+ files != dict(verification.file_digests)
171
+ or sizes != dict(verification.file_sizes)
172
+ or any(path_present(destination / name) for name in (MANIFEST_NAME, SIGNATURE_NAME))
173
+ ):
174
+ raise ValueError(
175
+ "retained deployment kit snapshot differs or is incomplete; preserve it for review"
176
+ )
177
+ return MaterializedOfflineArtifacts(
178
+ terraform_binary=terraform,
179
+ provider_mirror=destination / verification.provider_mirror_prefix,
180
+ deployment_bundle=destination / verification.deployment_bundle,
181
+ python_wheels=destination / "python",
182
+ )
183
+
184
+
185
+ def execution_bundle_destination(work_dir: Path) -> Path:
186
+ """Create a fresh bundle destination without touching previous execution evidence."""
187
+
188
+ original = work_dir / "bundle"
189
+ if not path_present(original):
190
+ return original
191
+ return new_execution_copy(work_dir, "bundle-recheck-") / "bundle"