flyte 0.2.0b1__py3-none-any.whl → 2.0.0b46__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.
- flyte/__init__.py +83 -30
- flyte/_bin/connect.py +61 -0
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +87 -19
- flyte/_bin/serve.py +351 -0
- flyte/_build.py +3 -2
- flyte/_cache/cache.py +6 -5
- flyte/_cache/local_cache.py +216 -0
- flyte/_code_bundle/_ignore.py +31 -5
- flyte/_code_bundle/_packaging.py +42 -11
- flyte/_code_bundle/_utils.py +57 -34
- flyte/_code_bundle/bundle.py +130 -27
- flyte/_constants.py +1 -0
- flyte/_context.py +21 -5
- flyte/_custom_context.py +73 -0
- flyte/_debug/constants.py +37 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +315 -0
- flyte/_deploy.py +396 -75
- flyte/_deployer.py +109 -0
- flyte/_environment.py +94 -11
- flyte/_excepthook.py +37 -0
- flyte/_group.py +2 -1
- flyte/_hash.py +1 -16
- flyte/_image.py +544 -231
- flyte/_initialize.py +456 -316
- flyte/_interface.py +40 -5
- flyte/_internal/controllers/__init__.py +22 -8
- flyte/_internal/controllers/_local_controller.py +159 -35
- flyte/_internal/controllers/_trace.py +18 -10
- flyte/_internal/controllers/remote/__init__.py +38 -9
- flyte/_internal/controllers/remote/_action.py +82 -12
- flyte/_internal/controllers/remote/_client.py +6 -2
- flyte/_internal/controllers/remote/_controller.py +290 -64
- flyte/_internal/controllers/remote/_core.py +155 -95
- flyte/_internal/controllers/remote/_informer.py +40 -20
- flyte/_internal/controllers/remote/_service_protocol.py +2 -2
- flyte/_internal/imagebuild/__init__.py +2 -10
- flyte/_internal/imagebuild/docker_builder.py +391 -84
- flyte/_internal/imagebuild/image_builder.py +111 -55
- flyte/_internal/imagebuild/remote_builder.py +409 -0
- flyte/_internal/imagebuild/utils.py +79 -0
- flyte/_internal/resolvers/_app_env_module.py +92 -0
- flyte/_internal/resolvers/_task_module.py +5 -38
- flyte/_internal/resolvers/app_env.py +26 -0
- flyte/_internal/resolvers/common.py +8 -1
- flyte/_internal/resolvers/default.py +2 -2
- flyte/_internal/runtime/convert.py +319 -36
- flyte/_internal/runtime/entrypoints.py +106 -18
- flyte/_internal/runtime/io.py +71 -23
- flyte/_internal/runtime/resources_serde.py +21 -7
- flyte/_internal/runtime/reuse.py +125 -0
- flyte/_internal/runtime/rusty.py +196 -0
- flyte/_internal/runtime/task_serde.py +239 -66
- flyte/_internal/runtime/taskrunner.py +48 -8
- flyte/_internal/runtime/trigger_serde.py +162 -0
- flyte/_internal/runtime/types_serde.py +7 -16
- flyte/_keyring/file.py +115 -0
- flyte/_link.py +30 -0
- flyte/_logging.py +241 -42
- flyte/_map.py +312 -0
- flyte/_metrics.py +59 -0
- flyte/_module.py +74 -0
- flyte/_pod.py +30 -0
- flyte/_resources.py +296 -33
- flyte/_retry.py +1 -7
- flyte/_reusable_environment.py +72 -7
- flyte/_run.py +462 -132
- flyte/_secret.py +47 -11
- flyte/_serve.py +333 -0
- flyte/_task.py +245 -56
- flyte/_task_environment.py +219 -97
- flyte/_task_plugins.py +47 -0
- flyte/_tools.py +8 -8
- flyte/_trace.py +15 -24
- flyte/_trigger.py +1027 -0
- flyte/_utils/__init__.py +12 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +5 -4
- flyte/_utils/description_parser.py +19 -0
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/helpers.py +45 -19
- flyte/_utils/module_loader.py +123 -0
- flyte/_utils/org_discovery.py +57 -0
- flyte/_utils/uv_script_parser.py +8 -1
- flyte/_version.py +16 -3
- flyte/app/__init__.py +27 -0
- flyte/app/_app_environment.py +362 -0
- flyte/app/_connector_environment.py +40 -0
- flyte/app/_deploy.py +130 -0
- flyte/app/_parameter.py +343 -0
- flyte/app/_runtime/__init__.py +3 -0
- flyte/app/_runtime/app_serde.py +383 -0
- flyte/app/_types.py +113 -0
- flyte/app/extras/__init__.py +9 -0
- flyte/app/extras/_auth_middleware.py +217 -0
- flyte/app/extras/_fastapi.py +93 -0
- flyte/app/extras/_model_loader/__init__.py +3 -0
- flyte/app/extras/_model_loader/config.py +7 -0
- flyte/app/extras/_model_loader/loader.py +288 -0
- flyte/cli/__init__.py +12 -0
- flyte/cli/_abort.py +28 -0
- flyte/cli/_build.py +114 -0
- flyte/cli/_common.py +493 -0
- flyte/cli/_create.py +371 -0
- flyte/cli/_delete.py +45 -0
- flyte/cli/_deploy.py +401 -0
- flyte/cli/_gen.py +316 -0
- flyte/cli/_get.py +446 -0
- flyte/cli/_option.py +33 -0
- flyte/{_cli → cli}/_params.py +57 -17
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_prefetch.py +292 -0
- flyte/cli/_run.py +690 -0
- flyte/cli/_serve.py +338 -0
- flyte/cli/_update.py +86 -0
- flyte/cli/_user.py +20 -0
- flyte/cli/main.py +246 -0
- flyte/config/__init__.py +2 -167
- flyte/config/_config.py +215 -163
- flyte/config/_internal.py +10 -1
- flyte/config/_reader.py +225 -0
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +330 -0
- flyte/connectors/_server.py +194 -0
- flyte/connectors/utils.py +159 -0
- flyte/errors.py +134 -2
- flyte/extend.py +24 -0
- flyte/extras/_container.py +69 -56
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +279 -0
- flyte/io/__init__.py +8 -1
- flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
- flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
- flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
- flyte/io/_dir.py +575 -113
- flyte/io/_file.py +587 -141
- flyte/io/_hashing_io.py +342 -0
- flyte/io/extend.py +7 -0
- flyte/models.py +635 -0
- flyte/prefetch/__init__.py +22 -0
- flyte/prefetch/_hf_model.py +563 -0
- flyte/remote/__init__.py +14 -3
- flyte/remote/_action.py +879 -0
- flyte/remote/_app.py +346 -0
- flyte/remote/_auth_metadata.py +42 -0
- flyte/remote/_client/_protocols.py +62 -4
- flyte/remote/_client/auth/_auth_utils.py +19 -0
- flyte/remote/_client/auth/_authenticators/base.py +8 -2
- flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
- flyte/remote/_client/auth/_authenticators/factory.py +4 -0
- flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
- flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
- flyte/remote/_client/auth/_channel.py +47 -18
- flyte/remote/_client/auth/_client_config.py +5 -3
- flyte/remote/_client/auth/_keyring.py +15 -2
- flyte/remote/_client/auth/_token_client.py +3 -3
- flyte/remote/_client/controlplane.py +206 -18
- flyte/remote/_common.py +66 -0
- flyte/remote/_data.py +107 -22
- flyte/remote/_logs.py +116 -33
- flyte/remote/_project.py +21 -19
- flyte/remote/_run.py +164 -631
- flyte/remote/_secret.py +72 -29
- flyte/remote/_task.py +387 -46
- flyte/remote/_trigger.py +368 -0
- flyte/remote/_user.py +43 -0
- flyte/report/_report.py +10 -6
- flyte/storage/__init__.py +13 -1
- flyte/storage/_config.py +237 -0
- flyte/storage/_parallel_reader.py +289 -0
- flyte/storage/_storage.py +268 -59
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +414 -0
- flyte/types/__init__.py +39 -0
- flyte/types/_interface.py +22 -7
- flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
- flyte/types/_string_literals.py +8 -9
- flyte/types/_type_engine.py +226 -126
- flyte/types/_utils.py +1 -1
- flyte-2.0.0b46.data/scripts/debug.py +38 -0
- flyte-2.0.0b46.data/scripts/runtime.py +194 -0
- flyte-2.0.0b46.dist-info/METADATA +352 -0
- flyte-2.0.0b46.dist-info/RECORD +221 -0
- flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
- flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
- flyte/_api_commons.py +0 -3
- flyte/_cli/_common.py +0 -299
- flyte/_cli/_create.py +0 -42
- flyte/_cli/_delete.py +0 -23
- flyte/_cli/_deploy.py +0 -140
- flyte/_cli/_get.py +0 -235
- flyte/_cli/_run.py +0 -174
- flyte/_cli/main.py +0 -98
- flyte/_datastructures.py +0 -342
- flyte/_internal/controllers/pbhash.py +0 -39
- flyte/_protos/common/authorization_pb2.py +0 -66
- flyte/_protos/common/authorization_pb2.pyi +0 -108
- flyte/_protos/common/authorization_pb2_grpc.py +0 -4
- flyte/_protos/common/identifier_pb2.py +0 -71
- flyte/_protos/common/identifier_pb2.pyi +0 -82
- flyte/_protos/common/identifier_pb2_grpc.py +0 -4
- flyte/_protos/common/identity_pb2.py +0 -48
- flyte/_protos/common/identity_pb2.pyi +0 -72
- flyte/_protos/common/identity_pb2_grpc.py +0 -4
- flyte/_protos/common/list_pb2.py +0 -36
- flyte/_protos/common/list_pb2.pyi +0 -69
- flyte/_protos/common/list_pb2_grpc.py +0 -4
- flyte/_protos/common/policy_pb2.py +0 -37
- flyte/_protos/common/policy_pb2.pyi +0 -27
- flyte/_protos/common/policy_pb2_grpc.py +0 -4
- flyte/_protos/common/role_pb2.py +0 -37
- flyte/_protos/common/role_pb2.pyi +0 -53
- flyte/_protos/common/role_pb2_grpc.py +0 -4
- flyte/_protos/common/runtime_version_pb2.py +0 -28
- flyte/_protos/common/runtime_version_pb2.pyi +0 -24
- flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
- flyte/_protos/logs/dataplane/payload_pb2.py +0 -96
- flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/definition_pb2.py +0 -49
- flyte/_protos/secret/definition_pb2.pyi +0 -93
- flyte/_protos/secret/definition_pb2_grpc.py +0 -4
- flyte/_protos/secret/payload_pb2.py +0 -62
- flyte/_protos/secret/payload_pb2.pyi +0 -94
- flyte/_protos/secret/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/secret_pb2.py +0 -38
- flyte/_protos/secret/secret_pb2.pyi +0 -6
- flyte/_protos/secret/secret_pb2_grpc.py +0 -198
- flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
- flyte/_protos/validate/validate/validate_pb2.py +0 -76
- flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
- flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- flyte/_protos/workflow/queue_service_pb2.py +0 -106
- flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
- flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- flyte/_protos/workflow/run_definition_pb2.py +0 -128
- flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
- flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
- flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- flyte/_protos/workflow/run_service_pb2.py +0 -133
- flyte/_protos/workflow/run_service_pb2.pyi +0 -175
- flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
- flyte/_protos/workflow/state_service_pb2.py +0 -58
- flyte/_protos/workflow/state_service_pb2.pyi +0 -71
- flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
- flyte/_protos/workflow/task_definition_pb2.py +0 -72
- flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
- flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/task_service_pb2.py +0 -44
- flyte/_protos/workflow/task_service_pb2.pyi +0 -31
- flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
- flyte/io/_dataframe.py +0 -0
- flyte/io/pickle/__init__.py +0 -0
- flyte/remote/_console.py +0 -18
- flyte-0.2.0b1.dist-info/METADATA +0 -179
- flyte-0.2.0b1.dist-info/RECORD +0 -204
- flyte-0.2.0b1.dist-info/entry_points.txt +0 -3
- /flyte/{_cli → _debug}/__init__.py +0 -0
- /flyte/{_protos → _keyring}/__init__.py +0 -0
- {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
- {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import json
|
|
5
5
|
import typing
|
|
6
|
+
from importlib.metadata import entry_points
|
|
6
7
|
from typing import ClassVar, Dict, Optional, Tuple
|
|
7
8
|
|
|
8
9
|
from async_lru import alru_cache
|
|
@@ -10,18 +11,26 @@ from pydantic import BaseModel
|
|
|
10
11
|
from typing_extensions import Protocol
|
|
11
12
|
|
|
12
13
|
from flyte._image import Architecture, Image
|
|
14
|
+
from flyte._initialize import _get_init_config
|
|
13
15
|
from flyte._logging import logger
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class ImageBuilder(Protocol):
|
|
17
19
|
async def build_image(self, image: Image, dry_run: bool) -> str: ...
|
|
18
20
|
|
|
21
|
+
def get_checkers(self) -> Optional[typing.List[typing.Type[ImageChecker]]]:
|
|
22
|
+
"""
|
|
23
|
+
Returns ImageCheckers that can be used to check if the image exists in the registry.
|
|
24
|
+
If None, then use the default checkers.
|
|
25
|
+
"""
|
|
26
|
+
return None
|
|
27
|
+
|
|
19
28
|
|
|
20
29
|
class ImageChecker(Protocol):
|
|
21
30
|
@classmethod
|
|
22
31
|
async def image_exists(
|
|
23
32
|
cls, repository: str, tag: str, arch: Tuple[Architecture, ...] = ("linux/amd64",)
|
|
24
|
-
) ->
|
|
33
|
+
) -> Optional[str]: ...
|
|
25
34
|
|
|
26
35
|
|
|
27
36
|
class DockerAPIImageChecker(ImageChecker):
|
|
@@ -32,13 +41,12 @@ class DockerAPIImageChecker(ImageChecker):
|
|
|
32
41
|
"""
|
|
33
42
|
|
|
34
43
|
@classmethod
|
|
35
|
-
async def image_exists(
|
|
44
|
+
async def image_exists(
|
|
45
|
+
cls, repository: str, tag: str, arch: Tuple[Architecture, ...] = ("linux/amd64",)
|
|
46
|
+
) -> Optional[str]:
|
|
36
47
|
import httpx
|
|
37
48
|
|
|
38
|
-
if "/" in repository:
|
|
39
|
-
if not repository.startswith("library/"):
|
|
40
|
-
raise ValueError("This checker only works with Docker Hub")
|
|
41
|
-
else:
|
|
49
|
+
if "/" not in repository:
|
|
42
50
|
repository = f"library/{repository}"
|
|
43
51
|
|
|
44
52
|
auth_url = "https://auth.docker.io/token"
|
|
@@ -50,8 +58,10 @@ class DockerAPIImageChecker(ImageChecker):
|
|
|
50
58
|
auth_response = await client.get(auth_url, params={"service": service, "scope": scope})
|
|
51
59
|
if auth_response.status_code != 200:
|
|
52
60
|
raise Exception(f"Failed to get auth token: {auth_response.status_code}")
|
|
61
|
+
|
|
53
62
|
token = auth_response.json()["token"]
|
|
54
63
|
|
|
64
|
+
# ghcr.io/union-oss/flyte:latest
|
|
55
65
|
manifest_url = f"https://registry-1.docker.io/v2/{repository}/manifests/{tag}"
|
|
56
66
|
headers = {
|
|
57
67
|
"Authorization": f"Bearer {token}",
|
|
@@ -60,26 +70,30 @@ class DockerAPIImageChecker(ImageChecker):
|
|
|
60
70
|
"application/vnd.docker.distribution.manifest.list.v2+json"
|
|
61
71
|
),
|
|
62
72
|
}
|
|
63
|
-
manifest_response = await client.get(manifest_url, headers=headers)
|
|
64
73
|
|
|
74
|
+
manifest_response = await client.get(manifest_url, headers=headers)
|
|
65
75
|
if manifest_response.status_code != 200:
|
|
66
|
-
|
|
76
|
+
logger.warning(f"Image not found: {repository}:{tag} (HTTP {manifest_response.status_code})")
|
|
77
|
+
return None
|
|
78
|
+
|
|
67
79
|
manifest_list = manifest_response.json()["manifests"]
|
|
68
|
-
architectures = [f"{
|
|
80
|
+
architectures = [f"{m['platform']['os']}/{m['platform']['architecture']}" for m in manifest_list]
|
|
69
81
|
|
|
70
|
-
if set(
|
|
71
|
-
logger.debug(f"Image {repository}:{tag} found
|
|
72
|
-
return
|
|
82
|
+
if set(arch).issubset(set(architectures)):
|
|
83
|
+
logger.debug(f"Image {repository}:{tag} found with arch {architectures}")
|
|
84
|
+
return f"{repository}:{tag}"
|
|
73
85
|
else:
|
|
74
|
-
logger.debug(f"Image {repository}:{tag}
|
|
75
|
-
return
|
|
86
|
+
logger.debug(f"Image {repository}:{tag} has {architectures}, but missing {arch}")
|
|
87
|
+
return None
|
|
76
88
|
|
|
77
89
|
|
|
78
90
|
class LocalDockerCommandImageChecker(ImageChecker):
|
|
79
91
|
command_name: ClassVar[str] = "docker"
|
|
80
92
|
|
|
81
93
|
@classmethod
|
|
82
|
-
async def image_exists(
|
|
94
|
+
async def image_exists(
|
|
95
|
+
cls, repository: str, tag: str, arch: Tuple[Architecture, ...] = ("linux/amd64",)
|
|
96
|
+
) -> Optional[str]:
|
|
83
97
|
# Check if the image exists locally by running the docker inspect command
|
|
84
98
|
process = await asyncio.create_subprocess_exec(
|
|
85
99
|
cls.command_name,
|
|
@@ -90,9 +104,9 @@ class LocalDockerCommandImageChecker(ImageChecker):
|
|
|
90
104
|
stderr=asyncio.subprocess.PIPE,
|
|
91
105
|
)
|
|
92
106
|
stdout, stderr = await process.communicate()
|
|
93
|
-
if stderr and "manifest unknown" in stderr.decode():
|
|
107
|
+
if (stderr and "manifest unknown") or "no such manifest" in stderr.decode():
|
|
94
108
|
logger.debug(f"Image {repository}:{tag} not found using the docker command.")
|
|
95
|
-
return
|
|
109
|
+
return None
|
|
96
110
|
|
|
97
111
|
if process.returncode != 0:
|
|
98
112
|
raise RuntimeError(f"Failed to run docker image inspect {repository}:{tag}")
|
|
@@ -104,11 +118,11 @@ class LocalDockerCommandImageChecker(ImageChecker):
|
|
|
104
118
|
architectures = [f"{x['platform']['os']}/{x['platform']['architecture']}" for x in manifest_list]
|
|
105
119
|
if set(architectures) >= set(arch):
|
|
106
120
|
logger.debug(f"Image {repository}:{tag} found for architecture(s) {arch}, has {architectures}")
|
|
107
|
-
return
|
|
121
|
+
return f"{repository}:{tag}"
|
|
108
122
|
|
|
109
123
|
# Otherwise write a message and return false to trigger build
|
|
110
124
|
logger.debug(f"Image {repository}:{tag} not found for architecture(s) {arch}, only has {architectures}")
|
|
111
|
-
return
|
|
125
|
+
return None
|
|
112
126
|
|
|
113
127
|
|
|
114
128
|
class LocalPodmanCommandImageChecker(LocalDockerCommandImageChecker):
|
|
@@ -120,55 +134,54 @@ class ImageBuildEngine:
|
|
|
120
134
|
ImageBuildEngine contains a list of builders that can be used to build an ImageSpec.
|
|
121
135
|
"""
|
|
122
136
|
|
|
123
|
-
|
|
124
|
-
_SEEN_IMAGES: typing.ClassVar[typing.Dict[str, str]] = {
|
|
125
|
-
# Set default for the auto container. See Image._identifier_override for more info.
|
|
126
|
-
"auto": Image.auto().uri,
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
@classmethod
|
|
130
|
-
def register(cls, builder_type: str, image_builder: ImageBuilder, priority: int = 5):
|
|
131
|
-
cls._REGISTRY[builder_type] = (image_builder, priority)
|
|
132
|
-
|
|
133
|
-
@classmethod
|
|
134
|
-
def get_registry(cls) -> Dict[str, Tuple[ImageBuilder, int]]:
|
|
135
|
-
return cls._REGISTRY
|
|
137
|
+
ImageBuilderType = typing.Literal["local", "remote"]
|
|
136
138
|
|
|
137
139
|
@staticmethod
|
|
138
140
|
@alru_cache
|
|
139
|
-
async def image_exists(image: Image) ->
|
|
141
|
+
async def image_exists(image: Image) -> Optional[str]:
|
|
140
142
|
if image.base_image is not None and not image._layers:
|
|
141
143
|
logger.debug(f"Image {image} has a base image: {image.base_image} and no layers. Skip existence check.")
|
|
142
|
-
return
|
|
143
|
-
assert image.registry is not None, f"Image registry is not set for {image}"
|
|
144
|
+
return image.uri
|
|
144
145
|
assert image.name is not None, f"Image name is not set for {image}"
|
|
145
146
|
|
|
146
|
-
repository = image.registry + "/" + image.name
|
|
147
147
|
tag = image._final_tag
|
|
148
148
|
|
|
149
149
|
if tag == "latest":
|
|
150
150
|
logger.debug(f"Image {image} has tag 'latest', skip existence check, always build")
|
|
151
|
-
return
|
|
151
|
+
return image.uri
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
builder = None
|
|
154
|
+
cfg = _get_init_config()
|
|
155
|
+
if cfg and cfg.image_builder:
|
|
156
|
+
builder = cfg.image_builder
|
|
157
|
+
image_builder = ImageBuildEngine._get_builder(builder)
|
|
158
|
+
image_checker = image_builder.get_checkers()
|
|
159
|
+
if image_checker is None:
|
|
160
|
+
logger.info(f"No image checkers found for builder `{image_builder}`, assuming it exists")
|
|
161
|
+
return image.uri
|
|
162
|
+
for checker in image_checker:
|
|
156
163
|
try:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
repository = image.registry + "/" + image.name if image.registry else image.name
|
|
165
|
+
image_uri = await checker.image_exists(repository, tag, tuple(image.platform))
|
|
166
|
+
if image_uri:
|
|
167
|
+
logger.debug(f"Image {image_uri} in registry")
|
|
168
|
+
return image_uri
|
|
160
169
|
except Exception as e:
|
|
161
170
|
logger.debug(f"Error checking image existence with {checker.__name__}: {e}")
|
|
162
171
|
continue
|
|
163
172
|
|
|
164
173
|
# If all checkers fail, then assume the image exists. This is current flytekit behavior
|
|
165
174
|
logger.info(f"All checkers failed to check existence of {image.uri}, assuming it does exists")
|
|
166
|
-
return
|
|
175
|
+
return image.uri
|
|
167
176
|
|
|
168
177
|
@classmethod
|
|
169
178
|
@alru_cache
|
|
170
179
|
async def build(
|
|
171
|
-
cls,
|
|
180
|
+
cls,
|
|
181
|
+
image: Image,
|
|
182
|
+
builder: ImageBuildEngine.ImageBuilderType | None = None,
|
|
183
|
+
dry_run: bool = False,
|
|
184
|
+
force: bool = False,
|
|
172
185
|
) -> str:
|
|
173
186
|
"""
|
|
174
187
|
Build the image. Images to be tagged with latest will always be built. Otherwise, this engine will check the
|
|
@@ -181,30 +194,59 @@ class ImageBuildEngine:
|
|
|
181
194
|
:return:
|
|
182
195
|
"""
|
|
183
196
|
# Always trigger a build if this is a dry run since builder shouldn't really do anything, or a force.
|
|
197
|
+
image_uri = (await cls.image_exists(image)) or image.uri
|
|
184
198
|
if force or dry_run or not await cls.image_exists(image):
|
|
185
|
-
logger.info(f"Image {
|
|
199
|
+
logger.info(f"Image {image_uri} does not exist in registry or force/dry-run, building...")
|
|
186
200
|
|
|
187
201
|
# Validate the image before building
|
|
188
202
|
image.validate()
|
|
189
203
|
|
|
190
|
-
# If builder is not specified, use the first registered builder
|
|
204
|
+
# If a builder is not specified, use the first registered builder
|
|
205
|
+
cfg = _get_init_config()
|
|
206
|
+
if cfg and cfg.image_builder:
|
|
207
|
+
builder = builder or cfg.image_builder
|
|
191
208
|
img_builder = ImageBuildEngine._get_builder(builder)
|
|
209
|
+
logger.debug(f"Using `{img_builder}` image builder to build image.")
|
|
192
210
|
|
|
193
211
|
result = await img_builder.build_image(image, dry_run=dry_run)
|
|
194
212
|
return result
|
|
195
213
|
else:
|
|
196
|
-
logger.info(f"Image {
|
|
197
|
-
return
|
|
214
|
+
logger.info(f"Image {image_uri} already exists in registry. Skipping build.")
|
|
215
|
+
return image_uri
|
|
198
216
|
|
|
199
217
|
@classmethod
|
|
200
|
-
def _get_builder(cls, builder:
|
|
201
|
-
if
|
|
202
|
-
|
|
218
|
+
def _get_builder(cls, builder: ImageBuildEngine.ImageBuilderType | None = "local") -> ImageBuilder:
|
|
219
|
+
if builder is None:
|
|
220
|
+
builder = "local"
|
|
221
|
+
if builder == "remote":
|
|
222
|
+
from flyte._internal.imagebuild.remote_builder import RemoteImageBuilder
|
|
223
|
+
|
|
224
|
+
return RemoteImageBuilder()
|
|
225
|
+
elif builder == "local":
|
|
226
|
+
from flyte._internal.imagebuild.docker_builder import DockerImageBuilder
|
|
203
227
|
|
|
204
228
|
return DockerImageBuilder()
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
229
|
+
else:
|
|
230
|
+
return cls._load_custom_type_transformers(builder)
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def _load_custom_type_transformers(cls, name: str) -> ImageBuilder:
|
|
234
|
+
plugins = entry_points(group="flyte.plugins.image_builders")
|
|
235
|
+
for ep in plugins:
|
|
236
|
+
if ep.name != name:
|
|
237
|
+
continue
|
|
238
|
+
try:
|
|
239
|
+
logger.info(f"Loading image builder: {ep.name}")
|
|
240
|
+
builder = ep.load()
|
|
241
|
+
if callable(builder):
|
|
242
|
+
return builder()
|
|
243
|
+
return builder
|
|
244
|
+
except Exception as e:
|
|
245
|
+
raise RuntimeError(f"Failed to load image builder {ep.name} with error: {e}")
|
|
246
|
+
raise ValueError(
|
|
247
|
+
f"Unknown image builder type: {name}. Available builders:"
|
|
248
|
+
f" {[ep.name for ep in plugins] + ['local', 'remote']}"
|
|
249
|
+
)
|
|
208
250
|
|
|
209
251
|
|
|
210
252
|
class ImageCache(BaseModel):
|
|
@@ -239,3 +281,17 @@ class ImageCache(BaseModel):
|
|
|
239
281
|
val = cls.model_validate_json(json_str)
|
|
240
282
|
val.serialized_form = s
|
|
241
283
|
return val
|
|
284
|
+
|
|
285
|
+
def repr(self) -> typing.List[typing.List[Tuple[str, str]]]:
|
|
286
|
+
"""
|
|
287
|
+
Returns a detailed representation of the deployed environments.
|
|
288
|
+
"""
|
|
289
|
+
tuples = []
|
|
290
|
+
for k, v in self.image_lookup.items():
|
|
291
|
+
tuples.append(
|
|
292
|
+
[
|
|
293
|
+
("Name", k),
|
|
294
|
+
("image", v),
|
|
295
|
+
]
|
|
296
|
+
)
|
|
297
|
+
return tuples
|