Skip to content

Commit 5307e82

Browse files
committed
added support for cmd specific env vars and auth
1 parent 000d062 commit 5307e82

File tree

6 files changed

+106
-114
lines changed

6 files changed

+106
-114
lines changed

.github/workflows/test.yaml

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@ jobs:
99
strategy:
1010
matrix:
1111
os:
12-
# - ubuntu-latest
12+
- ubuntu-latest
1313
- windows-latest
14-
# - macos-latest
14+
- macos-latest
1515
python-version:
16-
# - "3.7"
17-
# - "3.8"
18-
# - "3.9"
19-
# - "3.10"
20-
# - "3.11"
16+
- "3.7"
17+
- "3.8"
18+
- "3.9"
19+
- "3.10"
20+
- "3.11"
2121
- "3.12"
2222
# - "3.13"
23-
# exclude:
24-
# - os: macos-latest
25-
# python-version: "3.7"
23+
exclude:
24+
- os: macos-latest
25+
python-version: "3.7"
2626
runs-on: ${{matrix.os}}
2727
name: 'Run Tests on ${{matrix.os}} with Python ${{matrix.python-version}}'
2828

@@ -44,11 +44,6 @@ jobs:
4444
python3 -m pip install --upgrade pip
4545
pip install -r requirements.txt
4646
47-
# - name: Install psycopg2 for non-Windows OS
48-
# if: matrix.os != 'windows-latest'
49-
# run: |
50-
# pip install psycopg2
51-
5247
# Windows
5348
- name: Install psycopg2-binary for Windows
5449
if: matrix.os == 'windows-latest'
@@ -99,26 +94,12 @@ jobs:
9994
- name: Debug Shell Information
10095
if: matrix.os == 'windows-latest'
10196
shell: pwsh
102-
env:
103-
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
104-
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
105-
GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
106-
STACKQL_GITHUB_USERNAME: ${{ secrets.STACKQL_GITHUB_USERNAME }}
107-
STACKQL_GITHUB_PASSWORD: ${{ secrets.STACKQL_GITHUB_PASSWORD }}
108-
CUSTOM_STACKQL_GITHUB_USERNAME: ${{ secrets.CUSTOM_STACKQL_GITHUB_USERNAME }}
109-
CUSTOM_STACKQL_GITHUB_PASSWORD: ${{ secrets.CUSTOM_STACKQL_GITHUB_PASSWORD }}
110-
AWS_REGION: ${{ vars.AWS_REGION }}
111-
AWS_REGIONS: ${{ vars.AWS_REGIONS }}
112-
GCP_PROJECT: ${{ vars.GCP_PROJECT }}
113-
GCP_ZONE: ${{ vars.GCP_ZONE }}
11497
run: |
11598
Write-Host "Checking PowerShell version and environment variables:"
11699
$PSVersionTable
117100
Write-Host "Environment variables:"
118101
Get-ChildItem Env:
119102
Write-Host "Current Shell: $SHELL"
120-
Write-Host "Command-Specific Username: $Env:AWS_REGION"
121-
Write-Host "Command-Specific Password: $Env:AWS_ACCESS_KEY_ID"
122103
123104
- name: Run tests on Windows
124105
env:

pystackql/_util.py

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ def _get_download_dir():
3838
return site.getuserbase()
3939

4040
def _get_binary_name(platform):
41-
if platform == 'Windows':
41+
if platform.startswith('Windows'):
4242
return r'stackql.exe'
43-
elif platform == 'Darwin':
43+
elif platform.startswith('Darwin'):
4444
return r'stackql/Payload/stackql'
4545
else:
4646
return r'stackql'
@@ -79,28 +79,71 @@ def _download_file(url, path, showprogress=True):
7979
print("ERROR: [_download_file] %s" % (str(e)))
8080
exit(1)
8181

82+
# def _setup(download_dir, platform, showprogress=False):
83+
# try:
84+
# print('installing stackql...')
85+
# binary_name = _get_binary_name(platform)
86+
# url = _get_url()
87+
# print("downloading latest version of stackql from %s to %s" % (url, download_dir))
88+
# archive_file_name = os.path.join(download_dir, os.path.basename(url))
89+
# _download_file(url, archive_file_name, showprogress)
90+
# # if Platform is starting with Darwin, then it is a MacOS
91+
# if platform.startswith('Darwin'):
92+
# unpacked_file_name = os.path.join(download_dir, 'stackql')
93+
# command = 'pkgutil --expand-full {} {}'.format(archive_file_name, unpacked_file_name)
94+
# # if there are files in unpacked_file_name, then remove them
95+
# if os.path.exists(unpacked_file_name):
96+
# os.system('rm -rf {}'.format(unpacked_file_name))
97+
# os.system(command)
98+
# else:
99+
# with zipfile.ZipFile(archive_file_name, 'r') as zip_ref:
100+
# zip_ref.extractall(download_dir)
101+
# os.chmod(os.path.join(download_dir, binary_name), 0o755)
102+
# except Exception as e:
103+
# print("ERROR: [_setup] %s" % (str(e)))
104+
# exit(1)
105+
82106
def _setup(download_dir, platform, showprogress=False):
83107
try:
84108
print('installing stackql...')
85-
binary_name = _get_binary_name(platform)
109+
binary_name = _get_binary_name(platform) # Should return 'stackql.exe' for Windows
86110
url = _get_url()
87-
print("downloading latest version of stackql from %s to %s" % (url, download_dir))
111+
print(f"Downloading latest version of stackql from {url} to {download_dir}")
112+
113+
# Paths
88114
archive_file_name = os.path.join(download_dir, os.path.basename(url))
115+
binary_path = os.path.join(download_dir, binary_name)
116+
117+
# Download and extract
89118
_download_file(url, archive_file_name, showprogress)
90-
# if Platform is starting with Darwin, then it is a MacOS
119+
120+
# Handle extraction
91121
if platform.startswith('Darwin'):
92122
unpacked_file_name = os.path.join(download_dir, 'stackql')
93-
command = 'pkgutil --expand-full {} {}'.format(archive_file_name, unpacked_file_name)
94-
# if there are files in unpacked_file_name, then remove them
123+
command = f'pkgutil --expand-full {archive_file_name} {unpacked_file_name}'
95124
if os.path.exists(unpacked_file_name):
96-
os.system('rm -rf {}'.format(unpacked_file_name))
125+
os.system(f'rm -rf {unpacked_file_name}')
97126
os.system(command)
98-
else:
127+
128+
else: # Handle Windows and Linux
99129
with zipfile.ZipFile(archive_file_name, 'r') as zip_ref:
100-
zip_ref.extractall(download_dir)
101-
os.chmod(os.path.join(download_dir, binary_name), 0o755)
130+
zip_ref.extractall(download_dir)
131+
132+
# Specific check for Windows to ensure `stackql.exe` is extracted
133+
if platform.startswith("Windows"):
134+
if not os.path.exists(binary_path) and os.path.exists(os.path.join(download_dir, "stackql")):
135+
os.rename(os.path.join(download_dir, "stackql"), binary_path)
136+
137+
# Confirm binary presence and set permissions
138+
if os.path.exists(binary_path):
139+
print(f"StackQL executable successfully located at: {binary_path}")
140+
os.chmod(binary_path, 0o755)
141+
else:
142+
print(f"ERROR: Expected binary '{binary_path}' not found after extraction.")
143+
exit(1)
144+
102145
except Exception as e:
103-
print("ERROR: [_setup] %s" % (str(e)))
146+
print(f"ERROR: [_setup] {str(e)}")
104147
exit(1)
105148

106149
def _get_version(bin_path):

pystackql/stackql.py

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,12 @@ def _run_query(self, query, custom_auth=None, env_vars=None):
207207
"""
208208
local_params = self.params.copy()
209209
local_params.insert(1, f'"{query}"')
210+
script_path = None
210211

211212
# Handle custom authentication if provided
212213
if custom_auth:
213214
if '--auth' in local_params:
215+
# override auth set in the constructor with the command-specific auth
214216
auth_index = local_params.index('--auth')
215217
local_params.pop(auth_index) # remove --auth
216218
local_params.pop(auth_index) # remove the auth string
@@ -220,50 +222,23 @@ def _run_query(self, query, custom_auth=None, env_vars=None):
220222
output = {}
221223
env_command_prefix = ""
222224

223-
# # Determine platform and set environment command prefix accordingly
224-
# if env_vars:
225-
# if self.platform.startswith("Windows"):
226-
# # For Windows, use PowerShell syntax
227-
# env_command_prefix = "& { " + " ".join([f'$env:{key} = "{value}";' for key, value in env_vars.items()]) + " "
228-
# full_command = f"{env_command_prefix}{self.bin_path} " + " ".join(local_params) + " }"
229-
# is_github_actions = os.environ.get('GITHUB_ACTIONS') == 'true'
230-
# # ok new approach, if we are in github actions, dump the command to a file and run it
231-
# # GO!
232-
# else:
233-
# # For Linux/Mac, use standard env variable syntax
234-
# env_command_prefix = "env " + " ".join([f'{key}="{value}"' for key, value in env_vars.items()]) + " "
235-
# full_command = env_command_prefix + " ".join([self.bin_path] + local_params)
236-
# else:
237-
# full_command = " ".join([self.bin_path] + local_params)
238-
239-
# print(full_command) # For debugging
240-
241225
# Determine platform and set environment command prefix accordingly
242226
if env_vars:
243227
if self.platform.startswith("Windows"):
244-
# For Windows, use PowerShell syntax with GitHub Actions check
245-
env_command_prefix = "& { " + " ".join([f'$env:{key} = "{value}";' for key, value in env_vars.items()]) + " "
246-
full_command = f"{env_command_prefix}{self.bin_path} " + " ".join(local_params) + " }"
247-
is_github_actions = os.environ.get('GITHUB_ACTIONS') == 'true'
248-
249-
# If in GitHub Actions, write command to a PowerShell script file and execute it
250-
if is_github_actions:
251-
with tempfile.NamedTemporaryFile(delete=False, suffix=".ps1", mode="w") as script_file:
252-
# Write environment variable setup and command to script file
253-
for key, value in env_vars.items():
254-
script_file.write(f'$env:{key} = "{value}";\n')
255-
script_file.write(f"{self.bin_path} " + " ".join(local_params) + "\n")
256-
script_path = script_file.name
257-
full_command = f"powershell -File {script_path}"
228+
with tempfile.NamedTemporaryFile(delete=False, suffix=".ps1", mode="w") as script_file:
229+
# Write environment variable setup and command to script file
230+
for key, value in env_vars.items():
231+
script_file.write(f'$env:{key} = "{value}";\n')
232+
script_file.write(f"{self.bin_path} " + " ".join(local_params) + "\n")
233+
script_path = script_file.name
234+
full_command = f"powershell -File {script_path}"
258235
else:
259236
# For Linux/Mac, use standard env variable syntax
260237
env_command_prefix = "env " + " ".join([f'{key}="{value}"' for key, value in env_vars.items()]) + " "
261238
full_command = env_command_prefix + " ".join([self.bin_path] + local_params)
262239
else:
263240
full_command = " ".join([self.bin_path] + local_params)
264241

265-
print(full_command) # For debugging
266-
267242
try:
268243
with subprocess.Popen(full_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as iqlPopen:
269244
stdout, stderr = iqlPopen.communicate()
@@ -290,8 +265,11 @@ def _run_query(self, query, custom_auth=None, env_vars=None):
290265
"stderr": stderr.decode('utf-8') if 'stderr' in locals() and isinstance(stderr, bytes) else ""
291266
}
292267
output["exception"] = f"ERROR: {json.dumps(error_details)}"
293-
294-
return output
268+
finally:
269+
# Clean up the temporary script file
270+
if script_path is not None:
271+
os.remove(script_path)
272+
return output
295273

296274
def __init__(self,
297275
server_mode=False,

run_tests.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# install packages
2-
pip.exe install -r requirements.txt --user
3-
pip install psycopg2-binary --user
2+
# pip.exe install -r requirements.txt --user
3+
# pip install psycopg2-binary --user
44

55
# Load environment variables
66
. .\tests\creds\env_vars\test.env.ps1

tests/pystackql_tests.py

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,8 @@ def setUpModule():
4040

4141
print("downloading aws provider for tests...")
4242
res = PyStackQLTestsBase.stackql.executeStmt(registry_pull_aws_query)
43-
print(res)
4443
print("downloading google provider for tests...")
4544
res = PyStackQLTestsBase.stackql.executeStmt(registry_pull_google_query)
46-
print(res)
4745
print("starting stackql server...")
4846
PyStackQLTestsBase.server_process = subprocess.Popen([PyStackQLTestsBase.stackql.bin_path, "srv", "--pgsrv.address", server_address, "--pgsrv.port", str(server_port)])
4947
time.sleep(10)
@@ -159,40 +157,37 @@ def test_10_executeStmt(self):
159157
okta_result = okta_result_dict["message"]
160158
expected_pattern = registry_pull_resp_pattern("okta")
161159
self.assertTrue(re.search(expected_pattern, okta_result), f"Expected pattern not found in result: {okta_result}")
162-
github_result_dict = self.stackql.executeStmt(registry_pull_github_query)
163-
github_result = github_result_dict["message"]
164-
expected_pattern = registry_pull_resp_pattern("github")
165-
self.assertTrue(re.search(expected_pattern, github_result), f"Expected pattern not found in result: {github_result}")
166-
print_test_result(f"""Test 10 executeStmt method\nRESULTS:\n{okta_result_dict}\n{github_result_dict}""", True)
160+
print_test_result(f"""Test 10 executeStmt method\nRESULTS:\n{okta_result_dict}""", True)
167161

168162
@pystackql_test_setup(output="csv")
169163
def test_11_executeStmt_with_csv_output(self):
170-
okta_result = self.stackql.executeStmt(registry_pull_okta_query)
171-
expected_pattern = registry_pull_resp_pattern("okta")
172-
self.assertTrue(re.search(expected_pattern, okta_result), f"Expected pattern not found in result: {okta_result}")
173164
github_result = self.stackql.executeStmt(registry_pull_github_query)
174165
expected_pattern = registry_pull_resp_pattern("github")
175166
self.assertTrue(re.search(expected_pattern, github_result), f"Expected pattern not found in result: {github_result}")
176-
print_test_result(f"""Test 11 executeStmt method with csv output\nRESULTS:\n{okta_result}\n{github_result}""", True)
167+
print_test_result(f"""Test 11 executeStmt method with csv output\nRESULTS:\n{github_result}""", True)
177168

178169
@pystackql_test_setup(output="pandas")
179170
def test_12_executeStmt_with_pandas_output(self):
180-
okta_result_df = self.stackql.executeStmt(registry_pull_okta_query)
181-
okta_result = okta_result_df['message'].iloc[0]
182-
expected_pattern = registry_pull_resp_pattern("okta")
183-
self.assertTrue(re.search(expected_pattern, okta_result), f"Expected pattern not found in result: {okta_result}")
184-
github_result_df = self.stackql.executeStmt(registry_pull_github_query)
185-
github_result = github_result_df['message'].iloc[0]
186-
expected_pattern = registry_pull_resp_pattern("github")
187-
self.assertTrue(re.search(expected_pattern, github_result), f"Expected pattern not found in result: {github_result}")
188-
print_test_result(f"""Test 12 executeStmt method with pandas output\nRESULTS:\n{okta_result_df}\n{github_result_df}""", True)
171+
homebrew_result_df = self.stackql.executeStmt(registry_pull_homebrew_query)
172+
homebrew_result = homebrew_result_df['message'].iloc[0]
173+
expected_pattern = registry_pull_resp_pattern("homebrew")
174+
self.assertTrue(re.search(expected_pattern, homebrew_result), f"Expected pattern not found in result: {homebrew_result}")
175+
print_test_result(f"""Test 12 executeStmt method with pandas output\nRESULTS:\n{homebrew_result_df}""", True)
189176

190177
@pystackql_test_setup()
191178
def test_13_execute_with_defaults(self):
192-
result = self.stackql.execute(google_query)
193-
is_valid_data_resp = isinstance(result, list) and all(isinstance(item, dict) for item in result)
194-
self.assertTrue(is_valid_data_resp, f"Result is not valid: {result}")
195-
print_test_result(f"Test 13 execute with defaults\nRESULT: {result}", is_valid_data_resp)
179+
result = self.stackql.execute(google_show_services_query)
180+
is_valid_data_resp = (
181+
isinstance(result, list)
182+
and all(isinstance(item, dict) and 'error' not in item for item in result)
183+
)
184+
# Truncate the result message if it's too long
185+
truncated_result = (
186+
str(result)[:500] + '...' if len(str(result)) > 500 else str(result)
187+
)
188+
self.assertTrue(is_valid_data_resp, f"Result is not valid: {truncated_result}")
189+
print_test_result(f"Test 13 execute with defaults\nRESULT: {truncated_result}", is_valid_data_resp)
190+
196191

197192
def test_14_execute_with_defaults_null_response(self):
198193
result = self.stackql.execute("SELECT 1 WHERE 1=0")
@@ -234,29 +229,23 @@ def test_16_execute_with_csv_output(self, mock_execute):
234229

235230
@pystackql_test_setup()
236231
def test_17_execute_default_auth_dict_output(self):
237-
query = "select login from github.users.users"
238-
result = self.stackql.execute(query)
239-
232+
result = self.stackql.execute(github_query)
240233
# Expected result based on default auth
241234
expected_result = [
242235
{"login": "stackql-devops-1"}
243236
]
244-
245237
self.assertTrue(isinstance(result, list), "Result should be a list")
246238
self.assertEqual(result, expected_result, f"Expected result: {expected_result}, got: {result}")
247239
print_test_result(f"Test 17 execute with default auth and dict output\nRESULT: {result}", result == expected_result)
248240

249241

250242
@pystackql_test_setup()
251243
def test_18_execute_custom_auth_env_vars(self):
252-
query = "select login from github.users.users"
253-
254244
# Set up custom environment variables for authentication
255245
env_vars = {
256246
'command_specific_username': os.getenv('CUSTOM_STACKQL_GITHUB_USERNAME'),
257247
'command_specific_password': os.getenv('CUSTOM_STACKQL_GITHUB_PASSWORD')
258248
}
259-
260249
# Define custom authentication configuration
261250
custom_auth = {
262251
"github": {
@@ -265,14 +254,11 @@ def test_18_execute_custom_auth_env_vars(self):
265254
"password_var": "command_specific_password"
266255
}
267256
}
268-
269-
result = self.stackql.execute(query, custom_auth=custom_auth, env_vars=env_vars)
270-
257+
result = self.stackql.execute(github_query, custom_auth=custom_auth, env_vars=env_vars)
271258
# Expected result based on custom auth
272259
expected_result = [
273260
{"login": "stackql-devops-2"}
274261
]
275-
276262
self.assertTrue(isinstance(result, list), "Result should be a list")
277263
self.assertEqual(result, expected_result, f"Expected result: {expected_result}, got: {result}")
278264
print_test_result(f"Test 18 execute with custom auth and command-specific environment variables\nRESULT: {result}", result == expected_result)

0 commit comments

Comments
 (0)