Skip to content

Commit 0c85c36

Browse files
fix: retry LookupAccountName with exponential backoff to prevent ERROR_NONE_MAPPED on fresh EC2 instances (#262)
* fix: avoid LookupAccountName for SYSTEM user in named pipe security attributes When CodeBuild Windows integration tests run with LOGNAME=SYSTEM, getpass.getuser() returns "SYSTEM" and the subsequent LookupAccountName("", "SYSTEM") call fails with ERROR_NONE_MAPPED (1332) on freshly started reserved-fleet EC2 instances because the LSA service hasn't finished initializing. SYSTEM (S-1-5-18) is a well-known SID constant that never varies across Windows machines. Use ConvertStringSidToSid("S-1-5-18") to bypass the LSA lookup entirely, consistent with how the network SID (S-1-5-2) is already handled in the same method. Fixes intermittent Windows integration test failures in deadline-cloud-for-cinema-4d and 6 other DCC repos that share the LOGNAME=SYSTEM CodeBuild workaround. Signed-off-by: leon-li-inspire <2182521+leon-li-inspire@users.noreply.github.com> * style: fix black formatting in test_named_pipe_helper.py Signed-off-by: Leon Li <2182521+leon-li-inspire@users.noreply.github.com> * fix: use real PySID in tests to prevent TypeError on Windows LookupAccountName mock must return a real PySID object (not a string) because downstream code passes it to dacl.AddAccessAllowedAce() which validates the type on Windows. Signed-off-by: Leon Li <2182521+leon-li-inspire@users.noreply.github.com> * chore: remove CHANGELOG entry, will be auto-generated at release Signed-off-by: Leon Li <2182521+leon-li-inspire@users.noreply.github.com> --------- Signed-off-by: leon-li-inspire <2182521+leon-li-inspire@users.noreply.github.com> Signed-off-by: Leon Li <2182521+leon-li-inspire@users.noreply.github.com>
1 parent cd2178c commit 0c85c36

2 files changed

Lines changed: 57 additions & 7 deletions

File tree

src/openjd/adaptor_runtime_client/named_pipe/named_pipe_helper.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,23 @@ def create_security_attributes():
128128
# Get the username of the current user
129129
username = getpass.getuser()
130130

131-
# Get the SID for the current user
132-
user_sid, _, _ = win32security.LookupAccountName(
133-
"", # systemName: The name of the system or server where the account resides.
134-
# Search for the account on the local computer.
135-
# If Domain/User Format is used here, it will fetch the Name from the AD.
136-
username,
137-
)
131+
# Retry with exponential backoff to handle ERROR_NONE_MAPPED (1332) which occurs
132+
# when the LSA service hasn't finished initializing on freshly started EC2 instances.
133+
_LOOKUP_MAX_RETRIES = 3
134+
for attempt in range(_LOOKUP_MAX_RETRIES):
135+
try:
136+
user_sid, _, _ = win32security.LookupAccountName(
137+
"", # systemName: The name of the system or server where the account resides.
138+
# Search for the account on the local computer.
139+
# If Domain/User Format is used here, it will fetch the Name from the AD.
140+
username,
141+
)
142+
break
143+
except pywintypes.error as e:
144+
if e.winerror == 1332 and attempt < _LOOKUP_MAX_RETRIES - 1:
145+
time.sleep(2**attempt)
146+
continue
147+
raise
138148

139149
# Users who log on across a network. "S-1-5-2" is a group identifier added to the token of a process
140150
# when it was logged on across a network.

test/openjd/adaptor_runtime/unit/named_pipe/test_named_pipe_helper.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,43 @@ def test_failed_to_generate_pipe_name(self, mock_check_named_pipe_exists, mock_g
100100
match="Cannot find an available pipe name.",
101101
):
102102
named_pipe_helper.NamedPipeHelper.generate_pipe_name("AdaptorTest")
103+
104+
@patch("getpass.getuser", return_value="regularuser")
105+
def test_create_security_attributes_uses_lookup(self, mock_getuser):
106+
win32security = pytest.importorskip("win32security")
107+
fake_sid = win32security.ConvertStringSidToSid("S-1-1-0")
108+
with patch.object(
109+
win32security, "LookupAccountName", return_value=(fake_sid, None, None)
110+
) as mock_lookup:
111+
named_pipe_helper.NamedPipeHelper.create_security_attributes()
112+
mock_lookup.assert_called_once_with("", "regularuser")
113+
114+
@patch("time.sleep")
115+
@patch("getpass.getuser", return_value="regularuser")
116+
def test_create_security_attributes_retries_on_lsa_not_ready(self, mock_getuser, mock_sleep):
117+
# ERROR_NONE_MAPPED (1332) is raised when LSA hasn't finished initializing on a
118+
# fresh EC2 instance. The lookup should retry with exponential backoff and succeed.
119+
win32security = pytest.importorskip("win32security")
120+
fake_sid = win32security.ConvertStringSidToSid("S-1-1-0")
121+
lsa_error = pywintypes.error(
122+
1332, "LookupAccountName", "No mapping between account names and security IDs was done."
123+
)
124+
with patch.object(
125+
win32security, "LookupAccountName", side_effect=[lsa_error, (fake_sid, None, None)]
126+
) as mock_lookup:
127+
named_pipe_helper.NamedPipeHelper.create_security_attributes()
128+
assert mock_lookup.call_count == 2
129+
mock_sleep.assert_called_once_with(1) # 2**0 = 1s after first failure
130+
131+
@patch("time.sleep")
132+
@patch("getpass.getuser", return_value="regularuser")
133+
def test_create_security_attributes_raises_after_max_retries(self, mock_getuser, mock_sleep):
134+
# After exhausting all retries, the original error should be re-raised.
135+
win32security = pytest.importorskip("win32security")
136+
lsa_error = pywintypes.error(
137+
1332, "LookupAccountName", "No mapping between account names and security IDs was done."
138+
)
139+
with patch.object(win32security, "LookupAccountName", side_effect=lsa_error):
140+
with pytest.raises(pywintypes.error) as exc_info:
141+
named_pipe_helper.NamedPipeHelper.create_security_attributes()
142+
assert exc_info.value.winerror == 1332

0 commit comments

Comments
 (0)