Esempio n. 1
0
 async def test_execute_failing(self):
     async with ASandbox(TEST_URL) as s:
         result = await s.execute({"commands": ["false"]})
     self.assertEqual(1, len(result["execution"]))
     real_total = sum(r["time"] for r in result["execution"])
     self.assertTrue(
         result["total_time"] - 0.5 <= real_total <= result["total_time"])
Esempio n. 2
0
    async def test_execute_result_not_found(self):
        async with ASandbox(TEST_URL) as s:
            result = await s.execute({
                "commands": ["true"],
                "result_path": "unknown.txt"
            })

        self.assertEqual(SandboxErrCode.RESULT_NOT_FOUND, result["status"])
        self.assertEqual(1, len(result["execution"]))
Esempio n. 3
0
    async def poll_specifications(
            self) -> Tuple['SandboxSpecs', 'ContainerSpecs']:
        """Poll the specifications of the sandbox's host and its containers.

        The sandbox must be enabled, `SandboxDisabledError` will be raised
        otherwise."""
        if not self.enabled:
            raise SandboxDisabledError(
                "Cannot poll specifications of a disabled sandbox")

        async with ASandbox(self.url) as asandbox:
            raw_specs, libs = await asyncio.gather(asandbox.specifications(),
                                                   asandbox.libraries())

        host, container = raw_specs["host"], raw_specs["container"]

        aw1 = database_sync_to_async(
            SandboxSpecs.objects.filter(sandbox=self).update)(
                polled=True,
                sandbox_version=host["sandbox_version"],
                docker_version=host["docker_version"],
                cpu_core=host["cpu"]["core"],
                cpu_logical=host["cpu"]["logical"],
                cpu_freq_min=host["cpu"]["freq_min"],
                cpu_freq_max=host["cpu"]["freq_max"],
                memory_ram=host["memory"]["ram"],
                memory_swap=host["memory"]["swap"],
                memory_storage=host["memory"]["storage"])
        aw2 = database_sync_to_async(
            ContainerSpecs.objects.filter(sandbox=self).update)(
                polled=True,
                working_dir_device=container["working_dir_device"],
                count=container["count"],
                process=container["process"],
                cpu_count=container["cpu"]["count"],
                cpu_period=container["cpu"]["period"],
                cpu_shares=container["cpu"]["shares"],
                cpu_quota=container["cpu"]["quota"],
                memory_ram=container["memory"]["ram"],
                memory_swap=container["memory"]["swap"],
                memory_storage=container["memory"]["storage"],
                writing_io=container["io"]["read_iops"],
                writing_bytes=container["io"]["read_bps"],
                reading_io=container["io"]["write_iops"],
                reading_bytes=container["io"]["write_bps"],
                libraries=libs["libraries"],
                bin=libs["bin"])
        await asyncio.gather(aw1, aw2)

        host = await database_sync_to_async(SandboxSpecs.objects.get
                                            )(sandbox=self)
        container = await database_sync_to_async(ContainerSpecs.objects.get
                                                 )(sandbox=self)

        return host, container
Esempio n. 4
0
 async def test_execute_ok_environ(self):
     async with ASandbox(TEST_URL) as s:
         result = await s.execute({
             "commands": ['echo $VAR1'],
             "environ": {
                 "VAR1": "My var"
             },
         })
     self.assertEqual(0, result["status"])
     self.assertEqual(1, len(result["execution"]))
     self.assertEqual("My var", result["execution"][0]["stdout"])
Esempio n. 5
0
    async def test_check_env(self):
        async with ASandbox(TEST_URL) as s:
            f = open(
                os.path.join(RESOURCE_DIR,
                             "dae5f9a3-a911-4df4-82f8-b9343241ece5.tgz"), "rb")

            response = await s.execute({
                "commands": ["true"],
                "save": True,
            }, f)
            time.sleep(0.1)
            self.assertTrue(await s.check(response["environment"]))
Esempio n. 6
0
    async def test_download_file_unknown(self):
        async with ASandbox(TEST_URL) as s:
            f = open(
                os.path.join(RESOURCE_DIR,
                             "dae5f9a3-a911-4df4-82f8-b9343241ece5.tgz"), "rb")

            response = await s.execute({
                "commands": ["true"],
                "save": True,
            }, f)

            time.sleep(0.1)
            with self.assertRaises(Sandbox404):
                await s.download(response["environment"], "unknown.unk")
Esempio n. 7
0
    async def test_download_file(self):
        f = open(
            os.path.join(RESOURCE_DIR,
                         "dae5f9a3-a911-4df4-82f8-b9343241ece5.tgz"), "rb")
        async with ASandbox(TEST_URL) as s:
            response = await s.execute({
                "commands": ["true"],
                "save": True,
            }, f)
            time.sleep(0.1)
            downloaded = await s.download(response["environment"],
                                          "dir/file1.txt")

        self.assertEqual(b"env1\n", downloaded.read())
Esempio n. 8
0
    async def retrieve(self,
                       environment: str,
                       file: str = None) -> Optional[BinaryIO]:
        """Download an environment of the Sandbox.

        The sandbox must be enabled, `SandboxDisabledError` will be raised
        otherwise.

        If `file` is given, it must be the path of a file inside the
        environment, only this file will be downloaded."""
        if not self.enabled:
            raise SandboxDisabledError(
                "Cannot retrieve from a disabled sandbox")

        async with ASandbox(self.url) as asandbox:
            return await asandbox.download(environment, file)
Esempio n. 9
0
    async def test_execute_ok_with_env_body_save(self):
        async with ASandbox(TEST_URL) as s:
            result = await s.execute(
                {
                    "commands": ['echo "Hello World !" > result.txt'],
                    "result_path": "result.txt",
                    "save": True,
                }, open(os.path.join(RESOURCES_ROOT, f"{ENV1}.tgz"), "rb"))

        self.assertEqual(0, result["status"])
        self.assertEqual(1, len(result["execution"]))
        self.assertIn("environment", result)
        self.assertEqual("Hello World !\n", result["result"])

        real_total = sum(r["time"] for r in result["execution"])
        self.assertTrue(
            result["total_time"] - 0.5 <= real_total <= result["total_time"])
Esempio n. 10
0
    async def test_execute_ok_without_env(self):
        async with ASandbox(TEST_URL) as s:
            result = await s.execute({
                "commands": [
                    "true",
                    {
                        "command": "echo $((1+1))",
                        "timeout": 1
                    },
                    "-false",
                ]
            })

        self.assertEqual(0, result["status"])
        self.assertEqual(3, len(result["execution"]))
        self.assertEqual("2", result["execution"][1]["stdout"])

        real_total = sum(r["time"] for r in result["execution"])
        self.assertTrue(
            result["total_time"] - 0.5 <= real_total <= result["total_time"])
Esempio n. 11
0
    async def test_execute_ok_with_env_config(self):
        async with ASandbox(TEST_URL) as s:
            env = (await s.execute({
                "commands": ["true"],
                "save": True,
            }))["environment"]
            result = await s.execute({
                "commands": ['echo "Hello World !" > result.txt'],
                "environment":
                env,
                "result_path":
                "result.txt"
            })

        self.assertEqual(0, result["status"])
        self.assertEqual(1, len(result["execution"]))
        self.assertNotIn("environment", result)
        self.assertEqual("Hello World !\n", result["result"])

        real_total = sum(r["time"] for r in result["execution"])
        self.assertTrue(
            result["total_time"] - 0.5 <= real_total <= result["total_time"])
Esempio n. 12
0
    async def test_download_env(self):
        f = open(
            os.path.join(RESOURCE_DIR,
                         "dae5f9a3-a911-4df4-82f8-b9343241ece5.tgz"), "rb")
        async with ASandbox(TEST_URL) as s:
            response = await s.execute({
                "commands": ["true"],
                "save": True,
            }, f)
            time.sleep(0.1)
            downloaded = await s.download(response["environment"])

        f = open(
            os.path.join(RESOURCE_DIR,
                         "dae5f9a3-a911-4df4-82f8-b9343241ece5.tgz"), "rb")
        with tarfile.open(fileobj=f, mode="r|gz") as t1, \
                tarfile.open(fileobj=downloaded, mode="r|gz") as t2:
            l1 = [m.name for m in t1.getmembers() if m.name]
            l2 = [m.name for m in t2.getmembers() if m.name]

        self.assertEqual(len(l1), len(l2))
        self.assertEqual(sorted(l1), sorted(l2))
Esempio n. 13
0
 async def test_execute_timeout(self):
     async with ASandbox(TEST_URL) as s:
         result = await s.execute({
             "commands": [
                 {
                     "command": "echo $((1+1))",
                     "timeout": 1
                 },
                 {
                     "command": "sleep 1",
                     "timeout": 0.2
                 },
             ],
         })
     self.assertEqual(SandboxErrCode.TIMEOUT, result["status"])
     self.assertEqual("sleep 1", result["execution"][1]["command"])
     self.assertEqual(SandboxErrCode.TIMEOUT,
                      result["execution"][1]["exit_code"])
     self.assertEqual("", result["execution"][1]["stdout"])
     self.assertEqual(f"Command timed out after 0.2 seconds\n",
                      result["execution"][1]["stderr"])
     self.assertIsInstance(result["execution"][1]["time"], float)
     self.assertLessEqual(result["execution"][1]["time"], 0.25)
Esempio n. 14
0
    async def poll_usage(self) -> 'Usage':
        """Poll the current usage of the Sandbox.

        If the sandbox is disabled, a `SandboxUsage` with its field `enabled`
        will be produced, all the other fields, with the exception of `sandbox`
        and `date` will be None."""
        if not self.enabled:
            return await database_sync_to_async(Usage.objects.create
                                                )(sandbox=self, enabled=False)

        try:
            async with ASandbox(self.url) as asandbox:
                raw = await asandbox.usage()

            usage = await database_sync_to_async(Usage.objects.create)(
                sandbox=self,
                cpu_usage=[raw["cpu"]["usage"]] + raw["cpu"]["usage_avg"],
                cpu_freq=raw["cpu"]["frequency"],
                memory_ram=raw["memory"]["ram"],
                memory_swap=raw["memory"]["swap"],
                memory_storage=raw["memory"]["storage"],
                writing_io=raw["io"]["read_iops"],
                writing_bytes=raw["io"]["read_bps"],
                reading_io=raw["io"]["write_iops"],
                reading_bytes=raw["io"]["write_bps"],
                process=raw["process"],
                container=raw["container"],
                sending_packets=raw["network"]["sent_packets"],
                sending_bytes=raw["network"]["sent_bytes"],
                receiving_packets=raw["network"]["received_packets"],
                receiving_bytes=raw["network"]["received_bytes"])
        except ClientError:  # pragma: no cover
            usage = await database_sync_to_async(Usage.objects.create
                                                 )(sandbox=self, reached=False)

        return usage
Esempio n. 15
0
 async def test_specifications_without_context_manager(self):
     s = ASandbox(TEST_URL)
     self.assertTrue(dict, type(await s.specifications()))
     await s.close()
Esempio n. 16
0
 async def test_specifications(self):
     async with ASandbox(TEST_URL) as s:
         self.assertTrue(dict, type(await s.specifications()))
Esempio n. 17
0
 async def test_execute_config_not_dict(self):
     async with ASandbox(TEST_URL) as s:
         with self.assertRaises(Sandbox400):
             await s.execute("Definitely not json")
Esempio n. 18
0
    async def test_download_env_unknown(self):
        async with ASandbox(TEST_URL) as s:
            time.sleep(0.1)

            with self.assertRaises(Sandbox404):
                await s.download("unknown")
Esempio n. 19
0
 async def test_usage(self):
     async with ASandbox(TEST_URL) as s:
         self.assertTrue(dict, type(await s.usage()))
Esempio n. 20
0
    async def execute(self,
                      user: Union[AnonymousUser, User],
                      config: Dict[str, Any],
                      environment: BinaryIO = None) -> 'Request':
        """Execute a request on the Sandbox based on `config` and
        `environment`.

        `environment` can be an optional binary stream representing the tgz
        containing the environment.

        `config` must be a dictionary containing information about the
         execution :

         * Mandatory key :
            * `commands` - A list of bash command to be executed. A failing
               command (exit code different than 0) will stop the sandbox,
               except if the command start with an hyphen -. Each command
               can also specify a timeout in seconds, like in the example.
         * Optional keys :
            * `result_path` - Path to the file from which the result field
               of the response will be extracted. if result_path is absent
               from the request, result will not be present in the response.
            * `environ` - A list of environments variables as dictionary
               containing the var name and its value.
            * `environment` - Use this environment stored in the sandbox as
               a base environment. File present in this function's
               environment will be added to this environment (file with the
               same name are overwritten).
            * `save` - Boolean indicating if the resulting environment
               should be saved. If true, the environment's UUID will be sent
               in the response in the field environment. It'll be kept on
               the sandbox for a time define in the sandbox's settings.
               That expiration date will be sent in the response in the
               `expire` field (ISO 8601 format). If the field save is
               missing, it is assumed to be False.

        The sandbox must be enabled, a failed SandboxExecution will be produced
        otherwise."""
        try:
            if not self.enabled:
                raise SandboxDisabledError(
                    "Cannot execute on a disabled sandbox")

            async with ASandbox(self.url) as asandbox:
                r = await asandbox.execute(config, environment)

            response = await database_sync_to_async(Response.objects.create)(
                status=r["status"],
                total_time=r["total_time"],
                result=r.get("result", ""),
                environment=r.get("environment", ""),
                expire=isoparse(r["expire"]) if "expire" in r else None)

            for e in r["execution"]:
                await database_sync_to_async(CommandResult.objects.create
                                             )(response=response,
                                               command=e["command"],
                                               exit_code=e["exit_code"],
                                               stdout=e["stdout"],
                                               stderr=e["stderr"],
                                               time=e["time"])

            request = await database_sync_to_async(Request.objects.create)(
                sandbox=self,
                config=config,
                success=True,
                response=response,
                user=user if user.is_authenticated else None)

        except ClientError as e:  # pragma: no cover
            request = await database_sync_to_async(Request.objects.create)(
                sandbox=self,
                config=config,
                success=False,
                traceback=traceback.format_exc(),
                response=None,
                user=user if user.is_authenticated else None)
            logger.warning(
                f"Execution failed on sandbox {self}, see request of id '{request.pk}'",
                exc_info=e)

        except SandboxDisabledError:
            request = await database_sync_to_async(Request.objects.create)(
                sandbox=self,
                config=config,
                success=False,
                traceback=traceback.format_exc(),
                response=None,
                user=user if user.is_authenticated else None)
            logger.warning(
                f"Execution failed on sandbox {self} because it was disabled,"
                f"see request of id '{request.pk}'")

        return request
Esempio n. 21
0
 async def test_check_env_unknown(self):
     async with ASandbox(TEST_URL) as s:
         time.sleep(0.1)
         self.assertFalse(await s.check("unknown"))
Esempio n. 22
0
 async def test_libraries(self):
     async with ASandbox(TEST_URL) as s:
         self.assertTrue(dict, type(await s.libraries()))
Esempio n. 23
0
 async def test_execute_missing_config(self):
     async with ASandbox(TEST_URL) as s:
         with self.assertRaises(Sandbox400):
             await s.execute({})