prefect-client 2.18.0__py3-none-any.whl → 2.18.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 (49) hide show
  1. prefect/_internal/schemas/fields.py +31 -12
  2. prefect/automations.py +162 -0
  3. prefect/blocks/core.py +1 -1
  4. prefect/blocks/notifications.py +2 -2
  5. prefect/blocks/system.py +2 -3
  6. prefect/client/orchestration.py +309 -30
  7. prefect/client/schemas/objects.py +11 -8
  8. prefect/client/schemas/sorting.py +9 -0
  9. prefect/client/utilities.py +25 -3
  10. prefect/concurrency/asyncio.py +11 -5
  11. prefect/concurrency/events.py +3 -3
  12. prefect/concurrency/services.py +1 -1
  13. prefect/concurrency/sync.py +9 -5
  14. prefect/deployments/deployments.py +27 -18
  15. prefect/deployments/runner.py +34 -26
  16. prefect/engine.py +3 -1
  17. prefect/events/actions.py +2 -1
  18. prefect/events/cli/automations.py +207 -46
  19. prefect/events/clients.py +53 -20
  20. prefect/events/filters.py +31 -4
  21. prefect/events/instrument.py +40 -40
  22. prefect/events/related.py +2 -1
  23. prefect/events/schemas/automations.py +52 -7
  24. prefect/events/schemas/deployment_triggers.py +16 -228
  25. prefect/events/schemas/events.py +18 -11
  26. prefect/events/schemas/labelling.py +1 -1
  27. prefect/events/utilities.py +1 -1
  28. prefect/events/worker.py +10 -7
  29. prefect/flows.py +42 -24
  30. prefect/input/actions.py +9 -9
  31. prefect/input/run_input.py +51 -37
  32. prefect/new_flow_engine.py +444 -0
  33. prefect/new_task_engine.py +488 -0
  34. prefect/results.py +3 -2
  35. prefect/runner/runner.py +3 -2
  36. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +45 -4
  37. prefect/settings.py +47 -0
  38. prefect/states.py +25 -19
  39. prefect/tasks.py +146 -19
  40. prefect/utilities/asyncutils.py +41 -0
  41. prefect/utilities/engine.py +6 -4
  42. prefect/utilities/schema_tools/validation.py +1 -1
  43. prefect/workers/process.py +2 -1
  44. {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/METADATA +1 -1
  45. {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/RECORD +48 -46
  46. prefect/concurrency/common.py +0 -0
  47. {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/LICENSE +0 -0
  48. {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/WHEEL +0 -0
  49. {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,488 @@
1
+ import asyncio
2
+ import inspect
3
+ import logging
4
+ from contextlib import asynccontextmanager, contextmanager
5
+ from dataclasses import dataclass, field
6
+ from typing import (
7
+ Any,
8
+ Callable,
9
+ Coroutine,
10
+ Dict,
11
+ Generic,
12
+ Iterable,
13
+ Literal,
14
+ Optional,
15
+ TypeVar,
16
+ Union,
17
+ cast,
18
+ )
19
+ from uuid import uuid4
20
+
21
+ import pendulum
22
+ from typing_extensions import ParamSpec
23
+
24
+ from prefect import Task, get_client
25
+ from prefect._internal.concurrency.cancellation import (
26
+ AlarmCancelScope,
27
+ AsyncCancelScope,
28
+ CancelledError,
29
+ )
30
+ from prefect.client.orchestration import PrefectClient
31
+ from prefect.client.schemas import TaskRun
32
+ from prefect.client.schemas.objects import TaskRunResult
33
+ from prefect.context import FlowRunContext, TaskRunContext
34
+ from prefect.futures import PrefectFuture, resolve_futures_to_states
35
+ from prefect.logging.loggers import get_logger, task_run_logger
36
+ from prefect.results import ResultFactory
37
+ from prefect.server.schemas.states import State
38
+ from prefect.settings import PREFECT_TASKS_REFRESH_CACHE
39
+ from prefect.states import (
40
+ Pending,
41
+ Retrying,
42
+ Running,
43
+ StateDetails,
44
+ exception_to_crashed_state,
45
+ exception_to_failed_state,
46
+ return_value_to_state,
47
+ )
48
+ from prefect.utilities.asyncutils import A, Async, is_async_fn, run_sync
49
+ from prefect.utilities.callables import parameters_to_args_kwargs
50
+ from prefect.utilities.engine import (
51
+ _dynamic_key_for_task_run,
52
+ _get_hook_name,
53
+ _resolve_custom_task_run_name,
54
+ collect_task_run_inputs,
55
+ propose_state,
56
+ )
57
+
58
+ P = ParamSpec("P")
59
+ R = TypeVar("R")
60
+
61
+
62
+ @asynccontextmanager
63
+ async def timeout(seconds: Optional[float] = None):
64
+ try:
65
+ with AsyncCancelScope(timeout=seconds):
66
+ yield
67
+ except CancelledError:
68
+ raise TimeoutError(f"Task timed out after {seconds} second(s).")
69
+
70
+
71
+ @contextmanager
72
+ def timeout_sync(seconds: Optional[float] = None):
73
+ try:
74
+ with AlarmCancelScope(timeout=seconds):
75
+ yield
76
+ except CancelledError:
77
+ raise TimeoutError(f"Task timed out after {seconds} second(s).")
78
+
79
+
80
+ @dataclass
81
+ class TaskRunEngine(Generic[P, R]):
82
+ task: Union[Task[P, R], Task[P, Coroutine[Any, Any, R]]]
83
+ logger: logging.Logger = field(default_factory=lambda: get_logger("engine"))
84
+ parameters: Optional[Dict[str, Any]] = None
85
+ task_run: Optional[TaskRun] = None
86
+ retries: int = 0
87
+ _is_started: bool = False
88
+ _client: Optional[PrefectClient] = None
89
+
90
+ def __post_init__(self):
91
+ if self.parameters is None:
92
+ self.parameters = {}
93
+
94
+ @property
95
+ def client(self) -> PrefectClient:
96
+ if not self._is_started or self._client is None:
97
+ raise RuntimeError("Engine has not started.")
98
+ return self._client
99
+
100
+ @property
101
+ def state(self) -> State:
102
+ if not self.task_run:
103
+ raise ValueError("Task run is not set")
104
+ return self.task_run.state
105
+
106
+ @property
107
+ def can_retry(self) -> bool:
108
+ retry_condition: Optional[
109
+ Callable[[Task[P, Coroutine[Any, Any, R]], TaskRun, State], bool]
110
+ ] = self.task.retry_condition_fn
111
+ if not self.task_run:
112
+ raise ValueError("Task run is not set")
113
+ return not retry_condition or retry_condition(
114
+ self.task, self.task_run, self.state
115
+ )
116
+
117
+ async def _run_hooks(self, state: State) -> None:
118
+ """Run the on_failure and on_completion hooks for a task, making sure to
119
+ catch and log any errors that occur.
120
+ """
121
+ task = self.task
122
+ task_run = self.task_run
123
+
124
+ if not task_run:
125
+ raise ValueError("Task run is not set")
126
+
127
+ hooks = None
128
+ if state.is_failed() and task.on_failure:
129
+ hooks = task.on_failure
130
+ elif state.is_completed() and task.on_completion:
131
+ hooks = task.on_completion
132
+
133
+ if hooks:
134
+ for hook in hooks:
135
+ hook_name = _get_hook_name(hook)
136
+ try:
137
+ self.logger.info(
138
+ f"Running hook {hook_name!r} in response to entering state"
139
+ f" {state.name!r}"
140
+ )
141
+ if is_async_fn(hook):
142
+ await hook(task, task_run, state)
143
+ else:
144
+ hook(task, task_run, state)
145
+ except Exception:
146
+ self.logger.error(
147
+ f"An error was encountered while running hook {hook_name!r}",
148
+ exc_info=True,
149
+ )
150
+ else:
151
+ self.logger.info(
152
+ f"Hook {hook_name!r} finished running successfully"
153
+ )
154
+
155
+ def _compute_state_details(
156
+ self, include_cache_expiration: bool = False
157
+ ) -> StateDetails:
158
+ ## setup cache metadata
159
+ task_run_context = TaskRunContext.get()
160
+ cache_key = (
161
+ self.task.cache_key_fn(
162
+ task_run_context,
163
+ self.parameters or {},
164
+ )
165
+ if self.task.cache_key_fn
166
+ else None
167
+ )
168
+ # Ignore the cached results for a cache key, default = false
169
+ # Setting on task level overrules the Prefect setting (env var)
170
+ refresh_cache = (
171
+ self.task.refresh_cache
172
+ if self.task.refresh_cache is not None
173
+ else PREFECT_TASKS_REFRESH_CACHE.value()
174
+ )
175
+
176
+ if include_cache_expiration:
177
+ cache_expiration = (
178
+ (pendulum.now("utc") + self.task.cache_expiration)
179
+ if self.task.cache_expiration
180
+ else None
181
+ )
182
+ else:
183
+ cache_expiration = None
184
+ return StateDetails(
185
+ cache_key=cache_key,
186
+ refresh_cache=refresh_cache,
187
+ cache_expiration=cache_expiration,
188
+ )
189
+
190
+ async def begin_run(self):
191
+ state_details = self._compute_state_details()
192
+ new_state = Running(state_details=state_details)
193
+ state = await self.set_state(new_state)
194
+ while state.is_pending():
195
+ await asyncio.sleep(1)
196
+ state = await self.set_state(new_state)
197
+
198
+ async def set_state(self, state: State, force: bool = False) -> State:
199
+ if not self.task_run:
200
+ raise ValueError("Task run is not set")
201
+ new_state = await propose_state(
202
+ self.client, state, task_run_id=self.task_run.id, force=force
203
+ ) # type: ignore
204
+
205
+ # currently this is a hack to keep a reference to the state object
206
+ # that has an in-memory result attached to it; using the API state
207
+ # could result in losing that reference
208
+ self.task_run.state = new_state
209
+ if new_state.is_final():
210
+ await self._run_hooks(new_state)
211
+ return new_state
212
+
213
+ async def result(self, raise_on_failure: bool = True) -> "Union[R, State, None]":
214
+ _result = self.state.result(raise_on_failure=raise_on_failure, fetch=True)
215
+ # state.result is a `sync_compatible` function that may or may not return an awaitable
216
+ # depending on whether the parent frame is sync or not
217
+ if inspect.isawaitable(_result):
218
+ _result = await _result
219
+ return _result
220
+
221
+ async def handle_success(self, result: R) -> R:
222
+ result_factory = getattr(TaskRunContext.get(), "result_factory", None)
223
+ if result_factory is None:
224
+ raise ValueError("Result factory is not set")
225
+ terminal_state = await return_value_to_state(
226
+ await resolve_futures_to_states(result),
227
+ result_factory=result_factory,
228
+ )
229
+ terminal_state.state_details = self._compute_state_details(
230
+ include_cache_expiration=True
231
+ )
232
+ await self.set_state(terminal_state)
233
+ return result
234
+
235
+ async def handle_retry(self, exc: Exception) -> bool:
236
+ """
237
+ If the task has retries left, and the retry condition is met, set the task to retrying.
238
+ - If the task has no retries left, or the retry condition is not met, return False.
239
+ - If the task has retries left, and the retry condition is met, return True.
240
+ """
241
+ if self.retries < self.task.retries and self.can_retry:
242
+ await self.set_state(Retrying(), force=True)
243
+ self.retries = self.retries + 1
244
+ return True
245
+ return False
246
+
247
+ async def handle_exception(self, exc: Exception) -> None:
248
+ # If the task fails, and we have retries left, set the task to retrying.
249
+ if not await self.handle_retry(exc):
250
+ # If the task has no retries left, or the retry condition is not met, set the task to failed.
251
+ context = TaskRunContext.get()
252
+ state = await exception_to_failed_state(
253
+ exc,
254
+ message="Task run encountered an exception",
255
+ result_factory=getattr(context, "result_factory", None),
256
+ )
257
+ await self.set_state(state)
258
+
259
+ async def handle_crash(self, exc: BaseException) -> None:
260
+ state = await exception_to_crashed_state(exc)
261
+ self.logger.error(f"Crash detected! {state.message}")
262
+ self.logger.debug("Crash details:", exc_info=exc)
263
+ await self.set_state(state, force=True)
264
+
265
+ async def create_task_run(self, client: PrefectClient) -> TaskRun:
266
+ flow_run_ctx = FlowRunContext.get()
267
+ parameters = self.parameters or {}
268
+ try:
269
+ task_run_name = _resolve_custom_task_run_name(self.task, parameters)
270
+ except TypeError:
271
+ task_run_name = None
272
+
273
+ # prep input tracking
274
+ task_inputs = {
275
+ k: await collect_task_run_inputs(v) for k, v in parameters.items()
276
+ }
277
+
278
+ # anticipate nested runs
279
+ task_run_ctx = TaskRunContext.get()
280
+ if task_run_ctx:
281
+ task_inputs["wait_for"] = [TaskRunResult(id=task_run_ctx.task_run.id)] # type: ignore
282
+
283
+ # TODO: implement wait_for
284
+ # if wait_for:
285
+ # task_inputs["wait_for"] = await collect_task_run_inputs(wait_for)
286
+
287
+ if flow_run_ctx:
288
+ dynamic_key = _dynamic_key_for_task_run(
289
+ context=flow_run_ctx, task=self.task
290
+ )
291
+ else:
292
+ dynamic_key = uuid4().hex
293
+ task_run = await client.create_task_run(
294
+ task=self.task, # type: ignore
295
+ name=task_run_name,
296
+ flow_run_id=(
297
+ getattr(flow_run_ctx.flow_run, "id", None)
298
+ if flow_run_ctx and flow_run_ctx.flow_run
299
+ else None
300
+ ),
301
+ dynamic_key=str(dynamic_key),
302
+ state=Pending(),
303
+ task_inputs=task_inputs, # type: ignore
304
+ )
305
+ return task_run
306
+
307
+ @asynccontextmanager
308
+ async def enter_run_context(self, client: Optional[PrefectClient] = None):
309
+ if client is None:
310
+ client = self.client
311
+
312
+ if not self.task_run:
313
+ raise ValueError("Task run is not set")
314
+
315
+ self.task_run = await client.read_task_run(self.task_run.id)
316
+
317
+ with TaskRunContext(
318
+ task=self.task,
319
+ log_prints=self.task.log_prints or False,
320
+ task_run=self.task_run,
321
+ parameters=self.parameters,
322
+ result_factory=await ResultFactory.from_autonomous_task(self.task), # type: ignore
323
+ client=client,
324
+ ):
325
+ self.logger = task_run_logger(task_run=self.task_run, task=self.task) # type: ignore
326
+ yield
327
+
328
+ @contextmanager
329
+ def enter_run_context_sync(self, client: Optional[PrefectClient] = None):
330
+ if client is None:
331
+ client = self.client
332
+ if not self.task_run:
333
+ raise ValueError("Task run is not set")
334
+
335
+ self.task_run = run_sync(client.read_task_run(self.task_run.id))
336
+
337
+ with TaskRunContext(
338
+ task=self.task,
339
+ log_prints=self.task.log_prints or False,
340
+ task_run=self.task_run,
341
+ parameters=self.parameters,
342
+ result_factory=run_sync(ResultFactory.from_autonomous_task(self.task)), # type: ignore
343
+ client=client,
344
+ ):
345
+ self.logger = task_run_logger(task_run=self.task_run, task=self.task) # type: ignore
346
+ yield
347
+
348
+ @asynccontextmanager
349
+ async def start(self):
350
+ """
351
+ Enters a client context and creates a task run if needed.
352
+ """
353
+ async with get_client() as client:
354
+ self._client = client
355
+ self._is_started = True
356
+ try:
357
+ if not self.task_run:
358
+ self.task_run = await self.create_task_run(client)
359
+ yield self
360
+ except Exception:
361
+ # regular exceptions are caught and re-raised to the user
362
+ raise
363
+ except BaseException as exc:
364
+ # BaseExceptions are caught and handled as crashes
365
+ await self.handle_crash(exc)
366
+ raise
367
+ finally:
368
+ self._is_started = False
369
+ self._client = None
370
+
371
+ @contextmanager
372
+ def start_sync(self):
373
+ """
374
+ Enters a client context and creates a task run if needed.
375
+ """
376
+ client = get_client()
377
+ run_sync(client.__aenter__())
378
+ self._client = client
379
+ self._is_started = True
380
+ try:
381
+ if not self.task_run:
382
+ self.task_run = run_sync(self.create_task_run(client))
383
+ yield self
384
+ except Exception:
385
+ # regular exceptions are caught and re-raised to the user
386
+ raise
387
+ except BaseException as exc:
388
+ # BaseExceptions are caught and handled as crashes
389
+ run_sync(self.handle_crash(exc))
390
+ raise
391
+ finally:
392
+ # quickly close client
393
+ run_sync(client.__aexit__(None, None, None))
394
+ self._is_started = False
395
+ self._client = None
396
+
397
+ async def get_client(self):
398
+ if not self._is_started:
399
+ raise RuntimeError("Engine has not started.")
400
+ else:
401
+ return self._client
402
+
403
+ def is_running(self) -> bool:
404
+ if getattr(self, "task_run", None) is None:
405
+ return False
406
+ return getattr(self, "task_run").state.is_running()
407
+
408
+ def is_pending(self) -> bool:
409
+ if getattr(self, "task_run", None) is None:
410
+ return False # TODO: handle this differently?
411
+ return getattr(self, "task_run").state.is_pending()
412
+
413
+
414
+ async def run_task(
415
+ task: Task[P, Coroutine[Any, Any, R]],
416
+ task_run: Optional[TaskRun] = None,
417
+ parameters: Optional[Dict[str, Any]] = None,
418
+ wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
419
+ return_type: Literal["state", "result"] = "result",
420
+ ) -> Union[R, State, None]:
421
+ """
422
+ Runs a task against the API.
423
+
424
+ We will most likely want to use this logic as a wrapper and return a coroutine for type inference.
425
+ """
426
+ engine = TaskRunEngine[P, R](task=task, parameters=parameters, task_run=task_run)
427
+
428
+ # This is a context manager that keeps track of the run of the task run.
429
+ async with engine.start() as run:
430
+ await run.begin_run()
431
+
432
+ while run.is_running():
433
+ async with run.enter_run_context():
434
+ try:
435
+ # This is where the task is actually run.
436
+ async with timeout(seconds=run.task.timeout_seconds):
437
+ call_args, call_kwargs = parameters_to_args_kwargs(
438
+ task.fn, run.parameters or {}
439
+ )
440
+ result = cast(R, await task.fn(*call_args, **call_kwargs)) # type: ignore
441
+
442
+ # If the task run is successful, finalize it.
443
+ await run.handle_success(result)
444
+ if return_type == "result":
445
+ return result
446
+
447
+ except Exception as exc:
448
+ await run.handle_exception(exc)
449
+
450
+ if return_type == "state":
451
+ return run.state
452
+ return await run.result()
453
+
454
+
455
+ def run_task_sync(
456
+ task: Task[P, R],
457
+ task_run: Optional[TaskRun] = None,
458
+ parameters: Optional[Dict[str, Any]] = None,
459
+ wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
460
+ return_type: Literal["state", "result"] = "result",
461
+ ) -> Union[R, State, None]:
462
+ engine = TaskRunEngine[P, R](task=task, parameters=parameters, task_run=task_run)
463
+
464
+ # This is a context manager that keeps track of the run of the task run.
465
+ with engine.start_sync() as run:
466
+ run_sync(run.begin_run())
467
+
468
+ while run.is_running():
469
+ with run.enter_run_context_sync():
470
+ try:
471
+ # This is where the task is actually run.
472
+ with timeout_sync(seconds=run.task.timeout_seconds):
473
+ call_args, call_kwargs = parameters_to_args_kwargs(
474
+ task.fn, run.parameters or {}
475
+ )
476
+ result = cast(R, task.fn(*call_args, **call_kwargs)) # type: ignore
477
+
478
+ # If the task run is successful, finalize it.
479
+ run_sync(run.handle_success(result))
480
+ if return_type == "result":
481
+ return result
482
+
483
+ except Exception as exc:
484
+ run_sync(run.handle_exception(exc))
485
+
486
+ if return_type == "state":
487
+ return run.state
488
+ return run_sync(run.result())
prefect/results.py CHANGED
@@ -16,7 +16,7 @@ from typing import (
16
16
  )
17
17
  from uuid import UUID
18
18
 
19
- from typing_extensions import Self
19
+ from typing_extensions import ParamSpec, Self
20
20
 
21
21
  import prefect
22
22
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
@@ -63,6 +63,7 @@ def DEFAULT_STORAGE_KEY_FN():
63
63
 
64
64
 
65
65
  logger = get_logger("results")
66
+ P = ParamSpec("P")
66
67
  R = TypeVar("R")
67
68
 
68
69
 
@@ -286,7 +287,7 @@ class ResultFactory(pydantic.BaseModel):
286
287
  @classmethod
287
288
  @inject_client
288
289
  async def from_autonomous_task(
289
- cls: Type[Self], task: "Task", client: "PrefectClient" = None
290
+ cls: Type[Self], task: "Task[P, R]", client: "PrefectClient" = None
290
291
  ) -> Self:
291
292
  """
292
293
  Create a new result factory for an autonomous task.
prefect/runner/runner.py CHANGED
@@ -29,6 +29,7 @@ Example:
29
29
  ```
30
30
 
31
31
  """
32
+
32
33
  import asyncio
33
34
  import datetime
34
35
  import inspect
@@ -80,7 +81,7 @@ from prefect.deployments.runner import (
80
81
  )
81
82
  from prefect.deployments.schedules import FlexibleScheduleList
82
83
  from prefect.engine import propose_state
83
- from prefect.events import DeploymentTriggerTypes
84
+ from prefect.events import DeploymentTriggerTypes, TriggerTypes
84
85
  from prefect.exceptions import (
85
86
  Abort,
86
87
  )
@@ -232,7 +233,7 @@ class Runner:
232
233
  schedule: Optional[SCHEDULE_TYPES] = None,
233
234
  is_schedule_active: Optional[bool] = None,
234
235
  parameters: Optional[dict] = None,
235
- triggers: Optional[List[DeploymentTriggerTypes]] = None,
236
+ triggers: Optional[List[Union[DeploymentTriggerTypes, TriggerTypes]]] = None,
236
237
  description: Optional[str] = None,
237
238
  tags: Optional[List[str]] = None,
238
239
  version: Optional[str] = None,
@@ -64,7 +64,7 @@
64
64
  }
65
65
  },
66
66
  "description": "Execute flow runs as subprocesses on a worker. Works well for local execution when first getting started.",
67
- "display_name": "Local Subprocess",
67
+ "display_name": "Process",
68
68
  "documentation_url": "https://docs.prefect.io/latest/api-ref/prefect/workers/process/",
69
69
  "install_command": "pip install prefect",
70
70
  "is_beta": false,
@@ -111,10 +111,12 @@
111
111
  "taskRoleArn": "{{ task_role_arn }}"
112
112
  },
113
113
  "tags": "{{ labels }}",
114
- "taskDefinition": "{{ task_definition_arn }}"
114
+ "taskDefinition": "{{ task_definition_arn }}",
115
+ "capacityProviderStrategy": "{{ capacity_provider_strategy }}"
115
116
  },
116
117
  "configure_cloudwatch_logs": "{{ configure_cloudwatch_logs }}",
117
118
  "cloudwatch_logs_options": "{{ cloudwatch_logs_options }}",
119
+ "cloudwatch_logs_prefix": "{{ cloudwatch_logs_prefix }}",
118
120
  "network_configuration": "{{ network_configuration }}",
119
121
  "stream_output": "{{ stream_output }}",
120
122
  "task_start_timeout_seconds": "{{ task_start_timeout_seconds }}",
@@ -191,6 +193,14 @@
191
193
  ],
192
194
  "type": "string"
193
195
  },
196
+ "capacity_provider_strategy": {
197
+ "title": "Capacity Provider Strategy",
198
+ "description": "The capacity provider strategy to use when running the task. If a capacity provider strategy is specified, the selected launch type will be ignored.",
199
+ "type": "array",
200
+ "items": {
201
+ "$ref": "#/definitions/CapacityProvider"
202
+ }
203
+ },
194
204
  "image": {
195
205
  "title": "Image",
196
206
  "description": "The image to use for the Prefect container in the task. If this value is not null, it will override the value in the task definition. This value defaults to a Prefect base image matching your local versions.",
@@ -239,6 +249,11 @@
239
249
  "type": "string"
240
250
  }
241
251
  },
252
+ "cloudwatch_logs_prefix": {
253
+ "title": "Cloudwatch Logs Prefix",
254
+ "description": "When `configure_cloudwatch_logs` is enabled, this setting may be used to set a prefix for the log group. If not provided, the default prefix will be `prefect-logs_<work_pool_name>_<deployment_id>`. If `awslogs-stream-prefix` is present in `Cloudwatch logs options` this setting will be ignored.",
255
+ "type": "string"
256
+ },
242
257
  "network_configuration": {
243
258
  "title": "Network Configuration",
244
259
  "description": "When `network_configuration` is supplied it will override ECS Worker'sawsvpcConfiguration that defined in the ECS task executing your workload. See the [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-awsvpcconfiguration.html) for available options.",
@@ -370,6 +385,30 @@
370
385
  "aws_secret_access_key"
371
386
  ],
372
387
  "block_schema_references": {}
388
+ },
389
+ "CapacityProvider": {
390
+ "title": "CapacityProvider",
391
+ "description": "The capacity provider strategy to use when running the task.",
392
+ "type": "object",
393
+ "properties": {
394
+ "capacityProvider": {
395
+ "title": "Capacityprovider",
396
+ "type": "string"
397
+ },
398
+ "weight": {
399
+ "title": "Weight",
400
+ "type": "integer"
401
+ },
402
+ "base": {
403
+ "title": "Base",
404
+ "type": "integer"
405
+ }
406
+ },
407
+ "required": [
408
+ "capacityProvider",
409
+ "weight",
410
+ "base"
411
+ ]
373
412
  }
374
413
  }
375
414
  }
@@ -1100,7 +1139,9 @@
1100
1139
  "serviceAccount": "{{ service_account_name }}",
1101
1140
  "maxRetries": "{{ max_retries }}",
1102
1141
  "timeout": "{{ timeout }}",
1103
- "vpcAccess": "{{ vpc_connector_name }}",
1142
+ "vpcAccess": {
1143
+ "connector": "{{ vpc_connector_name }}"
1144
+ },
1104
1145
  "containers": [
1105
1146
  {
1106
1147
  "env": [],
@@ -1647,4 +1688,4 @@
1647
1688
  "type": "kubernetes"
1648
1689
  }
1649
1690
  }
1650
- }
1691
+ }