From 0366429ea6d798292622da43094fb69a46157d7c Mon Sep 17 00:00:00 2001 From: Matthias Veit Date: Thu, 2 Nov 2023 08:43:13 +0100 Subject: [PATCH] [resotoworker][fix] Do not use the name to compute the resource identifier (#1811) --- plugins/aws/resoto_plugin_aws/resource/base.py | 7 ++++++- plugins/aws/resoto_plugin_aws/resource/cognito.py | 10 +++++++++- .../resoto_plugin_digitalocean/resources.py | 9 +++++++-- plugins/gcp/resoto_plugin_gcp/resources/base.py | 7 ++++++- resotolib/resotolib/baseresources.py | 15 ++++++--------- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/plugins/aws/resoto_plugin_aws/resource/base.py b/plugins/aws/resoto_plugin_aws/resource/base.py index 0a224a7ee3..0e7683a594 100644 --- a/plugins/aws/resoto_plugin_aws/resource/base.py +++ b/plugins/aws/resoto_plugin_aws/resource/base.py @@ -5,7 +5,7 @@ from concurrent.futures import Future from datetime import datetime, timezone, timedelta from functools import lru_cache -from typing import Any, Callable, ClassVar, Dict, Iterator, List, Optional, Type, TypeVar +from typing import Any, Callable, ClassVar, Dict, Iterator, List, Optional, Type, TypeVar, Tuple from math import ceil from attr import evolve @@ -107,6 +107,11 @@ class AwsResource(BaseResource, ABC): # The AWS specific identifier of the resource. Not available for all resources. arn: Optional[str] = None + def _keys(self) -> Tuple[Any, ...]: + if self.arn is not None: + return tuple(list(super()._keys()) + [self.arn]) + return super()._keys() + def update_resource_tag(self, client: AwsClient, key: str, value: str) -> bool: return False diff --git a/plugins/aws/resoto_plugin_aws/resource/cognito.py b/plugins/aws/resoto_plugin_aws/resource/cognito.py index c4c7511a01..6036949a50 100644 --- a/plugins/aws/resoto_plugin_aws/resource/cognito.py +++ b/plugins/aws/resoto_plugin_aws/resource/cognito.py @@ -1,5 +1,5 @@ from attrs import define, field -from typing import ClassVar, Dict, List, Optional, Type +from typing import ClassVar, Dict, List, Optional, Type, Tuple, Any from resoto_plugin_aws.aws_client import AwsClient from resoto_plugin_aws.resource.base import AwsApiSpec, AwsResource, GraphBuilder from resoto_plugin_aws.resource.iam import AwsIamRole @@ -91,6 +91,13 @@ class AwsCognitoUser(AwsResource, BaseUser): enabled: Optional[bool] = field(default=None) user_status: Optional[str] = field(default=None) mfa_options: List[AwsCognitoMFAOptionType] = field(factory=list) + pool_name: Optional[str] = None + + def _keys(self) -> Tuple[Any, ...]: + # in case different user pools include the same user: we add the pool name to the keys + if self.pool_name is not None: + return tuple(list(super()._keys()) + [self.pool_name]) + return super()._keys() @classmethod def service_name(cls) -> str: @@ -197,6 +204,7 @@ def add_tags(pool: AwsCognitoUserPool) -> None: builder.submit_work(service_name, add_tags, pool_instance) for user in builder.client.list(service_name, "list-users", "Users", UserPoolId=pool_instance.id): if user_instance := AwsCognitoUser.from_api(user, builder): + user_instance.pool_name = pool_instance.name builder.add_node(user_instance, user) builder.add_edge(from_node=pool_instance, edge_type=EdgeType.default, node=user_instance) for group in builder.client.list(service_name, "list-groups", "Groups", UserPoolId=pool_instance.id): diff --git a/plugins/digitalocean/resoto_plugin_digitalocean/resources.py b/plugins/digitalocean/resoto_plugin_digitalocean/resources.py index 03d4136d6e..48702b3bfc 100644 --- a/plugins/digitalocean/resoto_plugin_digitalocean/resources.py +++ b/plugins/digitalocean/resoto_plugin_digitalocean/resources.py @@ -1,6 +1,6 @@ import logging from attrs import define -from typing import ClassVar, Dict, List, Optional +from typing import ClassVar, Dict, List, Optional, Tuple, Any from resoto_plugin_digitalocean.client import StreamingWrapper from resoto_plugin_digitalocean.client import get_team_credentials @@ -46,6 +46,11 @@ class DigitalOceanResource(BaseResource): kind: ClassVar[str] = "digitalocean_resource" urn: str = "" + def _keys(self) -> Tuple[Any, ...]: + if self.urn: + return tuple(list(super()._keys()) + [self.urn]) + return super()._keys() + def delete_uri_path(self) -> Optional[str]: return None @@ -75,7 +80,7 @@ def delete(self, graph: Graph) -> bool: raise NotImplementedError def to_json(self) -> Json: - return _to_json(self, strip_nulls=True, keep_untouched=set(["tags"])) + return _to_json(self, strip_nulls=True, keep_untouched={"tags"}) @define(eq=False, slots=False) diff --git a/plugins/gcp/resoto_plugin_gcp/resources/base.py b/plugins/gcp/resoto_plugin_gcp/resources/base.py index f0257ac758..4137fd60d5 100644 --- a/plugins/gcp/resoto_plugin_gcp/resources/base.py +++ b/plugins/gcp/resoto_plugin_gcp/resources/base.py @@ -5,7 +5,7 @@ from concurrent.futures import Future from threading import Lock from types import TracebackType -from typing import Callable, List, ClassVar, Optional, TypeVar, Type, Any, Dict, Set +from typing import Callable, List, ClassVar, Optional, TypeVar, Type, Any, Dict, Set, Tuple from attr import define, field from google.auth.credentials import Credentials as GoogleAuthCredentials @@ -267,6 +267,11 @@ class GcpResource(BaseResource): link: Optional[str] = None label_fingerprint: Optional[str] = None + def _keys(self) -> Tuple[Any, ...]: + if self.link is not None: + return tuple(list(super()._keys()) + [self.link]) + return super()._keys() + def delete(self, graph: Graph) -> bool: if not self.api_spec: return False diff --git a/resotolib/resotolib/baseresources.py b/resotolib/resotolib/baseresources.py index 8d1df13ad4..15420ff961 100644 --- a/resotolib/resotolib/baseresources.py +++ b/resotolib/resotolib/baseresources.py @@ -196,15 +196,7 @@ def _keys(self) -> Tuple[Any, ...]: """ if self._graph is None: raise RuntimeError(f"_keys() called on {self.rtdname} before resource was added to graph") - return ( - self.kind, - self.cloud().id, - self.account().id, - self.region().id, - self.zone().id, - self.id, - self.name, - ) + return self.kind, self.cloud().id, self.account().id, self.region().id, self.zone().id, self.id @property def safe_name(self) -> str: @@ -717,6 +709,11 @@ class BaseRegion(BaseResource): kind: ClassVar[str] = "region" metadata: ClassVar[Dict[str, Any]] = {"icon": "region", "group": "control"} + def _keys(self) -> Tuple[Any, ...]: + if self._graph is None: + raise RuntimeError(f"_keys() called on {self.rtdname} before resource was added to graph") + return self.kind, self.cloud().id, self.account().id, self.region().id, self.zone().id, self.id, self.name + def region(self, graph: Optional[Any] = None) -> BaseRegion: return self