Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Add location scanner #2075

Merged
merged 12 commits into from Oct 16, 2018
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions configs/server/forseti_conf_server.yaml.in
Expand Up @@ -117,6 +117,8 @@ scanner:
enabled: true
- name: lien
enabled: true
- name: location
enabled: true
- name: log_sink
enabled: true
- name: service_account_key
Expand Down Expand Up @@ -362,6 +364,22 @@ notifier:
# gcs_path should begin with "gs://"
gcs_path: gs://{FORSETI_BUCKET}/scanner_violations

- resource: location_violations
should_notify: true
notifiers:
# Email violations
- name: email_violations
configuration:
sendgrid_api_key: {SENDGRID_API_KEY}
sender: {EMAIL_SENDER}
recipient: {EMAIL_RECIPIENT}
# Upload violations to GCS.
- name: gcs_violations
configuration:
data_format: csv
# gcs_path should begin with "gs://"
gcs_path: gs://{FORSETI_BUCKET}/scanner_violations

- resource: log_sink_violations
should_notify: true
notifiers:
Expand Down
1 change: 1 addition & 0 deletions google/cloud/forseti/common/data_access/violation_map.py
Expand Up @@ -43,6 +43,7 @@
'INSTANCE_NETWORK_INTERFACE_VIOLATION': (
'instance_network_interface_violations'),
'LIEN_VIOLATION': 'lien_violations',
'LOCATION_VIOLATION': 'location_violations',
'LOG_SINK_VIOLATION': 'log_sink_violations',
'SERVICE_ACCOUNT_KEY_VIOLATION': (
'service_account_key_violations'),
Expand Down
30 changes: 30 additions & 0 deletions google/cloud/forseti/common/gcp_type/bucket.py
Expand Up @@ -16,6 +16,8 @@
See: https://cloud.google.com/storage/docs/json_api/v1/
"""

import json

from google.cloud.forseti.common.gcp_type import resource


Expand All @@ -37,6 +39,7 @@ def __init__(
name=None,
display_name=None,
parent=None,
locations=None,
lifecycle_state=BucketLifecycleState.UNSPECIFIED):
"""Initialize.

Expand All @@ -47,6 +50,8 @@ def __init__(
name (str): The bucket's unique GCP name, with the
format "buckets/{id}".
display_name (str): The bucket's display name.
locations (List[str]): Locations this bucket resides in. If set,
there should be exactly one element in the list.
parent (Resource): The parent Resource.
lifecycle_state (LifecycleState): The lifecycle state of the
bucket.
Expand All @@ -57,6 +62,31 @@ def __init__(
name=name,
display_name=display_name,
parent=parent,
locations=locations,
lifecycle_state=lifecycle_state)
self.full_name = full_name
self.data = data

@classmethod
def from_json(cls, parent, json_string):
"""Create a bucket from a JSON string.

Args:
parent (Resource): resource this bucket belongs to.
json_string(str): JSON string of a bucket GCP API response.

Returns:
Bucket: bucket resource.
"""
bucket_dict = json.loads(json_string)

bucket_id = bucket_dict['id']
return cls(
parent=parent,
bucket_id=bucket_id,
name=cls.RESOURCE_NAME_FMT % bucket_id,
full_name='{}bucket/{}/'.format(parent.full_name, bucket_id),
display_name=bucket_id,
locations=[bucket_dict['location']],
data=json_string,
)
15 changes: 15 additions & 0 deletions google/cloud/forseti/common/gcp_type/resource.py
Expand Up @@ -116,6 +116,7 @@ def __init__(
name=None,
display_name=None,
parent=None,
locations=None,
lifecycle_state=LifecycleState.UNSPECIFIED):
"""Initialize.

Expand All @@ -125,6 +126,10 @@ def __init__(
name (str): The resource unique name,
e.g. "{resource type}/{id}".
display_name (str): The resource display name.
locations (List[str]): Locations the resource resides in. Some
resources have multiple locations (e.g. GKE), some support
have a single location (e.g. GCS), while some have none
(e.g. Project).
parent (Resource): The parent Resource object.
lifecycle_state (LifecycleState): The lifecycle state of the
Resource.
Expand All @@ -140,6 +145,7 @@ def __init__(
# organization has no parent, whereas projects and folders can
# have either another folder or organization as a parent.
self._parent = parent
self._locations = locations
self._lifecycle_state = lifecycle_state

def __eq__(self, other):
Expand Down Expand Up @@ -229,6 +235,15 @@ def parent(self):
"""
return self._parent

@property
def locations(self):
"""Locations the resource resides in.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention that some resources support multiple locations (GKE), others will always have a single location (GCS/BQ/...), and others do not support location (will return None).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


Returns:
List[str]: Locations the resource resides in.
"""
return self._locations

@property
def lifecycle_state(self):
"""Lifecycle state.
Expand Down
21 changes: 21 additions & 0 deletions google/cloud/forseti/common/gcp_type/resource_util.py
Expand Up @@ -79,6 +79,27 @@ def create_resource(resource_id, resource_type, **kwargs):
resource_id, **kwargs)


def create_resource_from_json(resource_type, parent, json_string):
"""Factory to create a certain kind of Resource from JSON data.

Args:
resource_type (str): The resource type.
parent (Resource): parent resource of this type.
json_string (str): resource's JSON data.

Returns:
Resource: The new Resource based on the type, if supported,
otherwise None.
"""
if resource_type not in _RESOURCE_TYPE_MAP:
return None
resource_type = _RESOURCE_TYPE_MAP[resource_type]
if not resource_type.get('can_create_resource'):
return None

return resource_type.get('class').from_json(parent, json_string)


def get_ancestors_from_full_name(full_name):
"""Creates a Resource for each resource in the full ancestory path.

Expand Down