コード例 #1
0
 def add_clarity(self, config: DockerConfig):
     if self.node_count < 1:
         raise Exception("There must be at lease one casperlabs node")
     with self._lock:
         node_name = self.cl_nodes[0].node.container_name
         self.grpc_web_proxy_node = DockerGrpcWebProxy(config, node_name)
         self.clarity_node = DockerClarity(
             config, self.grpc_web_proxy_node.container_name)
         self.selenium_node = DockerSelenium(config)
         wait_for_clarity_started(self.clarity_node, config.command_timeout,
                                  1)
         # Since we need pull selenium images from docker hub, it will take more time
         wait_for_selenium_started(self.selenium_node, 5 * 60, 1)
         if self.in_docker:
             # If these integration tests are running in a docker container, then we need connect the docker container
             # to the network of selenium
             network = self.selenium_node.network_from_name(config.network)
             # Gets name of container name of integration_testing
             integration_test_node = os.getenv("HOSTNAME")
             network.connect(integration_test_node)
             remote_drive = f"http://{self.selenium_node.name}:4444/wd/hub"
         else:
             remote_drive = f"http://127.0.0.1:4444/wd/hub"
         chrome_options = Options()
         prefs = {
             "profile.default_content_setting_values.automatic_downloads": 1
         }
         chrome_options.add_experimental_option("prefs", prefs)
         self.selenium_driver = webdriver.Remote(remote_drive,
                                                 DesiredCapabilities.CHROME,
                                                 options=chrome_options)
         self.selenium_driver.implicitly_wait(30)
コード例 #2
0
class CasperLabsNetwork:
    """
    CasperLabsNetwork is the base object for a network of 0-many CasperLabNodes.

    A subclass should implement `_create_cl_network` to stand up the type of network it needs.
    """

    grpc_encryption = False
    behind_proxy = False
    initial_motes = INITIAL_MOTES_AMOUNT

    def __init__(self,
                 docker_client: DockerClient,
                 extra_docker_params: Dict = None):
        self.extra_docker_params = extra_docker_params or {}
        self._next_key_number = FIRST_VALIDATOR_ACCOUNT
        self.docker_client = docker_client
        self.cl_nodes: List[CasperLabsNode] = []
        self.clarity_node: DockerClarity = None
        self.selenium_node: DockerSelenium = None
        self.selenium_driver: WebDriver = None
        self.grpc_web_proxy_node: DockerGrpcWebProxy = None
        self._created_networks: List[str] = []
        self._lock = (threading.RLock()
                      )  # protect self.cl_nodes and self._created_networks
        self._accounts_lock = threading.Lock()
        self.test_accounts = {}
        self.next_key = 1

    @property
    def node_count(self) -> int:
        return len(self.cl_nodes)

    @property
    def docker_nodes(self) -> List[DockerNode]:
        with self._lock:
            return [cl_node.node for cl_node in self.cl_nodes]

    @property
    def execution_engines(self) -> List[DockerExecutionEngine]:
        with self._lock:
            return [cl_node.execution_engine for cl_node in self.cl_nodes]

    @property
    def genesis_account(self):
        """ Genesis Account Address """
        return GENESIS_ACCOUNT

    @property
    def in_docker(self) -> bool:
        return os.getenv("IN_DOCKER") == "true"

    def lookup_node(self, node_id):
        m = {node.node_id: node for node in self.docker_nodes}
        return m[node_id]

    def test_account(self,
                     node,
                     amount=TEST_ACCOUNT_INITIAL_BALANCE) -> Account:
        name = test_name()
        if not name:
            # This happens when a thread tries to deploy.
            # Name of the test that spawned the thread does not appear on the inspect.stack.
            # Threads that don't want to use genesis account
            # should pass from_address, public_key and private_key to deploy explicitly.
            return self.genesis_account
        elif name not in self.test_accounts:
            with self._accounts_lock:
                self.test_accounts[name] = Account(self.next_key)
                logging.info(
                    f"=== Creating test account #{self.next_key} {self.test_accounts[name].public_key_hex} for {name} "
                )
                block_hash = node.transfer_to_account(self.next_key, amount)
                # Waiting for the block with transaction that created new account to propagate to all nodes.
                # Expensive, but some tests may rely on it.
                wait_for_block_hash_propagated_to_all_nodes(
                    node.cl_network.docker_nodes, block_hash)
                for deploy in node.client.show_deploys(block_hash):
                    assert (deploy.is_error is
                            False), f"Account creation failed: {deploy}"
                self.next_key += 1
        return self.test_accounts[name]

    def from_address(self, node) -> str:
        return self.test_account(node).public_key_hex

    def get_key(self):
        key_pair = Account(self._next_key_number)
        self._next_key_number += 1
        return key_pair

    def create_cl_network(self, **kwargs):
        """
        Should be implemented with each network class to setup custom nodes and networks.
        """
        raise NotImplementedError(
            "Must implement 'create_cl_network' in subclass.")

    def create_docker_network(self) -> str:
        with self._lock:
            tag_name = os.environ.get("TAG_NAME") or "latest"
            tag_name += f"-{self.docker_client.cl_unique_run_num}"
            network_name = f"casperlabs_{random_string(5)}_{tag_name}"
            self._created_networks.append(network_name)
            self.docker_client.networks.create(network_name, driver="bridge")
            logging.info(f"Docker network {network_name} created.")
            return network_name

    def _add_cl_node(self, config: DockerConfig) -> None:
        with self._lock:
            config.number = self.node_count
            cl_node = CasperLabsNode(self, config)
            self.cl_nodes.append(cl_node)

    def add_new_node_to_network(self,
                                generate_config=None,
                                account: Account = None) -> Account:
        if account is None:
            account = self.get_key()

        if generate_config is not None:
            config = generate_config(account)
        else:
            config = DockerConfig(
                self.docker_client,
                node_private_key=account.private_key,
                node_account=account,
                grpc_encryption=self.grpc_encryption,
                behind_proxy=self.behind_proxy,
            )

        self.add_cl_node(config)
        self.wait_method(wait_for_approved_block_received_handler_state, 1)
        self.wait_for_peers()
        return account

    def add_bootstrap(self,
                      config: DockerConfig,
                      bootstrap_address: str = None) -> None:
        config.is_bootstrap = True
        config.bootstrap_address = bootstrap_address
        self._add_cl_node(config)
        self.wait_method(wait_for_node_started, 0)
        wait_for_genesis_block(self.docker_nodes[0])

    def add_clarity(self, config: DockerConfig):
        if self.node_count < 1:
            raise Exception("There must be at lease one casperlabs node")
        with self._lock:
            node_name = self.cl_nodes[0].node.container_name
            self.grpc_web_proxy_node = DockerGrpcWebProxy(config, node_name)
            self.clarity_node = DockerClarity(
                config, self.grpc_web_proxy_node.container_name)
            self.selenium_node = DockerSelenium(config)
            wait_for_clarity_started(self.clarity_node, config.command_timeout,
                                     1)
            # Since we need pull selenium images from docker hub, it will take more time
            wait_for_selenium_started(self.selenium_node, 5 * 60, 1)
            if self.in_docker:
                # If these integration tests are running in a docker container, then we need connect the docker container
                # to the network of selenium
                network = self.selenium_node.network_from_name(config.network)
                # Gets name of container name of integration_testing
                integration_test_node = os.getenv("HOSTNAME")
                network.connect(integration_test_node)
                remote_drive = f"http://{self.selenium_node.name}:4444/wd/hub"
            else:
                remote_drive = f"http://127.0.0.1:4444/wd/hub"
            chrome_options = Options()
            prefs = {
                "profile.default_content_setting_values.automatic_downloads": 1
            }
            chrome_options.add_experimental_option("prefs", prefs)
            self.selenium_driver = webdriver.Remote(remote_drive,
                                                    DesiredCapabilities.CHROME,
                                                    options=chrome_options)
            self.selenium_driver.implicitly_wait(30)

    def add_cl_node(
        self,
        config: DockerConfig,
        network_with_bootstrap: bool = True,
        bootstrap_address: str = None,
    ) -> None:
        with self._lock:
            if self.node_count == 0:
                raise Exception("Must create bootstrap first")
            config.bootstrap_address = (bootstrap_address
                                        or self.cl_nodes[0].node.address)
            if network_with_bootstrap:
                config.network = self.cl_nodes[0].node.config.network
            self._add_cl_node(config)

    def stop_cl_node(self, node_number: int) -> None:
        with self._lock:
            cl_node = self.cl_nodes[node_number]

        cl_node.execution_engine.stop()
        node = cl_node.node
        with wait_for_log_watcher(GoodbyeInLogLine(node.container)):
            node.stop()

    def start_cl_node(self, node_number: int) -> None:
        with self._lock:
            self.cl_nodes[node_number].execution_engine.start()
            node = self.cl_nodes[node_number].node
            node.truncate_logs()
            node.start()
            wait_for_approved_block_received_handler_state(
                node, node.config.command_timeout)
            wait_for_genesis_block(self.docker_nodes[node_number])

    def wait_for_peers(self) -> None:
        if self.node_count < 2:
            return
        for cl_node in self.cl_nodes:
            wait_for_peers_count_at_least(cl_node.node, self.node_count - 1,
                                          cl_node.config.command_timeout)

    def wait_method(self, method: Callable, node_number: int) -> None:
        """
        Calls a wait method with the node and timeout from internal objects

        Blocks until satisfied or timeout.

        :param method: wait method to call
        :param node_number: index of self.cl_nodes to use as node
        :return: None
        """
        cl_node = self.cl_nodes[node_number]
        method(cl_node.node, cl_node.config.command_timeout)

    def __enter__(self):
        return self

    def __exit__(self, exception_type, exception_value, traceback=None):
        if exception_type is not None:
            import traceback as tb

            logging.error(
                f"Python Exception Occurred: {exception_type} {exception_value} {tb.format_exc()}"
            )

        with self._lock:
            if self.clarity_node:
                self.clarity_node.cleanup()
            if self.grpc_web_proxy_node:
                self.grpc_web_proxy_node.cleanup()
            if self.selenium_node:
                self.selenium_node.cleanup()
            for node in self.cl_nodes:
                node.cleanup()
            if self.in_docker and self.selenium_node:
                # If these integration tests are running in a docker container,
                # then we need disconnect the docker container from the network of selenium
                network = self.selenium_node.network_from_name(
                    self.selenium_node.config.network)
                # Gets name of container name of integration_testing
                integration_test_node = os.getenv("HOSTNAME")
                network.disconnect(integration_test_node)

            self.cleanup()

        return True

    def cleanup(self):
        with self._lock:
            for network_name in self._created_networks:
                try:
                    self.docker_client.networks.get(network_name).remove()
                except (NotFound, Exception) as e:
                    logging.warning(
                        f"Exception in cleanup while trying to remove network {network_name}: {str(e)}"
                    )