Add PR validation (#1)

This commit is contained in:
Reuven V. Gonzales 2019-12-30 17:24:25 -08:00 committed by GitHub
parent 5051c49e31
commit e08529c885
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 258 additions and 0 deletions

View File

@ -0,0 +1,2 @@
cbor==1.0.0
PyGithub==1.45

View File

@ -0,0 +1,38 @@
from unpack_entities import unpack_entities, InvalidEntitiesDetected
import unittest
import os
import shutil
import tempfile
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
class TestUnpack(unittest.TestCase):
def setUp(self):
self.test_temp_dir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.test_temp_dir)
def fixture_dir(self, name):
return os.path.join(
CURRENT_DIR,
'fixtures', '%s_entity_packages' % name
)
def test_entity_package_missing_files(self):
"""Tests when the entity package is missing files"""
with self.assertRaises(InvalidEntitiesDetected):
unpack_entities(self.fixture_dir('bad1'), self.test_temp_dir)
def test_entity_package_node_not_registered(self):
"""Tests when the entity package node is not registered properly"""
with self.assertRaises(InvalidEntitiesDetected):
unpack_entities(self.fixture_dir('bad2'), self.test_temp_dir)
def test_succeeds(self):
unpack_entities(self.fixture_dir('good'), self.test_temp_dir)
if __name__ == "__main__":
unittest.main()

113
.github/scripts/python/unpack_entities.py vendored Executable file
View File

@ -0,0 +1,113 @@
#!/usr/bin/env python3
import json
import os
import sys
import tempfile
import tarfile
import logging
import base64
import cbor
ENTITY_FILENAME_SUFFIX = '-entity.tar.gz'
EXPECTED_FILES = [
'entity/entity.json',
'entity/entity_genesis.json',
'node/node_genesis.json',
]
logger = logging.getLogger('unpack_entities')
# May want a more granular error in the future
class InvalidEntitiesDetected(Exception):
pass
def unpack_entities(src_entities_dir_path, dest_entities_dir_path):
invalid_entity_packages = []
# Unpack all of the entity packages in the form of `*-entity.tar.gz`. Also
# unpack the entities in lexicographical order so it is potentially easier
# to read logs.
for filename in sorted(os.listdir(src_entities_dir_path)):
if filename.endswith(ENTITY_FILENAME_SUFFIX):
entity_owner = filename[:-len(ENTITY_FILENAME_SUFFIX)]
unpacked_entity_dir_path = os.path.join(
dest_entities_dir_path,
entity_owner
)
# Create the new entity directory
logger.info('Unpack package for entity owner "%s"' % entity_owner)
os.mkdir(unpacked_entity_dir_path)
package = tarfile.open(os.path.join(src_entities_dir_path,
filename))
package.extractall(unpacked_entity_dir_path)
if not validate_entity_package(unpacked_entity_dir_path):
invalid_entity_packages.append(entity_owner)
else:
logger.info('Entity owned by "%s" is valid' % entity_owner)
if len(invalid_entity_packages) > 0:
for entity_owner in invalid_entity_packages:
logger.error('Invalid Entity for %s' % entity_owner)
raise InvalidEntitiesDetected()
def validate_entity_package(package_path):
is_valid = True
# Validate that the expected directory structure exists
for expected_file_name in EXPECTED_FILES:
expected_file_path = os.path.join(package_path, expected_file_name)
if not os.path.isfile(expected_file_path):
logger.warning('Expected file "%s" missing' % expected_file_path)
is_valid = False
if not is_valid:
return is_valid
# Ensure that the node is properly loaded into the
# FIXME we should do this check using something written with oasis-core as a
# library. This is quick and dirty.
entity_genesis_path = os.path.join(
package_path, 'entity/entity_genesis.json')
node_genesis_path = os.path.join(package_path, 'node/node_genesis.json')
with open(entity_genesis_path) as entity_genesis_file:
entity_genesis = json.load(entity_genesis_file)
with open(node_genesis_path) as node_genesis_file:
node_genesis = json.load(node_genesis_file)
entity_descriptor = cbor.loads(base64.b64decode(
entity_genesis['untrusted_raw_value']))
node_descriptor = cbor.loads(
base64.b64decode(node_genesis['untrusted_raw_value']))
entity_nodes = entity_descriptor['nodes'] or []
if not node_descriptor['id'] in entity_nodes:
logger.warning('Expected node to be added to entity')
is_valid = False
return is_valid
def main():
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
src_entities_dir_path = os.path.abspath(sys.argv[1])
dest_entities_dir_path = os.path.abspath(sys.argv[2])
logger.info('Unpacking to %s' % dest_entities_dir_path)
try:
unpack_entities(src_entities_dir_path, dest_entities_dir_path)
except InvalidEntitiesDetected:
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,62 @@
#!/usr/bin/env python3
import os
import sys
from github import Github
class InvalidEntityPR(Exception):
pass
def validate_entity_pull_request(gh, repo, pr_number):
"""Validate if this pull request is a valid entity pull request."""
repo = gh.get_repo(repo)
pr = repo.get_pull(pr_number)
pr_creator = pr.user.login
is_valid = False
expected_filename = "entities/%s-entity.tar.gz" % pr_creator
for changed_file in pr.get_files():
if changed_file.filename == expected_filename:
is_valid = True
if not is_valid:
raise InvalidEntityPR(
'The entity file is expected to be named %s. Please remediate.' % expected_filename
)
def main():
token = os.environ.get('GITHUB_TOKEN')
github_ref = os.environ.get('GITHUB_REF', '')
if not token:
print('No github token specified')
sys.exit(1)
if not github_ref:
print('No github_ref specified')
sys.exit(1)
try:
pr_number = int(github_ref.split('/')[2])
except TypeError:
print("This might not be a PR or something is wrong")
sys.exit(1)
gh = Github(token)
print("Validating PR #%d" % pr_number)
try:
validate_entity_pull_request(
gh,
'oasislabs/the-quest-entities',
pr_number
)
except InvalidEntityPR as e:
print(e)
sys.exit(1)
if __name__ == '__main__':
main()

19
.github/workflows/test-scripts.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Check validation scripts
on: [push]
jobs:
build:
name: Test that the validation scripts work as expected
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Setup Python 3
uses: actions/setup-python@v1
with:
python-version: '3.x'
- run: pip3 install -r .github/scripts/python/requirements.txt
- run: python3 .github/scripts/python/test_unpack_entities.py

View File

@ -0,0 +1,24 @@
name: Validate Entity Package PR
on: [pull_request]
jobs:
validate_package_file_name:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Setup Python 3
uses: actions/setup-python@v1
with:
python-version: '3.x'
- run: pip3 install -r .github/scripts/python/requirements.txt
- name: Validate the entity package name
run: python3 .github/scripts/python/validate_pull_request.py
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Validate entity packages
run: mkdir /tmp/unpack && python3 .github/scripts/python/unpack_entities.py ./entities /tmp/unpack