From 8b1c20f4ba5a1f659e6ca0352f1876372fed0419 Mon Sep 17 00:00:00 2001 From: yackob03 Date: Wed, 9 Oct 2013 23:52:28 -0400 Subject: [PATCH] Add a complex db generator and create a more interesting repo. --- config.py | 1 + initdb.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ test.db | Bin 40960 -> 56320 bytes 3 files changed, 88 insertions(+) diff --git a/config.py b/config.py index 9189c0727..ca5e02a16 100644 --- a/config.py +++ b/config.py @@ -93,6 +93,7 @@ class DebugConfig(FlaskConfig, MailConfig, LocalStorage, SQLiteDB, 'format': LOG_FORMAT } SEND_FILE_MAX_AGE_DEFAULT = 0 + POPULATE_DB_TEST_DATA = True class LocalHostedConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL, diff --git a/initdb.py b/initdb.py index a35737ffb..84f30f6a2 100644 --- a/initdb.py +++ b/initdb.py @@ -1,4 +1,91 @@ +import logging +import string + +from random import SystemRandom +from datetime import datetime + from data.database import initialize_db +from data import model +from app import app + + +logger = logging.getLogger(__name__) +logging.basicConfig(**app.config['LOGGING_CONFIG']) + + +def __gen_hex_id(length=64): + random = SystemRandom() + return ''.join([random.choice('abcdef' + string.digits) + for x in range(length)]) + + +def __gen_checksum(): + return 'tarsum+sha256:' + __gen_hex_id(64) + + +def create_subtree(repo, structure, parent): + num_nodes, subtrees, last_node_tag = structure + + # create the nodes + for i in range(num_nodes): + docker_image_id = __gen_hex_id() + checksum = __gen_checksum() + + new_image = model.create_image(docker_image_id, repo) + model.set_image_checksum(docker_image_id, repo, checksum) + + new_image = model.set_image_metadata(docker_image_id, repo.namespace, + repo.name, str(datetime.now()), + 'no comment', parent) + + parent = new_image + + if last_node_tag: + model.create_or_update_tag(repo.namespace, repo.name, last_node_tag, + new_image.docker_image_id) + + for subtree in subtrees: + create_subtree(repo, subtree, new_image) + + +def __generate_repository(user, name, is_public, permissions, structure): + repo = model.create_repository(user.username, name, user) + + if is_public: + model.set_repository_visibility(repo, 'public') + + for delegate, role in permissions: + model.set_user_repo_permission(delegate.username, user.username, name, + role) + + create_subtree(repo, structure, None) + if __name__ == '__main__': initialize_db() + + if app.config.get('POPULATE_DB_TEST_DATA', False): + logger.debug('Populating the DB with test data.') + + new_user_1 = model.create_user('devtable', 'password', + 'jake@devtable.com') + new_user_1.verified = True + new_user_1.save() + + new_user_2 = model.create_user('public', 'password', + 'jacob.moshenko@gmail.com') + new_user_2.verified = True + new_user_2.save() + + __generate_repository(new_user_1, 'simple', False, [], (4, [], 'latest')) + + __generate_repository(new_user_1, 'complex', False, [], + (2, [(3, [], 'v2.0'), + (1, [(1, [(1, [], 'latest')], 'staging'), + (1, [], None)], None)], None)) + + __generate_repository(new_user_2, 'publicrepo', True, [], + (10, [], 'latest')) + + __generate_repository(new_user_1, 'shared', False, + [(new_user_2, 'write')], (5, [], 'latest')) diff --git a/test.db b/test.db index 25c547a62ae39b501470b799f00825baaff94198..58d66eb390753be9270252bd319f0962050bceb6 100644 GIT binary patch literal 56320 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 WtX9RZ@71$fXS%Cb&uXpz&i@BM$68td literal 40960 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