|
1 | | -import re |
| 1 | +import dataclasses |
2 | 2 | import os |
3 | 3 | import typing |
4 | | -import dataclasses |
5 | 4 | from collections import OrderedDict |
6 | 5 |
|
7 | 6 | import mock |
8 | 7 | import pytest |
9 | 8 |
|
10 | 9 | import flytekit.configuration |
11 | | -from flytekit import ContainerTask, ImageSpec, kwtypes |
| 10 | +from flytekit import ContainerTask, ImageSpec, LaunchPlan, kwtypes |
12 | 11 | from flytekit.configuration import Image, ImageConfig, SerializationSettings |
| 12 | +from flytekit.core.array_node_map_task import map_task |
13 | 13 | from flytekit.core.condition import conditional |
| 14 | +from flytekit.core.dynamic_workflow_task import dynamic |
| 15 | +from flytekit.core.options import Options |
14 | 16 | from flytekit.core.python_auto_container import get_registerable_container_image |
15 | 17 | from flytekit.core.resources import Resources, ResourceSpec |
16 | | -from flytekit.core.dynamic_workflow_task import dynamic |
17 | | -from flytekit.core.array_node_map_task import map_task |
18 | 18 | from flytekit.core.task import eager, task |
19 | 19 | from flytekit.core.workflow import workflow |
20 | 20 | from flytekit.exceptions.user import FlyteAssertion, FlyteMissingTypeException |
21 | 21 | from flytekit.image_spec.image_spec import ImageBuildEngine |
| 22 | +from flytekit.models import task as task_models |
22 | 23 | from flytekit.models.admin.workflow import WorkflowSpec |
23 | | -from flytekit.models.annotation import TypeAnnotation |
24 | 24 | from flytekit.models.literals import ( |
25 | 25 | BindingData, |
26 | 26 | BindingDataCollection, |
|
31 | 31 | Union, |
32 | 32 | Void, |
33 | 33 | ) |
| 34 | +from flytekit.models.security import Identity, Secret, SecurityContext |
34 | 35 | from flytekit.models.types import LiteralType, SimpleType, TypeStructure, UnionType |
35 | | -from flytekit.models import task as task_models |
36 | 36 | from flytekit.tools.translator import get_serializable |
37 | 37 | from flytekit.types.error.error import FlyteError |
38 | 38 |
|
@@ -1272,3 +1272,31 @@ async def t1_eager(a: int) -> int: |
1272 | 1272 | ], |
1273 | 1273 | limits=[] |
1274 | 1274 | ) |
| 1275 | + |
| 1276 | + |
| 1277 | +def test_launch_plan_security_context_merge(): |
| 1278 | + """Registration options override the authored launch plan per field. |
| 1279 | +
|
| 1280 | + Registering with only a service account must not drop secrets/tokens that were |
| 1281 | + authored on the launch plan. |
| 1282 | + """ |
| 1283 | + |
| 1284 | + @task |
| 1285 | + def t_sc() -> int: |
| 1286 | + return 1 |
| 1287 | + |
| 1288 | + @workflow |
| 1289 | + def wf_sc() -> int: |
| 1290 | + return t_sc() |
| 1291 | + |
| 1292 | + lp = LaunchPlan.get_or_create( |
| 1293 | + workflow=wf_sc, |
| 1294 | + name="lp_sc_merge", |
| 1295 | + security_context=SecurityContext(secrets=[Secret(group="g", key="k")]), |
| 1296 | + ) |
| 1297 | + opts = Options(security_context=SecurityContext(run_as=Identity(k8s_service_account="my-sa"))) |
| 1298 | + |
| 1299 | + serialized = get_serializable(OrderedDict(), serialization_settings, lp, options=opts) |
| 1300 | + sc = serialized.spec.security_context |
| 1301 | + assert sc.run_as.k8s_service_account == "my-sa" |
| 1302 | + assert [(s.group, s.key) for s in sc.secrets] == [("g", "k")] |
0 commit comments