Skip to content

Commit 8154937

Browse files
committed
v1.2.0 - Additional Tags support
1 parent 9b4fdaa commit 8154937

5 files changed

Lines changed: 50 additions & 46 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## 1.2.0 - 2024-11-07
6+
### Added features
7+
- Added the ability to define tags for every resource created by the solution. This can be configured using the `configuration/solution_parameters/parameters.json` file.
8+
9+
### Changed
10+
- Re-implemented the `safe_time_compare` feature to better accommodate scheduling execution delays and avoid data transfer duplications.
11+
512
## 1.1.1 - 2024-10-25
6-
### Bug Fixes
13+
### Bug Fixes
714
- Fixed a typo on the CDK Stack preventing new deployments.
815
- Fixed an issue with the `sync_files` lambda function while comparing timestamps. For now the `safe_time_compare` feature is disabled until a better method can be implemented.
916

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ This project is built using Python3 and CDK, before you start, make sure to have
193193

194194
## Deployment
195195

196+
### Tagging resources
197+
By default the solution applies some native CDK and Name tags. If your deployment requires additional tags (for eg. Cost Allocation, Environment, Team, etc), you can now update the [solution parameters file](configuration/solution_parameters/parameters.json) by modifying the JSON for `additional_tags` with your specific Keys and Values.
198+
196199
### Permission Boundaries
197200
If you are enforcing the usage of IAM Permission Boundaries for IAM Roles created in the account, you can update the [solution parameters file](configuration/solution_parameters/parameters.json) and add the managed policy ARN you are using to the `permission_boundary_policy_arn` parameter.
198201

configuration/solution_parameters/parameters.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@
33
"powertools_service_name": "transfer-sync-service",
44
"powertools_log_level": "INFO",
55
"boto_version": "1.35.47",
6-
"pyawscron_version": "1.0.7"
6+
"pyawscron_version": "1.0.7",
7+
"additional_tags": {
8+
"Project": "transfer-sync-service",
9+
"Environment": "dev"
10+
}
711
}

transfer_sync_service/lambda/sync_files/sync_files.py

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -164,32 +164,28 @@ def should_transfer_file(file: Dict[str, Any], first_copy: bool, safe_time_compa
164164
# If it's the first copy, transfer all files
165165
if first_copy:
166166
return True
167-
168-
#############################################################################################################################
169-
## Need to implement a better safe_time_compare method to avoid cost duplication. Disabled for now. ##
170-
## Only data updated in source SFTP between previous execution and S3 Object timestamp should be copied. ##
171-
## Also need to add a feature flag to allow the user to decide if deleted files in target S3 Bucket needs to be re-copied. ##
172-
#############################################################################################################################
173167

174168
# For subsequent copies, check if the file is new or modified
175-
try:
176-
# Check if the file exists in the destination and compare modification times
177-
obj = s3.head_object(
178-
Bucket=event['SyncSetting']['LocalRepository']['BucketName'],
179-
Key=f"{event['SyncSetting']['LocalRepository']['Prefix']}{file['filePath']}"
180-
)
181-
if obj['LastModified'] < file_time:
182-
logger.info(f"File {file['filePath']} has been modified since last copy.")
169+
if safe_time_compare < file_time:
170+
try:
171+
# Check if the file exists in the destination and compare modification times
172+
obj = s3.head_object(
173+
Bucket=event['SyncSetting']['LocalRepository']['BucketName'],
174+
Key=f"{event['SyncSetting']['LocalRepository']['Prefix']}{file['filePath']}"
175+
)
176+
if obj['LastModified'] < file_time:
177+
logger.info(f"File {file['filePath']} has been modified since last copy.")
178+
return True
179+
else:
180+
logger.info(f"File {file['filePath']} has not been modified since last copy.")
181+
return False
182+
except botocore.exceptions.ClientError:
183+
# If the file doesn't exist in the destination, it should be transferred
184+
logger.info(f"File {file['filePath']} has not been copied before.")
183185
return True
184-
# elif obj['LastModified'] >= safe_time_compare:
185-
# logger.info(f"File {file['filePath']} has been modified since safe time.")
186-
else:
187-
logger.info(f"File {file['filePath']} has not been modified since last copy.")
188-
return False
189-
except botocore.exceptions.ClientError:
190-
# If the file doesn't exist in the destination, it should be transferred
191-
logger.info(f"File {file['filePath']} has not been copied before.")
192-
return True
186+
else:
187+
logger.info(f"File {file['filePath']} is not new.")
188+
return False
193189

194190
def transfer_files(file_list: List[str], event: Dict[str, Any]) -> None:
195191
"""
@@ -245,27 +241,15 @@ def calculate_safe_time_compare(schedule: str, start_time: datetime) -> datetime
245241
Returns:
246242
datetime: The safe time to compare against.
247243
"""
248-
# Get the next 2 scheduled times
249-
next_times = AWSCron.get_next_n_schedule(2, start_time, schedule)
250-
251-
if len(next_times) < 2:
252-
# If we can't get 2 next times, use the start time
253-
return start_time
254-
255-
# Calculate the interval between executions
256-
interval = (next_times[1] - next_times[0]).total_seconds() / 60 # in minutes
257244

258-
if 1 <= interval <= 5:
259-
n = 3
260-
elif 5 < interval <= 10:
261-
n = 2
262-
elif 10 < interval <= 60:
263-
n = 1
264-
else:
265-
n = 0
245+
# Getting the previous 2 executions time based on the cron schedule
246+
prev_times = AWSCron.get_prev_n_schedule(2, start_time, schedule)
247+
248+
# Calculating the time gap between execution to accommodate execution delays safely
249+
start_diff = (start_time - prev_times[0]).total_seconds()
250+
expected_diff = (prev_times[0] - prev_times[1]).total_seconds()
266251

267-
if n > 0:
268-
prev_times = AWSCron.get_prev_n_schedule(n, start_time, schedule)
269-
return prev_times[-1] if prev_times else start_time
252+
if expected_diff > start_diff:
253+
return prev_times[1]
270254
else:
271-
return start_time
255+
return prev_times[0]

transfer_sync_service/transfer_sync_service_stack.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Stack,
55
RemovalPolicy,
66
CfnTag,
7+
Tags,
78
aws_s3 as s3,
89
aws_sns as sns,
910
aws_iam as iam,
@@ -29,6 +30,7 @@
2930

3031
boto_version = solution_parameters['boto_version']
3132
permission_boundary_policy_arn = solution_parameters['permission_boundary_policy_arn']
33+
additional_tags = solution_parameters['additional_tags']
3234

3335
class TransferSyncServiceStack(Stack):
3436
def install_package(package, target):
@@ -101,6 +103,10 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
101103
)
102104
iam.PermissionsBoundary.of(self).apply(boundary_policy)
103105

106+
if additional_tags != '':
107+
for key, value in additional_tags.items():
108+
Tags.of(self).add(key=key,value=value)
109+
104110
# Monitoring services
105111
# Create SNS topic for notifications
106112
notification_topic = sns.Topic(self, 'NotificationTopic')

0 commit comments

Comments
 (0)