1+ """State of release - central data model for the release process.
2+
3+ Used by the behavior tree to keep track of the release progress, plays
4+ blackboard role for behavior tree.
5+
6+ Ephemeral and persistent fields
7+ --------------------------------
8+
9+ The main purpose of ephemeral fields is to prevent retry loops and to allow
10+ extensive status reporting.
11+
12+ Each workflow step has a pair of fields indicating the step status:
13+ One ephemeral field is set when the step is attempted. It may have four states:
14+ - `None` (default): Step has not been attempted
15+ - `common.Status.RUNNING`: Step is currently running
16+ - `common.Status.FAILURE`: Step has been attempted and failed
17+ - `common.Status.SUCCESS`: Step has been attempted and succeeded
18+
19+ Ephemeral fields are reset on each run. Their values are persisted but only until
20+ next run is started.
21+ So they indicate either current (if run is in progress) or last run state.
22+
23+ The other field indicates the step result, it may either have some value or be empty.
24+ This field is persisted across runs.
25+
26+ For example for trigger step we have `trigger_workflow` ephemeral
27+ and `triggered_at` result fields.
28+
29+ Optional message field may be used to provide additional information about the step.
30+ For example wait_for_completion_message may contain information about timeout.
31+
32+ Given combination of ephemeral and result fields we can determine step status.
33+ Each step may be in one of the following states:
34+ Not started
35+ Failed
36+ Succeeded or OK
37+ Incorrect (this shouln't happen)
38+
39+ The following decision table show how step status is determined for trigger step.
40+ In general this is applicable to all steps.
41+
42+ tigger_workflow -> | None (default) | Running | Failure | Success |
43+ triggered_at: | | | | |
44+ None | Not started | In progress | Failed | Incorrect |
45+ Has value | OK | Incorrect | Incorrect | OK |
46+
47+ The result field (triggered_at in this case) should not be set while step is
48+ running, if step was not started or if it's failed.
49+ And it should be set if trigger_workflow is successful.
50+ It may be set if trigger_workflow is None, which is the case when release
51+ process was restarted and all ephemeral fields are reset, but the particular
52+ step was successful in previous run.
53+
54+ Correct values are not eforced it's up to the implementation to correctly
55+ set the fields.
56+ """
57+
158import json
259import logging
360from datetime import datetime
865from py_trees .common import Status
966from pydantic import BaseModel , Field
1067
11- from redis_release .models import (
68+ from ..config import Config , PackageConfig
69+ from ..models import (
1270 HomebrewChannel ,
1371 PackageType ,
72+ RedisModule ,
1473 ReleaseType ,
1574 SnapRiskLevel ,
1675 WorkflowConclusion ,
1776 WorkflowStatus ,
1877 WorkflowType ,
1978)
2079
21- from ..config import Config , PackageConfig
22-
2380logger = logging .getLogger (__name__ )
2481
25- SUPPORTED_STATE_VERSION = 3
82+ SUPPORTED_STATE_VERSION = 4
2683
2784
2885class WorkflowEphemeral (BaseModel ):
29- """Ephemeral workflow state. Reset on each run.
30-
31- The main purpose of ephemeral fields is to prevent retry loops and to allow extensive status reporting.
32-
33- Each workflow step has a pair of fields indicating the step status:
34- One ephemeral field is set when the step is attempted. It may have four states:
35- - `None` (default): Step has not been attempted
36- - `common.Status.RUNNING`: Step is currently running
37- - `common.Status.FAILURE`: Step has been attempted and failed
38- - `common.Status.SUCCESS`: Step has been attempted and succeeded
39-
40- Ephemeral fields are reset on each run. Their values are persisted but only until
41- next run is started.
42- So they indicate either current (if run is in progress) or last run state.
43-
44- The other field indicates the step result, it may either have some value or be empty.
45-
46- For example for trigger step we have `trigger_workflow` ephemeral
47- and `triggered_at` result fields.
48-
49- Optional message field may be used to provide additional information about the step.
50- For example wait_for_completion_message may contain information about timeout.
51-
52- Given combination of ephemeral and result fields we can determine step status.
53- Each step may be in one of the following states:
54- Not started
55- Failed
56- Succeeded or OK
57- Incorrect (this shouln't happen)
58-
59- The following decision table show how step status is determined for trigger step.
60- In general this is applicable to all steps.
61-
62- tigger_workflow -> | None (default) | Running | Failure | Success |
63- triggered_at: | | | | |
64- None | Not started | In progress | Failed | Incorrect |
65- Has value | OK | Incorrect | Incorrect | OK |
66-
67- The result field (triggered_at in this case) should not be set while step is
68- running, if step was not started or if it's failed.
69- And it should be set if trigger_workflow is successful.
70- It may be set if trigger_workflow is None, which is the case when release
71- process was restarted and all ephemeral fields are reset, but the particular
72- step was successful in previous run.
73-
74- Correct values are not eforced it's up to the implementation to correctly
75- set the fields.
76- """
86+ """Ephemeral workflow state. Reset on each run."""
7787
7888 identify_workflow : Optional [common .Status ] = None
7989 trigger_workflow : Optional [common .Status ] = None
@@ -138,6 +148,10 @@ class SnapMetaEphemeral(PackageMetaEphemeral):
138148 pass
139149
140150
151+ class DockerMetaEphemeral (PackageMetaEphemeral ):
152+ pass
153+
154+
141155class PackageMeta (BaseModel ):
142156 """Metadata for a package (base/generic type)."""
143157
@@ -176,6 +190,14 @@ class SnapMeta(PackageMeta):
176190 ephemeral : SnapMetaEphemeral = Field (default_factory = SnapMetaEphemeral ) # type: ignore[assignment]
177191
178192
193+ class DockerMeta (PackageMeta ):
194+ """Metadata for Docker package."""
195+
196+ serialization_hint : Literal ["docker" ] = "docker" # type: ignore[assignment]
197+ module_versions : Optional [Dict [RedisModule , str ]] = None
198+ ephemeral : DockerMetaEphemeral = Field (default_factory = DockerMetaEphemeral ) # type: ignore[assignment]
199+
200+
179201class Package (BaseModel ):
180202 """State for a package in the release.
181203
@@ -186,7 +208,7 @@ class Package(BaseModel):
186208 - serialization_hint="snap" -> SnapMeta
187209 """
188210
189- meta : Union [HomebrewMeta , SnapMeta , PackageMeta ] = Field (
211+ meta : Union [HomebrewMeta , SnapMeta , PackageMeta , DockerMeta ] = Field (
190212 default_factory = PackageMeta , discriminator = "serialization_hint"
191213 )
192214 build : Workflow = Field (default_factory = Workflow )
@@ -227,7 +249,7 @@ def _create_package_meta_from_config(
227249 package_config: Package configuration
228250
229251 Returns:
230- PackageMeta subclass instance (HomebrewMeta, SnapMeta, or PackageMeta)
252+ PackageMeta subclass instance (HomebrewMeta, SnapMeta, DockerMeta or PackageMeta)
231253
232254 Raises:
233255 ValueError: If package_type is None
@@ -246,6 +268,13 @@ def _create_package_meta_from_config(
246268 package_type = package_config .package_type ,
247269 publish_internal_release = package_config .publish_internal_release ,
248270 )
271+ elif package_config .package_type == PackageType .DOCKER :
272+ return DockerMeta (
273+ repo = package_config .repo ,
274+ ref = package_config .ref ,
275+ package_type = package_config .package_type ,
276+ publish_internal_release = package_config .publish_internal_release ,
277+ )
249278 elif package_config .package_type is not None :
250279 return PackageMeta (
251280 repo = package_config .repo ,
0 commit comments