Skip to content

Commit 18355b1

Browse files
authored
Merge pull request #133 from kevinrizza/add-filename-to-logs
Add filename to warnings and errors
2 parents 77aea37 + 55f5519 commit 18355b1

File tree

5 files changed

+129
-83
lines changed

5 files changed

+129
-83
lines changed

operatorcourier/build.py

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
import yaml
23
import operatorcourier.identify as identify
34

@@ -9,26 +10,58 @@ def _get_empty_bundle(self):
910
customResourceDefinitions=[],
1011
clusterServiceVersions=[],
1112
packages=[],
13+
),
14+
metadata=dict(
15+
filenames=dict()
1216
)
1317
)
1418

1519
def _get_field_entry(self, yamlContent):
1620
yaml_type = identify.get_operator_artifact_type(yamlContent)
1721
return yaml_type[0:1].lower() + yaml_type[1:] + 's'
1822

19-
def _updateBundle(self, operatorBundle, yamlContent):
20-
field_entry = operatorBundle["data"][self._get_field_entry(yamlContent)]
21-
field_entry.append(yaml.safe_load(yamlContent))
23+
def _get_relative_path(self, path):
24+
"""
25+
:param path: the path of the file
26+
:return: the file name along with its parent folder
27+
"""
28+
path = os.path.normpath(path)
29+
parts = path.split(os.sep)
30+
return os.path.join(parts[-2], parts[-1])
31+
32+
def _updateBundle(self, operatorBundle, file_name, yaml_string):
33+
# Determine which operator file type the yaml is
34+
operator_artifact = self._get_field_entry(yaml_string)
35+
36+
# Marshal the yaml into a dictionary
37+
yaml_data = yaml.safe_load(yaml_string)
38+
39+
# Add the data dictionary to the correct list
40+
operatorBundle["data"][operator_artifact].append(yaml_data)
41+
42+
# Encode the dictionary into a string, then use that as a key to reference
43+
# the file name associated with that yaml file. Then add it to the metadata.
44+
if file_name != "":
45+
unencoded_yaml = yaml.dump(yaml_data)
46+
relative_path = self._get_relative_path(file_name)
47+
operatorBundle["metadata"]["filenames"][hash(unencoded_yaml)] = relative_path
48+
2249
return operatorBundle
2350

24-
def build_bundle(self, strings):
25-
"""build_bundle takes an array of yaml files and generates a 'bundle'
51+
def build_bundle(self, bundle_data):
52+
"""build_bundle takes bundle_data, a list of yaml files and
53+
associated metadata, and generates a 'bundle'
2654
with those yaml files generated in the bundle format.
2755
28-
:param strings: Array of yaml strings to bundle
56+
:param bundle_data: Array of tuples consisting of yaml blobs
57+
and associated metadata for those blobs
2958
"""
59+
# Generate an empty bundle
3060
bundle = self._get_empty_bundle()
31-
for item in strings:
32-
bundle = self._updateBundle(bundle, item)
61+
62+
# For each file, append the file to the right place in the bundle
63+
# and add the associated metadata to the metadata field
64+
for data in bundle_data:
65+
bundle = self._updateBundle(bundle, data[0], data[1])
3366

3467
return bundle

operatorcourier/validate.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import json
33
import semver
4+
import yaml
45

56
import validators as v
67

@@ -10,11 +11,19 @@
1011
metadata_annotations_required_fields,
1112
spec_required_fields)
1213

14+
log_info = {'current_manifest_file': ''}
1315
logger = logging.getLogger(__name__)
16+
logger.propagate = False
17+
handler = logging.StreamHandler()
18+
formatter = logging.Formatter('%(levelname)s: %(message)s [%(current_manifest_file)s]')
19+
handler.setFormatter(formatter)
20+
logger.addHandler(handler)
21+
logger = logging.LoggerAdapter(logger, log_info)
1422

1523

1624
class ValidateCmd():
1725
dataKey = "data"
26+
metadataKey = "metadata"
1827
crdKey = "customResourceDefinitions"
1928
csvKey = "clusterServiceVersions"
2029
pkgsKey = "packages"
@@ -46,6 +55,17 @@ def _log_error(self, message, *args, **kwargs):
4655
self.validation_json['errors'].append(message % args)
4756
logger.error(message, *args, **kwargs)
4857

58+
def get_filename_from_metadata(self, metadata, yaml_dict):
59+
"""
60+
:param yaml_dict: The yaml dictionary associated with a filename
61+
:return: Filename associated with a yaml dictionary
62+
"""
63+
filename = ""
64+
hashed_key = hash(yaml.dump(yaml_dict))
65+
if hashed_key in metadata["filenames"]:
66+
filename = metadata["filenames"][hashed_key]
67+
return filename
68+
4969
def validate(self, bundle, repository=None):
5070
"""validate takes a bundle as a dictionary and returns a boolean value that
5171
describes if the bundle is valid. It also logs verification information when
@@ -69,15 +89,15 @@ def _bundle_validation(self, bundle, repository=None):
6989

7090
bundleData = bundle[self.dataKey]
7191

72-
validationDict[self.crdKey] = self._type_validation(bundleData, self.crdKey,
92+
validationDict[self.crdKey] = self._type_validation(bundle, self.crdKey,
7393
self._crd_validation, False)
74-
validationDict[self.csvKey] = self._type_validation(bundleData, self.csvKey,
94+
validationDict[self.csvKey] = self._type_validation(bundle, self.csvKey,
7595
self._csv_validation, True)
76-
validationDict[self.pkgsKey] = self._type_validation(bundleData, self.pkgsKey,
96+
validationDict[self.pkgsKey] = self._type_validation(bundle, self.pkgsKey,
7797
self._pkgs_validation, True)
7898
if self.ui_validate_io:
7999
validationDict[self.csvKey] &= self._type_validation(
80-
bundleData, self.csvKey, self._ui_validation_io, True)
100+
bundle, self.csvKey, self._ui_validation_io, True)
81101
if validationDict[self.pkgsKey] and repository is not None:
82102
packageName = bundleData['packages'][0]['packageName']
83103
if repository != packageName:
@@ -93,13 +113,18 @@ def _bundle_validation(self, bundle, repository=None):
93113

94114
return valid
95115

96-
def _crd_validation(self, bundleData):
116+
def _crd_validation(self, bundle):
97117
logger.info("Validating custom resource definitions.")
98118
valid = True
99119

120+
bundle_metadata = bundle[self.metadataKey]
121+
bundleData = bundle[self.dataKey]
100122
crds = bundleData[self.crdKey]
101123

102124
for crd in crds:
125+
crd_file_name = self.get_filename_from_metadata(bundle_metadata, crd)
126+
log_info['current_manifest_file'] = crd_file_name
127+
103128
if "metadata" in crd:
104129
if "name" in crd["metadata"]:
105130
logger.info("Evaluating crd %s", crd["metadata"]["name"])
@@ -137,13 +162,18 @@ def _crd_validation(self, bundleData):
137162

138163
return valid
139164

140-
def _csv_validation(self, bundleData):
165+
def _csv_validation(self, bundle):
141166
valid = True
142167
logger.info("Validating cluster service versions.")
143168

169+
bundle_metadata = bundle[self.metadataKey]
170+
bundleData = bundle[self.dataKey]
144171
csvs = bundleData[self.csvKey]
145172

146173
for csv in csvs:
174+
csv_file_name = self.get_filename_from_metadata(bundle_metadata, csv)
175+
log_info['current_manifest_file'] = csv_file_name
176+
147177
if "metadata" in csv:
148178
if self._csv_metadata_validation(csv["metadata"]) is False:
149179
valid = False
@@ -357,10 +387,12 @@ def _csv_metadata_validation(self, metadata):
357387

358388
return valid
359389

360-
def _pkgs_validation(self, bundleData):
390+
def _pkgs_validation(self, bundle):
361391
valid = True
362392
logger.info("Validating packages.")
363393

394+
bundle_metadata = bundle[self.metadataKey]
395+
bundleData = bundle[self.dataKey]
364396
pkgs = bundleData[self.pkgsKey]
365397

366398
num_pkgs = len(pkgs)
@@ -370,6 +402,8 @@ def _pkgs_validation(self, bundleData):
370402
return False
371403

372404
pkg = pkgs[0]
405+
pkg_file_name = self.get_filename_from_metadata(bundle_metadata, pkg)
406+
log_info['current_manifest_file'] = pkg_file_name
373407

374408
if "packageName" in pkg:
375409
logger.info("Evaluating package %s", pkg["packageName"])
@@ -409,11 +443,13 @@ def _pkgs_validation(self, bundleData):
409443

410444
return valid
411445

412-
def _type_validation(self, bundleData, typeName, validator, required):
446+
def _type_validation(self, bundle, typeName, validator, required):
447+
bundleData = bundle[self.dataKey]
448+
413449
valid = True
414450
if typeName in bundleData:
415451
if len(bundleData[typeName]) != 0:
416-
valid = validator(bundleData)
452+
valid = validator(bundle)
417453
elif required:
418454
self._log_error("Bundle does not contain any %s." % typeName)
419455
valid = False
@@ -423,10 +459,11 @@ def _type_validation(self, bundleData, typeName, validator, required):
423459

424460
return valid
425461

426-
def _ui_validation_io(self, bundleData):
462+
def _ui_validation_io(self, bundle):
427463
valid = True
428464
logger.info("Validating cluster service versions for operatorhub.io UI.")
429465

466+
bundleData = bundle[self.dataKey]
430467
csvs = bundleData[self.csvKey]
431468

432469
for csv in csvs:

operatorcourier/verified_manifest.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def __init__(self, source_dir, yamls, ui_validate_io, repository):
2525
self.nested = False
2626

2727
if yamls:
28-
manifests = {FLAT_KEY: yamls}
28+
yaml_strings_with_metadata = self._set_empty_filepaths(yamls)
29+
manifests = {FLAT_KEY: yaml_strings_with_metadata}
2930
else:
3031
manifests = self.get_manifests_info(source_dir)
3132

@@ -34,6 +35,14 @@ def __init__(self, source_dir, yamls, ui_validate_io, repository):
3435
self.get_validation_dict_from_manifests(manifests, ui_validate_io, repository)
3536
self.is_valid = False if self.__validation_dict['errors'] else True
3637

38+
def _set_empty_filepaths(self, yamls):
39+
yaml_strings_with_metadata = []
40+
for yaml_string in yamls:
41+
yaml_tuple = ("", yaml_string)
42+
yaml_strings_with_metadata.append(yaml_tuple)
43+
44+
return yaml_strings_with_metadata
45+
3746
def get_manifests_info(self, source_dir):
3847
"""
3948
Given a source directory, this method returns a dict containing all
@@ -68,22 +77,18 @@ def get_manifests_info(self, source_dir):
6877
for manifest_path in manifest_paths:
6978
manifest_dir_name = os.path.basename(manifest_path)
7079

71-
files_content = []
7280
crd_files_info, csv_files_info = get_crd_csv_files_info(manifest_path)
73-
for (_, file_content) in (crd_files_info + csv_files_info):
74-
files_content.append(file_content)
75-
manifests[manifest_dir_name] = files_content
81+
manifests[manifest_dir_name] = crd_files_info + csv_files_info
7682
for manifest_dir_name in manifests:
77-
manifests[manifest_dir_name].append(pkg_path_and_content[1])
83+
manifests[manifest_dir_name].append(pkg_path_and_content)
7884
# flat layout: collect all valid manifest files and add to FLAT_KEY entry
7985
elif pkg_path_and_content and csvs_path_and_content:
8086
logger.info('The source directory is in flat structure.')
8187
crd_files_info, csv_files_info = get_crd_csv_files_info(root_path)
82-
files_content = [pkg_path_and_content[1]]
83-
files_content.extend(
84-
[file_content for (_, file_content) in (crd_files_info + csv_files_info)])
88+
files_info = [pkg_path_and_content]
89+
files_info.extend(crd_files_info + csv_files_info)
8590

86-
manifests[FLAT_KEY] = files_content
91+
manifests[FLAT_KEY] = files_info
8792
else:
8893
msg = 'The source directory structure is not in valid flat or nested format,'\
8994
'because no valid CSV file is found in root or manifest directories.'
@@ -108,8 +113,8 @@ def get_validation_dict_from_manifests(self, manifests, ui_validate_io=False,
108113
bundle_dict = None
109114
# validate on all bundles files and combine log messages
110115
validation_dict = ValidateCmd(ui_validate_io).validation_json
111-
for version, manifest_files_content in manifests.items():
112-
bundle_dict = BuildCmd().build_bundle(manifest_files_content)
116+
for version, manifest_files_info in manifests.items():
117+
bundle_dict = BuildCmd().build_bundle(manifest_files_info)
113118
if version != FLAT_KEY:
114119
logger.info("Parsing version: %s", version)
115120
_, validation_dict_temp = ValidateCmd(ui_validate_io, self.nested) \

tests/test_build.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ def test_create_bundle():
1010
yamls = []
1111
for path in paths:
1212
with open(path) as f:
13-
yamls.append(f.read())
13+
yaml_data = f.read()
14+
filename = ""
15+
yaml = (filename, yaml_data)
16+
yamls.append(yaml)
1417

1518
bundle = BuildCmd().build_bundle(yamls)
1619
assert bool(bundle["data"]["packages"]) is True

0 commit comments

Comments
 (0)