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

get projects beyond the org's root #58

Merged
merged 10 commits into from
Dec 27, 2021
2 changes: 1 addition & 1 deletion chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def send_messages_to_chat(projects_by_owner):
send_message_to_this_owner = False
for project in projects_by_owner.get(owner):
project_id = project.get('projectId')
org = ORGS_NAME_MAPPING.get(project.get('parent').get('id'))
org = ORGS_NAME_MAPPING.get(project.get('org'))
created_days_ago = int(project.get('createdDaysAgo'))
cost = project.get('costSincePreviousMonth', 0.0)
currency = project.get('costCurrency', '$')
Expand Down
5 changes: 4 additions & 1 deletion example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ filters:
projects:
- my-special-project
- my-other-special-project
# Activate/deactivate organization information integration.
org_info:
activate: false
slack:
# Activate/deactivate Slack messages integration.
activate: false
Expand Down Expand Up @@ -72,11 +75,11 @@ org_names_mapping:
'8888888888888': my-other-org
# Activate/deactivate further debugging info.
debug:
active_projects: false
enriched_projects: false
filtered_by_projects: false
filtered_by_users: false
filtered_by_age: false
filtered_by_org: false
grouped_by_owners: false
# Filename for a dump of the projects with enriched information in JSON format.
# Set null to not generate the dump file.
Expand Down
7 changes: 2 additions & 5 deletions filters.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import re


def filter_active_projects_matching_org_level(orgs):
def filter_projects_matching_org_level(orgs):
def filter_projects(project):
if project.get('lifecycleState') == 'ACTIVE' and\
project.get('parent') and\
project.get('parent').get('type') == 'organization'and\
project.get('parent').get('id') in orgs:
if project.get('org') in orgs:
return True
else:
return False
Expand Down
69 changes: 52 additions & 17 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
group_projects_by_owner
)
from filters import (
filter_active_projects_matching_org_level,
filter_projects_matching_org_level,
filter_older_than,
filter_owners,
filter_users,
Expand All @@ -36,33 +36,34 @@
CHAT_ACTIVATED = CONFIG['chat']['activate'].get(bool)
BILLING_ACTIVATED = CONFIG['billing']['activate'].get(bool)
DUMP_JSON_FILE_NAME = CONFIG['dump_json_file_name'].get()
ORGS_ACTIVATED = CONFIG['org_info']['activate'].get(bool)

DEBUG_ACTIVE_PROJECTS = CONFIG['debug']['active_projects'].get(bool)
DEBUG_ENRICHED_PROJECTS = CONFIG['debug']['enriched_projects'].get(bool)
DEBUG_FILTERED_BY_PROJECTS = CONFIG['debug']['filtered_by_projects'].get(bool)
DEBUG_FILTERED_BY_USERS = CONFIG['debug']['filtered_by_users'].get(bool)
DEBUG_FILTERED_BY_AGE = CONFIG['debug']['filtered_by_age'].get(bool)
DEBUG_GROUPED_BY_OWNERS = CONFIG['debug']['grouped_by_owners'].get(bool)
DEBUG_FILTERED_BY_ORGS = CONFIG['debug']['filtered_by_org'].get(bool)

def main():
client = _get_resource_manager_client()
client_v1 = _get_resource_manager_client_v1()

logger.info('Retrieving Projects.')
projects = _get_projects(client)

logger.info('Filtering active Projects.')
active_projects = list(filter(filter_active_projects_matching_org_level(
ORGS_FILTER), projects))

if DEBUG_ACTIVE_PROJECTS:
logger.debug('Active Projects:\n%s', pformat(active_projects))
active_projects = _get_projects(client)

logger.info('Calculating Project age information.')
enriched_projects = _enrich_project_info_with_age(active_projects)

logger.info('Retrieving Project owners information.')
enriched_projects = _enrich_project_info_with_owners(client, enriched_projects)

if ORGS_ACTIVATED:
logger.info('Retrieving Project organization information.')
enriched_projects = _enrich_project_info_with_orgs(client_v1, enriched_projects)
else:
logger.info('Project organization information is not active.')

if BILLING_ACTIVATED:
logger.info('Retrieving Project cost information.')
enriched_projects = _enrich_project_info_with_costs(enriched_projects)
Expand Down Expand Up @@ -98,8 +99,16 @@ def main():
if DEBUG_FILTERED_BY_AGE:
logger.debug('Aged Projects filter applied:\n%s', pformat(older_projects))

logger.info('Filtering Projects by org level.')

org_projects = list(filter(filter_projects_matching_org_level(
ORGS_FILTER), older_projects))

if DEBUG_FILTERED_BY_ORGS:
logger.debug('Project by orgs:\n%s', pformat(org_projects))

logger.info('Grouping Projects by owner(s).')
projects_by_owner = group_projects_by_owner(older_projects)
projects_by_owner = group_projects_by_owner(org_projects)

if DEBUG_GROUPED_BY_OWNERS:
logger.debug('Project by owner:\n%s', pformat(projects_by_owner))
Expand All @@ -122,7 +131,7 @@ def main():


def _get_projects(client):
project_list_request = client.projects().list()
project_list_request = client.projects().search(query='state:ACTIVE')
project_list_response = project_list_request.execute()
projects = project_list_response.get('projects', [])
while project_list_response.get('nextPageToken'):
Expand All @@ -134,10 +143,15 @@ def _get_projects(client):


def _get_resource_manager_client():
client = discovery.build("cloudresourcemanager", "v1")
client = discovery.build("cloudresourcemanager", "v3")
return client


def _get_resource_manager_client_v1():
client_v1 = discovery.build("cloudresourcemanager", "v1")
return client_v1


def _enrich_project_info_with_owners(client, projects):
for project in projects:
project['owners'] = _get_owners(client, project)
Expand Down Expand Up @@ -172,6 +186,13 @@ def _enrich_project_info_with_costs(projects):
return projects


def _enrich_project_info_with_orgs(client_v1, projects):
for project in projects:
project['org'] = _get_organization(client_v1, project)
logger.debug('Organization root for Project %s: %s',project.get('projectId'), project.get('org'))
return projects


andersonj-cit marked this conversation as resolved.
Show resolved Hide resolved
def _get_cost_since_previous_month_full(costs_by_project, project):
project_id = project.get('projectId')
cost = costs_by_project.get(project_id, {})
Expand Down Expand Up @@ -217,21 +238,35 @@ def _get_owners_id(owners):

def _get_owners(client, project):
users = []
project_id = project.get('projectId')
iamPolicy = client.projects().getIamPolicy(resource=project_id, body={}).execute()
project_name = project.get('name')
iamPolicy = client.projects().getIamPolicy(resource=project_name, body={}).execute()
bindings = iamPolicy.get('bindings', [])
owners = list(filter(filter_owners, bindings))
if not owners:
logger.debug('No owners found for Project %s.', project_id)
logger.debug('No owners found for Project %s.', project_name)
else:
members = owners[0].get('members')
users = list(filter(filter_users, members))
if not users:
logger.debug('No owner is a user for Project %s.', project_id)
logger.debug('No owner is a user for Project %s.', project_name)
users = set([user.strip('user:') for user in users])
return list(users)


def _get_organization(client_v1, project):
projectId = project.get('projectId')
ancestry_request = client_v1.projects().getAncestry(projectId=projectId, body=None)
ancestry_response=ancestry_request.execute()
for resourceId in ancestry_response['ancestor']:
if resourceId['resourceId']['type'] in ['organization','project', 'folder']:
if resourceId['resourceId']['type'] == 'organization':
org = resourceId['resourceId']['id']
else:
org = 'No organization'
logger.debug('No organization info for project %s.', projectId)
return org


def _get_created_days_ago(project):
now = dt.now()
create_time = project.get('createTime')
Expand Down
2 changes: 1 addition & 1 deletion slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def send_messages(projects_by_owner):
send_message_to_this_owner = False
for project in projects_by_owner.get(owner):
project_id = project.get('projectId')
org = ORGS_NAME_MAPPING.get(project.get('parent').get('id'))
org = ORGS_NAME_MAPPING.get(project.get('org'))
created_days_ago = int(project.get('createdDaysAgo'))
cost = project.get('costSincePreviousMonth', 0.0)
currency = project.get('costCurrency', '$')
Expand Down