diff --git a/src/scriptworker/context.py b/src/scriptworker/context.py index a57eb0f3..8a5d04df 100644 --- a/src/scriptworker/context.py +++ b/src/scriptworker/context.py @@ -59,7 +59,7 @@ class Context(object): config: Optional[Dict[str, Any]] = None credentials_timestamp: Optional[int] = None - credentials_fd: int = -1 + credentials_file = None proc: Optional[task_process.TaskProcess] = None queue: Optional[Queue] = None session: Optional[aiohttp.ClientSession] = None @@ -99,8 +99,7 @@ def claim_task(self, claim_task: Optional[Dict[str, Any]]) -> None: if claim_task: self.task = claim_task["task"] self.verify_task() - # flags=0 to let the child inherit this fd - self.credentials_fd = os.memfd_create("scriptworker_temp_creds", flags=0) + self.credentials_file = tempfile.TemporaryFile() self.temp_credentials = claim_task["credentials"] path = os.path.join(self.config["work_dir"], "task.json") assert self.task @@ -108,8 +107,9 @@ def claim_task(self, claim_task: Optional[Dict[str, Any]]) -> None: else: self.temp_credentials = None self.task = None - os.close(self.credentials_fd) - self.credentials_fd = -1 + if self.credentials_file is not None: + self.credentials_file.close() + self.credentials_file = None def verify_task(self) -> None: """Run some task sanity checks on ``self.task``.""" @@ -201,7 +201,7 @@ def temp_credentials(self, credentials: Optional[Dict[str, Any]]) -> None: if credentials: data = json.dumps(credentials, indent=2, sort_keys=True).encode("ascii") # use pwrite so we don't confuse the child by changing the file offset - assert os.pwrite(self.credentials_fd, data, 0) == len(data) + assert os.pwrite(self.credentials_file.fileno(), data, 0) == len(data) def write_json(self, path: str, contents: Dict[str, Any], message: str) -> None: """Write json to disk. diff --git a/src/scriptworker/task.py b/src/scriptworker/task.py index ad0e8d69..19fc6660 100644 --- a/src/scriptworker/task.py +++ b/src/scriptworker/task.py @@ -672,7 +672,8 @@ async def run_task(context, to_cancellable_process): env["TASK_ID"] = context.task_id or "None" env["RUN_ID"] = str(get_run_id(context.claim_task)) env["TASKCLUSTER_ROOT_URL"] = context.config["taskcluster_root_url"] - env["TASKCLUSTER_CREDENTIALS_FD"] = str(context.credentials_fd) + credentials_fd = context.credentials_file.fileno() + env["TASKCLUSTER_CREDENTIALS_FD"] = str(credentials_fd) kwargs = { "stdout": PIPE, "stderr": PIPE, @@ -680,7 +681,7 @@ async def run_task(context, to_cancellable_process): "close_fds": True, "preexec_fn": lambda: os.setsid(), "env": env, - "pass_fds": (context.credentials_fd,), + "pass_fds": (credentials_fd,), } # pragma: no branch timeout = get_task_maxruntime(context.task, context.config["task_max_timeout"]) diff --git a/tests/test_context.py b/tests/test_context.py index 5549262c..eaad1f4f 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -77,42 +77,42 @@ async def test_set_reset_task(rw_context, claim_task, reclaim_task): assert rw_context.temp_queue is None -def test_credentials_fd_initial(rw_context): - assert rw_context.credentials_fd == -1 +def test_credentials_file_initial(rw_context): + assert rw_context.credentials_file is None @pytest.mark.asyncio -async def test_credentials_fd_opened_on_claim_task(rw_context, claim_task): +async def test_credentials_file_opened_on_claim_task(rw_context, claim_task): rw_context.claim_task = claim_task - assert rw_context.credentials_fd >= 0 - os.fstat(rw_context.credentials_fd) # raises OSError if fd is invalid + assert rw_context.credentials_file is not None + os.fstat(rw_context.credentials_file.fileno()) # raises OSError if fd is invalid @pytest.mark.asyncio -async def test_credentials_fd_content(rw_context, claim_task): +async def test_credentials_file_content(rw_context, claim_task): rw_context.claim_task = claim_task - fd = rw_context.credentials_fd + fd = rw_context.credentials_file.fileno() size = os.fstat(fd).st_size data = os.pread(fd, size, 0) assert json.loads(data) == claim_task["credentials"] @pytest.mark.asyncio -async def test_credentials_fd_updated_on_reclaim(rw_context, claim_task, reclaim_task): +async def test_credentials_file_updated_on_reclaim(rw_context, claim_task, reclaim_task): rw_context.claim_task = claim_task rw_context.reclaim_task = reclaim_task - fd = rw_context.credentials_fd + fd = rw_context.credentials_file.fileno() size = os.fstat(fd).st_size data = os.pread(fd, size, 0) assert json.loads(data) == reclaim_task["credentials"] @pytest.mark.asyncio -async def test_credentials_fd_closed_on_reset(rw_context, claim_task): +async def test_credentials_file_closed_on_reset(rw_context, claim_task): rw_context.claim_task = claim_task - fd = rw_context.credentials_fd + fd = rw_context.credentials_file.fileno() rw_context.claim_task = None - assert rw_context.credentials_fd == -1 + assert rw_context.credentials_file is None with pytest.raises(OSError): os.fstat(fd)