Skip to content

Commit

Permalink
Merge pull request hyperledger#1129 from ArtObr/indy_1859
Browse files Browse the repository at this point in the history
 INDY-1859: Config handler implementation and test
  • Loading branch information
ashcherbakov authored Jan 22, 2019
2 parents 8165be3 + 1bf35c4 commit 3c2f623
Show file tree
Hide file tree
Showing 5 changed files with 311 additions and 6 deletions.
27 changes: 27 additions & 0 deletions indy_node/server/request_handlers/config_batch_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from indy_node.server.pool_config import PoolConfig

from indy_common.constants import CONFIG_LEDGER_ID
from indy_node.server.upgrader import Upgrader
from plenum.common.txn_util import is_forced
from plenum.server.batch_handlers.batch_request_handler import BatchRequestHandler
from plenum.server.database_manager import DatabaseManager


class ConfigBatchHandler(BatchRequestHandler):
def __init__(self, database_manager: DatabaseManager,
upgrader: Upgrader, pool_config: PoolConfig):
super().__init__(database_manager, CONFIG_LEDGER_ID)
self.upgrader = upgrader
self.pool_config = pool_config

def commit_batch(self, txn_count, state_root, txn_root, pp_time, prev_result):
committed_txns = super().commit_batch(txn_count, state_root, txn_root, pp_time, prev_result)
for txn in committed_txns:
# Handle POOL_UPGRADE or POOL_CONFIG transaction here
# only in case it is not forced.
# If it is forced then it was handled earlier
# in applyForced method.
if not is_forced(txn):
self.upgrader.handleUpgradeTxn(txn)
self.pool_config.handleConfigTxn(txn)
return committed_txns
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from indy_common.types import Request
from plenum.common.txn_util import append_txn_metadata
from plenum.server.request_handlers.handler_interfaces.write_request_handler import WriteRequestHandler


class ConfigWriteRequestHandler(WriteRequestHandler):

def apply_request(self, request: Request, batch_ts, prev_result):
self._validate_request_type(request)
txn = append_txn_metadata(self._req_to_txn(request),
txn_time=batch_ts)
self.ledger.append_txns_metadata([txn])
(start, _), _ = self.ledger.appendTxns([txn])
return start, txn
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
from plenum.server.request_handlers.handler_interfaces.write_request_handler import WriteRequestHandler
from indy_node.server.pool_config import PoolConfig

from indy_common.authorize.auth_actions import AuthActionEdit
from indy_common.authorize.auth_request_validator import WriteRequestValidator
from indy_common.constants import POOL_CONFIG, CONFIG_LEDGER_ID, ACTION
from indy_node.server.request_handlers.config_req_handlers.config_write_request_handler import ConfigWriteRequestHandler
from plenum.common.request import Request
from plenum.server.database_manager import DatabaseManager

class PoolConfigHandler(WriteRequestHandler):
pass

class PoolConfigHandler(ConfigWriteRequestHandler):

def __init__(self, database_manager: DatabaseManager,
write_request_validator: WriteRequestValidator,
pool_config: PoolConfig):
super().__init__(database_manager, POOL_CONFIG, CONFIG_LEDGER_ID)
self.write_request_validator = write_request_validator
self.pool_config = pool_config

def static_validation(self, request: Request):
self._validate_request_type(request)

def dynamic_validation(self, request: Request):
self._validate_request_type(request)
action = '*'
status = '*'
self.write_request_validator.validate(request,
[AuthActionEdit(txn_type=self.txn_type,
field=ACTION,
old_value=status,
new_value=action)])

def apply_forced_request(self, req: Request):
super().apply_forced_request(req)
txn = self._req_to_txn(req)
self.pool_config.handleConfigTxn(txn)

# Config handler don't use state for any validation for now
def update_state(self, txn, prev_result, is_committed=False):
pass

def gen_state_key(self, txn):
pass
Original file line number Diff line number Diff line change
@@ -1,5 +1,119 @@
from plenum.server.request_handlers.handler_interfaces.write_request_handler import WriteRequestHandler
from indy_common.authorize.auth_actions import AuthActionAdd, AuthActionEdit
from indy_node.server.request_handlers.config_req_handlers.config_write_request_handler import ConfigWriteRequestHandler
from indy_node.utils.node_control_utils import NodeControlUtil

from indy_common.config_util import getConfig

class PoolUpgrade(WriteRequestHandler):
pass
from indy_common.constants import CONFIG_LEDGER_ID, POOL_UPGRADE, \
ACTION, CANCEL, START, SCHEDULE, PACKAGE, APP_NAME, REINSTALL

from indy_common.authorize.auth_request_validator import WriteRequestValidator
from indy_node.server.upgrader import Upgrader
from plenum.common.constants import FORCE, VERSION, NAME
from plenum.common.exceptions import InvalidClientRequest
from plenum.common.request import Request
from plenum.common.txn_util import get_request_data, get_payload_data
from plenum.server.database_manager import DatabaseManager
from plenum.server.pool_manager import TxnPoolManager


class PoolUpgradeHandler(ConfigWriteRequestHandler):

def __init__(self, database_manager: DatabaseManager,
upgrader: Upgrader,
write_request_validator: WriteRequestValidator,
pool_manager: TxnPoolManager):
super().__init__(database_manager, POOL_UPGRADE, CONFIG_LEDGER_ID)
self.upgrader = upgrader
self.write_request_validator = write_request_validator
self.pool_manager = pool_manager

def static_validation(self, request: Request):
self._validate_request_type(request)
identifier, req_id, operation = get_request_data(request)
action = operation.get(ACTION)
if action not in (START, CANCEL):
raise InvalidClientRequest(identifier, req_id,
"{} not a valid action".
format(action))
if action == START:
schedule = operation.get(SCHEDULE, {})
force = operation.get(FORCE)
force = str(force) == 'True'
isValid, msg = self.upgrader.isScheduleValid(
schedule, self.pool_manager.getNodesServices(), force)
if not isValid:
raise InvalidClientRequest(identifier, req_id,
"{} not a valid schedule since {}".
format(schedule, msg))

def dynamic_validation(self, request: Request):
self._validate_request_type(request)
identifier, req_id, operation = get_request_data(request)
status = '*'
pkt_to_upgrade = operation.get(PACKAGE, getConfig().UPGRADE_ENTRY)
if pkt_to_upgrade:
currentVersion, cur_deps = self.curr_pkt_info(pkt_to_upgrade)
if not currentVersion:
raise InvalidClientRequest(identifier, req_id,
"Packet {} is not installed and cannot be upgraded".
format(pkt_to_upgrade))
if all([APP_NAME not in d for d in cur_deps]):
raise InvalidClientRequest(identifier, req_id,
"Packet {} doesn't belong to pool".format(pkt_to_upgrade))
else:
raise InvalidClientRequest(identifier, req_id, "Upgrade packet name is empty")

targetVersion = operation[VERSION]
reinstall = operation.get(REINSTALL, False)
if not Upgrader.is_version_upgradable(currentVersion, targetVersion, reinstall):
# currentVersion > targetVersion
raise InvalidClientRequest(identifier, req_id, "Version is not upgradable")

action = operation.get(ACTION)
# TODO: Some validation needed for making sure name and version
# present
txn = self.upgrader.get_upgrade_txn(
lambda txn: get_payload_data(txn).get(
NAME,
None) == operation.get(
NAME,
None) and get_payload_data(txn).get(VERSION) == operation.get(VERSION),
reverse=True)
if txn:
status = get_payload_data(txn).get(ACTION, '*')

if status == START and action == START:
raise InvalidClientRequest(
identifier,
req_id,
"Upgrade '{}' is already scheduled".format(
operation.get(NAME)))
if status == '*':
auth_action = AuthActionAdd(txn_type=POOL_UPGRADE,
field=ACTION,
value=action)
else:
auth_action = AuthActionEdit(txn_type=POOL_UPGRADE,
field=ACTION,
old_value=status,
new_value=action)
self.write_request_validator.validate(request,
[auth_action])

def apply_forced_request(self, req: Request):
super().apply_forced_request(req)
txn = self._req_to_txn(req)
self.upgrader.handleUpgradeTxn(txn)

def curr_pkt_info(self, pkg_name):
if pkg_name == APP_NAME:
return Upgrader.getVersion(), [APP_NAME]
return NodeControlUtil.curr_pkt_info(pkg_name)

# Config handler don't use state for any validation for now
def update_state(self, txn, prev_result, is_committed=False):
pass

def gen_state_key(self, txn):
pass
112 changes: 112 additions & 0 deletions indy_node/test/request_handlers/test_pool_upgrade_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import pytest
from indy_common.constants import POOL_UPGRADE, ACTION, START, PACKAGE, APP_NAME, REINSTALL
from indy_node.server.request_handlers.config_req_handlers.pool_upgrade_handler import PoolUpgradeHandler
from plenum.common.constants import VERSION, TXN_PAYLOAD, TXN_PAYLOAD_DATA
from plenum.common.exceptions import InvalidClientRequest

from plenum.common.request import Request
from plenum.common.util import randomString
from plenum.test.testing_utils import FakeSomething


@pytest.fixture(scope='function')
def pool_upgrade_request():
return Request(identifier=randomString(),
reqId=5,
operation={
'type': POOL_UPGRADE,
ACTION: START,
PACKAGE: 'smth'
})


@pytest.fixture(scope='function')
def pool_upgrade_handler():
return PoolUpgradeHandler(None,
FakeSomething(),
FakeSomething(),
FakeSomething())


def test_pool_upgrade_static_validation_fails_action(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_request.operation[ACTION] = 'smth'
with pytest.raises(InvalidClientRequest) as e:
pool_upgrade_handler.static_validation(pool_upgrade_request)
e.match('not a valid action')


def test_pool_upgrade_static_validation_fails_schedule(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_handler.pool_manager.getNodesServices = lambda: 1
pool_upgrade_handler.upgrader.isScheduleValid = lambda schedule, node_srvs, force: (False, '')
with pytest.raises(InvalidClientRequest) as e:
pool_upgrade_handler.static_validation(pool_upgrade_request)
e.match('not a valid schedule since')


def test_pool_upgrade_static_validation_passes(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_handler.pool_manager.getNodesServices = lambda: 1
pool_upgrade_handler.upgrader.isScheduleValid = lambda schedule, node_srvs, force: (True, '')
pool_upgrade_handler.static_validation(pool_upgrade_request)


def test_pool_upgrade_dynamic_validation_fails_pckg(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_request.operation[PACKAGE] = ''
with pytest.raises(InvalidClientRequest) as e:
pool_upgrade_handler.dynamic_validation(pool_upgrade_request)
e.match('Upgrade packet name is empty')


def test_pool_upgrade_dynamic_validation_fails_not_installed(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_handler.curr_pkt_info = lambda pkt: (None, None)
with pytest.raises(InvalidClientRequest) as e:
pool_upgrade_handler.dynamic_validation(pool_upgrade_request)
e.match('is not installed and cannot be upgraded')


def test_pool_upgrade_dynamic_validation_fails_belong(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_handler.curr_pkt_info = lambda pkt: ('1.1.1', ['some_pckg'])
with pytest.raises(InvalidClientRequest) as e:
pool_upgrade_handler.dynamic_validation(pool_upgrade_request)
e.match('doesn\'t belong to pool')


def test_pool_upgrade_dynamic_validation_fails_upgradable(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_handler.curr_pkt_info = lambda pkt: ('1.1.1', [APP_NAME])
pool_upgrade_request.operation[VERSION] = '1.1.1'
pool_upgrade_request.operation[REINSTALL] = False
with pytest.raises(InvalidClientRequest) as e:
pool_upgrade_handler.dynamic_validation(pool_upgrade_request)
e.match('Version is not upgradable')


def test_pool_upgrade_dynamic_validation_fails_scheduled(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_handler.curr_pkt_info = lambda pkt: ('1.1.1', [APP_NAME])
pool_upgrade_request.operation[VERSION] = '1.1.1'
pool_upgrade_request.operation[REINSTALL] = True
pool_upgrade_handler.upgrader.get_upgrade_txn = \
lambda predicate, reverse: \
{TXN_PAYLOAD: {TXN_PAYLOAD_DATA: {ACTION: START}}}

with pytest.raises(InvalidClientRequest) as e:
pool_upgrade_handler.dynamic_validation(pool_upgrade_request)
e.match('is already scheduled')


def test_pool_upgrade_dynamic_validation_passes(pool_upgrade_handler,
pool_upgrade_request):
pool_upgrade_handler.curr_pkt_info = lambda pkt: ('1.1.1', [APP_NAME])
pool_upgrade_request.operation[VERSION] = '1.1.1'
pool_upgrade_request.operation[REINSTALL] = True
pool_upgrade_handler.upgrader.get_upgrade_txn = \
lambda predicate, reverse: \
{TXN_PAYLOAD: {TXN_PAYLOAD_DATA: {}}}
pool_upgrade_handler.write_request_validator.validate = lambda a, b: 0
pool_upgrade_handler.dynamic_validation(pool_upgrade_request)

0 comments on commit 3c2f623

Please sign in to comment.