-
Notifications
You must be signed in to change notification settings - Fork 276
Allow user to specify the violation payload format (csv / json) #1301
Changes from 14 commits
fe71b1a
5f717a4
3c63f37
769018c
0aac45c
1f7b29b
28ee1c4
b5b92f8
01c8b27
59a595c
7f61d0c
368df57
5af92d2
cecf5c9
3119daf
31f1175
9af2655
56a75b4
b6eef3a
17d11fa
210e9c7
b8c50cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,11 +14,14 @@ | |
|
||
"""Email notifier to perform notifications""" | ||
|
||
import tempfile | ||
|
||
from google.cloud.forseti.common.data_access import csv_writer | ||
from google.cloud.forseti.common.util import email | ||
from google.cloud.forseti.common.util import date_time | ||
from google.cloud.forseti.common.util import email | ||
from google.cloud.forseti.common.util import errors as util_errors | ||
from google.cloud.forseti.common.util import logger | ||
from google.cloud.forseti.common.util import parser | ||
from google.cloud.forseti.common.util import string_formats | ||
from google.cloud.forseti.notifier.notifiers import base_notification | ||
|
||
|
@@ -52,28 +55,14 @@ def __init__(self, resource, cycle_timestamp, | |
self.mail_util = email.EmailUtil( | ||
self.notification_config['sendgrid_api_key']) | ||
|
||
def _get_output_filename(self): | ||
"""Create the output filename. | ||
|
||
Returns: | ||
str: The output filename for the violations json. | ||
""" | ||
now_utc = date_time.get_utc_now_datetime() | ||
output_timestamp = now_utc.strftime( | ||
string_formats.TIMESTAMP_TIMEZONE_FILES) | ||
output_filename = string_formats.VIOLATION_CSV_FMT.format( | ||
self.resource, | ||
self.cycle_timestamp, | ||
output_timestamp) | ||
return output_filename | ||
|
||
def _make_attachment(self): | ||
"""Create the attachment object. | ||
def _make_attachment_csv(self): | ||
"""Create the attachment object in csv format. | ||
|
||
Returns: | ||
attachment: SendGrid attachment object. | ||
""" | ||
output_file_name = self._get_output_filename() | ||
output_file_name = self._get_output_filename( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: be consistent whether you want to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
string_formats.VIOLATION_CSV_FMT) | ||
with csv_writer.write_csv(resource_name='violations', | ||
data=self.violations, | ||
write_header=True) as csv_file: | ||
|
@@ -86,6 +75,25 @@ def _make_attachment(self): | |
|
||
return attachment | ||
|
||
def _make_attachment_json(self): | ||
"""Create the attachment object json format. | ||
|
||
Returns: | ||
attachment: SendGrid attachment object. | ||
""" | ||
output_file_name = self._get_output_filename( | ||
string_formats.VIOLATION_JSON_FMT) | ||
with tempfile.NamedTemporaryFile() as tmp_violations: | ||
tmp_violations.write(parser.json_stringify(self.violations)) | ||
tmp_violations.flush() | ||
LOGGER.info('JSON filename: %s', tmp_violations.name) | ||
attachment = self.mail_util.create_attachment( | ||
file_location=tmp_violations.name, | ||
content_type='text/csv', filename=output_file_name, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can you put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
content_id='Violations') | ||
|
||
return attachment | ||
|
||
def _make_content(self): | ||
"""Create the email content. | ||
|
||
|
@@ -121,11 +129,21 @@ def _compose(self, **kwargs): | |
|
||
email_map = {} | ||
|
||
attachment = self._make_attachment() | ||
subject, content = self._make_content() | ||
email_map['subject'] = subject | ||
email_map['content'] = content | ||
email_map['attachment'] = attachment | ||
data_format = self.notification_config.get('data_format', 'csv') | ||
if data_format not in ['json', 'csv']: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to suggest breaking out
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
LOGGER.error( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nicer to raise a custom exception here. Perhaps something like Then, you can remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
'Email violations: invalid data format: %s', data_format) | ||
else: | ||
attachment = None | ||
if data_format == 'csv': | ||
attachment = self._make_attachment_csv() | ||
else: | ||
attachment = self._make_attachment_json() | ||
subject, content = self._make_content() | ||
email_map['subject'] = subject | ||
email_map['content'] = content | ||
email_map['attachment'] = attachment | ||
|
||
return email_map | ||
|
||
def _send(self, **kwargs): | ||
|
@@ -156,4 +174,5 @@ def _send(self, **kwargs): | |
def run(self): | ||
"""Run the email notifier""" | ||
email_notification = self._compose() | ||
self._send(notification=email_notification) | ||
if email_notification: | ||
self._send(notification=email_notification) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,11 +14,12 @@ | |
|
||
"""Upload violations to GCS.""" | ||
|
||
import tempfile | ||
|
||
from google.cloud.forseti.common.data_access import csv_writer | ||
from google.cloud.forseti.common.gcp_api import storage | ||
from google.cloud.forseti.common.util import date_time | ||
from google.cloud.forseti.common.util import logger | ||
from google.cloud.forseti.common.util import parser | ||
from google.cloud.forseti.common.util import string_formats | ||
from google.cloud.forseti.notifier.notifiers import base_notification | ||
|
||
|
@@ -29,30 +30,46 @@ | |
class GcsViolations(base_notification.BaseNotification): | ||
"""Upload violations to GCS.""" | ||
|
||
def _get_output_filename(self): | ||
"""Create the output filename. | ||
def _upload_json(self, gcs_upload_path): | ||
"""Upload violations in json format. | ||
|
||
Returns: | ||
str: The output filename for the violations CSV file. | ||
Args: | ||
gcs_upload_path (string): the GCS upload path. | ||
""" | ||
now_utc = date_time.get_utc_now_datetime() | ||
output_timestamp = now_utc.strftime( | ||
string_formats.TIMESTAMP_TIMEZONE_FILES) | ||
output_filename = string_formats.VIOLATION_CSV_FMT.format( | ||
self.resource, self.cycle_timestamp, output_timestamp) | ||
return output_filename | ||
with tempfile.NamedTemporaryFile() as tmp_violations: | ||
tmp_violations.write(parser.json_stringify(self.violations)) | ||
tmp_violations.flush() | ||
storage_client = storage.StorageClient() | ||
storage_client.put_text_file(tmp_violations.name, gcs_upload_path) | ||
|
||
def run(self): | ||
"""Generate the temporary CSV file and upload to GCS.""" | ||
def _upload_csv(self, gcs_upload_path): | ||
"""Upload violations in csv format. | ||
|
||
Args: | ||
gcs_upload_path (string): the GCS upload path. | ||
""" | ||
with csv_writer.write_csv(resource_name='violations', | ||
data=self.violations, | ||
write_header=True) as csv_file: | ||
LOGGER.info('CSV filename: %s', csv_file.name) | ||
storage_client = storage.StorageClient() | ||
storage_client.put_text_file(csv_file.name, gcs_upload_path) | ||
|
||
gcs_upload_path = '{}/{}'.format( | ||
self.notification_config['gcs_path'], | ||
self._get_output_filename()) | ||
|
||
if gcs_upload_path.startswith('gs://'): | ||
storage_client = storage.StorageClient() | ||
storage_client.put_text_file(csv_file.name, gcs_upload_path) | ||
def run(self): | ||
"""Generate the temporary (CSV xor JSON) file and upload to GCS.""" | ||
if self.notification_config['gcs_path'].startswith('gs://'): | ||
data_format = self.notification_config.get('data_format', 'csv') | ||
if data_format not in ['json', 'csv']: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the second time this is checked, see my comment above in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
LOGGER.error('GCS upload: invalid data format: %s', data_format) | ||
elif data_format == 'csv': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like to break up the nested
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
gcs_upload_path = '{}/{}'.format( | ||
self.notification_config['gcs_path'], | ||
self._get_output_filename( | ||
string_formats.VIOLATION_CSV_FMT)) | ||
self._upload_csv(gcs_upload_path) | ||
else: | ||
gcs_upload_path = '{}/{}'.format( | ||
self.notification_config['gcs_path'], | ||
self._get_output_filename( | ||
string_formats.VIOLATION_JSON_FMT)) | ||
self._upload_json(gcs_upload_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you should just call this variable,
utc_now_datetime
since that is what you got back from above, and also shows why strftime can be called here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.