def create_robot(robot_shortname, parent, description="", unstructured_metadata=None): (username_valid, username_issue) = validate_username(robot_shortname) if not username_valid: raise InvalidRobotException( "The name for the robot '%s' is invalid: %s" % (robot_shortname, username_issue) ) username = format_robot_username(parent.username, robot_shortname) try: User.get(User.username == username) msg = "Existing robot with name: %s" % username logger.info(msg) raise InvalidRobotException(msg) except User.DoesNotExist: pass service = LoginService.get(name="quayrobot") try: with db_transaction(): created = User.create(username=username, email=str(uuid.uuid4()), robot=True) token = random_string_generator(length=64)() RobotAccountToken.create(robot_account=created, token=token, fully_migrated=True) FederatedLogin.create( user=created, service=service, service_ident="robot:%s" % created.id ) RobotAccountMetadata.create( robot_account=created, description=description[0:255], unstructured_json=unstructured_metadata or {}, ) return created, token except Exception as ex: raise DataModelException(ex.message)
def create_user_noverify( username, email, email_required=True, prompts=tuple(), is_possible_abuser=False ): if email_required: if not validate_email(email): raise InvalidEmailAddressException("Invalid email address: %s" % email) else: # If email addresses are not required and none was specified, then we just use a unique # ID to ensure that the database consistency check remains intact. email = email or str(uuid.uuid4()) (username_valid, username_issue) = validate_username(username) if not username_valid: raise InvalidUsernameException("Invalid namespace %s: %s" % (username, username_issue)) try: existing = User.get((User.username == username) | (User.email == email)) logger.info("Existing user with same username or email.") # A user already exists with either the same username or email if existing.username == username: assert not existing.robot msg = ( "Username has already been taken by an organization and cannot be reused: %s" % username ) if not existing.organization: msg = "Username has already been taken by user cannot be reused: %s" % username raise InvalidUsernameException(msg) raise InvalidEmailAddressException("Email has already been used: %s" % email) except User.DoesNotExist: # This is actually the happy path logger.debug("Email and username are unique!") # Create the user. try: default_expr_s = _convert_to_s(config.app_config["DEFAULT_TAG_EXPIRATION"]) default_max_builds = config.app_config.get("DEFAULT_NAMESPACE_MAXIMUM_BUILD_COUNT") threat_max_builds = config.app_config.get("THREAT_NAMESPACE_MAXIMUM_BUILD_COUNT") if is_possible_abuser and threat_max_builds is not None: default_max_builds = threat_max_builds new_user = User.create( username=username, email=email, removed_tag_expiration_s=default_expr_s, maximum_queued_builds_count=default_max_builds, ) for prompt in prompts: create_user_prompt(new_user, prompt) return new_user except Exception as ex: raise DataModelException(ex.message)
def test_cleanup_old_robots(initialized_db): before_robot_count = User.select().where(User.robot == True).count() before_user_count = User.select().count() # Run the cleanup once, and ensure it does nothing. cleanup_old_robots(force=True) after_robot_count = User.select().where(User.robot == True).count() after_user_count = User.select().count() assert before_robot_count == after_robot_count assert before_user_count == after_user_count # Create some orphan robots. created = set() for index in range(0, 50): created.add('doesnotexist+a%s' % index) created.add('anothernamespace+b%s' % index) User.create(username='******' % index, robot=True) User.create(username='******' % index, robot=True) before_robot_count = User.select().where(User.robot == True).count() before_user_count = User.select().count() cleanup_old_robots(page_size=10, force=True) after_robot_count = User.select().where(User.robot == True).count() after_user_count = User.select().count() assert before_robot_count == after_robot_count + len(created) assert before_user_count == after_user_count + len(created) for name in created: with pytest.raises(User.DoesNotExist): User.get(username=name)
def test_readreplica(init_db_path, tmpdir_factory): primary_file = str(tmpdir_factory.mktemp("data").join("primary.db")) replica_file = str(tmpdir_factory.mktemp("data").join("replica.db")) # Copy the initialized database to two different locations. shutil.copy2(init_db_path, primary_file) shutil.copy2(init_db_path, replica_file) db_config = { "DB_URI": "sqlite:///{0}".format(primary_file), "DB_READ_REPLICAS": [{"DB_URI": "sqlite:///{0}".format(replica_file)},], "DB_CONNECTION_ARGS": {"threadlocals": True, "autorollback": True,}, "DB_TRANSACTION_FACTORY": lambda x: FakeTransaction(), "FOR_TESTING": True, "DATABASE_SECRET_KEY": "anothercrazykey!", } # Initialize the DB with the primary and the replica. configure(db_config) assert not read_only_config.obj.is_readonly assert read_only_config.obj.read_replicas # Ensure we can read the data. devtable_user = User.get(username="******") assert devtable_user.username == "devtable" # Configure with a bad primary. Reading should still work since we're hitting the replica. db_config["DB_URI"] = "sqlite:///does/not/exist" configure(db_config) assert not read_only_config.obj.is_readonly assert read_only_config.obj.read_replicas devtable_user = User.get(username="******") assert devtable_user.username == "devtable" # Force us to hit the master and ensure it doesn't work. with db_disallow_replica_use(): with pytest.raises(OperationalError): User.get(username="******") # Test read replica again. devtable_user = User.get(username="******") assert devtable_user.username == "devtable" # Try to change some data. This should fail because the primary is broken. with pytest.raises(OperationalError): devtable_user.email = "newlychanged" devtable_user.save() # Fix the primary and try again. db_config["DB_URI"] = "sqlite:///{0}".format(primary_file) configure(db_config) assert not read_only_config.obj.is_readonly assert read_only_config.obj.read_replicas devtable_user.email = "newlychanged" devtable_user.save() # Mark the system as readonly. db_config["DB_URI"] = "sqlite:///{0}".format(primary_file) db_config["REGISTRY_STATE"] = "readonly" configure(db_config) assert read_only_config.obj.is_readonly assert read_only_config.obj.read_replicas # Ensure all write operations raise a readonly mode exception. with pytest.raises(ReadOnlyModeException): devtable_user.email = "newlychanged2" devtable_user.save() with pytest.raises(ReadOnlyModeException): User.create(username="******") with pytest.raises(ReadOnlyModeException): User.delete().where(User.username == "foo").execute() with pytest.raises(ReadOnlyModeException): User.update(username="******").where(User.username == "foo").execute() # Reset the config on the DB, so we don't mess up other tests. configure( { "DB_URI": "sqlite:///{0}".format(primary_file), "DB_CONNECTION_ARGS": {"threadlocals": True, "autorollback": True,}, "DB_TRANSACTION_FACTORY": lambda x: FakeTransaction(), "DATABASE_SECRET_KEY": "anothercrazykey!", } )