Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/e2e-k8s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ jobs:
submodules: true
- name: Test conditional disk cleanup
run: bash .github/scripts/free-disk-space-test.sh
- name: Test local HTTP upstream fixture and Compose wiring
run: python3 -B shenyu-e2e/shenyu-e2e-case/compose/fixtures/test_httpbin.py
- uses: ./actions/paths-filter
id: filter
with:
Expand Down
57 changes: 57 additions & 0 deletions shenyu-e2e/shenyu-e2e-case/compose/fixtures/httpbin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Local upstream for the Divide URI and HTTP method routing scenarios."""

import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlsplit


class HttpbinHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"

def respond(self):
path = urlsplit(self.path)
data = self.rfile.read(int(self.headers.get("Content-Length", "0")))
status = 200
if path.path == "/status/200":
body = b""
elif path.path == "/anything" or path.path.startswith("/anything/"):
body = json.dumps({
"method": self.command,
"url": self.path,
"args": parse_qs(path.query),
"data": data.decode("utf-8"),
}).encode("utf-8")
else:
status = 404
body = b'{"error":"unknown fixture endpoint"}'
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
if self.command != "HEAD":
self.wfile.write(body)

do_GET = respond
do_POST = respond
do_PUT = respond
do_DELETE = respond
do_HEAD = respond


if __name__ == "__main__":
ThreadingHTTPServer(("0.0.0.0", 80), HttpbinHandler).serve_forever()
31 changes: 31 additions & 0 deletions shenyu-e2e/shenyu-e2e-case/compose/fixtures/httpbin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

services:
httpbin:
image: python:3.12-alpine
command: ["python", "-B", "-u", "/opt/httpbin.py"]
volumes:
- ./httpbin.py:/opt/httpbin.py:ro
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1/anything', timeout=2)"]
interval: 2s
timeout: 3s
retries: 15
networks:
shenyu:
# Existing routing scenarios resolve their upstream on this test network.
aliases:
- httpbin.org
97 changes: 97 additions & 0 deletions shenyu-e2e/shenyu-e2e-case/compose/fixtures/test_httpbin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Exercise the upstream over HTTP and validate every consuming Compose file."""

import http.client
import json
from pathlib import Path
import subprocess
import threading
import unittest

from httpbin import HttpbinHandler, ThreadingHTTPServer


class HttpbinTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.server = ThreadingHTTPServer(("127.0.0.1", 0), HttpbinHandler)
cls.thread = threading.Thread(target=cls.server.serve_forever)
cls.thread.start()

@classmethod
def tearDownClass(cls):
cls.server.shutdown()
cls.server.server_close()
cls.thread.join()

def setUp(self):
self.connection = http.client.HTTPConnection(*self.server.server_address, timeout=3)

def tearDown(self):
self.connection.close()

def test_echo_methods_paths_and_request_bodies(self):
for method in ("GET", "POST", "PUT", "DELETE"):
for path in ("/anything", "/anything/xx", "/anything/xx/yy", "/anything/xxx"):
with self.subTest(method=method, path=path):
self.connection.request(method, path + "?id=23", body="request body")
response = self.connection.getresponse()
self.assertEqual(200, response.status)
body = json.loads(response.read())
self.assertEqual(method, body["method"])
self.assertEqual(path + "?id=23", body["url"])
self.assertEqual({"id": ["23"]}, body["args"])
self.assertEqual("request body", body["data"])

def test_status_200_has_empty_body_for_every_method(self):
for method in ("GET", "POST", "PUT", "DELETE", "HEAD"):
with self.subTest(method=method):
self.connection.request(method, "/status/200")
response = self.connection.getresponse()
self.assertEqual(200, response.status)
self.assertEqual(b"", response.read())

def test_unknown_path_is_not_a_success(self):
self.connection.request("GET", "/not-a-fixture")
response = self.connection.getresponse()
self.assertEqual(404, response.status)
response.read()

def test_compose_uses_healthy_local_upstream(self):
directory = Path(__file__).resolve().parent
compose = directory.parent
files = sorted((compose / "storage").glob("shenyu-storage-*.yml"))
files += sorted((compose / "sync").glob("shenyu-sync-websocket-cluster-*.yml"))
self.assertEqual(6, len(files))
for path in files:
with self.subTest(compose=path.name):
result = subprocess.run(
["docker", "compose", "-f", str(path), "config", "--format", "json"],
check=True, capture_output=True, text=True,
)
services = json.loads(result.stdout)["services"]
fixture = services["httpbin"]
self.assertIn("httpbin.org", fixture["networks"]["shenyu"]["aliases"])
self.assertEqual("service_healthy", services["shenyu-bootstrap"]["depends_on"]["httpbin"]["condition"])
mount = next(v for v in fixture["volumes"] if v["target"] == "/opt/httpbin.py")
self.assertEqual(directory / "httpbin.py", Path(mount["source"]))
self.assertTrue(mount["read_only"])
self.assertIn("/anything", fixture["healthcheck"]["test"][-1])


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
version: '3.9'

services:
httpbin:
extends:
file: ../fixtures/httpbin.yml
service: httpbin

shenyu-admin:
image: apache/shenyu-admin:latest
Expand Down Expand Up @@ -63,6 +67,8 @@ services:
start_period: 30s
restart: always
depends_on:
httpbin:
condition: service_healthy
shenyu-admin:
condition: service_healthy
networks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
version: '3.9'

services:
httpbin:
extends:
file: ../fixtures/httpbin.yml
service: httpbin
shenyu-mysql:
image: mysql:8.0
container_name: shenyu-mysql
Expand Down Expand Up @@ -98,6 +102,8 @@ services:
start_period: 30s
restart: always
depends_on:
httpbin:
condition: service_healthy
shenyu-admin:
condition: service_healthy
networks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
version: '3.9'

services:
httpbin:
extends:
file: ../fixtures/httpbin.yml
service: httpbin
shenyu-opengauss:
image: enmotech/opengauss-lite:5.0.1
container_name: shenyu-opengauss
Expand Down Expand Up @@ -97,6 +101,8 @@ services:
start_period: 30s
restart: always
depends_on:
httpbin:
condition: service_healthy
shenyu-admin:
condition: service_healthy
networks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
version: '3.9'

services:
httpbin:
extends:
file: ../fixtures/httpbin.yml
service: httpbin
shenyu-postgres:
image: postgres:14
container_name: shenyu-postgres
Expand Down Expand Up @@ -97,6 +101,8 @@ services:
start_period: 30s
restart: always
depends_on:
httpbin:
condition: service_healthy
shenyu-admin:
condition: service_healthy
networks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
version: '3.9'

services:
httpbin:
extends:
file: ../fixtures/httpbin.yml
service: httpbin
shenyu-mysql:
image: mysql:8.0
container_name: shenyu-mysql
Expand Down Expand Up @@ -133,6 +137,8 @@ services:
start_period: 30s
restart: always
depends_on:
httpbin:
condition: service_healthy
shenyu-admin-1:
condition: service_healthy
shenyu-admin-2:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
version: '3.9'

services:
httpbin:
extends:
file: ../fixtures/httpbin.yml
service: httpbin
shenyu-zookeeper-cluster:
image: zookeeper:3.9
container_name: shenyu-zookeeper-cluster
Expand Down Expand Up @@ -158,6 +162,8 @@ services:
start_period: 30s
restart: always
depends_on:
httpbin:
condition: service_healthy
shenyu-admin-1:
condition: service_healthy
shenyu-admin-2:
Expand Down
Loading
Loading