From 86b3b89813275fce7286340a2fd77878dda9a654 Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Sat, 27 Apr 2024 15:08:34 -0400 Subject: [PATCH 1/8] Init resource verification override Signed-off-by: Michael Honaker --- oper8/component.py | 30 ++++++++++++++++-------------- oper8/dag/node.py | 20 +++++++++++++------- oper8/managed_object.py | 6 ++++-- oper8/session.py | 10 +++++----- oper8/verify_resources.py | 22 +++++++++++++++++----- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/oper8/component.py b/oper8/component.py index 1180987..eb9f706 100644 --- a/oper8/component.py +++ b/oper8/component.py @@ -2,28 +2,28 @@ Component base class for building larger abstractions off of """ -# Standard -from typing import Any, List, Optional, Tuple import abc import os - -# Third Party -import yaml +# Standard +from typing import Any, List, Optional, Tuple # First Party import aconfig import alog +# Third Party +import yaml # Local from . import config -from .constants import INTERNAL_NAME_ANOTATION_NAME, TEMPORARY_PATCHES_ANNOTATION_NAME +from .constants import (INTERNAL_NAME_ANOTATION_NAME, + TEMPORARY_PATCHES_ANNOTATION_NAME) from .dag import Graph, Node, ResourceNode from .exceptions import assert_cluster from .managed_object import ManagedObject from .patch import apply_patches -from .session import VERIFY_FUNCTION, Session +from .session import COMPONENT_VERIFY_FUNCTION, Session from .utils import abstractclassproperty, sanitize_for_serialization -from .verify_resources import verify_resource +from .verify_resources import RESOURCE_VERIFY_FUNCTION, verify_resource log = alog.use_channel("COMP-BASE") @@ -232,6 +232,7 @@ def add_resource( self, name: str, # pylint: disable=redefined-builtin obj: Any, + verify_function: Optional[RESOURCE_VERIFY_FUNCTION] = None, ) -> Optional[ ResourceNode ]: # pylint: disable=unused-argument, redefined-builtin, invalid-name @@ -251,7 +252,7 @@ def add_resource( # Add namespace to obj if not present obj.setdefault("metadata", {}).setdefault("namespace", self.session_namespace) - node = ResourceNode(name, obj) + node = ResourceNode(name, obj, verify_function) self.graph.add_node(node) return node @@ -259,7 +260,7 @@ def add_dependency( self, session: Session, *components: "Component", - verify_function: Optional[VERIFY_FUNCTION] = None, + verify_function: Optional[COMPONENT_VERIFY_FUNCTION] = None, ): """This add_dependency function sets up a dependency between this component and a list of other components. To add a dependency between resources inside @@ -385,6 +386,7 @@ def _default_verify(self, session, is_subsystem=False): session=session, is_subsystem=is_subsystem, namespace=resource.namespace, + verify_function=resource.verify_function ): log.debug("[%s/%s] not verified", resource.kind, resource.name) return False @@ -468,7 +470,7 @@ def __render(self, session): # Iterate all ApiObject children in dependency order and perform the # rendering, including patches and backend modifications. self._managed_objects = [] - for name, obj in resource_list: + for name, obj, verify_func in resource_list: # Apply any patches to this object log.debug2("Applying patches to managed object: %s", name) log.debug4("Before Patching: %s", obj) @@ -495,7 +497,7 @@ def __render(self, session): obj = self.update_object_definition(session, name, obj) # Add the resource to the set managed by the is component - managed_obj = ManagedObject(obj) + managed_obj = ManagedObject(obj, verify_func) log.debug2("Adding managed object: %s", managed_obj) log.debug4("Final Definition: %s", obj) self._managed_objects.append(managed_obj) @@ -521,7 +523,7 @@ def __gather_resources(self, session) -> List[Tuple[str, dict]]: for child in children: # Construct the managed object with its internal name child_name = ".".join([self.name, child.get_name()]) - child_obj = child.get_data() - resource_list.append((child_name, child_obj)) + child_obj, verify_func = child.get_data() + resource_list.append((child_name, child_obj, verify_func)) return resource_list diff --git a/oper8/dag/node.py b/oper8/dag/node.py index 29e02ac..f21f8da 100644 --- a/oper8/dag/node.py +++ b/oper8/dag/node.py @@ -2,7 +2,7 @@ This module contains a collection of classes for implementing nodes of a Graph """ # Standard -from typing import Any, List, Optional +from typing import Any, Callable, List, Optional class Node: @@ -114,11 +114,12 @@ def __hash__(self) -> str: class ResourceNode(Node): - """Class for representing a kubernetes resource in the Graph""" + """Class for representing a kubernetes resource in the Graph with + a function for verifying said resource""" - def __init__(self, name: str, manifest: dict): + def __init__(self, name: str, manifest: dict, verify_func: Callable = None): # Override init to require name/manifest parameters - super().__init__(name, manifest) + super().__init__(name, (manifest, verify_func)) ## ApiObject Parameters and Functions ###################################### @property @@ -129,23 +130,28 @@ def api_group(self) -> str: @property def api_version(self) -> str: """The full kubernetes apiVersion""" - return self.get_data().get("apiVersion") + return self.manifest.get("apiVersion") @property def kind(self) -> str: """The resource kind""" - return self.get_data().get("kind") + return self.manifest.get("kind") @property def metadata(self) -> dict: """The full resource metadata dict""" - return self.get_data().get("metadata", {}) + return self.manifest.get("metadata", {}) @property def name(self) -> str: """The resource metadata.name""" return self.metadata.get("name") + @property + def manifest(self) -> str: + """The resource manifest""" + return self.get_data()[0] + def add_dependency(self, node: "ResourceNode"): """Add a child dependency to this node""" self.add_child(node) diff --git a/oper8/managed_object.py b/oper8/managed_object.py index c3e5fde..ded7623 100644 --- a/oper8/managed_object.py +++ b/oper8/managed_object.py @@ -1,8 +1,9 @@ """ Helper object to represent a kubernetes object that is managed by the operator """ -# Standard import uuid +# Standard +from typing import Callable KUBE_LIST_IDENTIFIER = "List" @@ -10,7 +11,7 @@ class ManagedObject: # pylint: disable=too-many-instance-attributes """Basic struct to represent a managed kubernetes object""" - def __init__(self, definition): + def __init__(self, definition, verify_function: Callable=None): self.kind = definition.get("kind") self.metadata = definition.get("metadata", {}) self.name = self.metadata.get("name") @@ -19,6 +20,7 @@ def __init__(self, definition): self.resource_version = self.metadata.get("resourceVersion") self.api_version = definition.get("apiVersion") self.definition = definition + self.verify_function = verify_function # If resource is not list then check name if KUBE_LIST_IDENTIFIER not in self.kind: diff --git a/oper8/session.py b/oper8/session.py index 0dd759c..39380bc 100644 --- a/oper8/session.py +++ b/oper8/session.py @@ -2,10 +2,10 @@ This module holds the core session state for an individual reconciliation """ +import hashlib # Standard from functools import partial from typing import Callable, List, Optional, Tuple, Union -import hashlib # First Party import aconfig @@ -26,10 +26,10 @@ # Maximum length for a kubernetes name MAX_NAME_LEN = 63 -# Type definition for the signature of a verify function +# Type definition for the signature of a component verify function # NOTE: I'm not sure why pylint dislikes this name. In my view, this is a shared # global which should have all-caps casing. -VERIFY_FUNCTION = Callable[["Session"], bool] # pylint: disable=invalid-name +COMPONENT_VERIFY_FUNCTION = Callable[["Session"], bool] # pylint: disable=invalid-name # Helper Definition to define when a session should use its own namespace # or the one passed in as an argument @@ -232,7 +232,7 @@ def add_component_dependency( self, component: Union[str, COMPONENT_INSTANCE_TYPE], upstream_component: Union[str, COMPONENT_INSTANCE_TYPE], - verify_function: Optional[VERIFY_FUNCTION] = None, + verify_function: Optional[COMPONENT_VERIFY_FUNCTION] = None, ): """Add a dependency indicating that one component requires an upstream component to be deployed before it can be deployed. @@ -338,7 +338,7 @@ def get_components(self, disabled: bool = False) -> List[COMPONENT_INSTANCE_TYPE def get_component_dependencies( self, component: Union[str, COMPONENT_INSTANCE_TYPE], - ) -> List[Tuple[COMPONENT_INSTANCE_TYPE, Optional[VERIFY_FUNCTION]]]: + ) -> List[Tuple[COMPONENT_INSTANCE_TYPE, Optional[COMPONENT_VERIFY_FUNCTION]]]: """Get the list of (upstream_name, verify_function) tuples for a given component. diff --git a/oper8/verify_resources.py b/oper8/verify_resources.py index 79fe2e6..a7f1428 100644 --- a/oper8/verify_resources.py +++ b/oper8/verify_resources.py @@ -6,13 +6,12 @@ # Standard from datetime import datetime from functools import partial -from typing import List, Optional - -# Third Party -import dateutil.parser +from typing import Callable, List, Optional # First Party import alog +# Third Party +import dateutil.parser # Local from . import status @@ -27,6 +26,12 @@ PROGRESSING_CONDITION_KEY = "Progressing" NEW_RS_AVAILABLE_REASON = "NewReplicaSetAvailable" +# Type definition for the signature of a resource verify function +# NOTE: I'm not sure why pylint dislikes this name. In my view, this is a shared +# global which should have all-caps casing. +RESOURCE_VERIFY_FUNCTION = Callable[[dict], bool] # pylint: disable=invalid-name + + ## Main Functions ############################################################## @@ -39,6 +44,7 @@ def verify_resource( # Use a predfined _SESSION_NAMESPACE default instead of None to differentiate between # nonnamespaced resources (which pass None) and those that use session.namespace namespace: Optional[str] = _SESSION_NAMESPACE, + verify_function: RESOURCE_VERIFY_FUNCTION = None, is_subsystem: bool = False, condition_type: str = None, timestamp_key: str = None, @@ -107,7 +113,7 @@ def verify_resource( ) # Run the appropriate verification function if there is one available - verify_fn = _resource_verifiers.get(kind) + verify_fn = verify_function or _resource_verifiers.get(kind) if not verify_fn and is_subsystem: log.debug("Using oper8 subsystem verifier for [%s/%s]", kind, name) verify_fn = partial( @@ -275,3 +281,9 @@ def is_expected_reason() -> bool: return obj_reason == expected_reason return is_expected_status() and is_expected_reason() + return is_expected_status() and is_expected_reason() + + return is_expected_status() and is_expected_reason() + return is_expected_status() and is_expected_reason() + return is_expected_status() and is_expected_reason() + return is_expected_status() and is_expected_reason() From 623dfa685581a62140fa043599b4aeaa4bd59eba Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Sat, 27 Apr 2024 15:18:34 -0400 Subject: [PATCH 2/8] Add Tests and FMT Signed-off-by: Michael Honaker --- oper8/component.py | 14 +++++++------- oper8/dag/node.py | 2 +- oper8/managed_object.py | 4 ++-- oper8/session.py | 2 +- oper8/verify_resources.py | 5 +++-- tests/test_component.py | 30 ++++++++++++++++++++++++++++++ tests/test_verify_resources.py | 9 +++++++++ 7 files changed, 53 insertions(+), 13 deletions(-) diff --git a/oper8/component.py b/oper8/component.py index eb9f706..614a456 100644 --- a/oper8/component.py +++ b/oper8/component.py @@ -2,21 +2,21 @@ Component base class for building larger abstractions off of """ -import abc -import os # Standard from typing import Any, List, Optional, Tuple +import abc +import os + +# Third Party +import yaml # First Party import aconfig import alog -# Third Party -import yaml # Local from . import config -from .constants import (INTERNAL_NAME_ANOTATION_NAME, - TEMPORARY_PATCHES_ANNOTATION_NAME) +from .constants import INTERNAL_NAME_ANOTATION_NAME, TEMPORARY_PATCHES_ANNOTATION_NAME from .dag import Graph, Node, ResourceNode from .exceptions import assert_cluster from .managed_object import ManagedObject @@ -386,7 +386,7 @@ def _default_verify(self, session, is_subsystem=False): session=session, is_subsystem=is_subsystem, namespace=resource.namespace, - verify_function=resource.verify_function + verify_function=resource.verify_function, ): log.debug("[%s/%s] not verified", resource.kind, resource.name) return False diff --git a/oper8/dag/node.py b/oper8/dag/node.py index f21f8da..5380a17 100644 --- a/oper8/dag/node.py +++ b/oper8/dag/node.py @@ -151,7 +151,7 @@ def name(self) -> str: def manifest(self) -> str: """The resource manifest""" return self.get_data()[0] - + def add_dependency(self, node: "ResourceNode"): """Add a child dependency to this node""" self.add_child(node) diff --git a/oper8/managed_object.py b/oper8/managed_object.py index ded7623..f4bc71e 100644 --- a/oper8/managed_object.py +++ b/oper8/managed_object.py @@ -1,9 +1,9 @@ """ Helper object to represent a kubernetes object that is managed by the operator """ -import uuid # Standard from typing import Callable +import uuid KUBE_LIST_IDENTIFIER = "List" @@ -11,7 +11,7 @@ class ManagedObject: # pylint: disable=too-many-instance-attributes """Basic struct to represent a managed kubernetes object""" - def __init__(self, definition, verify_function: Callable=None): + def __init__(self, definition, verify_function: Callable = None): self.kind = definition.get("kind") self.metadata = definition.get("metadata", {}) self.name = self.metadata.get("name") diff --git a/oper8/session.py b/oper8/session.py index 39380bc..104492d 100644 --- a/oper8/session.py +++ b/oper8/session.py @@ -2,10 +2,10 @@ This module holds the core session state for an individual reconciliation """ -import hashlib # Standard from functools import partial from typing import Callable, List, Optional, Tuple, Union +import hashlib # First Party import aconfig diff --git a/oper8/verify_resources.py b/oper8/verify_resources.py index a7f1428..ea0d1d2 100644 --- a/oper8/verify_resources.py +++ b/oper8/verify_resources.py @@ -8,11 +8,12 @@ from functools import partial from typing import Callable, List, Optional -# First Party -import alog # Third Party import dateutil.parser +# First Party +import alog + # Local from . import status from .session import _SESSION_NAMESPACE # pylint: disable=cyclic-import diff --git a/tests/test_component.py b/tests/test_component.py index 6296872..31add6d 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -206,6 +206,36 @@ def build_chart(self, session): assert objs[0].metadata.namespace == "unique" +def test_custom_resource_verify(): + """Make sure that a user can override the verify function for a particular resource.""" + session = setup_session() + bar = {"kind": "Foo", "apiVersion": "v1", "metadata": {"name": "bar"}} + + class TestSuccessVerify(Component): + name = "bar" + + def build_chart(self, session): + self.add_resource("bar", bar) + + class TestFailedVerify(Component): + name = "bar_fail" + + def build_chart(self, session): + self.add_resource("bar", bar, lambda resource: False) + + suc_comp = TestSuccessVerify(session=session) + failed_comp = TestFailedVerify(session=session) + + with library_config(internal_name_annotation=True): + suc_comp.render_chart(session) + suc_comp.deploy(session) + assert suc_comp.verify(session) + + failed_comp.render_chart(session) + failed_comp.deploy(session) + assert not failed_comp.verify(session) + + def test_internal_name_annotation(): """Make sure the internal name annotation is added to output resources if configured diff --git a/tests/test_verify_resources.py b/tests/test_verify_resources.py index 4b1f346..83eeb4d 100644 --- a/tests/test_verify_resources.py +++ b/tests/test_verify_resources.py @@ -166,6 +166,15 @@ def test_verify_pod_null_namespace(): ) +def test_verify_pod_custom_verification(): + """Make sure a ready pod fails to verify with a custom override""" + assert not run_test_verify( + kind="Pod", + conditions=[make_condition("Ready", True)], + verify_function=lambda resource: False, + ) + + ################# ## Deployments ## ################# From b475934f243723e68af3b8e3537f0b67c92141cc Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Mon, 29 Apr 2024 11:34:48 -0400 Subject: [PATCH 3/8] Fix FMT Signed-off-by: Michael Honaker --- tests/test_verify_resources.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/test_verify_resources.py b/tests/test_verify_resources.py index c16b0d6..8249b00 100644 --- a/tests/test_verify_resources.py +++ b/tests/test_verify_resources.py @@ -12,13 +12,19 @@ # Local from oper8 import status from oper8.session import _SESSION_NAMESPACE -from oper8.test_helpers.helpers import (MockDeployManager, configure_logging, - setup_session) +from oper8.test_helpers.helpers import ( + MockDeployManager, + configure_logging, + setup_session, +) from oper8.utils import nested_set -from oper8.verify_resources import (AVAILABLE_CONDITION_KEY, - DEFAULT_TIMESTAMP_KEY, - NEW_RS_AVAILABLE_REASON, - PROGRESSING_CONDITION_KEY, verify_resource) +from oper8.verify_resources import ( + AVAILABLE_CONDITION_KEY, + DEFAULT_TIMESTAMP_KEY, + NEW_RS_AVAILABLE_REASON, + PROGRESSING_CONDITION_KEY, + verify_resource, +) configure_logging() log = alog.use_channel("TEST") @@ -544,4 +550,4 @@ def test_datetime_timestamp(): "Ready", True, timestamp=datetime.now(), timestamp_str=False ), ], - ) \ No newline at end of file + ) From 998414aa025899f590ff1f1e0f6936837858ea2c Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Mon, 29 Apr 2024 13:38:28 -0400 Subject: [PATCH 4/8] Fix Bug in SanitizeForSerialization Signed-off-by: Michael Honaker --- oper8/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oper8/utils.py b/oper8/utils.py index 2ed9dfe..5cc265e 100644 --- a/oper8/utils.py +++ b/oper8/utils.py @@ -139,7 +139,7 @@ def sanitize_for_serialization(obj): # pylint: disable=too-many-return-statemen elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() elif isinstance(obj, ResourceNode): - return sanitize_for_serialization(obj.get_data()) + return sanitize_for_serialization(obj.manifest) elif isinstance(obj, property): return sanitize_for_serialization(obj.fget()) From f5ea1828e451ec3bac10c5f29ae4b8e0a5bb644d Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Thu, 2 May 2024 17:13:18 -0400 Subject: [PATCH 5/8] Revert API breaking change Signed-off-by: Michael Honaker --- oper8/component.py | 3 +-- oper8/dag/node.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/oper8/component.py b/oper8/component.py index 614a456..ad23108 100644 --- a/oper8/component.py +++ b/oper8/component.py @@ -523,7 +523,6 @@ def __gather_resources(self, session) -> List[Tuple[str, dict]]: for child in children: # Construct the managed object with its internal name child_name = ".".join([self.name, child.get_name()]) - child_obj, verify_func = child.get_data() - resource_list.append((child_name, child_obj, verify_func)) + resource_list.append((child_name, child.manifest, child.verify_function)) return resource_list diff --git a/oper8/dag/node.py b/oper8/dag/node.py index 5380a17..86d3d08 100644 --- a/oper8/dag/node.py +++ b/oper8/dag/node.py @@ -119,7 +119,8 @@ class ResourceNode(Node): def __init__(self, name: str, manifest: dict, verify_func: Callable = None): # Override init to require name/manifest parameters - super().__init__(name, (manifest, verify_func)) + super().__init__(name, manifest) + self._verify_function = verify_func ## ApiObject Parameters and Functions ###################################### @property @@ -150,7 +151,12 @@ def name(self) -> str: @property def manifest(self) -> str: """The resource manifest""" - return self.get_data()[0] + return self.get_data() + + @property + def verify_function(self) -> str: + """The resource manifest""" + return self._verify_function def add_dependency(self, node: "ResourceNode"): """Add a child dependency to this node""" From 90474212d8c4221d13c817875efda48d3a52a33d Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Thu, 2 May 2024 17:31:44 -0400 Subject: [PATCH 6/8] Address NIT Comments Signed-off-by: Michael Honaker --- oper8/managed_object.py | 2 +- oper8/verify_resources.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/oper8/managed_object.py b/oper8/managed_object.py index f4bc71e..c800d82 100644 --- a/oper8/managed_object.py +++ b/oper8/managed_object.py @@ -11,7 +11,7 @@ class ManagedObject: # pylint: disable=too-many-instance-attributes """Basic struct to represent a managed kubernetes object""" - def __init__(self, definition, verify_function: Callable = None): + def __init__(self, definition: dict, verify_function: Callable = None): self.kind = definition.get("kind") self.metadata = definition.get("metadata", {}) self.name = self.metadata.get("name") diff --git a/oper8/verify_resources.py b/oper8/verify_resources.py index b31aac8..769f0df 100644 --- a/oper8/verify_resources.py +++ b/oper8/verify_resources.py @@ -45,10 +45,10 @@ def verify_resource( # Use a predfined _SESSION_NAMESPACE default instead of None to differentiate between # nonnamespaced resources (which pass None) and those that use session.namespace namespace: Optional[str] = _SESSION_NAMESPACE, - verify_function: RESOURCE_VERIFY_FUNCTION = None, + verify_function: Optional[RESOURCE_VERIFY_FUNCTION] = None, is_subsystem: bool = False, - condition_type: str = None, - timestamp_key: str = None, + condition_type: Optional[str] = None, + timestamp_key: Optional[str] = None, ) -> bool: """Verify a resource detailed in a ManagedObject. From 4275ad860c7aba5a57072faf6d5738ab9a9ade6f Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Thu, 2 May 2024 17:36:08 -0400 Subject: [PATCH 7/8] Fix AutoFormatter and at typing Signed-off-by: Michael Honaker --- oper8/dag/node.py | 4 +++- oper8/managed_object.py | 4 ++-- oper8/verify_resources.py | 6 ------ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/oper8/dag/node.py b/oper8/dag/node.py index 86d3d08..f1cafce 100644 --- a/oper8/dag/node.py +++ b/oper8/dag/node.py @@ -117,7 +117,9 @@ class ResourceNode(Node): """Class for representing a kubernetes resource in the Graph with a function for verifying said resource""" - def __init__(self, name: str, manifest: dict, verify_func: Callable = None): + def __init__( + self, name: str, manifest: dict, verify_func: Optional[Callable] = None + ): # Override init to require name/manifest parameters super().__init__(name, manifest) self._verify_function = verify_func diff --git a/oper8/managed_object.py b/oper8/managed_object.py index c800d82..27f13a4 100644 --- a/oper8/managed_object.py +++ b/oper8/managed_object.py @@ -2,7 +2,7 @@ Helper object to represent a kubernetes object that is managed by the operator """ # Standard -from typing import Callable +from typing import Callable, Optional import uuid KUBE_LIST_IDENTIFIER = "List" @@ -11,7 +11,7 @@ class ManagedObject: # pylint: disable=too-many-instance-attributes """Basic struct to represent a managed kubernetes object""" - def __init__(self, definition: dict, verify_function: Callable = None): + def __init__(self, definition: dict, verify_function: Optional[Callable] = None): self.kind = definition.get("kind") self.metadata = definition.get("metadata", {}) self.name = self.metadata.get("name") diff --git a/oper8/verify_resources.py b/oper8/verify_resources.py index 769f0df..f50ac2f 100644 --- a/oper8/verify_resources.py +++ b/oper8/verify_resources.py @@ -289,9 +289,3 @@ def is_expected_reason() -> bool: return obj_reason == expected_reason return is_expected_status() and is_expected_reason() - return is_expected_status() and is_expected_reason() - - return is_expected_status() and is_expected_reason() - return is_expected_status() and is_expected_reason() - return is_expected_status() and is_expected_reason() - return is_expected_status() and is_expected_reason() From 18e483825957c6533b64f510dfda9d6acb4dbc84 Mon Sep 17 00:00:00 2001 From: Michael Honaker Date: Thu, 2 May 2024 17:41:57 -0400 Subject: [PATCH 8/8] fix dag typing Signed-off-by: Michael Honaker --- oper8/dag/node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oper8/dag/node.py b/oper8/dag/node.py index f1cafce..cff690c 100644 --- a/oper8/dag/node.py +++ b/oper8/dag/node.py @@ -151,12 +151,12 @@ def name(self) -> str: return self.metadata.get("name") @property - def manifest(self) -> str: + def manifest(self) -> dict: """The resource manifest""" return self.get_data() @property - def verify_function(self) -> str: + def verify_function(self) -> Optional[Callable]: """The resource manifest""" return self._verify_function