Skip to content

Commit

Permalink
feat: proper file loading support (#95)
Browse files Browse the repository at this point in the history
**Issue #, if available:**

## Description of changes:

<!--- One or two sentences as a summary of what's being changed -->

**Checklist**

<!--- Leave unchecked if your change doesn't seem to apply -->

* [x] Update tests
* [ ] Update docs
* [x] PR title follows [conventional commit
semantics](https://www.conventionalcommits.org/en/v1.0.0-beta.2/#commit-message-for-a-fix-using-an-optional-issue-number)

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
  • Loading branch information
Nr18 authored Jul 20, 2023
1 parent 10a932f commit a3d00d9
Show file tree
Hide file tree
Showing 25 changed files with 864 additions and 563 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ help: ## Display this help
.PHONY: lint
lint: _black _mypy ## Lint all project files

.PHONY: test # lint complexity-baseline
test: ## Run the test suite defined in the project
.PHONY: test
test: lint complexity ## Run the test suite defined in the project
pytest --cov=./landingzone_organization --cov-report term-missing --junitxml=reports/pytest.xml --cov-report xml:reports/coverage.xml

.PHONY: install
Expand Down
20 changes: 10 additions & 10 deletions landingzone_organization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@

__version__ = "0.7.0"
__all__ = [
AWSOrganization,
Organization,
OrganizationUnit,
Workloads,
Workload,
Account,
Groups,
Group,
Profile,
Profiles,
"AWSOrganization",
"Organization",
"OrganizationUnit",
"Workloads",
"Workload",
"Account",
"Groups",
"Group",
"Profile",
"Profiles",
]
2 changes: 1 addition & 1 deletion landingzone_organization/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ def environment(self) -> Optional[str]:

@property
def weight(self) -> int:
return resolve_account_weight(self.environment)
return resolve_account_weight(self.environment) if self.environment else 100
4 changes: 2 additions & 2 deletions landingzone_organization/cli/commands/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def cli():
pass


@cli.command()
@cli.command() # type: ignore
@click.argument("account-id")
@click.pass_obj
def view(ctx: Context, account_id: str):
Expand All @@ -28,7 +28,7 @@ def view(ctx: Context, account_id: str):
click.echo(f"The {account_id} is not known to this organization.")


@cli.command()
@cli.command() # type: ignore
@click.argument("output")
@click.pass_obj
def export(ctx: Context, output: str):
Expand Down
2 changes: 1 addition & 1 deletion landingzone_organization/cli/commands/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def cli():
pass


@cli.command()
@cli.command() # type: ignore
@click.argument("config-path")
@click.argument("ou-path")
@click.pass_obj
Expand Down
2 changes: 1 addition & 1 deletion landingzone_organization/cli/commands/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def cli():
pass


@cli.command()
@cli.command() # type: ignore
@click.pass_obj
def download(ctx: Context):
"""Download the organization structure"""
Expand Down
2 changes: 1 addition & 1 deletion landingzone_organization/cli/commands/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def cli():
pass


@cli.command()
@cli.command() # type: ignore
@click.argument("organization-name")
@click.option("--sso-start-url", prompt="SSO start URL")
@click.option("--sso-region", prompt="SSO region")
Expand Down
2 changes: 1 addition & 1 deletion landingzone_organization/cli/commands/workload.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def cli():
pass


@cli.command(name="list")
@cli.command(name="list") # type: ignore
@click.pass_obj
@click.option("-l", "--location")
def list_workloads(ctx: Context, location: Optional[str]):
Expand Down
10 changes: 5 additions & 5 deletions landingzone_organization/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ def accounts(self) -> List[Account]:
if not self._organization:
return []

unit: Union[Organization, OrganizationUnit] = self._organization
champion: Union[Organization, OrganizationUnit] = self._organization

for ou in self.organizational_unit:
unit = unit.by_name(ou)
challenger = champion.by_name(ou)

if not unit:
break
if challenger:
champion = challenger

return unit.accounts if unit else []
return champion.accounts # type: ignore

def organization(self, organization: Organization) -> None:
self._organization = organization
16 changes: 7 additions & 9 deletions landingzone_organization/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Organization:
def accounts_recursive(self) -> List[Account]:
return self.unit.accounts_recursive

def by_name(self, name: str) -> OrganizationUnit:
def by_name(self, name: str) -> Optional[OrganizationUnit]:
return self.unit.by_name(name)

def by_account_id(self, account_id: str) -> Optional[Account]:
Expand All @@ -38,20 +38,17 @@ def __resolve_organization_unit(
unit = unit.by_name(ou_name)

if not unit:
break
return None

return unit

def accounts(self, ou_names: List[str] = []) -> List[Account]:
unit = self.__resolve_organization_unit(ou_names) or self.unit
return unit.accounts_recursive

def workloads(self, ou_names: List[str]) -> Optional[Workloads]:
def workloads(self, ou_names: List[str]) -> Workloads:
workloads = Workloads(workloads=[])
unit = self.unit

if len(ou_names) > 0:
unit = self.__resolve_organization_unit(ou_names)
unit = self.__resolve_organization_unit(ou_names)

if unit:
for account in unit.accounts_recursive:
Expand All @@ -62,9 +59,10 @@ def workloads(self, ou_names: List[str]) -> Optional[Workloads]:

@property
def platform_accounts(self) -> List[Account]:
workload_accounts = self.workloads(ou_names=[]).accounts
workloads = self.workloads(ou_names=[])

workload_account_ids = list(
map(lambda account: account.account_id, workload_accounts)
map(lambda account: account.account_id, workloads.accounts)
)

def not_a_workload_account(account: Account):
Expand Down
2 changes: 1 addition & 1 deletion landingzone_organization/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def __init__(self, config_file: str):
self.__config_file = config_file
self.__config = configparser.ConfigParser()
self.__config.read(config_file)
self.__client_errors = {}
self.__client_errors: Dict[str, Exception] = {}

@property
def names(self) -> List[str]:
Expand Down
19 changes: 14 additions & 5 deletions landingzone_organization/schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
import yaml
from jsonschema import validate


schema_path = os.path.dirname(os.path.abspath(__file__))
from landingzone_organization.account import Account


class InvalidSchemaException(Exception):
Expand All @@ -14,7 +13,7 @@ def __init__(self, file: str, message: str):


def load_schema(file: str) -> dict:
with open(os.path.join(schema_path, file), "r") as f:
with open(file, "r") as f:
return yaml.safe_load(f)


Expand All @@ -29,5 +28,15 @@ def safe_load_file(schema: dict, file_path: str) -> dict:
return data


WorkloadSchema = load_schema("workload.yaml")
EnvironmentSchema = load_schema("environment.yaml")
def environment_resolver(path: str) -> Account:
data = safe_load_file(EnvironmentSchema, path)

return Account(
name=data["Name"],
account_id=data["AccountId"],
)


schema_path = os.path.dirname(os.path.abspath(__file__))
WorkloadSchema = load_schema(os.path.join(schema_path, "workload.yaml"))
EnvironmentSchema = load_schema(os.path.join(schema_path, "environment.yaml"))
10 changes: 10 additions & 0 deletions landingzone_organization/schemas/environment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
type: object
additionalProperties: False
required:
- AccountId
- Name
properties:
AccountId:
type: string
Name:
type: string
2 changes: 2 additions & 0 deletions landingzone_organization/schemas/workload.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ additionalProperties: False
required:
- Name
properties:
DisplayName:
type: string
Name:
type: string
Environments:
Expand Down
13 changes: 11 additions & 2 deletions landingzone_organization/workload.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@


class Workload:
def __init__(self, name: str, accounts: List[Account]) -> None:
def __init__(
self, name: str, display_name: Optional[str], accounts: List[Account]
) -> None:
self.__name = name
self.__display_name = display_name
self.__accounts = accounts

@property
def name(self) -> str:
return self.__name

@property
def display_name(self) -> Optional[str]:
return self.__display_name

@property
def accounts(self) -> List[Account]:
return sorted(self.__accounts, key=lambda x: x.weight)
Expand All @@ -32,4 +39,6 @@ def append(self, account: Account) -> None:

@staticmethod
def from_dict(data: dict, accounts: List[Account]) -> Workload:
return Workload(name=data["Name"], accounts=accounts)
return Workload(
name=data["Name"], display_name=data.get("DisplayName"), accounts=accounts
)
51 changes: 45 additions & 6 deletions landingzone_organization/workloads.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from __future__ import annotations
from typing import List, Optional, Set

import glob
import os
from typing import List, Optional, Set, Callable

from landingzone_organization.account import Account
from landingzone_organization.schemas import safe_load_file, WorkloadSchema
from landingzone_organization.workload import Workload
from landingzone_organization.filtering import resolve_workload_name

Expand Down Expand Up @@ -42,12 +46,47 @@ def environments(self) -> Set[str]:

def resolve_account(self, account: Account) -> None:
workload_name = resolve_workload_name(account.name)
workload = self.by_name(workload_name)

if not workload:
self.__workloads.append(Workload(name=workload_name, accounts=[account]))
else:
workload.append(account)
if workload_name:
workload = self.by_name(workload_name)

if not workload:
self.__workloads.append(
Workload(name=workload_name, display_name=None, accounts=[account])
)
else:
workload.append(account)

def by_name(self, name: str) -> Optional[Workload]:
return next(filter(lambda w: w.name == name, self.__workloads), None) # type: ignore

@staticmethod
def __load_workload_by_file(
environment_resolver: Callable[[str], Account], path: str
) -> Optional[Workload]:
data = safe_load_file(WorkloadSchema, path)

def convert_environments_to_file_locations(environment: str) -> str:
return os.path.join(os.path.dirname(path), f"{environment}.yaml")

accounts_files = list(
map(convert_environments_to_file_locations, data.get("Environments", []))
)
response = list(map(environment_resolver, accounts_files))
accounts = list(filter(None, response))

return Workload.from_dict(data, accounts)

@classmethod
def load_by_path(
cls, path: str, environment_resolver: Callable[[str], Account]
) -> Workloads:
def load_workload(workload_path: str) -> Optional[Workload]:
return cls.__load_workload_by_file(
environment_resolver=environment_resolver, path=workload_path
)

workloads = glob.glob(os.path.join(path, "**", "info.yaml"), recursive=True)
response = list(map(load_workload, workloads))

return Workloads(workloads=list(filter(None, response)))
Loading

0 comments on commit a3d00d9

Please sign in to comment.