Beispiel #1
0
    def render(
        self,
        compiled_module: CompiledModule,
        chroot_context: ChrootContext,
        basedir: Path,
        input_table: ArrowTable,
        params: Params,
        tab: Tab,
        fetch_result: Optional[FetchResult],
        output_filename: str,
    ) -> RenderResult:
        """
        Run the module's `render_thrift()` function and return its result.

        Raise ModuleError if the module has a bug.
        """
        chroot_dir = chroot_context.chroot.root
        basedir_seen_by_module = Path("/") / basedir.relative_to(chroot_dir)
        request = ttypes.RenderRequest(
            str(basedir_seen_by_module),
            input_table.to_thrift(),
            params.to_thrift(),
            tab.to_thrift(),
            None if fetch_result is None else fetch_result.to_thrift(),
            output_filename,
        )
        try:
            with chroot_context.writable_file(basedir / output_filename):
                result = self._run_in_child(
                    chroot_dir=chroot_dir,
                    network_config=pyspawner.NetworkConfig(),  # TODO disallow networking
                    compiled_module=compiled_module,
                    timeout=self.render_timeout,
                    result=ttypes.RenderResult(),
                    function="render_thrift",
                    args=[request],
                )
        finally:
            chroot_context.clear_unowned_edits()

        if result.table.filename and result.table.filename != output_filename:
            raise ModuleExitedError(0, "Module wrote to wrong output file")

        try:
            # RenderResult.from_thrift() verifies all filenames passed by the
            # module are in the directory the module has access to. It assumes
            # the Arrow file (if there is one) is untrusted, so it can raise
            # ValidateError
            render_result = RenderResult.from_thrift(result, basedir)
        except ValidateError as err:
            raise ModuleExitedError(0, "Module produced invalid data: %s" % str(err))
        return render_result
Beispiel #2
0
    def fetch(
        self,
        compiled_module: CompiledModule,
        chroot_context: ChrootContext,
        basedir: Path,
        params: Params,
        secrets: Dict[str, Any],
        last_fetch_result: Optional[FetchResult],
        input_parquet_filename: str,
        output_filename: str,
    ) -> FetchResult:
        """
        Run the module's `fetch_thrift()` function and return its result.

        Raise ModuleError if the module has a bug.
        """
        chroot_dir = chroot_context.chroot.root
        basedir_seen_by_module = Path("/") / basedir.relative_to(chroot_dir)
        request = ttypes.FetchRequest(
            str(basedir_seen_by_module),
            params.to_thrift(),
            RawParams(secrets).to_thrift(),
            None if last_fetch_result is None else last_fetch_result.to_thrift(),
            input_parquet_filename,
            output_filename,
        )
        try:
            with chroot_context.writable_file(basedir / output_filename):
                result = self._run_in_child(
                    chroot_dir=chroot_dir,
                    network_config=pyspawner.NetworkConfig(),
                    compiled_module=compiled_module,
                    timeout=self.fetch_timeout,
                    result=ttypes.FetchResult(),
                    function="fetch_thrift",
                    args=[request],
                )
        finally:
            chroot_context.clear_unowned_edits()

        if result.filename and result.filename != output_filename:
            raise ModuleExitedError(0, "Module wrote to wrong output file")

        # TODO validate result isn't too large. If result is dataframe it makes
        # sense to truncate; but fetch results aren't necessarily data frames.
        # It's up to the module to enforce this logic ... but we need to set a
        # maximum file size.
        return FetchResult.from_thrift(result, basedir)
Beispiel #3
0
    def render(
        self,
        compiled_module: CompiledModule,
        basedir: Path,
        input_table: ArrowTable,
        params: Params,
        tab: Tab,
        fetch_result: Optional[FetchResult],
        output_filename: str,
    ) -> RenderResult:
        request = ttypes.RenderRequest(
            str(basedir),
            input_table.to_thrift(),
            params.to_thrift(),
            tab.to_thrift(),
            None if fetch_result is None else fetch_result.to_thrift(),
            output_filename,
        )
        with _chroot_dir_context(provide_paths=[basedir],
                                 extract_paths=[basedir / output_filename
                                                ]) as chroot:
            result = self._run_in_child(
                chroot=chroot,
                chroot_paths=[basedir] + DATA_PATHS + PARQUET_PATHS +
                NETWORKING_PATHS,  # TODO nix networking
                compiled_module=compiled_module,
                timeout=self.render_timeout,
                result=ttypes.RenderResult(),
                function="render_thrift",
                args=[request],
            )
            if result.table.filename and result.table.filename != output_filename:
                raise ModuleExitedError(0, "Module wrote to wrong output file")

        # RenderResult.from_thrift() verifies all filenames passed by the
        # module are in the directory the module has access to.
        render_result = RenderResult.from_thrift(result, basedir)
        if render_result.table.table is not None:
            validate(render_result.table.table, render_result.table.metadata)
        return render_result
Beispiel #4
0
 def fetch(
     self,
     compiled_module: CompiledModule,
     basedir: Path,
     params: Params,
     secrets: Dict[str, Any],
     last_fetch_result: Optional[FetchResult],
     input_parquet_filename: str,
     output_filename: str,
 ) -> FetchResult:
     request = ttypes.FetchRequest(
         str(basedir),
         params.to_thrift(),
         RawParams(secrets).to_thrift(),
         None
         if last_fetch_result is None else last_fetch_result.to_thrift(),
         input_parquet_filename,
         output_filename,
     )
     with _chroot_dir_context(provide_paths=[basedir],
                              extract_paths=[basedir / output_filename
                                             ]) as chroot:
         result = self._run_in_child(
             chroot=chroot,
             chroot_paths=[basedir] + DATA_PATHS + PARQUET_PATHS +
             NETWORKING_PATHS,
             compiled_module=compiled_module,
             timeout=self.fetch_timeout,
             result=ttypes.FetchResult(),
             function="fetch_thrift",
             args=[request],
         )
         if result.filename and result.filename != output_filename:
             raise ModuleExitedError(0, "Module wrote to wrong output file")
     # TODO validate result isn't too large. If result is dataframe it makes
     # sense to truncate; but fetch results aren't necessarily data frames.
     # It's up to the module to enforce this logic ... but we need to set a
     # maximum file size.
     return FetchResult.from_thrift(result, basedir)