Skip to content

Commit a26e63e

Browse files
author
Victor Machado
committed
Added "froster test" command
1 parent 6edc357 commit a26e63e

File tree

3 files changed

+149
-195
lines changed

3 files changed

+149
-195
lines changed

Diff for: froster/froster.py

+148-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"""
77

88
# internal modules
9+
import random
10+
import string
911
from textual.widgets import DataTable, Footer, Button
1012
from textual.widgets import Label, Input, LoadingIndicator
1113
from textual.screen import ModalScreen
@@ -1776,30 +1778,63 @@ def create_bucket(self, bucket_name, region):
17761778
print_error()
17771779
return False
17781780

1779-
def delete_bucket(self, bucket_name):
1780-
'''Delete the given bucket'''
1781+
def empty_bucket(self, bucket_name):
17811782

1783+
if not bucket_name:
1784+
raise ValueError('No bucket name provided')
1785+
1786+
# Added a check to prevent accidental deletion of buckets
17821787
if os.environ.get('DEBUG') != '1':
1783-
raise ValueError('Buckets cannot be deleted outside DEBUG mode.')
1788+
raise ValueError('Objects in bucket cannot be deleted outside DEBUG mode.')
1789+
1790+
# Additional check to prevent accidental deletion of buckets
1791+
if not bucket_name.startswith('froster-unittest') and not bucket_name.startswith('froster-cli-test'):
1792+
raise ValueError('Bucket name must start with "froster-unittest" or "froster-cli-test" to be emptied.')
1793+
1794+
try:
1795+
paginator = self.s3_client.get_paginator('list_objects_v2')
1796+
for page in paginator.paginate(Bucket=bucket_name):
1797+
for obj in page.get('Contents', []):
1798+
# print(f"Deleting {obj['Key']}")
1799+
self.s3_client.delete_object(Bucket=bucket_name, Key=obj['Key'])
1800+
1801+
return True
1802+
1803+
except Exception:
1804+
print_error()
1805+
return False
1806+
1807+
def delete_bucket(self, bucket_name):
1808+
'''Delete the given bucket'''
17841809

17851810
if not bucket_name:
17861811
raise ValueError('No bucket name provided')
1812+
1813+
# Added a check to prevent accidental deletion of buckets
1814+
if os.environ.get('DEBUG') != '1':
1815+
raise ValueError('Objects in bucket cannot be deleted outside DEBUG mode.')
17871816

1817+
# Additional check to prevent accidental deletion of buckets
1818+
if not bucket_name.startswith('froster-unittest') and not bucket_name.startswith('froster-cli-test'):
1819+
raise ValueError('Bucket name must start with "froster-unittest" or "froster-cli-test" to be deleted.')
1820+
17881821
try:
17891822

17901823
s3_buckets = self.get_buckets()
17911824

17921825
# Delete the bucket if its exists
17931826
if bucket_name in s3_buckets:
1827+
self.empty_bucket(bucket_name)
17941828
self.s3_client.delete_bucket(Bucket=bucket_name)
17951829
log(f'Bucket {bucket_name} deleted\n')
17961830

17971831
# This is here in case there is a mistake and the S3 buckets are not deleted
1798-
# This will erase all the froster-unittest* buckets at once
1799-
elif bucket_name == "froster-unittest":
1832+
# This will erase all the froster-unittest* or froster-cli-test buckets at once
1833+
elif bucket_name == "froster-unittest" or bucket_name == "froster-cli-test":
18001834
for bucket in s3_buckets:
18011835
if bucket.startswith(bucket_name):
18021836
try:
1837+
self.empty_bucket(bucket_name)
18031838
self.s3_client.delete_bucket(Bucket=bucket)
18041839
except Exception as e:
18051840
print(f'Error: {e}')
@@ -1874,7 +1909,7 @@ def get_regions(self):
18741909
print_error()
18751910
sys.exit(1)
18761911

1877-
def list_objects_in_bucket(self, bucket_name):
1912+
def get_objects(self, bucket_name):
18781913
'''List all the objects in the given bucket'''
18791914

18801915
if not bucket_name:
@@ -6878,6 +6913,104 @@ def subcmd_umount(self, arch: Archiver):
68786913
print_error()
68796914
return False
68806915

6916+
def subcmd_test(self, cfg: ConfigManager, arch: Archiver, aws: AWSBoto):
6917+
'''Test basic functionality of Froster'''
6918+
6919+
try:
6920+
def tearDown(bucket_name, folder_path):
6921+
# Delete the directory
6922+
shutil.rmtree(folder_path)
6923+
6924+
# Delete the bucket
6925+
os.environ['DEBUG'] = '1'
6926+
aws.delete_bucket(bucket_name)
6927+
6928+
log('\nTESTING FROSTER...')
6929+
6930+
if not aws.check_credentials(prints=True):
6931+
log('\nTESTING FAILED\n')
6932+
return False
6933+
6934+
# Generate a random bucket name
6935+
rnd = ''.join(random.choices(
6936+
string.ascii_lowercase + string.digits, k=4))
6937+
new_bucket_name = f'froster-cli-test-{rnd}'
6938+
6939+
# Set temporary this new bucket name in the configuration
6940+
cfg.bucket_name = new_bucket_name
6941+
6942+
# Create a dummy file
6943+
folder_path = tempfile.mkdtemp(prefix='froster_test_')
6944+
file_path = os.path.join(folder_path, 'dummy_file')
6945+
6946+
log(f'\nCreating dummy file {file_path}...')
6947+
6948+
with open(file_path, 'wb') as f:
6949+
f.truncate(1)
6950+
6951+
subprocess.run(['touch', file_path])
6952+
6953+
log(f' ....dummy file create')
6954+
6955+
# Create a new bucket
6956+
if not aws.create_bucket(bucket_name=new_bucket_name, region=cfg.region):
6957+
tearDown(new_bucket_name, folder_path)
6958+
log('\nTESTING FAILED\n')
6959+
return False
6960+
6961+
# Mocking the index arguments
6962+
self.args.folders = [folder_path]
6963+
self.args.permissions = False
6964+
self.args.pwalkcopy = None
6965+
6966+
# Running index command
6967+
if not self.subcmd_index(cfg, arch):
6968+
tearDown(new_bucket_name, folder_path)
6969+
log('\nTESTING FAILED\n')
6970+
return False
6971+
6972+
# Mocking the archive arguments
6973+
self.args.older = 0
6974+
self.args.newer = 0
6975+
self.args.reset = False
6976+
self.args.recursive = False
6977+
self.args.nih = False
6978+
self.args.nihref = None
6979+
self.args.notar = True # True to check by file name in the archive
6980+
self.args.force = False
6981+
self.args.noslurm = True # Avoid slurm execution
6982+
6983+
# Running archive command
6984+
if not self.subcmd_archive(arch, aws):
6985+
tearDown(new_bucket_name, folder_path)
6986+
log('\nTESTING FAILED\n')
6987+
return False
6988+
6989+
# Mocking the delete command
6990+
self.args.bucket = None
6991+
self.args.debug = False
6992+
self.args.recursive = False
6993+
self.args.noslurm = True # Avoid slurm execution
6994+
6995+
# Running delete command
6996+
if not self.subcmd_delete(arch, aws):
6997+
tearDown(new_bucket_name, folder_path)
6998+
log('\nTESTING FAILED\n')
6999+
return False
7000+
7001+
# Clean up
7002+
tearDown(new_bucket_name, folder_path)
7003+
7004+
log('\nTEST SUCCESSFULLY COMPLETED\n')
7005+
7006+
return True
7007+
7008+
except Exception:
7009+
print_error()
7010+
aws.delete_bucket(new_bucket_name)
7011+
log('\nTESTING FAILED\n')
7012+
return False
7013+
68817014
def subcmd_ssh(self, cfg: ConfigManager, aws: AWSBoto):
68827015
'''SSH into an AWS EC2 instance'''
68837016

@@ -7281,6 +7414,13 @@ def parse_arguments(self):
72817414
parser_update.add_argument('--rclone', '-r', dest='rclone', action='store_true',
72827415
help="Update rclone to latests version")
72837416

7417+
# ***
7418+
7419+
parser_test = subparsers.add_parser('test', aliases=['tst'],
7420+
description=textwrap.dedent(f'''
7421+
Test basic functionality of Froster
7422+
'''), formatter_class=argparse.RawTextHelpFormatter)
7423+
72847424
return parser
72857425

72867426

@@ -7511,6 +7651,8 @@ def main():
75117651
# Calling check_update as we are checking for udpates (used to store the last timestamp check)
75127652
cfg.check_update()
75137653
res = cmd.subcmd_update(mute_no_update=False)
7654+
elif args.subcmd in ['test', 'tst']:
7655+
res = cmd.subcmd_test(cfg, arch, aws)
75147656
else:
75157657

75167658
# Check credentials

Diff for: pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "froster"
7-
version = "0.16.11"
7+
version = "0.16.12"
88
description = "Froster is a tool for easy data transfer between local file systems and AWS S3 storage."
99
authors = ["Victor Machado <[email protected]>"]
1010
readme = "README.md"

0 commit comments

Comments
 (0)