Пример #1
0
def test_payment_status_with_address(yagna_container):
    """Test `payment status` with explicit node address."""

    yagna = Cli(yagna_container).yagna

    status = yagna.payment_status()
    assert status
Пример #2
0
def test_id_show_by_unknown_alias_fails(yagna_container):
    """Test that `yagna show` with nonexistent identity alias fails."""

    yagna = Cli(yagna_container).yagna

    identity = yagna.id_show(alias_or_addr="unknown-alias")
    assert identity is None
Пример #3
0
def test_payment_status(yagna_container):
    """Test `payment status` subcommand."""

    yagna = Cli(yagna_container).yagna

    status = yagna.payment_status()
    assert status
Пример #4
0
def test_app_key_create_unknown_address_fails(yagna_container):
    """Test if `app-key create --id <address>` fails if `<address>` is unknown."""

    yagna = Cli(yagna_container).yagna

    with pytest.raises(CommandError):
        yagna.app_key_create("test key", alias_or_addr="unknown-alias")
Пример #5
0
def test_id_show(yagna_container):
    """Test that `yagna show` prints default identity."""

    yagna = Cli(yagna_container).yagna

    identity = yagna.id_show()
    assert identity.is_default is True
Пример #6
0
def test_app_key_create_with_alias(yagna_container):
    """Test `app-key create <name>` with explicit node alias."""

    yagna = Cli(yagna_container).yagna

    yagna.id_create(alias="alias-id")
    test_key = yagna.app_key_create("test key", alias_or_addr="alias-id")
    assert test_key
Пример #7
0
def test_id_show_by_alias(yagna_container):
    """Test `yagna show` with identity alias specified."""

    yagna = Cli(yagna_container).yagna

    new_identity = yagna.id_create(alias="id-alias")
    some_identity = yagna.id_show(alias_or_addr="id-alias")
    assert some_identity == new_identity
Пример #8
0
def test_app_key_list(yagna_container):
    """Test `app-key list` subcommand."""

    yagna = Cli(yagna_container).yagna

    key_1 = yagna.app_key_create("test key 1")
    key_2 = yagna.app_key_create("test key 2")
    keys = yagna.app_key_list()
    assert {info.key for info in keys} == {key_1, key_2}
Пример #9
0
def test_id_list_default(yagna_container):
    """Test thst the result of `yagna id list` contains the default identity."""

    yagna = Cli(yagna_container).yagna

    ids = yagna.id_list()
    assert len(ids) == 1

    default_identity = yagna.id_show()
    assert ids[0] == default_identity
Пример #10
0
def test_app_key_create_with_address(yagna_container):
    """Test `app-key create <name>` with explicit address."""

    yagna = Cli(yagna_container).yagna

    default_id = yagna.id_show()

    test_key = yagna.app_key_create("test key",
                                    alias_or_addr=default_id.address)
    assert test_key
Пример #11
0
def test_id_create(yagna_container):
    """Test `yagna id create` without arguments."""

    yagna = Cli(yagna_container).yagna

    identity = yagna.id_create()
    assert identity.is_default is False
    assert identity.alias is None

    another_identity = yagna.id_create()
    assert identity != another_identity
Пример #12
0
def test_app_key_create(yagna_container):
    """Test `app-key create <name>` subcommand."""

    yagna = Cli(yagna_container).yagna

    test_key = yagna.app_key_create("test key")
    assert test_key

    test_key_2 = yagna.app_key_create("test key 2")
    assert test_key_2

    assert test_key != test_key_2
Пример #13
0
def test_id_create_with_alias(yagna_container):
    """Test `yagna id create` with alias."""

    yagna = Cli(yagna_container).yagna

    identity = yagna.id_create(alias="id-alias")
    assert identity.is_default is False
    assert identity.alias == "id-alias"

    another_identity = yagna.id_create(alias="another-id-alias")
    assert another_identity.is_default is False
    assert another_identity.alias == "another-id-alias"
Пример #14
0
 def __init__(
     self,
     runner: "Runner",
     client: DockerClient,
     config: YagnaContainerConfig,
     log_config: LogConfig,
 ):
     self.runner = runner
     self._agents = OrderedDict()
     self._docker_client = client
     self._logger = ProbeLoggingAdapter(
         logger, {ProbeLoggingAdapter.EXTRA_PROBE_NAME: config.name}
     )
     config = self._setup_gftp_proxy(config)
     self.container = YagnaContainer(client, config, log_config)
     self.cli = Cli(self.container).yagna
     self._yagna_config = config
Пример #15
0
def test_app_key_create_duplicate_name_fails_2(yagna_container):
    """Test if `app-key create <name>` fails when app key `<name>` already exists.

    Even if both keys are created using different node aliases.
    """

    yagna = Cli(yagna_container).yagna

    yagna.id_create(alias="alias-1")
    yagna.id_create(alias="alias-2")
    yagna.app_key_create("test key", alias_or_addr="alias-1")

    with pytest.raises(KeyAlreadyExistsError, match="test key"):
        yagna.app_key_create("test key", alias_or_addr="alias-2")
Пример #16
0
def test_app_key_list_with_address(yagna_container):
    """Test `app-key list` subcommand with explicit node address."""

    yagna = Cli(yagna_container).yagna

    key_1 = yagna.app_key_create("test key 1")

    identity = yagna.id_create(alias="id-alias")
    key_2 = yagna.app_key_create("test key 2", alias_or_addr="id-alias")

    keys_1 = yagna.app_key_list()
    assert {info.key for info in keys_1} == {key_1, key_2}

    keys_2 = yagna.app_key_list(address=identity.address)
    assert {info.key for info in keys_2} == {key_2}

    new_identity = yagna.id_create()
    keys_3 = yagna.app_key_list(address=new_identity.address)
    assert keys_3 == []
Пример #17
0
def test_id_create_same_alias_fails(yagna_container):
    """Test that `yagna id create` fails if the same alias was used previously."""

    yagna = Cli(yagna_container).yagna

    yagna.id_create(alias="id-alias")

    with pytest.raises(CommandError):
        yagna.id_create(alias="id-alias")
Пример #18
0
def test_app_key_create_duplicate_name_fails(yagna_container):
    """Test if `app-key create <name>` fails when app key `<name>` already exists."""

    yagna = Cli(yagna_container).yagna

    yagna.app_key_create("test key")

    with pytest.raises(KeyAlreadyExistsError, match="test key"):
        yagna.app_key_create("test key")
Пример #19
0
def test_id_list_many(yagna_container):
    """Test that the result of `yagna id list` contains all identities."""
    yagna = Cli(yagna_container).yagna

    default_identity = yagna.id_show()
    another_identity = yagna.id_create()
    yet_another_identity = yagna.id_create(alias="id-alias")

    ids = yagna.id_list()
    assert set(ids) == {
        default_identity,
        another_identity,
        yet_another_identity,
    }
Пример #20
0
def test_payment_init_requestor_mode(yagna_container):
    """Test `payment init --sender`."""

    yagna = Cli(yagna_container).yagna

    yagna.payment_init(sender_mode=True)
Пример #21
0
class Probe(abc.ABC):
    """Provides a unified interface for interacting with and testing a single Yagna node.

    This interface consists of several independent modules which may be extended
    in subclasses (see `ProviderProbe` and `RequestorProbe`).
    """

    api: RestApiComponent
    """Component with clients for all three yagna REST APIs."""

    runner: "Runner"
    """A runner that created this probe."""

    cli: YagnaDockerCli
    """A module which enables calling the Yagna CLI on the daemon being tested."""

    container: YagnaContainer
    """A module which handles the lifecycle of the daemon's Docker container."""

    ip_address: Optional[str] = None
    """An IP address of the daemon's container in the Docker network."""

    _agents: "OrderedDict[str, AgentComponent]"
    """Collection of agent components that will be started as part of this probe.

    Keys are agent names, values are subclasses of `AgentComponent`.
    """

    _docker_client: DockerClient
    """A docker client used to create the deamon's container."""

    _gftp_script_dir: Path
    """Directory containing the `gftp` proxy script.

    This script forwards JSON RPC requests to the `gftp` binary running in the docker
    container managed by this probe, and returns responses from the binary.
    """

    _yagna_config: YagnaContainerConfig
    """Config object used for setting up the Yagna node for this probe."""

    def __init__(
        self,
        runner: "Runner",
        client: DockerClient,
        config: YagnaContainerConfig,
        log_config: LogConfig,
    ):
        self.runner = runner
        self._agents = OrderedDict()
        self._docker_client = client
        self._logger = ProbeLoggingAdapter(
            logger, {ProbeLoggingAdapter.EXTRA_PROBE_NAME: config.name}
        )
        config = self._setup_gftp_proxy(config)
        self.container = YagnaContainer(client, config, log_config)
        self.cli = Cli(self.container).yagna
        self._yagna_config = config

    def __str__(self):
        return self.name

    @property
    def agents(self) -> List[AgentComponent]:
        """List of agent components that will be started as part of this probe."""
        return list(self._agents.values())

    @property
    def address(self) -> Optional[str]:
        """Return address from id marked as default."""
        identity = self.cli.id_show()
        return identity.address if identity else None

    @property
    def app_key(self) -> Optional[str]:
        """Return first app key on the list."""
        keys = self.cli.app_key_list()
        return keys[0].key if keys else None

    @property
    def name(self) -> str:
        """Name of the container."""
        return self.container.name

    def _setup_gftp_proxy(self, config: YagnaContainerConfig) -> YagnaContainerConfig:
        """Create a proxy script and a dir for exchanging files with the container."""

        self._gftp_script_dir, gftp_volume_dir = gftp.create_gftp_dirs(config.name)
        self._logger.info("Created gftp script at %s", self._gftp_script_dir)

        new_config = copy.deepcopy(config)
        new_config.volumes[gftp_volume_dir] = gftp.CONTAINER_MOUNT_POINT
        self._logger.info(
            "Gftp volume %s will be mounted at %s in the container",
            gftp_volume_dir,
            gftp.CONTAINER_MOUNT_POINT,
        )

        return new_config

    def add_agent(self, agent: AgentComponent) -> None:
        """Add an agent to be started for this probe."""
        if self._agents.get(agent.name):
            raise KeyAlreadyExistsError(
                f"Probe already has agent component with name: `{agent.name}`"
            )
        self._agents[agent.name] = agent

    async def start(self) -> None:
        """Start the probe."""

        await self._start_container()
        self.api = RestApiComponent(self)

    async def start_agents(self):
        """Start all of the probe's agents."""
        for agent in self.agents:
            await agent.start()

    async def stop(self):
        """
        Stop the probe, removing the Docker container of the daemon being tested.

        Once stopped, a probe cannot be restarted.
        """
        self._logger.info("Stopping probe")
        for agent in self.agents:
            await agent.stop()
        if self.container.logs:
            await self.container.logs.stop()

    def remove(self) -> None:
        """Remove the underlying container."""
        if self.container:
            self.container.remove(force=True)
            self._logger.debug("Container removed")

    async def _start_container(self) -> None:
        """
        Start the probe's Docker container.

        Performs all necessary steps to make the daemon ready for testing
        (e.g. creating the default app key).
        """
        self.container.start()

        # Wait until the daemon is ready to create an app key.
        self._logger.info("Waiting for connection to ya-sb-router")
        if self.container.logs:
            await self.container.logs.wait_for_entry(
                ".*connected with server: ya-sb-router.*", timeout=30
            )
        await self.create_app_key()

        self._logger.info("Waiting for yagna REST API to be listening")
        if self.container.logs:
            await self.container.logs.wait_for_entry(
                "Starting .*actix-web-service.* service on .*.", timeout=30
            )

        # Obtain the IP address of the container
        self.ip_address = get_container_address(
            self._docker_client, self.container.name
        )
        self._logger.info("IP address: %s", self.ip_address)

    async def create_app_key(self, key_name: str = "test_key") -> str:
        """Attempt to create a new app key on the Yagna daemon.

        The key name can be specified via `key_name` parameter.
        Return the key as string.

        When `self.key_file` is set, this method also:
        - creates ID based on `self.key_file`
        - sets this new ID as default
        - restarts the container ( https://github.com/golemfactory/yagna/issues/458 )
        """
        address = None
        if self._yagna_config.payment_id:
            key_name = self._yagna_config.payment_id.key_file.name
            key_file: str = str(PAYMENT_MOUNT_PATH / key_name)
            self._logger.debug("create_id(alias=%s, key_file=%s", key_name, key_file)
            try:
                db_id = self.cli.id_create(alias=key_name, key_file=key_file)
                address = db_id.address
                self._logger.debug("create_id. alias=%s, address=%s", db_id, address)
            except KeyAlreadyExistsError as e:
                logger.critical("Id already exists : (%r)", e)
                raise
            db_id = self.cli.id_update(address, set_default=True)
            self._logger.debug("update_id. result=%r", db_id)
            self.container.restart()
            await asyncio.sleep(5)
        try:
            key = self.cli.app_key_create(key_name)
            self._logger.debug("create_app_key. key_name=%s, key=%s", key_name, key)
        except KeyAlreadyExistsError:
            app_key = next(
                filter(lambda k: k.name == key_name, self.cli.app_key_list())
            )
            key = app_key.key
        return key

    def set_agent_env_vars(self, env: Dict[str, str]) -> None:
        """Add vars needed to talk to the daemon in this probe's container to `env`."""

        if not self.app_key:
            raise AttributeError("Yagna application key is not set yet")
        path_var = env.get("PATH")
        env.update(
            {
                "YAGNA_APPKEY": self.app_key,
                "YAGNA_API_URL": YAGNA_REST_URL.substitute(host=self.ip_address),
                "GSB_URL": YAGNA_BUS_URL.substitute(host=self.ip_address),
                "PATH": f"{self._gftp_script_dir}:{path_var}",
            }
        )

    @contextlib.asynccontextmanager
    async def run_command_on_host(
        self,
        command: str,
        env: Optional[Dict[str, str]] = None,
        command_timeout: float = 300,
    ) -> Iterator[Tuple[asyncio.Task, PatternMatchingEventMonitor]]:
        """Run `command` on host in given `env` and with optional `timeout`.

        The command is run in the environment extending `env` with variables needed
        to communicate with the daemon running in this probe's container.

        Internally, this method uses `process.run_command()` to run `command`.
        The argument `command_timeout` is passed as the `timeout` parameter to
        `process.run_command()`.

        Returns the `asyncio` task that logs output from the command, and an event
        monitor that observes lines of output produced by the command.

        The task can be awaited in order to wait until the command completes.
        The monitor can be used for asserting properties of the command's output.
        """
        cmd_env = {**env} if env is not None else {}
        self.set_agent_env_vars(cmd_env)

        cmd_monitor = PatternMatchingEventMonitor(name="command output")
        cmd_monitor.start()

        try:
            with monitored_logger(
                f"goth.{self.name}.command_output", cmd_monitor
            ) as cmd_logger:

                cmd_task = asyncio.create_task(
                    process.run_command(
                        command.split(),
                        cmd_env,
                        log_level=logging.INFO,
                        cmd_logger=cmd_logger,
                        timeout=command_timeout,
                    )
                )
                yield cmd_task, cmd_monitor

                await cmd_task
                logger.debug("Command task has finished")

        except Exception as e:
            logger.warning(f"Cancelling command on error: {e!r}")
            if cmd_task and not cmd_task.done():
                cmd_task.cancel()
            raise

        finally:
            await cmd_monitor.stop()
            for assertion in cmd_monitor.failed:
                raise TemporalAssertionError(assertion.name)
Пример #22
0
def test_payment_init_provider_mode(yagna_container):
    """Test `payment init --receiver`."""

    yagna = Cli(yagna_container).yagna

    yagna.payment_init(receiver_mode=True)
Пример #23
0
def test_payment_init(yagna_container):
    """Test basic usage of `payment init` command."""

    yagna = Cli(yagna_container).yagna

    yagna.payment_init()