Skip to content

Commit 7aac99d

Browse files
authored
Merge pull request #246 from yuki462-b/session-graph-status
feat: add session dependency status.
2 parents 1288a52 + 18a7b16 commit 7aac99d

File tree

6 files changed

+121
-4
lines changed

6 files changed

+121
-4
lines changed

oper8/config/config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ manage_status: true
3333
# Whether or not to apply the internal name annotation to output resources
3434
internal_name_annotation: true
3535

36+
# Whether to include the session dependency graph in the CR status field.
37+
# When enabled, the dependency graph will be added to status.componentStatus.dependencyGraph
38+
include_dependency_graph_in_status: false
39+
3640
# Specify a single controller to watch, else an empty string leads to watches
3741
# for all controllers
3842
controller_name: ""

oper8/config/config_validation.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ working_dir:
3030
standalone:
3131
type: bool
3232

33+
include_dependency_graph_in_status:
34+
type: bool
35+
3336
strict_versioning:
3437
type: bool
3538

oper8/reconcile.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,13 @@ def _update_reconcile_completion_status(
10851085
completion_state: CompletionState
10861086
The result of the rollout
10871087
"""
1088-
status_update = {"component_state": completion_state}
1088+
status_update = {
1089+
"component_state": completion_state,
1090+
}
1091+
1092+
# Include dependency graph if configured
1093+
if config.library_config.include_dependency_graph_in_status:
1094+
status_update["dependency_graph"] = str(session.graph)
10891095

10901096
# If everything completed and verified, set ready and updating to STABLE
10911097
# and set the status's reconciled version to the desired version

oper8/status.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
COMPONENT_STATUS_FAILED_NODES = "failedComponents"
5858
COMPONENT_STATUS_DEPLOYED_COUNT = "deployed"
5959
COMPONENT_STATUS_VERIFIED_COUNT = "verified"
60+
COMPONENT_STATUS_DEPENDENCY_GRAPH = "dependencyGraph"
6061

6162
# The fields in status that hold the version information
6263
# NOTE: These intentionally match IBM CloudPak naming conventions
@@ -134,6 +135,7 @@ def make_application_status( # pylint: disable=too-many-arguments,too-many-loca
134135
supported_versions: Optional[List[str]] = None,
135136
operator_version: Optional[str] = None,
136137
kind: Optional[str] = None,
138+
dependency_graph: Optional[str] = None,
137139
) -> dict:
138140
"""Create a full status object for an application
139141
@@ -163,6 +165,8 @@ def make_application_status( # pylint: disable=too-many-arguments,too-many-loca
163165
The kind of reconciling CR. If specified, this function adds
164166
service status field which is compliant with IBM Cloud Pak
165167
requirements.
168+
dependency_graph: Optional[str]
169+
String representation of the session dependency graph
166170
167171
Returns:
168172
current_status: dict
@@ -184,7 +188,9 @@ def make_application_status( # pylint: disable=too-many-arguments,too-many-loca
184188
# track which components have deployed and verified
185189
if component_state is not None:
186190
log.debug2("Adding component state to status")
187-
status[COMPONENT_STATUS] = _make_component_state(component_state)
191+
status[COMPONENT_STATUS] = _make_component_state(
192+
component_state, dependency_graph
193+
)
188194
log.debug3(status[COMPONENT_STATUS])
189195

190196
# Create the versions section
@@ -491,7 +497,9 @@ def _make_updating_condition(
491497
)
492498

493499

494-
def _make_component_state(component_state: CompletionState) -> dict:
500+
def _make_component_state(
501+
component_state: CompletionState, dependency_graph: Optional[str] = None
502+
) -> dict:
495503
"""Make the component state object"""
496504
all_nodes = sorted([comp.get_name() for comp in component_state.all_nodes])
497505
deployed_nodes = sorted(
@@ -509,7 +517,7 @@ def _make_component_state(component_state: CompletionState) -> dict:
509517
[comp.get_name() for comp in component_state.unverified_nodes]
510518
)
511519
failed_nodes = sorted([comp.get_name() for comp in component_state.failed_nodes])
512-
return {
520+
result = {
513521
COMPONENT_STATUS_ALL_NODES: all_nodes,
514522
COMPONENT_STATUS_DEPLOYED_NODES: deployed_nodes,
515523
COMPONENT_STATUS_UNVERIFIED_NODES: unverified_nodes,
@@ -518,6 +526,12 @@ def _make_component_state(component_state: CompletionState) -> dict:
518526
COMPONENT_STATUS_VERIFIED_COUNT: f"{len(verified_nodes)}/{len(all_nodes)}",
519527
}
520528

529+
# Add dependency graph if provided
530+
if dependency_graph is not None:
531+
result[COMPONENT_STATUS_DEPENDENCY_GRAPH] = dependency_graph
532+
533+
return result
534+
521535

522536
def _make_available_version(version: str) -> dict:
523537
"""Make an object for the available version list following the IBM CloudPak

tests/test_reconcile.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,63 @@ def test_update_reconcile_completion_status(
13011301
check_status(dm, cr, ready_reason, updating_reason, completion_state)
13021302

13031303

1304+
@pytest.mark.parametrize(
1305+
["include_graph", "should_include_graph"],
1306+
[
1307+
[True, True],
1308+
[False, False],
1309+
],
1310+
)
1311+
def test_update_reconcile_completion_status_dependency_graph_by_config(
1312+
include_graph, should_include_graph
1313+
):
1314+
"""Test that dependency_graph is included in status based on config setting"""
1315+
# Local
1316+
from oper8.dag import Graph
1317+
1318+
# Setup CR
1319+
cr = setup_cr()
1320+
1321+
# Create a graph with nodes
1322+
graph = Graph()
1323+
node_a = Node("A")
1324+
node_b = Node("B")
1325+
graph.add_node(node_a)
1326+
graph.add_node(node_b)
1327+
graph.add_node_dependency(node_a, node_b)
1328+
1329+
completion_state = CompletionState(verified_nodes=[node_a, node_b])
1330+
1331+
dm = MockDeployManager(resources=[cr])
1332+
rm = ReconcileManager(deploy_manager=dm)
1333+
session = setup_session(full_cr=cr, deploy_manager=dm)
1334+
# Access the private __graph attribute
1335+
session._Session__graph = graph
1336+
1337+
# Set the config value
1338+
with library_config(include_dependency_graph_in_status=include_graph):
1339+
rm._update_reconcile_completion_status(session, completion_state)
1340+
1341+
# Check the status
1342+
obj = dm.get_obj(
1343+
kind=cr.kind,
1344+
name=cr.metadata.name,
1345+
namespace=cr.metadata.namespace,
1346+
api_version=cr.apiVersion,
1347+
)
1348+
assert obj is not None
1349+
assert obj.get("status")
1350+
1351+
component_status = obj["status"].get(status.COMPONENT_STATUS)
1352+
assert component_status is not None
1353+
1354+
if should_include_graph:
1355+
assert status.COMPONENT_STATUS_DEPENDENCY_GRAPH in component_status
1356+
assert component_status[status.COMPONENT_STATUS_DEPENDENCY_GRAPH] == str(graph)
1357+
else:
1358+
assert status.COMPONENT_STATUS_DEPENDENCY_GRAPH not in component_status
1359+
1360+
13041361
###############
13051362
## _update_error_status ##
13061363
###############

tests/test_status.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,39 @@ def test_make_application_status_component_state():
141141
}
142142

143143

144+
def test_make_application_status_component_state_with_dependency_graph():
145+
"""Make sure that dependency_graph is included in component status when provided"""
146+
# Local
147+
from oper8.dag import Graph
148+
149+
graph = Graph()
150+
node_a = Node("A")
151+
node_b = Node("B")
152+
graph.add_node(node_a)
153+
graph.add_node(node_b)
154+
graph.add_node_dependency(node_a, node_b)
155+
graph_str = str(graph)
156+
157+
res = status.make_application_status(
158+
component_state=CompletionState(
159+
verified_nodes=[node_a, node_b],
160+
),
161+
dependency_graph=graph_str,
162+
)
163+
assert res == {
164+
"conditions": [],
165+
status.COMPONENT_STATUS: {
166+
status.COMPONENT_STATUS_ALL_NODES: ["A", "B"],
167+
status.COMPONENT_STATUS_DEPLOYED_NODES: ["A", "B"],
168+
status.COMPONENT_STATUS_UNVERIFIED_NODES: [],
169+
status.COMPONENT_STATUS_FAILED_NODES: [],
170+
status.COMPONENT_STATUS_DEPLOYED_COUNT: "2/2",
171+
status.COMPONENT_STATUS_VERIFIED_COUNT: "2/2",
172+
status.COMPONENT_STATUS_DEPENDENCY_GRAPH: graph_str,
173+
},
174+
}
175+
176+
144177
def test_make_application_status_supported_versions():
145178
"""Make sure that setting supported versions in status creates the correct
146179
status.versions entries

0 commit comments

Comments
 (0)