Skip to content

Commit

Permalink
Add resource based validation
Browse files Browse the repository at this point in the history
  • Loading branch information
qtc-de committed Jun 26, 2024
1 parent d2c667d commit 5994ab9
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 13 deletions.
1 change: 1 addition & 0 deletions tricot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
from .tricot import *
from .validation import *
from .extractor import *
from .resource import *

name = 'tricot'
4 changes: 2 additions & 2 deletions tricot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,8 @@ def main():
tricot.Logger.print_with_indent_blue(str(e), e=True)
sys.exit(tricot.constants.PARSER_ERROR)

except tricot.TricotRequiredFile as e:
tricot.Logger.print_mixed_yellow('Error: Test configuration requires missing file:', str(e), e=True)
except tricot.ResourceValidationException as e:
tricot.Logger.print_mixed_yellow('Error: a resource validation has failed:', str(e), e=True)
tricot.Logger.print_mixed_blue('Affected configuration:', wrapper.path, e=True)
sys.exit(tricot.constants.MISSING_RESOURCE)

Expand Down
112 changes: 112 additions & 0 deletions tricot/resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python3

from __future__ import annotations

import os
import pathlib
import hashlib
import requests

from tricot.logging import Logger


class ResourceValidationException(Exception):
'''
'''


class Resource:
'''
The resource class represents an additional resource that
is required for tricot to run. These can be specified within
tricot configuration files.
'''

def __init__(self, attrs: dict) -> None:
'''
Initialize the resource with attributes taken from the
yaml file.
Parameters:
attrs attributes to initialize the resource with
Returns:
None
'''
self.url = None
self.path = None
self.hash = None
self.mode = None

for key, value in attrs.items():

if key == 'path':
self.path = value

elif key == 'url':
self.url = value

elif key == 'hash':
self.hash = value

elif key == 'mode':
self.mode = value

else:
raise ResourceValidationException('Unknown resource attribute:' + key)

if self.path is None:
raise ResourceValidationException('Required "path" attribute is missing.')


def validate(self) -> None:
'''
Validates whether the resource has the correct state.
Missing files are downloaded if url attribute was specified.
Files with incorrect permissions are adjusted.
Parameters:
None
Returns:
None
'''
path = pathlib.Path(self.path).expanduser()

if not path.is_file():

if not self.url:
raise ResourceValidationException(f'{path}: does not exist.')

Logger.print_mixed_yellow('Downloading missing resource from:', self.url)
r = requests.get(self.url)

if r.status_code != 200:
raise ResourceValidationException(f'{self.url}: did not return 200.')

Logger.print_mixed_blue('Writing resource data to:', str(path))
path.write_bytes(r.content)

if self.mode:
Logger.print_mixed_blue('Adjusting permissions of resource to:', self.mode)
os.chmod(path, int(self.mode, 8))

if self.hash:

triggered = False
content = path.read_bytes()

for hash_type in ['md5', 'sha1', 'sha256', 'sha512']:

hash_value = self.hash.get(hash_type)

if hash_value is not None:

triggered = True
computed = hashlib.new(hash_type, content).hexdigest()

if computed != hash_value:
raise ResourceValidationException(f'{path}: {computed} != {hash_value}')

if not triggered:
raise ResourceValidationException('hash attribute contains no valid hash types.')
19 changes: 8 additions & 11 deletions tricot/tricot.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from tricot.plugin import Plugin
from tricot.command import Command
from tricot.condition import Condition
from tricot.resource import Resource


skip_until = None
Expand All @@ -40,12 +41,6 @@ def __init__(self, message: str, path: Path = None) -> None:
super().__init__(message)


class TricotRequiredFile(Exception):
'''
Custom exception class for a missing required file.
'''


class TricotRequiredCommand(Exception):
'''
Custom exception class for a missing required command.
Expand Down Expand Up @@ -864,13 +859,15 @@ def check_requirements(self):

for file in requires.get('files', []):

if type(file) is str and not Path(file).exists():
raise ExceptionWrapper(TricotRequiredFile(file), self.path)
try:
if type(file) is str:
Resource({'path': file}).validate()

elif type(file) is dict:
elif type(file) is dict:
Resource(file).validate()

if not tricot.utils.file_integrity(file):
raise ExceptionWrapper(TricotRequiredFile(file.get('filename') + " (wrong hash value)"), self.path)
except Exception as e:
raise ExceptionWrapper(e, self.path)

for command in requires.get('commands', []):

Expand Down

0 comments on commit 5994ab9

Please sign in to comment.