Skip to content

Commit

Permalink
add is_configured and enabled fields to cloud accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
meln1k committed Oct 27, 2023
1 parent 8af00c5 commit 0852195
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 3 deletions.
2 changes: 2 additions & 0 deletions fixbackend/cloud_accounts/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class CloudAccount:
workspace_id: WorkspaceId
name: Optional[str]
access: CloudAccess
is_configured: bool
enabled: bool


@frozen
Expand Down
13 changes: 11 additions & 2 deletions fixbackend/cloud_accounts/models/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from typing import Optional

from fastapi_users_db_sqlalchemy.generics import GUID
from sqlalchemy import ForeignKey, String, UniqueConstraint
from sqlalchemy import ForeignKey, String, UniqueConstraint, Boolean
from sqlalchemy.orm import Mapped, mapped_column

from fixbackend.base_model import Base
Expand All @@ -35,6 +35,8 @@ class CloudAccount(Base):
aws_external_id: Mapped[ExternalId] = mapped_column(GUID, nullable=False)
aws_role_name: Mapped[str] = mapped_column(String(length=64), nullable=False)
name: Mapped[Optional[str]] = mapped_column(String(length=64), nullable=True)
is_configured: Mapped[bool] = mapped_column(Boolean, nullable=False)
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False)
__table_args__ = (UniqueConstraint("tenant_id", "account_id"),)

def to_model(self) -> models.CloudAccount:
Expand All @@ -47,4 +49,11 @@ def access() -> models.CloudAccess:
case _:
raise ValueError(f"Unknown cloud {self.cloud}")

return models.CloudAccount(id=self.id, workspace_id=self.tenant_id, access=access(), name=self.name)
return models.CloudAccount(
id=self.id,
workspace_id=self.tenant_id,
access=access(),
name=self.name,
is_configured=self.is_configured,
enabled=self.enabled,
)
4 changes: 4 additions & 0 deletions fixbackend/cloud_accounts/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ async def create(self, cloud_account: CloudAccount) -> CloudAccount:
aws_role_name=cloud_account.access.role_name,
aws_external_id=cloud_account.access.external_id,
name=cloud_account.name,
is_configured=cloud_account.is_configured,
enabled=cloud_account.enabled,
)
else:
raise ValueError(f"Unknown cloud {cloud_account.access}")
Expand All @@ -82,6 +84,8 @@ async def update(self, id: FixCloudAccountId, cloud_account: CloudAccount) -> Cl
raise ValueError(f"Cloud account {id} not found")

stored_account.name = cloud_account.name
stored_account.is_configured = cloud_account.is_configured
stored_account.enabled = cloud_account.enabled

match cloud_account.access:
case AwsCloudAccess(account_id, external_id, role_name):
Expand Down
10 changes: 10 additions & 0 deletions fixbackend/cloud_accounts/service_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ async def process_domain_event(self, message: Json, context: MessageContext) ->
),
)

case AwsAccountConfigured.kind:
configured_event = AwsAccountConfigured.from_json(message)
account = await self.cloud_account_repository.get(configured_event.cloud_account_id)
if account is None:
log.warning(f"Account {configured_event.cloud_account_id} not found, cannot mark as configured")
return None
await self.cloud_account_repository.update(account.id, evolve(account, is_configured=True))

case _:
pass # ignore other domain events

Expand Down Expand Up @@ -185,6 +193,8 @@ async def account_already_exists(workspace_id: WorkspaceId, account_id: str) ->
workspace_id=workspace_id,
access=AwsCloudAccess(aws_account_id=account_id, external_id=external_id, role_name=role_name),
name=None,
is_configured=False,
enabled=True,
)
if existing := await account_already_exists(workspace_id, account_id):
account = evolve(account, id=existing.id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""add configured flag to cloud account
Revision ID: e5a452318fa7
Revises: 69f29fc94a5c
Create Date: 2023-10-27 12:46:40.781041+00:00
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = "e5a452318fa7"
down_revision: Union[str, None] = "69f29fc94a5c"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("cloud_account", sa.Column("is_configured", sa.Boolean(), nullable=False, server_default=sa.false()))
op.add_column("cloud_account", sa.Column("enabled", sa.Boolean(), nullable=False, server_default=sa.true()))
# ### end Alembic commands ###
17 changes: 17 additions & 0 deletions static/openapi-events.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,20 @@ components:
aws_account_id:
type: string
description: Id of the aws account.

AwsAccountConfigured:
description: "This event emitted when a new aws account has been configured."
type: object
properties:
cloud_account_id:
type: string
description: Id of the cloud account.
tenant_id:
type: string
description: Id of the workspace.
cloud:
type: string
description: Name of the cloud.
aws_account_id:
type: string
description: Id of the aws account.
2 changes: 2 additions & 0 deletions tests/fixbackend/cloud_accounts/repository_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ async def test_create_cloud_account(
external_id=ExternalId(uuid.uuid4()),
),
name="foo",
is_configured=False,
enabled=True,
)

# create
Expand Down
10 changes: 10 additions & 0 deletions tests/fixbackend/cloud_accounts/router_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ async def create_aws_account(
workspace_id=workspace_id,
access=AwsCloudAccess(account_id, external_id, role_name),
name=None,
is_configured=False,
enabled=True,
)
self.accounts[account.id] = account
return account
Expand Down Expand Up @@ -128,6 +130,8 @@ async def test_delete_cloud_account(client: AsyncClient) -> None:
workspace_id=workspace_id,
access=AwsCloudAccess(account_id, external_id, role_name),
name="foo",
is_configured=False,
enabled=True,
)
response = await client.delete(f"/api/workspaces/{workspace_id}/cloud_account/{cloud_account_id}")
assert response.status_code == 200
Expand Down Expand Up @@ -171,6 +175,8 @@ async def test_get_cloud_account(client: AsyncClient) -> None:
workspace_id=workspace_id,
access=AwsCloudAccess(account_id, external_id, role_name),
name="foo",
is_configured=False,
enabled=True,
)

response = await client.get(f"/api/workspaces/{workspace_id}/cloud_account/{cloud_account_id}")
Expand All @@ -191,6 +197,8 @@ async def test_list_cloud_accounts(client: AsyncClient) -> None:
workspace_id=workspace_id,
access=AwsCloudAccess(account_id, external_id, role_name),
name="foo",
is_configured=False,
enabled=True,
)

response = await client.get(f"/api/workspaces/{workspace_id}/cloud_accounts")
Expand All @@ -212,6 +220,8 @@ async def test_update_cloud_account(client: AsyncClient) -> None:
workspace_id=workspace_id,
access=AwsCloudAccess(account_id, external_id, role_name),
name="foo",
is_configured=False,
enabled=True,
)

payload = {
Expand Down
46 changes: 46 additions & 0 deletions tests/fixbackend/cloud_accounts/service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ async def test_create_aws_account(
assert account.access.aws_account_id == account_id
assert account.access.role_name == role_name
assert account.access.external_id == external_id
assert account.name is None
assert account.is_configured is False
assert account.enabled is True

message = {
"cloud_account_id": str(account.id),
Expand Down Expand Up @@ -442,3 +445,46 @@ async def test_handle_account_discovered(arq_redis: Redis, default_config: Confi
assert event.cloud_account_id == account1.id
assert event.aws_account_id == account_id1
assert event.tenant_id == account1.workspace_id


@pytest.mark.asyncio
async def test_handle_account_configured(arq_redis: Redis, default_config: Config) -> None:
repository = CloudAccountRepositoryMock()
organization_repository = OrganizationServiceMock()
pubsub_publisher = RedisPubSubPublisherMock()
domain_sender = DomainEventSenderMock()
last_scan_repo = LastScanRepositoryMock()
account_setup_helper = AwsAccountSetupHelperMock()
service = CloudAccountServiceImpl(
organization_repository,
repository,
pubsub_publisher,
domain_sender,
last_scan_repo,
arq_redis,
default_config,
account_setup_helper,
account_setup_sleep_seconds=0.05,
)

account = await service.create_aws_account(test_workspace_id, account_id, role_name, external_id)
assert account.is_configured is False

event = AwsAccountConfigured(
cloud_account_id=account.id,
tenant_id=account.workspace_id,
aws_account_id=account_id,
)
# happy case, boto3 can assume role
await service.process_domain_event(
event.to_json(), MessageContext("test", event.kind, "test", datetime.utcnow(), datetime.utcnow())
)

after_configured = await service.get_cloud_account(account.id, test_workspace_id)
assert after_configured is not None
assert after_configured.is_configured is True
assert after_configured.access == account.access
assert after_configured.workspace_id == account.workspace_id
assert after_configured.name == account.name
assert after_configured.id == account.id
assert after_configured.enabled == account.enabled
9 changes: 8 additions & 1 deletion tests/fixbackend/dispatcher/dispatcher_service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ async def test_receive_workspace_created(
organization.id,
"foo",
AwsCloudAccess(aws_account_id, organization.external_id, "test"),
is_configured=False,
enabled=True,
)
)
# signal to the dispatcher that the new workspace was created
Expand Down Expand Up @@ -84,7 +86,12 @@ async def test_receive_aws_account_discovered(
aws_account_id = CloudAccountId("123")

account = CloudAccount(
cloud_account_id, organization.id, "foo", AwsCloudAccess(aws_account_id, organization.external_id, "test")
cloud_account_id,
organization.id,
"foo",
AwsCloudAccess(aws_account_id, organization.external_id, "test"),
is_configured=False,
enabled=True,
)
await cloud_account_repository.create(account)

Expand Down

0 comments on commit 0852195

Please sign in to comment.