flyte 0.0.1b0__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 flyte might be problematic. Click here for more details.

Files changed (390) hide show
  1. flyte/__init__.py +62 -0
  2. flyte/_api_commons.py +3 -0
  3. flyte/_bin/__init__.py +0 -0
  4. flyte/_bin/runtime.py +126 -0
  5. flyte/_build.py +25 -0
  6. flyte/_cache/__init__.py +12 -0
  7. flyte/_cache/cache.py +146 -0
  8. flyte/_cache/defaults.py +9 -0
  9. flyte/_cache/policy_function_body.py +42 -0
  10. flyte/_cli/__init__.py +0 -0
  11. flyte/_cli/_common.py +287 -0
  12. flyte/_cli/_create.py +42 -0
  13. flyte/_cli/_delete.py +23 -0
  14. flyte/_cli/_deploy.py +140 -0
  15. flyte/_cli/_get.py +235 -0
  16. flyte/_cli/_run.py +152 -0
  17. flyte/_cli/main.py +72 -0
  18. flyte/_code_bundle/__init__.py +8 -0
  19. flyte/_code_bundle/_ignore.py +113 -0
  20. flyte/_code_bundle/_packaging.py +187 -0
  21. flyte/_code_bundle/_utils.py +339 -0
  22. flyte/_code_bundle/bundle.py +178 -0
  23. flyte/_context.py +146 -0
  24. flyte/_datastructures.py +342 -0
  25. flyte/_deploy.py +202 -0
  26. flyte/_doc.py +29 -0
  27. flyte/_docstring.py +32 -0
  28. flyte/_environment.py +43 -0
  29. flyte/_group.py +31 -0
  30. flyte/_hash.py +23 -0
  31. flyte/_image.py +760 -0
  32. flyte/_initialize.py +634 -0
  33. flyte/_interface.py +84 -0
  34. flyte/_internal/__init__.py +3 -0
  35. flyte/_internal/controllers/__init__.py +115 -0
  36. flyte/_internal/controllers/_local_controller.py +118 -0
  37. flyte/_internal/controllers/_trace.py +40 -0
  38. flyte/_internal/controllers/pbhash.py +39 -0
  39. flyte/_internal/controllers/remote/__init__.py +40 -0
  40. flyte/_internal/controllers/remote/_action.py +141 -0
  41. flyte/_internal/controllers/remote/_client.py +43 -0
  42. flyte/_internal/controllers/remote/_controller.py +361 -0
  43. flyte/_internal/controllers/remote/_core.py +402 -0
  44. flyte/_internal/controllers/remote/_informer.py +361 -0
  45. flyte/_internal/controllers/remote/_service_protocol.py +50 -0
  46. flyte/_internal/imagebuild/__init__.py +11 -0
  47. flyte/_internal/imagebuild/docker_builder.py +416 -0
  48. flyte/_internal/imagebuild/image_builder.py +241 -0
  49. flyte/_internal/imagebuild/remote_builder.py +0 -0
  50. flyte/_internal/resolvers/__init__.py +0 -0
  51. flyte/_internal/resolvers/_task_module.py +54 -0
  52. flyte/_internal/resolvers/common.py +31 -0
  53. flyte/_internal/resolvers/default.py +28 -0
  54. flyte/_internal/runtime/__init__.py +0 -0
  55. flyte/_internal/runtime/convert.py +199 -0
  56. flyte/_internal/runtime/entrypoints.py +135 -0
  57. flyte/_internal/runtime/io.py +136 -0
  58. flyte/_internal/runtime/resources_serde.py +138 -0
  59. flyte/_internal/runtime/task_serde.py +210 -0
  60. flyte/_internal/runtime/taskrunner.py +190 -0
  61. flyte/_internal/runtime/types_serde.py +54 -0
  62. flyte/_logging.py +124 -0
  63. flyte/_protos/__init__.py +0 -0
  64. flyte/_protos/common/authorization_pb2.py +66 -0
  65. flyte/_protos/common/authorization_pb2.pyi +108 -0
  66. flyte/_protos/common/authorization_pb2_grpc.py +4 -0
  67. flyte/_protos/common/identifier_pb2.py +71 -0
  68. flyte/_protos/common/identifier_pb2.pyi +82 -0
  69. flyte/_protos/common/identifier_pb2_grpc.py +4 -0
  70. flyte/_protos/common/identity_pb2.py +48 -0
  71. flyte/_protos/common/identity_pb2.pyi +72 -0
  72. flyte/_protos/common/identity_pb2_grpc.py +4 -0
  73. flyte/_protos/common/list_pb2.py +36 -0
  74. flyte/_protos/common/list_pb2.pyi +69 -0
  75. flyte/_protos/common/list_pb2_grpc.py +4 -0
  76. flyte/_protos/common/policy_pb2.py +37 -0
  77. flyte/_protos/common/policy_pb2.pyi +27 -0
  78. flyte/_protos/common/policy_pb2_grpc.py +4 -0
  79. flyte/_protos/common/role_pb2.py +37 -0
  80. flyte/_protos/common/role_pb2.pyi +53 -0
  81. flyte/_protos/common/role_pb2_grpc.py +4 -0
  82. flyte/_protos/common/runtime_version_pb2.py +28 -0
  83. flyte/_protos/common/runtime_version_pb2.pyi +24 -0
  84. flyte/_protos/common/runtime_version_pb2_grpc.py +4 -0
  85. flyte/_protos/logs/dataplane/payload_pb2.py +96 -0
  86. flyte/_protos/logs/dataplane/payload_pb2.pyi +168 -0
  87. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +4 -0
  88. flyte/_protos/secret/definition_pb2.py +49 -0
  89. flyte/_protos/secret/definition_pb2.pyi +93 -0
  90. flyte/_protos/secret/definition_pb2_grpc.py +4 -0
  91. flyte/_protos/secret/payload_pb2.py +62 -0
  92. flyte/_protos/secret/payload_pb2.pyi +94 -0
  93. flyte/_protos/secret/payload_pb2_grpc.py +4 -0
  94. flyte/_protos/secret/secret_pb2.py +38 -0
  95. flyte/_protos/secret/secret_pb2.pyi +6 -0
  96. flyte/_protos/secret/secret_pb2_grpc.py +198 -0
  97. flyte/_protos/secret/secret_pb2_grpc_grpc.py +198 -0
  98. flyte/_protos/validate/validate/validate_pb2.py +76 -0
  99. flyte/_protos/workflow/node_execution_service_pb2.py +26 -0
  100. flyte/_protos/workflow/node_execution_service_pb2.pyi +4 -0
  101. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +32 -0
  102. flyte/_protos/workflow/queue_service_pb2.py +106 -0
  103. flyte/_protos/workflow/queue_service_pb2.pyi +141 -0
  104. flyte/_protos/workflow/queue_service_pb2_grpc.py +172 -0
  105. flyte/_protos/workflow/run_definition_pb2.py +128 -0
  106. flyte/_protos/workflow/run_definition_pb2.pyi +310 -0
  107. flyte/_protos/workflow/run_definition_pb2_grpc.py +4 -0
  108. flyte/_protos/workflow/run_logs_service_pb2.py +41 -0
  109. flyte/_protos/workflow/run_logs_service_pb2.pyi +28 -0
  110. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +69 -0
  111. flyte/_protos/workflow/run_service_pb2.py +133 -0
  112. flyte/_protos/workflow/run_service_pb2.pyi +175 -0
  113. flyte/_protos/workflow/run_service_pb2_grpc.py +412 -0
  114. flyte/_protos/workflow/state_service_pb2.py +58 -0
  115. flyte/_protos/workflow/state_service_pb2.pyi +71 -0
  116. flyte/_protos/workflow/state_service_pb2_grpc.py +138 -0
  117. flyte/_protos/workflow/task_definition_pb2.py +72 -0
  118. flyte/_protos/workflow/task_definition_pb2.pyi +65 -0
  119. flyte/_protos/workflow/task_definition_pb2_grpc.py +4 -0
  120. flyte/_protos/workflow/task_service_pb2.py +44 -0
  121. flyte/_protos/workflow/task_service_pb2.pyi +31 -0
  122. flyte/_protos/workflow/task_service_pb2_grpc.py +104 -0
  123. flyte/_resources.py +226 -0
  124. flyte/_retry.py +32 -0
  125. flyte/_reusable_environment.py +25 -0
  126. flyte/_run.py +411 -0
  127. flyte/_secret.py +61 -0
  128. flyte/_task.py +367 -0
  129. flyte/_task_environment.py +200 -0
  130. flyte/_timeout.py +47 -0
  131. flyte/_tools.py +27 -0
  132. flyte/_trace.py +128 -0
  133. flyte/_utils/__init__.py +20 -0
  134. flyte/_utils/asyn.py +119 -0
  135. flyte/_utils/coro_management.py +25 -0
  136. flyte/_utils/file_handling.py +72 -0
  137. flyte/_utils/helpers.py +108 -0
  138. flyte/_utils/lazy_module.py +54 -0
  139. flyte/_utils/uv_script_parser.py +49 -0
  140. flyte/_version.py +21 -0
  141. flyte/connectors/__init__.py +0 -0
  142. flyte/errors.py +143 -0
  143. flyte/extras/__init__.py +5 -0
  144. flyte/extras/_container.py +273 -0
  145. flyte/io/__init__.py +11 -0
  146. flyte/io/_dataframe.py +0 -0
  147. flyte/io/_dir.py +448 -0
  148. flyte/io/_file.py +468 -0
  149. flyte/io/pickle/__init__.py +0 -0
  150. flyte/io/pickle/transformer.py +117 -0
  151. flyte/io/structured_dataset/__init__.py +129 -0
  152. flyte/io/structured_dataset/basic_dfs.py +219 -0
  153. flyte/io/structured_dataset/structured_dataset.py +1061 -0
  154. flyte/py.typed +0 -0
  155. flyte/remote/__init__.py +25 -0
  156. flyte/remote/_client/__init__.py +0 -0
  157. flyte/remote/_client/_protocols.py +131 -0
  158. flyte/remote/_client/auth/__init__.py +12 -0
  159. flyte/remote/_client/auth/_authenticators/__init__.py +0 -0
  160. flyte/remote/_client/auth/_authenticators/base.py +397 -0
  161. flyte/remote/_client/auth/_authenticators/client_credentials.py +73 -0
  162. flyte/remote/_client/auth/_authenticators/device_code.py +118 -0
  163. flyte/remote/_client/auth/_authenticators/external_command.py +79 -0
  164. flyte/remote/_client/auth/_authenticators/factory.py +200 -0
  165. flyte/remote/_client/auth/_authenticators/pkce.py +516 -0
  166. flyte/remote/_client/auth/_channel.py +184 -0
  167. flyte/remote/_client/auth/_client_config.py +83 -0
  168. flyte/remote/_client/auth/_default_html.py +32 -0
  169. flyte/remote/_client/auth/_grpc_utils/__init__.py +0 -0
  170. flyte/remote/_client/auth/_grpc_utils/auth_interceptor.py +288 -0
  171. flyte/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +151 -0
  172. flyte/remote/_client/auth/_keyring.py +143 -0
  173. flyte/remote/_client/auth/_token_client.py +260 -0
  174. flyte/remote/_client/auth/errors.py +16 -0
  175. flyte/remote/_client/controlplane.py +95 -0
  176. flyte/remote/_console.py +18 -0
  177. flyte/remote/_data.py +155 -0
  178. flyte/remote/_logs.py +116 -0
  179. flyte/remote/_project.py +86 -0
  180. flyte/remote/_run.py +873 -0
  181. flyte/remote/_secret.py +132 -0
  182. flyte/remote/_task.py +227 -0
  183. flyte/report/__init__.py +3 -0
  184. flyte/report/_report.py +178 -0
  185. flyte/report/_template.html +124 -0
  186. flyte/storage/__init__.py +24 -0
  187. flyte/storage/_remote_fs.py +34 -0
  188. flyte/storage/_storage.py +251 -0
  189. flyte/storage/_utils.py +5 -0
  190. flyte/types/__init__.py +13 -0
  191. flyte/types/_interface.py +25 -0
  192. flyte/types/_renderer.py +162 -0
  193. flyte/types/_string_literals.py +120 -0
  194. flyte/types/_type_engine.py +2210 -0
  195. flyte/types/_utils.py +80 -0
  196. flyte-0.0.1b0.dist-info/METADATA +179 -0
  197. flyte-0.0.1b0.dist-info/RECORD +390 -0
  198. flyte-0.0.1b0.dist-info/WHEEL +5 -0
  199. flyte-0.0.1b0.dist-info/entry_points.txt +3 -0
  200. flyte-0.0.1b0.dist-info/top_level.txt +1 -0
  201. union/__init__.py +54 -0
  202. union/_api_commons.py +3 -0
  203. union/_bin/__init__.py +0 -0
  204. union/_bin/runtime.py +113 -0
  205. union/_build.py +25 -0
  206. union/_cache/__init__.py +12 -0
  207. union/_cache/cache.py +141 -0
  208. union/_cache/defaults.py +9 -0
  209. union/_cache/policy_function_body.py +42 -0
  210. union/_cli/__init__.py +0 -0
  211. union/_cli/_common.py +263 -0
  212. union/_cli/_create.py +40 -0
  213. union/_cli/_delete.py +23 -0
  214. union/_cli/_deploy.py +120 -0
  215. union/_cli/_get.py +162 -0
  216. union/_cli/_params.py +579 -0
  217. union/_cli/_run.py +150 -0
  218. union/_cli/main.py +72 -0
  219. union/_code_bundle/__init__.py +8 -0
  220. union/_code_bundle/_ignore.py +113 -0
  221. union/_code_bundle/_packaging.py +187 -0
  222. union/_code_bundle/_utils.py +342 -0
  223. union/_code_bundle/bundle.py +176 -0
  224. union/_context.py +146 -0
  225. union/_datastructures.py +295 -0
  226. union/_deploy.py +185 -0
  227. union/_doc.py +29 -0
  228. union/_docstring.py +26 -0
  229. union/_environment.py +43 -0
  230. union/_group.py +31 -0
  231. union/_hash.py +23 -0
  232. union/_image.py +760 -0
  233. union/_initialize.py +585 -0
  234. union/_interface.py +84 -0
  235. union/_internal/__init__.py +3 -0
  236. union/_internal/controllers/__init__.py +77 -0
  237. union/_internal/controllers/_local_controller.py +77 -0
  238. union/_internal/controllers/pbhash.py +39 -0
  239. union/_internal/controllers/remote/__init__.py +40 -0
  240. union/_internal/controllers/remote/_action.py +131 -0
  241. union/_internal/controllers/remote/_client.py +43 -0
  242. union/_internal/controllers/remote/_controller.py +169 -0
  243. union/_internal/controllers/remote/_core.py +341 -0
  244. union/_internal/controllers/remote/_informer.py +260 -0
  245. union/_internal/controllers/remote/_service_protocol.py +44 -0
  246. union/_internal/imagebuild/__init__.py +11 -0
  247. union/_internal/imagebuild/docker_builder.py +416 -0
  248. union/_internal/imagebuild/image_builder.py +243 -0
  249. union/_internal/imagebuild/remote_builder.py +0 -0
  250. union/_internal/resolvers/__init__.py +0 -0
  251. union/_internal/resolvers/_task_module.py +31 -0
  252. union/_internal/resolvers/common.py +24 -0
  253. union/_internal/resolvers/default.py +27 -0
  254. union/_internal/runtime/__init__.py +0 -0
  255. union/_internal/runtime/convert.py +163 -0
  256. union/_internal/runtime/entrypoints.py +121 -0
  257. union/_internal/runtime/io.py +136 -0
  258. union/_internal/runtime/resources_serde.py +134 -0
  259. union/_internal/runtime/task_serde.py +202 -0
  260. union/_internal/runtime/taskrunner.py +179 -0
  261. union/_internal/runtime/types_serde.py +53 -0
  262. union/_logging.py +124 -0
  263. union/_protos/__init__.py +0 -0
  264. union/_protos/common/authorization_pb2.py +66 -0
  265. union/_protos/common/authorization_pb2.pyi +106 -0
  266. union/_protos/common/authorization_pb2_grpc.py +4 -0
  267. union/_protos/common/identifier_pb2.py +71 -0
  268. union/_protos/common/identifier_pb2.pyi +82 -0
  269. union/_protos/common/identifier_pb2_grpc.py +4 -0
  270. union/_protos/common/identity_pb2.py +48 -0
  271. union/_protos/common/identity_pb2.pyi +72 -0
  272. union/_protos/common/identity_pb2_grpc.py +4 -0
  273. union/_protos/common/list_pb2.py +36 -0
  274. union/_protos/common/list_pb2.pyi +69 -0
  275. union/_protos/common/list_pb2_grpc.py +4 -0
  276. union/_protos/common/policy_pb2.py +37 -0
  277. union/_protos/common/policy_pb2.pyi +27 -0
  278. union/_protos/common/policy_pb2_grpc.py +4 -0
  279. union/_protos/common/role_pb2.py +37 -0
  280. union/_protos/common/role_pb2.pyi +51 -0
  281. union/_protos/common/role_pb2_grpc.py +4 -0
  282. union/_protos/common/runtime_version_pb2.py +28 -0
  283. union/_protos/common/runtime_version_pb2.pyi +24 -0
  284. union/_protos/common/runtime_version_pb2_grpc.py +4 -0
  285. union/_protos/logs/dataplane/payload_pb2.py +96 -0
  286. union/_protos/logs/dataplane/payload_pb2.pyi +168 -0
  287. union/_protos/logs/dataplane/payload_pb2_grpc.py +4 -0
  288. union/_protos/secret/definition_pb2.py +49 -0
  289. union/_protos/secret/definition_pb2.pyi +93 -0
  290. union/_protos/secret/definition_pb2_grpc.py +4 -0
  291. union/_protos/secret/payload_pb2.py +62 -0
  292. union/_protos/secret/payload_pb2.pyi +94 -0
  293. union/_protos/secret/payload_pb2_grpc.py +4 -0
  294. union/_protos/secret/secret_pb2.py +38 -0
  295. union/_protos/secret/secret_pb2.pyi +6 -0
  296. union/_protos/secret/secret_pb2_grpc.py +198 -0
  297. union/_protos/validate/validate/validate_pb2.py +76 -0
  298. union/_protos/workflow/node_execution_service_pb2.py +26 -0
  299. union/_protos/workflow/node_execution_service_pb2.pyi +4 -0
  300. union/_protos/workflow/node_execution_service_pb2_grpc.py +32 -0
  301. union/_protos/workflow/queue_service_pb2.py +75 -0
  302. union/_protos/workflow/queue_service_pb2.pyi +103 -0
  303. union/_protos/workflow/queue_service_pb2_grpc.py +172 -0
  304. union/_protos/workflow/run_definition_pb2.py +100 -0
  305. union/_protos/workflow/run_definition_pb2.pyi +256 -0
  306. union/_protos/workflow/run_definition_pb2_grpc.py +4 -0
  307. union/_protos/workflow/run_logs_service_pb2.py +41 -0
  308. union/_protos/workflow/run_logs_service_pb2.pyi +28 -0
  309. union/_protos/workflow/run_logs_service_pb2_grpc.py +69 -0
  310. union/_protos/workflow/run_service_pb2.py +133 -0
  311. union/_protos/workflow/run_service_pb2.pyi +173 -0
  312. union/_protos/workflow/run_service_pb2_grpc.py +412 -0
  313. union/_protos/workflow/state_service_pb2.py +58 -0
  314. union/_protos/workflow/state_service_pb2.pyi +69 -0
  315. union/_protos/workflow/state_service_pb2_grpc.py +138 -0
  316. union/_protos/workflow/task_definition_pb2.py +72 -0
  317. union/_protos/workflow/task_definition_pb2.pyi +65 -0
  318. union/_protos/workflow/task_definition_pb2_grpc.py +4 -0
  319. union/_protos/workflow/task_service_pb2.py +44 -0
  320. union/_protos/workflow/task_service_pb2.pyi +31 -0
  321. union/_protos/workflow/task_service_pb2_grpc.py +104 -0
  322. union/_resources.py +226 -0
  323. union/_retry.py +32 -0
  324. union/_reusable_environment.py +25 -0
  325. union/_run.py +374 -0
  326. union/_secret.py +61 -0
  327. union/_task.py +354 -0
  328. union/_task_environment.py +186 -0
  329. union/_timeout.py +47 -0
  330. union/_tools.py +27 -0
  331. union/_utils/__init__.py +11 -0
  332. union/_utils/asyn.py +119 -0
  333. union/_utils/file_handling.py +71 -0
  334. union/_utils/helpers.py +46 -0
  335. union/_utils/lazy_module.py +54 -0
  336. union/_utils/uv_script_parser.py +49 -0
  337. union/_version.py +21 -0
  338. union/connectors/__init__.py +0 -0
  339. union/errors.py +128 -0
  340. union/extras/__init__.py +5 -0
  341. union/extras/_container.py +263 -0
  342. union/io/__init__.py +11 -0
  343. union/io/_dataframe.py +0 -0
  344. union/io/_dir.py +425 -0
  345. union/io/_file.py +418 -0
  346. union/io/pickle/__init__.py +0 -0
  347. union/io/pickle/transformer.py +117 -0
  348. union/io/structured_dataset/__init__.py +122 -0
  349. union/io/structured_dataset/basic_dfs.py +219 -0
  350. union/io/structured_dataset/structured_dataset.py +1057 -0
  351. union/py.typed +0 -0
  352. union/remote/__init__.py +23 -0
  353. union/remote/_client/__init__.py +0 -0
  354. union/remote/_client/_protocols.py +129 -0
  355. union/remote/_client/auth/__init__.py +12 -0
  356. union/remote/_client/auth/_authenticators/__init__.py +0 -0
  357. union/remote/_client/auth/_authenticators/base.py +391 -0
  358. union/remote/_client/auth/_authenticators/client_credentials.py +73 -0
  359. union/remote/_client/auth/_authenticators/device_code.py +120 -0
  360. union/remote/_client/auth/_authenticators/external_command.py +77 -0
  361. union/remote/_client/auth/_authenticators/factory.py +200 -0
  362. union/remote/_client/auth/_authenticators/pkce.py +515 -0
  363. union/remote/_client/auth/_channel.py +184 -0
  364. union/remote/_client/auth/_client_config.py +83 -0
  365. union/remote/_client/auth/_default_html.py +32 -0
  366. union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
  367. union/remote/_client/auth/_grpc_utils/auth_interceptor.py +204 -0
  368. union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +144 -0
  369. union/remote/_client/auth/_keyring.py +154 -0
  370. union/remote/_client/auth/_token_client.py +258 -0
  371. union/remote/_client/auth/errors.py +16 -0
  372. union/remote/_client/controlplane.py +86 -0
  373. union/remote/_data.py +149 -0
  374. union/remote/_logs.py +74 -0
  375. union/remote/_project.py +86 -0
  376. union/remote/_run.py +820 -0
  377. union/remote/_secret.py +132 -0
  378. union/remote/_task.py +193 -0
  379. union/report/__init__.py +3 -0
  380. union/report/_report.py +178 -0
  381. union/report/_template.html +124 -0
  382. union/storage/__init__.py +24 -0
  383. union/storage/_remote_fs.py +34 -0
  384. union/storage/_storage.py +247 -0
  385. union/storage/_utils.py +5 -0
  386. union/types/__init__.py +11 -0
  387. union/types/_renderer.py +162 -0
  388. union/types/_string_literals.py +120 -0
  389. union/types/_type_engine.py +2131 -0
  390. union/types/_utils.py +80 -0
@@ -0,0 +1,516 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import base64
5
+ import hashlib
6
+ import os
7
+ import re
8
+ import threading
9
+ import time
10
+ import typing
11
+ import webbrowser
12
+ from http import HTTPStatus as _StatusCodes
13
+ from queue import Queue
14
+ from urllib import parse as _urlparse
15
+ from urllib.parse import urlencode as _urlencode
16
+
17
+ import click
18
+ import httpx
19
+ import pydantic
20
+ from h11 import Response
21
+
22
+ from flyte._logging import logger
23
+ from flyte.remote._client.auth._authenticators.base import Authenticator
24
+ from flyte.remote._client.auth._default_html import get_default_success_html
25
+ from flyte.remote._client.auth._keyring import Credentials
26
+ from flyte.remote._client.auth.errors import AccessTokenNotFoundError
27
+
28
+ _utf_8 = "utf-8"
29
+ _code_verifier_length = 64
30
+ _random_seed_length = 40
31
+
32
+
33
+ class PKCEAuthenticator(Authenticator):
34
+ """
35
+ This Authenticator encapsulates the entire PKCE flow and automatically opens a browser window for login
36
+
37
+ For Auth0 - you will need to manually configure your config.yaml to include a scopes list of the syntax:
38
+ admin.scopes: ["offline_access", "offline", "all", "openid"] and/or similar scopes in order to get the refresh
39
+ token + caching. Otherwise, it will just receive the access token alone. Your FlyteCTL Helm config however should
40
+ only contain ["offline", "all"] - as OIDC scopes are not-grantable in Auth0 customer APIs. They are simply requested
41
+ for in the POST request during the token caching process.
42
+ """
43
+
44
+ def __init__(
45
+ self,
46
+ **kwargs,
47
+ ):
48
+ """
49
+ Initialize with default creds from KeyStore using the endpoint name
50
+
51
+ :param kwargs: Keyword arguments passed to the base Authenticator
52
+
53
+ **Keyword Arguments passed to base Authenticator**:
54
+ :param endpoint: The endpoint URL for authentication (required)
55
+ :param cfg_store: Optional client configuration store for retrieving remote configuration
56
+ :param client_config: Optional client configuration containing authentication settings
57
+ :param credentials: Optional credentials to use for authentication
58
+ :param http_session: Optional HTTP session to use for requests
59
+ :param http_proxy_url: Optional HTTP proxy URL
60
+ :param verify: Whether to verify SSL certificates (default: True)
61
+ :param ca_cert_path: Optional path to CA certificate file
62
+ :param client_id: Client ID for authentication
63
+ :param scopes: List of scopes to request during authentication
64
+ :param audience: Audience for the token
65
+ :param redirect_uri: OAuth2 redirect URI for authentication
66
+ :param authorization_endpoint: Authorization endpoint for OAuth2 flow
67
+ :param token_endpoint: Token endpoint for OAuth2 flow
68
+ :param add_request_auth_code_params_to_request_access_token_params: Whether to add auth code params to token
69
+ request
70
+ :param request_auth_code_params: Parameters to add to login URI opened in browser
71
+ :param request_access_token_params: Parameters to add when exchanging auth code for access token
72
+ :param refresh_access_token_params: Parameters to add when refreshing access token
73
+ """
74
+ super().__init__(**kwargs)
75
+ self._auth_client = None
76
+
77
+ async def _initialize_auth_client(self):
78
+ if not self._auth_client:
79
+ code_verifier = await _generate_code_verifier()
80
+ code_challenge = await _create_code_challenge(code_verifier)
81
+
82
+ cfg = await self._resolve_config()
83
+ self._auth_client = AuthorizationClient(
84
+ endpoint=self._endpoint,
85
+ redirect_uri=cfg.redirect_uri,
86
+ client_id=cfg.client_id,
87
+ # Audience only needed for Auth0 - Taken from client config
88
+ audience=cfg.audience,
89
+ scopes=cfg.scopes,
90
+ # self._scopes refers to flytekit.configuration.PlatformConfig (config.yaml)
91
+ # cfg.scopes refers to PublicClientConfig scopes (can be defined in Helm deployments)
92
+ auth_endpoint=cfg.authorization_endpoint,
93
+ token_endpoint=cfg.token_endpoint,
94
+ verify=self._verify,
95
+ http_session=self._http_session,
96
+ request_auth_code_params={
97
+ "code_challenge": code_challenge,
98
+ "code_challenge_method": "S256",
99
+ },
100
+ request_access_token_params={
101
+ "code_verifier": code_verifier,
102
+ },
103
+ refresh_access_token_params={},
104
+ add_request_auth_code_params_to_request_access_token_params=True,
105
+ )
106
+
107
+ async def _do_refresh_credentials(self) -> Credentials:
108
+ """
109
+ Refreshes the authentication credentials using PKCE flow.
110
+
111
+ First attempts to refresh using a refresh token if available.
112
+ If that fails or if no credentials exist, initiates the full PKCE authorization flow,
113
+ which typically involves opening a browser for user authentication.
114
+
115
+ This method initializes the auth client if needed, then attempts to refresh or acquire
116
+ new credentials, and updates the internal credentials object.
117
+
118
+ :raises: May raise authentication-related exceptions if the refresh fails
119
+ """
120
+ await self._initialize_auth_client()
121
+ if self._creds:
122
+ """We have an access token so lets try to refresh it"""
123
+ try:
124
+ return await self._auth_client.refresh_access_token(self._creds)
125
+ except AccessTokenNotFoundError:
126
+ logger.warning("Failed to refresh token. Kicking off a full authorization flow.")
127
+
128
+ return await self._auth_client.get_creds_from_remote()
129
+
130
+
131
+ class AuthorizationClient(object):
132
+ """
133
+ Authorization client that stores the credentials in keyring and uses oauth2 standard flow to retrieve the
134
+ credentials. NOTE: This will open an web browser to retrieve the credentials.
135
+ """
136
+
137
+ def __init__(
138
+ self,
139
+ endpoint: str,
140
+ auth_endpoint: str,
141
+ token_endpoint: str,
142
+ http_session: httpx.AsyncClient,
143
+ audience: typing.Optional[str] = None,
144
+ scopes: typing.Optional[typing.List[str]] = None,
145
+ client_id: typing.Optional[str] = None,
146
+ redirect_uri: typing.Optional[str] = None,
147
+ endpoint_metadata: typing.Optional[EndpointMetadata] = None,
148
+ verify: bool = True,
149
+ ca_cert_path: typing.Optional[str] = None,
150
+ request_auth_code_params: typing.Optional[typing.Dict[str, str]] = None,
151
+ request_access_token_params: typing.Optional[typing.Dict[str, str]] = None,
152
+ refresh_access_token_params: typing.Optional[typing.Dict[str, str]] = None,
153
+ add_request_auth_code_params_to_request_access_token_params: typing.Optional[bool] = False,
154
+ ):
155
+ """
156
+ Create new AuthorizationClient
157
+
158
+ :param endpoint: The endpoint URL to connect to
159
+ :param auth_endpoint: The endpoint URL where auth metadata can be found
160
+ :param token_endpoint: The endpoint URL to retrieve token from
161
+ :param http_session: A custom httpx.AsyncClient object to use for making HTTP requests
162
+ :param audience: Audience parameter for Auth0 (optional)
163
+ :param scopes: List of OAuth2 scopes to request during authentication
164
+ :param client_id: OAuth2 client ID for authentication
165
+ :param redirect_uri: OAuth2 redirect URI for authentication callback
166
+ :param endpoint_metadata: EndpointMetadata object to control the rendering of the page on login successful or
167
+ failure
168
+ :param verify: A boolean that controls whether to verify the server's TLS certificate.
169
+ Defaults to ``True``. When set to ``False``, requests will accept any TLS certificate
170
+ presented by the server, and will ignore hostname mismatches and/or expired certificates,
171
+ which will make your application vulnerable to man-in-the-middle (MitM) attacks.
172
+ Setting verify to ``False`` may be useful during local development or testing.
173
+ :param ca_cert_path: Path to a certificate chain file for SSL verification (optional)
174
+ :param request_auth_code_params: Dictionary of parameters to add to login URI opened in the browser (optional)
175
+ :param request_access_token_params: Dictionary of parameters to add when exchanging the auth code for the
176
+ access token (optional)
177
+ :param refresh_access_token_params: Dictionary of parameters to add when refreshing the access token (optional)
178
+ :param add_request_auth_code_params_to_request_access_token_params: Whether to add the
179
+ `request_auth_code_params` to the parameters sent when exchanging the auth code for the access token.
180
+ Defaults to False. Required for the PKCE flow with the backend. Not required for the standard OAuth2 flow
181
+ on GCP.
182
+ """
183
+ self._endpoint = endpoint
184
+ self._auth_endpoint = auth_endpoint
185
+ if endpoint_metadata is None:
186
+ remote_url = _urlparse.urlparse(self._auth_endpoint)
187
+ self._remote = EndpointMetadata(endpoint=str(remote_url.hostname))
188
+ else:
189
+ self._remote = endpoint_metadata
190
+ self._token_endpoint = token_endpoint
191
+ self._client_id = client_id
192
+ self._audience = audience
193
+ self._scopes = scopes or []
194
+ self._redirect_uri = redirect_uri
195
+ state = _generate_state_parameter()
196
+ self._state = state
197
+ self._verify = verify
198
+ self._ca_cert_path = ca_cert_path
199
+ self._headers = {"content-type": "application/x-www-form-urlencoded"}
200
+ self._lock = threading.Lock()
201
+ self._cached_credentials: typing.Optional[Credentials] = None
202
+ self._cached_credentials_ts: float | None = None
203
+ self._http_session = http_session
204
+
205
+ self._request_auth_code_params = {
206
+ "client_id": client_id, # This must match the Client ID of the OAuth application.
207
+ "response_type": "code", # Indicates the authorization code grant
208
+ "scope": " ".join(s.strip("' ") for s in self._scopes).strip(
209
+ "[]'"
210
+ ), # ensures that the /token endpoint returns an ID and refresh token
211
+ # callback location where the user-agent will be directed to.
212
+ "redirect_uri": self._redirect_uri,
213
+ "state": state,
214
+ }
215
+
216
+ # Conditionally add audience param if provided - value is not None
217
+ if self._audience:
218
+ self._request_auth_code_params["audience"] = self._audience
219
+
220
+ if request_auth_code_params:
221
+ # Allow adding additional parameters to the request_auth_code_params
222
+ self._request_auth_code_params.update(request_auth_code_params)
223
+
224
+ self._request_access_token_params = request_access_token_params or {}
225
+ self._refresh_access_token_params = refresh_access_token_params or {}
226
+
227
+ if add_request_auth_code_params_to_request_access_token_params:
228
+ self._request_access_token_params.update(self._request_auth_code_params)
229
+
230
+ def __repr__(self):
231
+ return (
232
+ f"AuthorizationClient({self._auth_endpoint}, {self._token_endpoint}, {self._client_id}, {self._scopes},"
233
+ f" {self._redirect_uri})"
234
+ )
235
+
236
+ async def _create_callback_server(self):
237
+ server_url = _urlparse.urlparse(self._redirect_uri)
238
+ server_address = (server_url.hostname, server_url.port)
239
+ queue = Queue()
240
+ handler = OAuthCallbackHandler(queue, self._remote, server_url.path)
241
+ server = await asyncio.start_server(handler.handle, server_address[0], server_address[1])
242
+ return server, queue, handler
243
+
244
+ async def _request_authorization_code(self):
245
+ scheme, netloc, path, _, _, _ = _urlparse.urlparse(self._auth_endpoint)
246
+ query = _urlencode(self._request_auth_code_params)
247
+ endpoint = _urlparse.urlunparse((scheme, netloc, path, None, query, None))
248
+ logger.debug(f"Requesting authorization code through {endpoint}")
249
+
250
+ success = webbrowser.open_new_tab(endpoint) # type: ignore
251
+ if not success:
252
+ click.secho(f"Please open the following link in your browser to authenticate: {endpoint}")
253
+
254
+ async def _credentials_from_response(self, auth_token_resp) -> Credentials:
255
+ """
256
+ Extracts credentials from the authentication token response.
257
+
258
+ The auth_token_resp body is of the form:
259
+ {
260
+ "access_token": "foo",
261
+ "refresh_token": "bar",
262
+ "token_type": "Bearer"
263
+ }
264
+
265
+ Can additionally contain "expires_in" and "id_token" fields.
266
+
267
+ :param auth_token_resp: The HTTP response containing the token information
268
+ :return: Credentials object created from the response
269
+ :raises ValueError: If the response does not contain an access token
270
+ """
271
+ response_body = auth_token_resp.json()
272
+ refresh_token = None
273
+ expires_in = None
274
+
275
+ if "access_token" not in response_body:
276
+ raise ValueError('Expected "access_token" in response from oauth server')
277
+ if "refresh_token" in response_body:
278
+ refresh_token = response_body["refresh_token"]
279
+ if "expires_in" in response_body:
280
+ expires_in = response_body["expires_in"]
281
+ access_token = response_body["access_token"]
282
+
283
+ return Credentials(
284
+ access_token=access_token,
285
+ refresh_token=refresh_token,
286
+ for_endpoint=self._endpoint,
287
+ expires_in=expires_in,
288
+ )
289
+
290
+ async def _request_access_token(self, auth_code) -> Credentials:
291
+ if self._state != auth_code.state:
292
+ raise ValueError(f"Unexpected state parameter [{auth_code.state}] passed")
293
+
294
+ params = {
295
+ "code": auth_code.code,
296
+ "grant_type": "authorization_code",
297
+ }
298
+
299
+ params.update(self._request_access_token_params)
300
+
301
+ resp = await self._http_session.post(
302
+ url=self._token_endpoint,
303
+ data=params,
304
+ headers=self._headers,
305
+ follow_redirects=False,
306
+ )
307
+
308
+ if resp.status_code != _StatusCodes.OK:
309
+ raise RuntimeError(
310
+ "Failed to request access token with response: [{}] {!r}".format(resp.status_code, resp.content)
311
+ )
312
+
313
+ return await self._credentials_from_response(resp)
314
+
315
+ async def get_creds_from_remote(self) -> Credentials:
316
+ """
317
+ This is the entrypoint method. It will kickoff the full authentication
318
+ flow and trigger a web-browser to retrieve credentials. Because this
319
+ needs to open a port on localhost and may be called from a
320
+ multithreaded context (e.g. pyflyte register), this call may block
321
+ multiple threads and return a cached result for up to 60 seconds.
322
+
323
+ :return: Credentials obtained from the authentication flow
324
+ :raises: May raise authentication-related exceptions if the flow fails
325
+ """
326
+ # In the absence of globally-set token values, initiate the token request flow
327
+ with self._lock:
328
+ # Clear cache if it's been more than 60 seconds since the last check
329
+ cache_ttl_s = 60
330
+ if self._cached_credentials_ts is not None and self._cached_credentials_ts + cache_ttl_s < time.monotonic():
331
+ self._cached_credentials = None
332
+
333
+ if self._cached_credentials is not None:
334
+ return self._cached_credentials
335
+
336
+ server, queue, handler = await self._create_callback_server()
337
+ async with server:
338
+ await self._request_authorization_code()
339
+ # Wait for the callback handler to receive a response instead of serving forever
340
+ await handler.response_received.wait()
341
+
342
+ auth_code = queue.get()
343
+ self._cached_credentials = await self._request_access_token(auth_code)
344
+ self._cached_credentials_ts = time.monotonic()
345
+ return self._cached_credentials
346
+
347
+ async def refresh_access_token(self, credentials: Credentials) -> Credentials:
348
+ """
349
+ Refreshes the access token using the refresh token from the provided credentials.
350
+
351
+ :param credentials: The credentials containing the refresh token to use
352
+ :return: Updated credentials with a new access token
353
+ :raises AccessTokenNotFoundError: If no refresh token is available in the credentials
354
+ """
355
+ if credentials.refresh_token is None:
356
+ raise AccessTokenNotFoundError("no refresh token available with which to refresh authorization credentials")
357
+
358
+ data = {
359
+ "refresh_token": credentials.refresh_token,
360
+ "grant_type": "refresh_token",
361
+ "client_id": self._client_id,
362
+ }
363
+
364
+ data.update(self._refresh_access_token_params)
365
+
366
+ async with typing.cast(
367
+ typing.AsyncContextManager[Response],
368
+ self._http_session.post(
369
+ url=self._token_endpoint,
370
+ data=data,
371
+ headers=self._headers,
372
+ follow_redirects=False,
373
+ ),
374
+ ) as resp:
375
+ if resp.status_code != _StatusCodes.OK:
376
+ raise AccessTokenNotFoundError(f"Non-200 returned from refresh token endpoint {resp.status_code}")
377
+
378
+ return await self._credentials_from_response(resp)
379
+
380
+
381
+ class OAuthCallbackHandler:
382
+ """
383
+ Handles OAuth2 callback requests during the authentication flow.
384
+
385
+ This class implements an HTTP request handler that processes the callback from the OAuth2 provider,
386
+ extracts the authorization code, and passes it to the authentication flow.
387
+ """
388
+
389
+ def __init__(self, queue: Queue, remote_metadata: EndpointMetadata, redirect_path: str):
390
+ """
391
+ Initialize the OAuth callback handler.
392
+
393
+ :param queue: Queue to put the authorization code into when received
394
+ :param remote_metadata: Metadata about the remote endpoint for rendering success/failure pages
395
+ :param redirect_path: The path component of the redirect URI to match incoming requests against
396
+ """
397
+ self.queue = queue
398
+ self.remote_metadata = remote_metadata
399
+ self.redirect_path = redirect_path
400
+ self.response_received = asyncio.Event()
401
+
402
+ async def handle(self, reader, writer):
403
+ """
404
+ Handles an incoming HTTP request during the OAuth2 callback.
405
+
406
+ This method reads the incoming HTTP request, parses it, and if it matches the expected redirect path,
407
+ extracts the authorization code and state from the query parameters and puts them in the queue.
408
+ It then responds with an appropriate HTTP response.
409
+
410
+ :param reader: The StreamReader for reading the incoming request
411
+ :param writer: The StreamWriter for writing the response
412
+ """
413
+ data = await reader.read(1024)
414
+ message = data.decode()
415
+ headers = message.split("\r\n")
416
+ path = headers[0].split(" ")[1]
417
+ url = _urlparse.urlparse(path)
418
+ if url.path.strip("/") == self.redirect_path.strip("/"):
419
+ response = f"HTTP/1.1 {_StatusCodes.OK.value} {_StatusCodes.OK.phrase}\r\n"
420
+ response += "Content-Type: text/html\r\n\r\n"
421
+ self.handle_login(dict(_urlparse.parse_qsl(url.query)))
422
+ if self.remote_metadata.success_html is None:
423
+ response += get_default_success_html(self.remote_metadata.endpoint)
424
+ writer.write(response.encode(_utf_8))
425
+ await writer.drain()
426
+ else:
427
+ response = f"HTTP/1.1 {_StatusCodes.NOT_FOUND.value} {_StatusCodes.NOT_FOUND.phrase}\r\n\r\n"
428
+ writer.write(response.encode(_utf_8))
429
+ await writer.drain()
430
+ writer.close()
431
+ # Signal that we've received a response
432
+ self.response_received.set()
433
+
434
+ def handle_login(self, data: dict):
435
+ """
436
+ Processes the login data from the OAuth2 callback.
437
+
438
+ Extracts the authorization code and state from the query parameters and puts them in the queue
439
+ for the authentication flow to process.
440
+
441
+ :param data: Dictionary containing the query parameters from the callback URL
442
+ """
443
+ self.queue.put(AuthorizationCode(code=data["code"], state=data["state"]))
444
+
445
+
446
+ class EndpointMetadata(pydantic.BaseModel):
447
+ """
448
+ This class can be used to control the rendering of the page on login successful or failure.
449
+
450
+ :param endpoint: The endpoint URL or hostname for the remote service
451
+ :param success_html: Optional HTML content to display on successful authentication
452
+ :param failure_html: Optional HTML content to display on authentication failure
453
+ """
454
+
455
+ endpoint: str
456
+ success_html: typing.Optional[bytes] = None
457
+ failure_html: typing.Optional[bytes] = None
458
+
459
+
460
+ class AuthorizationCode(pydantic.BaseModel):
461
+ """
462
+ Represents an authorization code received from the OAuth2 provider.
463
+
464
+ :param code: The authorization code received from the OAuth2 provider
465
+ :param state: The state parameter that was sent in the original request
466
+ """
467
+
468
+ code: str
469
+ state: str
470
+
471
+
472
+ async def _create_code_challenge(code_verifier):
473
+ """
474
+ Creates a code challenge for PKCE flow from the provided code verifier.
475
+ Adapted from https://github.com/openstack/deb-python-oauth2client/blob/master/oauth2client/_pkce.py.
476
+
477
+ :param str code_verifier: A code verifier string generated by _generate_code_verifier()
478
+ :return str: Urlsafe base64-encoded sha256 hash digest of the code verifier
479
+ """
480
+ code_challenge = hashlib.sha256(code_verifier.encode(_utf_8)).digest()
481
+ code_challenge = base64.urlsafe_b64encode(code_challenge).decode(_utf_8)
482
+ # Eliminate invalid characters
483
+ code_challenge = code_challenge.replace("=", "")
484
+ return code_challenge
485
+
486
+
487
+ def _generate_state_parameter():
488
+ """
489
+ Generates a random state parameter for OAuth2 authorization requests.
490
+
491
+ The state parameter is used to maintain state between the request and callback
492
+ and to prevent cross-site request forgery attacks.
493
+
494
+ :return: A random string to use as the state parameter
495
+ """
496
+ state = base64.urlsafe_b64encode(os.urandom(_random_seed_length)).decode(_utf_8)
497
+ # Eliminate invalid characters.
498
+ code_verifier = re.sub("[^a-zA-Z0-9-_.,]+", "", state)
499
+ return code_verifier
500
+
501
+
502
+ async def _generate_code_verifier():
503
+ """
504
+ Generates a 'code_verifier' for PKCE OAuth2 flow as described in RFC 7636 section 4.1.
505
+ Adapted from https://github.com/openstack/deb-python-oauth2client/blob/master/oauth2client/_pkce.py.
506
+
507
+ :return str: A random string to use as the code verifier
508
+ """
509
+ code_verifier = base64.urlsafe_b64encode(os.urandom(_code_verifier_length)).decode(_utf_8)
510
+ # Eliminate invalid characters.
511
+ code_verifier = re.sub(r"[^a-zA-Z0-9_\-.~]+", "", code_verifier)
512
+ if len(code_verifier) < 43:
513
+ raise ValueError("Verifier too short. number of bytes must be > 30.")
514
+ elif len(code_verifier) > 128:
515
+ raise ValueError("Verifier too long. number of bytes must be < 97.")
516
+ return code_verifier