Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue/6025 implement discovery handler (2) #6415

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cb1198e
Small fixes
arnaudsjs Aug 17, 2023
f797916
Add tests
arnaudsjs Aug 17, 2023
a1011e6
Enhance tests
arnaudsjs Aug 18, 2023
c19ae69
Fix tests
arnaudsjs Aug 18, 2023
fe76a87
Factor out common setup, teardown and error handling logic
arnaudsjs Aug 18, 2023
540f6b8
Fix typing
arnaudsjs Aug 18, 2023
c9bbe75
Small enhancements
arnaudsjs Aug 21, 2023
50e1df1
Fix formatting
arnaudsjs Aug 21, 2023
634f304
Merge branch 'master' into issue/6025-implement-discovery-handler-bis
arnaudsjs Aug 21, 2023
1cf9641
Add changelog entry
arnaudsjs Aug 21, 2023
a7282af
Revert "Factor out common setup, teardown and error handling logic"
arnaudsjs Aug 23, 2023
44281e6
Revert "Revert "Factor out common setup, teardown and error handling …
arnaudsjs Aug 23, 2023
be4868c
Update src/inmanta/agent/handler.py
arnaudsjs Aug 23, 2023
caf9ab1
Update src/inmanta/agent/handler.py
arnaudsjs Aug 23, 2023
28ba0a2
Update changelogs/unreleased/6025-implement-discovery-resource-handle…
arnaudsjs Aug 23, 2023
2846025
Implement review comments
arnaudsjs Aug 23, 2023
322efbc
Merge branch 'issue/6025-implement-discovery-handler-bis' of github.c…
arnaudsjs Aug 23, 2023
57feffb
Merge branch 'master' into issue/6025-implement-discovery-handler-bis
arnaudsjs Aug 23, 2023
ab2c3a9
Fix changelog message
arnaudsjs Aug 23, 2023
2266406
Fix typing
arnaudsjs Aug 23, 2023
30e8a3e
Update changelogs/unreleased/6025-implement-discovery-resource-handle…
arnaudsjs Aug 23, 2023
f7ab0a8
Revert move error handling logic
arnaudsjs Aug 25, 2023
8426f89
Merge branch 'master' into issue/6025-implement-discovery-handler-bis
arnaudsjs Aug 25, 2023
1959d44
Implement review comments
arnaudsjs Aug 25, 2023
80065dd
Small fix
arnaudsjs Aug 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
description: "Add handler (DiscoveryHandler) to discover unmanaged resources."
issue-nr: 6025
change-type: minor
destination-branches: [master, iso6]
destination-branches: [master]
sections:
feature: "{{description}}"
upgrade-note: "The execution of do_reload() and execute() now execute between `pre()` and `post()` have been called."
arnaudsjs marked this conversation as resolved.
Show resolved Hide resolved
90 changes: 36 additions & 54 deletions src/inmanta/agent/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,29 @@ def filter_resources_in_unexpected_state(

failed_dependencies = [req for req, status in requires.items() if status != ResourceState.deployed]
if not any(failed_dependencies):
self.execute(ctx, resource)
self.post_execute(ctx, resource)
try:
self.pre(ctx, resource)
self.execute(ctx, resource)
except SkipResource as e:
ctx.set_status(const.ResourceState.skipped)
ctx.warning(msg="Resource %(resource_id)s was skipped: %(reason)s", resource_id=resource.id, reason=e.args)
except Exception as e:
ctx.set_status(const.ResourceState.failed)
ctx.exception(
"An error occurred during deployment of %(resource_id)s (exception: %(exception)s)",
resource_id=resource.id,
exception=f"{e.__class__.__name__}('{e}')",
traceback=traceback.format_exc(),
)
finally:
try:
self.post(ctx, resource)
except Exception as e:
ctx.exception(
"An error occurred after deployment of %(resource_id)s (exception: %(exception)s",
resource_id=resource.id,
exception=f"{e.__class__.__name__}('{e}')",
)
else:
ctx.set_status(const.ResourceState.skipped)
ctx.info(
Expand All @@ -518,53 +539,15 @@ def filter_resources_in_unexpected_state(
)

@abstractmethod
def _do_execute(self, ctx: HandlerContext, resource: TResource, dry_run: bool = False) -> None:
"""
This method contains the actual logic that enforced the intent of the resource without the setup, teardown
and error handling code. See documentation execute() method.
"""
raise NotImplementedError()

def execute(self, ctx: HandlerContext, resource: TResource, dry_run: bool = False) -> None:
"""
Enforce a resource's intent and inform the handler context of any relevant changes (e.g. set deployed status,
report attribute changes). Called only when all of its dependencies have successfully deployed.

This method contains all the common setup, teardown and error handling logic between the different handlers.
The specific execution logic for a certain handler is implemented in the abstract _do_execute() method.

:param ctx: Context object to report changes and logs to the agent and server.
:param resource: The resource to deploy.
:param dry_run: If set to true, the intent is not enforced, only the set of changes it would bring is computed.
"""
try:
self.pre(ctx, resource)
self._do_execute(ctx, resource, dry_run)
except SkipResource as e:
ctx.set_status(const.ResourceState.skipped)
ctx.warning(msg="Resource %(resource_id)s was skipped: %(reason)s", resource_id=resource.id, reason=e.args)
except Exception as e:
ctx.set_status(const.ResourceState.failed)
ctx.exception(
"An error occurred during deployment of %(resource_id)s (exception: %(exception)s)",
resource_id=resource.id,
exception=f"{e.__class__.__name__}('{e}')",
traceback=traceback.format_exc(),
)
finally:
try:
self.post(ctx, resource)
except Exception as e:
ctx.exception(
"An error occurred after deployment of %(resource_id)s (exception: %(exception)s",
resource_id=resource.id,
exception=f"{e.__class__.__name__}('{e}')",
)

def post_execute(self, ctx: HandlerContext, resource: TResource) -> None:
"""
This method is called by the deploy() method right after the execute() method was called.
"""
pass

def available(self, resource: TResource) -> bool:
Expand All @@ -576,7 +559,7 @@ def available(self, resource: TResource) -> bool:
return True

@abstractmethod
def check_facts(self, ctx: HandlerContext, resource: TResource) -> Dict[str, object]:
def check_facts(self, ctx: HandlerContext, resource: TResource) -> dict[str, object]:
"""
This method is called by the agent to query for facts.

Expand Down Expand Up @@ -607,7 +590,7 @@ def can_reload(self) -> bool:

def do_reload(self, ctx: HandlerContext, resource: TResource) -> None:
"""
Perform a reload of this resource. This method is executed after the post() method is called.
Perform a reload of this resource.

:param ctx: Context object to report changes and logs to the agent and server.
:param resource: The resource to reload.
Expand Down Expand Up @@ -739,7 +722,7 @@ def call() -> abc.Awaitable[Result]:
@stable_api
class ResourceHandler(HandlerAPI[TResource]):
"""
A class that handle resources.
A class that handles resources.
"""

def _diff(self, current: TResource, desired: TResource) -> dict[str, dict[str, typing.Any]]:
Expand Down Expand Up @@ -798,23 +781,19 @@ def do_changes(self, ctx: HandlerContext, resource: TResource, changes: abc.Mapp
"""
raise NotImplementedError()

def _do_execute(self, ctx: HandlerContext, resource: TResource, dry_run: bool = False) -> None:
def execute(self, ctx: HandlerContext, resource: TResource, dry_run: bool = False) -> None:
changes = self.list_changes(ctx, resource)
ctx.update_changes(changes)

if not dry_run:
self.do_changes(ctx, resource, changes)
ctx.set_status(const.ResourceState.deployed)

if self._should_reload(resource):
self.do_reload(ctx, resource)
else:
ctx.set_status(const.ResourceState.dry)

def post_execute(self, ctx: HandlerContext, resource: TResource) -> None:
"""
This method is called by the deploy() method right after the execute() method was called.
"""
if self._should_reload(resource):
self.do_reload(ctx, resource)

def _should_reload(self, resource: TResource) -> bool:
"""
Return True iff the given resource should be reloaded.
Expand Down Expand Up @@ -933,7 +912,7 @@ def calculate_diff(
"""
return self._diff(current, desired)

def _do_execute(self, ctx: HandlerContext, resource: TPurgeableResource, dry_run: bool = False) -> None:
def execute(self, ctx: HandlerContext, resource: TPurgeableResource, dry_run: bool = False) -> None:
# current is clone, except for purged is set to false to prevent a bug that occurs often where the desired
# state defines purged=true but the read_resource fails to set it to false if the resource does exist
desired = resource
Expand Down Expand Up @@ -965,6 +944,9 @@ def _do_execute(self, ctx: HandlerContext, resource: TPurgeableResource, dry_run
self.update_resource(ctx, dict(changes), desired)

ctx.set_status(const.ResourceState.deployed)

if self._should_reload(resource):
self.do_reload(ctx, resource)
else:
ctx.set_status(const.ResourceState.dry)

Expand Down Expand Up @@ -997,9 +979,9 @@ def discover_resources(
"""
raise NotImplementedError()

def _do_execute(self, ctx: HandlerContext, resource: TDiscovery, dry_run: bool = False) -> None:
def execute(self, ctx: HandlerContext, resource: TDiscovery, dry_run: bool = False) -> None:
"""
Generic logic to perform during resource discovery. This method is called when the agent wants
Logic to perform during resource discovery. This method is called when the agent wants
to deploy the corresponding discovery resource. The default behaviour of this method is to call
the `discover_resources` method, serialize the returned values and report them to the server.
"""
Expand Down