Skip to content

Commit 278e8e4

Browse files
empower-registry-testing (#514)
Summary: - Dynamically generate `nginx` LB config. - Expand LB testing. - Improved documentation for reverse proxied testing.
1 parent 7685649 commit 278e8e4

File tree

6 files changed

+260
-10
lines changed

6 files changed

+260
-10
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,8 @@ jobs:
492492
} >> "${GITHUB_ENV}"
493493
494494
if [ "${{ matrix.registry }}" = "test/registry" ]; then
495-
echo '127.0.0.1 storage.googleapis.com' | sudo tee -a /etc/hosts
495+
python3 test/python/tcp_lb.py --generate-hosts-entries | sudo tee -a /etc/hosts
496+
python3 test/python/tcp_lb.py --generate-nginx-lb > test/tcp/reverse-proxy/nginx/dynamic-sni-proxy.conf
496497
fi
497498
498499
@@ -510,7 +511,7 @@ jobs:
510511
| sudo tee /etc/apt/sources.list.d/nginx.list
511512
sudo apt-get update
512513
sudo apt-get install nginx
513-
sudo nginx -c "$(pwd)/test/tcp/reverse-proxy/nginx/elegant-sni-proxy.conf"
514+
sudo nginx -c "$(pwd)/test/tcp/reverse-proxy/nginx/dynamic-sni-proxy.conf"
514515
fi
515516
516517
- name: Install Python dependencies

docs/CICD.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ Summary:
1313
- This pattern does the below:
1414
- (a) Build and push by digest.
1515
- (b) Leverage [`docker buildx imagetools`](https://docs.docker.com/reference/cli/docker/buildx/imagetools/) to write desired tags.
16-
- This pattern is only required because if tag pushes are done concurrently, then identical multi-architecture tags are clobbered in a reverse race condition.
17-
- **NOTE**: The QEMU build for linux/arm64 is **very slow**. On the order of 30 minutes. This is currently unavoidable.
18-
- **TODO**: Migrate linux/arm64 docker build to native once GHA supports this platform as a first class citizen.
16+
- This pattern is only required because if tag pushes are done concurrently, then identical multi-architecture tags are clobbered in a reverse race condition.
1917
- **NOTE**. Be careful selecting from [the available github actions runners](https://github.com/actions/runner-images). Some runnner instances may be incompatible with assumed toolchain.
2018
- Docker images:
2119
- [production image at `stackql/stackql`](https://hub.docker.com/r/stackql/stackql/tags).

test/python/tcp_lb.py

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import argparse
2+
3+
from typing import List, Tuple, Iterable, Optional
4+
5+
_DEFAULT_PORT = 1070
6+
_GOOGLE_DEFAULT_PORT = 1080
7+
_GOOGLEADMIN_DEFAULT_PORT = 1098
8+
_OKTA_DEFAULT_PORT = 1090
9+
_AWS_DEFAULT_PORT = 1091
10+
_K8S_DEFAULT_PORT = 1092
11+
_GITHUB_DEFAULT_PORT = 1093
12+
_AZURE_DEFAULT_PORT = 1095
13+
_SUMOLOGIC_DEFAULT_PORT = 1096
14+
_DIGITALOCEAN_DEFAULT_PORT = 1097
15+
_STACKQL_TEST_DEFAULT_PORT = 1099
16+
_STACKQL_AUTH_TESTING_DEFAULT_PORT = 1170
17+
18+
_DEFAULT_AWS_GLOBAL_SERVICES: Tuple[str] = (
19+
'iam',
20+
'route53',
21+
)
22+
23+
_DEFAULT_AWS_REGIONAL_SERVICES: Tuple[str] = (
24+
"cloudcontrolapi", # cloudcontrol
25+
'cloudhsmv2', # cloudhsm
26+
"ec2",
27+
'logs', # cloudwatch
28+
"s3",
29+
"transfer",
30+
)
31+
32+
_DEFAULT_AWS_REGIONS: Tuple[str] = (
33+
"us-east-1",
34+
"us-east-2",
35+
"us-west-1",
36+
"us-west-2",
37+
"ap-south-1",
38+
"ap-northeast-1",
39+
"ap-northeast-2",
40+
"ap-southeast-1",
41+
"ap-southeast-2",
42+
"ap-northeast-3",
43+
"ca-central-1",
44+
"eu-central-1",
45+
"eu-west-1",
46+
"eu-west-2",
47+
"eu-west-3",
48+
"eu-north-1",
49+
"sa-east-1",
50+
)
51+
52+
_DEFAULT_GCP_SERVICES: Tuple[str] = (
53+
'compute',
54+
'storage',
55+
)
56+
57+
class _LB(object):
58+
59+
def __init__(self, lb_host: str, lb_port, backend_host: str, backend_port: int) -> None:
60+
self._lb_host = lb_host
61+
self._lb_port = lb_port
62+
self._backend_host = backend_host
63+
self._backend_port = backend_port
64+
65+
def get_lb_host(self) -> str:
66+
return self._lb_host
67+
68+
def get_lb_port(self) -> int:
69+
return self._lb_port
70+
71+
def get_backend_host(self) -> str:
72+
return self._backend_host
73+
74+
def get_backend_port(self) -> int:
75+
return self._backend_port
76+
77+
class _HostsGenerator(object):
78+
79+
def __init__(
80+
self,
81+
aws_global_services: Optional[Iterable[str]] = None,
82+
aws_regional_services: Optional[Iterable[str]] = None,
83+
aws_regions: Optional[Iterable[str]] = None,
84+
google_services: Optional[Iterable[str]] = None
85+
) -> None:
86+
self._aws_global_services = aws_global_services if aws_global_services is not None else _DEFAULT_AWS_GLOBAL_SERVICES
87+
self._aws_regional_services = aws_regional_services if aws_regional_services is not None else _DEFAULT_AWS_REGIONAL_SERVICES
88+
self._aws_regions = aws_regions if aws_regions is not None else _DEFAULT_AWS_REGIONS
89+
self._google_services = google_services if google_services is not None else _DEFAULT_GCP_SERVICES
90+
91+
@staticmethod
92+
def _generate_aws_hosts(aws_regional_services: Iterable[str], aws_regions: Iterable[str], aws_global_services: Iterable[str]) -> Iterable[_LB]:
93+
for service in aws_regional_services:
94+
for region in aws_regions:
95+
yield _LB(f'{service}.{region}.amazonaws.com', 443, '127.0.0.1', _AWS_DEFAULT_PORT)
96+
for service in aws_global_services:
97+
yield _LB(f'{service}.amazonaws.com', 443, '127.0.0.1', _AWS_DEFAULT_PORT)
98+
99+
@staticmethod
100+
def _generate_gcp_hosts(google_services: Iterable[str]) -> Iterable[_LB]:
101+
for service in google_services:
102+
yield _LB(f'{service}.googleapis.com', 443, '127.0.0.1', _GOOGLE_DEFAULT_PORT)
103+
104+
def generate_all_load_balancers(self) -> Iterable[_LB]:
105+
yield from self._generate_aws_hosts(self._aws_regional_services, self._aws_regions, self._aws_global_services)
106+
yield from self._generate_gcp_hosts(self._google_services)
107+
108+
109+
class _NginxConfigGenerator(object):
110+
111+
def __init__(self, n_indent: int = 2) -> None:
112+
self._n_indent = n_indent
113+
114+
@staticmethod
115+
def _generate_backends(hosts: Iterable[_LB], indent: int) -> Iterable[str]:
116+
"""
117+
Generate the nginx config.
118+
"""
119+
return [
120+
" " * indent + f'{host.get_lb_host()} {host.get_backend_host()}:{host.get_backend_port()};'
121+
for host in hosts
122+
]
123+
124+
def _generate_file_content(self, hosts: Iterable[_LB]) -> Tuple[str]:
125+
"""
126+
Generate the file content.
127+
"""
128+
lines : Tuple[str] = (
129+
'worker_processes 1;',
130+
'',
131+
'#error_log logs/error.log;',
132+
'#error_log logs/error.log notice;',
133+
'#error_log logs/error.log info;',
134+
'',
135+
'#pid logs/nginx.pid;',
136+
'',
137+
'',
138+
'events {',
139+
f'{" " * self._n_indent}worker_connections 1024;',
140+
'}',
141+
'',
142+
'stream {',
143+
'',
144+
f'{" " * self._n_indent}map $ssl_preread_server_name $targetBackend {{',
145+
*self._generate_backends(hosts, self._n_indent * 2),
146+
f'{" " * self._n_indent}}}',
147+
'',
148+
f'{" " * self._n_indent}server {{',
149+
f'{" " * self._n_indent}listen 443;',
150+
f'{" " * self._n_indent}',
151+
f'{" " * self._n_indent}proxy_connect_timeout 1s;',
152+
f'{" " * self._n_indent}proxy_timeout 3s;',
153+
f'{" " * self._n_indent}resolver 1.1.1.1;',
154+
f'{" " * self._n_indent}',
155+
f'{" " * self._n_indent}proxy_pass $targetBackend;',
156+
f'{" " * self._n_indent}ssl_preread on;',
157+
f'{" " * self._n_indent}}}',
158+
'}',
159+
)
160+
return lines
161+
162+
def generate_file_content(self, hosts: Iterable[_LB]) -> Tuple[str]:
163+
"""
164+
Generate the file content.
165+
"""
166+
return self._generate_file_content(hosts)
167+
168+
169+
class _HostsFileEntriesGenerator(object):
170+
171+
def __init__(self, hosts: Iterable[_LB]) -> None:
172+
self._hosts = hosts
173+
174+
def generate_entries(self) -> Iterable[str]:
175+
"""
176+
Generate the entries.
177+
"""
178+
return tuple(
179+
f'{host.get_backend_host()} {host.get_lb_host()}'
180+
for host in self._hosts
181+
)
182+
183+
184+
def _parse_args() -> argparse.Namespace:
185+
"""
186+
Parse the arguments.
187+
"""
188+
parser = argparse.ArgumentParser(description='Create a token.')
189+
parser.add_argument('--generate-nginx-lb', help='Opt-in nginx config generation', action=argparse.BooleanOptionalAction)
190+
parser.add_argument('--generate-hosts-entries', help='Opt-in hosts files entries generation', action=argparse.BooleanOptionalAction)
191+
# parser.add_argument('--header', type=str, help='The header.')
192+
return parser.parse_args()
193+
194+
def main():
195+
args = _parse_args()
196+
host_gen = _HostsGenerator()
197+
all_hosts = [lb for lb in host_gen.generate_all_load_balancers()]
198+
nginx_cfg_gen = _NginxConfigGenerator()
199+
if args.generate_nginx_lb:
200+
for l in nginx_cfg_gen.generate_file_content(all_hosts):
201+
print(l)
202+
return
203+
if args.generate_hosts_entries:
204+
hosts_gen = _HostsFileEntriesGenerator(all_hosts)
205+
for l in hosts_gen.generate_entries():
206+
print(l)
207+
return
208+
209+
if __name__ == '__main__':
210+
main()

test/robot/functional/stackql_mocked_from_cmd_line.robot

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7321,6 +7321,7 @@ Set Statement Update Auth Scenario Working
73217321
... stderr=${CURDIR}/tmp/Set-Statement-Update-Auth-Scenario-Working-Working-stderr.tmp
73227322

73237323
Busted Auth Throws Error Then Set Statement Update Auth Scenario Working
7324+
[Tags] registry tls_proxied
73247325
${inputStr} = Catenate
73257326
... select name, id, network, split_part(network, '/', 8) as network_region from google.compute.firewalls where project \= 'testing-project' order by id desc;
73267327
... set session "$.auth.google.credentialsfilepath"='${AUTH_GOOGLE_SA_KEY_PATH}';
@@ -7395,6 +7396,7 @@ Alternate App Root Persists All Temp Materials in Alotted Directory
73957396
Directory Should Exist ${TEST_TMP_EXEC_APP_ROOT_NATIVE}${/}src
73967397

73977398
View Tuple Replacement Working As Exemplified by AWS EC2 Instances List and Detail
7399+
[Tags] registry tls_proxied
73987400
${inputStr} = Catenate
73997401
... SELECT region, instance_id, tenancy, security_groups
74007402
... FROM aws.ec2_nextgen.instances
@@ -7427,7 +7429,7 @@ View Tuple Replacement Working As Exemplified by AWS EC2 Instances List and Deta
74277429
... repeat_count=20
74287430

74297431
Google Buckets List With Date Logic Exemplifies Use of SQLite Math Functions
7430-
[Tags] registry tls_proxied
7432+
[Tags] registry tls_proxied
74317433
Pass Execution If "${SQL_BACKEND}" == "postgres_tcp" This is a valid case where the test is targetted at SQLite only
74327434
${inputStr} = Catenate
74337435
... SELECT name, timeCreated, floor(julianday('2025-01-27')-julianday(timeCreated)) as days_since_ceiling
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dynamic-sni-proxy.conf

test/tcp/reverse-proxy/nginx/README.md

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
## Runnning nginx as a pass through TCP proxy
44

5+
These scenarios require `python` >= `3.11`, `nginx` >= `1.27.3` (and all of the `pip` requirements of this repository).
56

6-
### Automated testing scenario
7+
This is not ideal for local running; great for CI lifecycles. If you do this locally, you **must** restore `/etc/hosts`.
8+
Rough and ready `/etc/hosts` restoration instructions are supplied, but, honestly, it is better if you take a prior backup and
9+
clobber with same when done.
710

8-
This scenario requires `nginx` >= version `1.27.3` (and all of the `pip` requirements of this repository).
11+
We will need superuser privileges at times (via `sudo`) because we are editing `/etc/hosts` and using the protected port `443`.
912

10-
This is not ideal for local running; great for CI lifecycles. If you do this locally, you **must** restore `/etc/hosts`.
13+
### Automated testing scenario
1114

12-
We will need superuser privileges at times (via `sudo`) because we are editing `/etc/hosts` and using the protected port `443`.
1315

1416
Please run all commands from the root of the repository:
1517

@@ -41,3 +43,39 @@ robot \
4143
sed '/storage.googleapis.com/d' /etc/hosts | sudo tee /etc/hosts
4244

4345
```
46+
47+
### More fulsome testing
48+
49+
Please run all commands from the root of the repository:
50+
51+
```bash
52+
53+
python test/python/tcp_lb.py --generate-hosts-entries | sudo tee -a /etc/hosts
54+
55+
python test/python/tcp_lb.py --generate-nginx-lb > test/tcp/reverse-proxy/nginx/dynamic-sni-proxy.conf
56+
57+
sudo nginx -c $(pwd)/test/tcp/reverse-proxy/nginx/tls-pass-through.conf
58+
59+
```
60+
61+
Then, run the robot tests that are suitable (as tags evolve, you may want to be more selective):
62+
63+
```bash
64+
65+
robot \
66+
--variable 'SUNDRY_CONFIG:{"registry_path": "test/registry"}' \
67+
--include tls_proxied \
68+
-d test/robot/reports \
69+
test/robot/functional
70+
71+
```
72+
73+
**Very important**; after this, restore `/etc/hosts` (BSD and GNU compatible, clunky expression):
74+
75+
```bash
76+
77+
sed '/googleapis/d' /etc/hosts | sudo tee /etc/hosts
78+
79+
sed '/aws/d' /etc/hosts | sudo tee /etc/hosts
80+
81+
```

0 commit comments

Comments
 (0)