Skip to content

Commit 508e154

Browse files
Changed structure of the tests. Added new node implementation
1 parent 4de1637 commit 508e154

File tree

4 files changed

+201
-90
lines changed

4 files changed

+201
-90
lines changed

assets/node/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
22
"name" : "node_without_procfile",
3-
"version": "0.1.0"
3+
"version": "0.1.0",
4+
"dependencies": {
5+
"ip": "^1.1.8"
6+
}
47
}

assets/node/server.js

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,97 @@
11
var http = require('http');
2+
var https = require('https');
23
var url = require('url');
4+
var ip = require('ip');
35

46
HOST = null;
57

68
var host = "0.0.0.0";
79
var port = process.env.PORT || 3000;
810

9-
http.createServer(function (req, res) {
10-
res.writeHead(200, {'Content-Type': 'text/html'});
11-
res.write('<h1>Hello from a node app! ');
12-
res.write('via: ' + host + ':' + port);
13-
res.end('</h1>');
14-
}).listen(port, null);
11+
const ENDPOINT_TYPE_MAP = {
12+
'api.ipify.org': {
13+
validation_name: "IPv4",
14+
path: "/ipv4-test"
15+
},
16+
'api6.ipify.org': {
17+
validation_name: "IPv6",
18+
path: "/ipv6-test"
19+
},
20+
'api64.ipify.org': {
21+
validation_name: "Dual stack",
22+
path: "/dual-stack-test"
23+
}
24+
};
1525

16-
console.log('Server running at http://' + host + ':' + port + '/');
26+
function testIPAddress(endpoint, expectedType) {
27+
return new Promise((resolve) => {
28+
https.get(`https://${endpoint}`, (resp) => {
29+
let data = '';
30+
31+
resp.on('data', (chunk) => { data += chunk; });
32+
resp.on('end', () => {
33+
let success = false;
34+
let detectedType = 'unknown';
35+
36+
if (expectedType === "IPv4" && ip.isV4Format(data)) {
37+
success = true;
38+
detectedType = "IPv4";
39+
} else if (expectedType === "IPv6" && ip.isV6Format(data)) {
40+
success = true;
41+
detectedType = "IPv6";
42+
} else if (expectedType === "Dual stack") {
43+
if (ip.isV4Format(data)) {
44+
success = true;
45+
detectedType = "IPv4";
46+
} else if (ip.isV6Format(data)) {
47+
success = true;
48+
detectedType = "IPv6";
49+
}
50+
}
51+
52+
resolve({
53+
endpoint,
54+
success,
55+
ip_type: detectedType,
56+
error: success ? 'none' : `Expected ${expectedType}, but got ${data}`
57+
});
58+
});
59+
}).on("error", (err) => {
60+
resolve({ endpoint, success: false, ip_type: 'unknown', error: err.message });
61+
});
62+
});
63+
}
64+
65+
http.createServer(async function (req, res) {
66+
const parsedUrl = url.parse(req.url, true);
67+
const path = parsedUrl.pathname;
68+
69+
let endpoint = null;
70+
for (const [ep, { path: epPath }] of Object.entries(ENDPOINT_TYPE_MAP)) {
71+
if (path === epPath) {
72+
endpoint = ep;
73+
break;
74+
}
75+
}
76+
77+
if (endpoint) {
78+
const expectedType = ENDPOINT_TYPE_MAP[endpoint].validation_name;
79+
const result = await testIPAddress(endpoint, expectedType);
80+
81+
const responseCode = result.success ? 200 : 500;
82+
res.writeHead(responseCode, { 'Content-Type': 'text/plain' });
83+
84+
const responseMessage = `${expectedType} validation resulted in ${result.success ? 'success' : 'failure'}. Detected IP type is ${result.ip_type}. Error message: ${result.error}.`;
85+
86+
res.end(responseMessage);
87+
88+
} else {
89+
res.writeHead(200, { 'Content-Type': 'text/plain' });
90+
res.write('<h1>Hello from a node app! ');
91+
res.write('via: ' + host + ':' + port);
92+
res.end('</h1>');
93+
}
94+
95+
}).listen(port, host, () => {
96+
console.log('Server running at http://' + host + ':' + port + '/');
97+
});

assets/python/server.py

Lines changed: 57 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,23 @@
77
from socketserver import ThreadingMixIn
88

99
ENDPOINT_TYPE_MAP = {
10-
'api.ipify.org': "IPv4",
11-
'api6.ipify.org': "IPv6",
12-
'api64.ipify.org': "Dual stack"
13-
}
10+
'api.ipify.org': {
11+
'validation_name': "IPv4",
12+
'path': "/ipv4-test"
13+
},
14+
'api6.ipify.org': {
15+
'validation_name': "IPv6",
16+
'path': "/ipv6-test"
17+
},
18+
'api64.ipify.org': {
19+
'validation_name': "Dual stack",
20+
'path': "/dual-stack-test"
21+
}
22+
}
1423

1524
DEFAULT_PORT = '8080'
1625
HOST = '127.0.0.1'
1726

18-
OVERALL_SUCCESS_MESSAGE = "IPv6 egress test suite passed. All tests completed successfully."
19-
OVERALL_FAIL_MESSAGE = "IPv6 Egress Suite failed — investigate failed components."
20-
21-
# Set up logging
2227
logging.basicConfig(
2328
level=logging.INFO,
2429
format='%(asctime)s | %(levelname)s | %(message)s'
@@ -27,42 +32,28 @@
2732
class IPv6Tester:
2833
"""
2934
The `IPv6Tester` class is responsible for verifying the successful execution of
30-
egress calls using IPv4, IPv6, and Dual Stack configurations, sequentially.
31-
It offers logging at each step to track the progress of the calls.
32-
The test execution is deemed successful if all endpoints are reached without errors.
33-
Conversely, if any egress call fails, the test execution is marked as failed,
34-
and the application exits with an exit code of 1 to signal the failure.
35+
egress calls using IPv4, IPv6, and Dual Stack configurations, depending on the input.
36+
It offers logging result from the call.
37+
The test execution is deemed successful if the requested endpoint is reached without errors.
38+
3539
"""
3640

3741
def __init__(self, endpoints):
3842
self.endpoints = endpoints
39-
40-
def test_all_addresses(self):
41-
results = []
42-
all_successful = True
43-
for endpoint in self.endpoints:
44-
result = self.test_endpoint(endpoint)
45-
results.append((endpoint, result))
46-
self.print_result(endpoint, result)
47-
if not result['success']:
48-
all_successful = False
49-
50-
if all_successful:
51-
logging.info(OVERALL_SUCCESS_MESSAGE)
52-
else:
53-
logging.error(OVERALL_FAIL_MESSAGE)
54-
55-
return all_successful, results
56-
43+
44+
def test_single_address(self, endpoint):
45+
result = self.test_endpoint(endpoint)
46+
self.print_result(endpoint, result)
47+
return result
48+
5749
def print_result(self, endpoint, result):
58-
validation_type = ENDPOINT_TYPE_MAP.get(endpoint, "Unknown")
50+
validation_type = ENDPOINT_TYPE_MAP[endpoint]['validation_name']
5951
if result['success']:
6052
logging.info(f"{validation_type} validation succeeded.")
6153
else:
6254
logging.error(f"{validation_type} validation failed.")
63-
55+
6456
def test_endpoint(self, endpoint):
65-
6657
try:
6758
logging.info(f"Testing endpoint: {endpoint}")
6859
connection = http.client.HTTPConnection(endpoint, timeout=0.20)
@@ -83,9 +74,9 @@ def test_endpoint(self, endpoint):
8374
return {
8475
'success': False,
8576
'error': str(e),
86-
'ip_type': 'Unknown'
77+
'ip_type': 'Unknown'
8778
}
88-
79+
8980
@staticmethod
9081
def determine_ip_type(ip_string):
9182
try:
@@ -101,46 +92,46 @@ class Handler(BaseHTTPRequestHandler):
10192
to testing IPv6 egress calls, while the default path is used
10293
for testing the default Hello-Python buildpack test case.
10394
'''
95+
10496
def do_GET(self):
105-
path = self.path
106-
if path == "/ipv6-test":
107-
self.handle_ipv6_test()
97+
if self.path in [data['path'] for data in ENDPOINT_TYPE_MAP.values()]:
98+
self.handle_test()
10899
else:
109100
self.send_response(200)
110101
self.end_headers()
111-
message = "Hello python, world!"
102+
message = "Hello python, world!"
112103
self.wfile.write(message.encode('utf-8'))
113104
self.wfile.write('\n'.encode('utf-8'))
114-
115-
def handle_ipv6_test(self):
116-
tester = IPv6Tester(list(ENDPOINT_TYPE_MAP.keys()))
117-
all_successful, results = tester.test_all_addresses()
118-
119-
# Determine response status and message
120-
response_code = 200 if all_successful else 500
121-
overall_message = OVERALL_SUCCESS_MESSAGE if all_successful else OVERALL_FAIL_MESSAGE
122-
123-
# Send HTTP response status
124-
self.send_response(response_code)
125-
self.end_headers()
126-
127-
response_messages = []
128-
for endpoint, result in results:
129-
endpoint_results = f"{ENDPOINT_TYPE_MAP.get(endpoint, 'Unknown')} validation resulted in {'success' if result['success'] else 'failure'}. Detected IP type is {result.get('ip_type', 'unknown')}. Error message: {result.get('error', 'none')}."
130-
response_messages.append(endpoint_results)
131-
132-
response_content = "\n".join(response_messages + [overall_message])
133-
134-
# Write the detailed results and overall message to the web console
135-
self.wfile.write(response_content.encode('utf-8'))
136-
self.wfile.write('\n'.encode('utf-8'))
137-
138105

106+
def handle_test(self):
107+
endpoint = self.get_endpoint_from_path()
108+
if endpoint:
109+
tester = IPv6Tester([endpoint])
110+
result = tester.test_single_address(endpoint)
111+
response_code = 200 if result['success'] else 500
112+
self.send_response(response_code)
113+
self.end_headers()
114+
115+
validation_name = ENDPOINT_TYPE_MAP[endpoint]['validation_name']
116+
response_message = (f"{validation_name} validation resulted in "
117+
f"{'success' if result['success'] else 'failure'}. Detected IP type is "
118+
f"{result.get('ip_type', 'unknown')}. Error message: {result.get('error', 'none')}.")
119+
self.wfile.write(response_message.encode('utf-8'))
120+
self.wfile.write('\n'.encode('utf-8'))
121+
else:
122+
self.send_response(404)
123+
self.end_headers()
124+
self.wfile.write(b'Endpoint not found\n')
125+
126+
def get_endpoint_from_path(self):
127+
for endpoint, data in ENDPOINT_TYPE_MAP.items():
128+
if self.path == data['path']:
129+
return endpoint
130+
return None
139131

140132
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
141133
"""Handle requests in a separate thread."""
142134

143-
144135
if __name__ == '__main__':
145136
port = int(os.environ.get('PORT', DEFAULT_PORT))
146137
host = os.environ.get('VCAP_APP_HOST', HOST)
@@ -149,4 +140,4 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
149140
server = ThreadedHTTPServer((host, port), Handler)
150141
print('Starting server, use <Ctrl-C> to stop')
151142

152-
server.serve_forever()
143+
server.serve_forever()

ipv6/ipv6.go

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,60 @@ var _ = IPv6Describe("IPv6 Connectivity Tests", func() {
2525
Expect(cf.Cf("delete", appName, "-f", "-r").Wait()).To(Exit(0))
2626
})
2727

28-
Describe("Egress Capability in Python App", func() {
28+
ENDPOINT_TYPE_MAP := map[string]struct {
29+
validationName string
30+
path string
31+
}{
32+
"api.ipify.org": {
33+
validationName: "IPv4",
34+
path: "/ipv4-test",
35+
},
36+
"api6.ipify.org": {
37+
validationName: "IPv6",
38+
path: "/ipv6-test",
39+
},
40+
"api64.ipify.org": {
41+
validationName: "Dual stack",
42+
path: "/dual-stack-test",
43+
},
44+
"default": {
45+
validationName: "Default app",
46+
path: "",
47+
},
48+
}
49+
50+
describeIPv6Tests := func(buildpackPath string, stack string) {
51+
appName = random_name.CATSRandomName("APP")
52+
Expect(cf.Cf("push", appName,
53+
"-m", DEFAULT_MEMORY_LIMIT,
54+
"-p", buildpackPath,
55+
"-s", stack,
56+
).Wait(Config.DetectTimeoutDuration())).To(Exit(0))
57+
58+
for _, data := range ENDPOINT_TYPE_MAP {
59+
response := helpers.CurlApp(Config, appName, data.path)
60+
61+
if data.path == "" {
62+
Expect(response).To(ContainSubstring("Hello"))
63+
} else {
64+
Expect(response).To(ContainSubstring(fmt.Sprintf("%s validation resulted in success", data.validationName)))
65+
}
66+
}
67+
}
68+
69+
Describe("Egress Capability in Apps", func() {
2970
for _, stack := range Config.GetStacks() {
3071
stack := stack
31-
Context(fmt.Sprintf("Using stack: %s", stack), func() {
32-
It("validates IPv6 egress and examines test results", func() {
33-
Expect(cf.Cf("push", appName,
34-
"-m", DEFAULT_MEMORY_LIMIT,
35-
"-p", assets.NewAssets().Python,
36-
"-s", stack,
37-
).Wait(Config.DetectTimeoutDuration())).To(Exit(0))
38-
39-
response := helpers.CurlApp(Config, appName, "/ipv6-test")
40-
41-
Expect(response).To(ContainSubstring("IPv4 validation resulted in success"))
42-
Expect(response).To(ContainSubstring("IPv6 validation resulted in success"))
43-
Expect(response).To(ContainSubstring("Dual stack validation resulted in success"))
44-
Expect(response).NotTo(ContainSubstring("IPv6 Egress Suite failed"))
45-
Expect(response).To(ContainSubstring("IPv6 egress test suite passed."))
72+
73+
Context(fmt.Sprintf("Using Python stack: %s", stack), func() {
74+
It("validates IPv6 egress for Python App", func() {
75+
describeIPv6Tests(assets.NewAssets().Python, stack)
76+
})
77+
})
78+
79+
Context(fmt.Sprintf("Using Node.js stack: %s", stack), func() {
80+
It("validates IPv6 egress for Node.js App", func() {
81+
describeIPv6Tests(assets.NewAssets().Node, stack)
4682
})
4783
})
4884
}

0 commit comments

Comments
 (0)