diff --git a/docs/sources/k6/next/javascript-api/k6-x-dns/_index.md b/docs/sources/k6/next/javascript-api/k6-x-dns/_index.md new file mode 100644 index 0000000000..9ba62c32b1 --- /dev/null +++ b/docs/sources/k6/next/javascript-api/k6-x-dns/_index.md @@ -0,0 +1,107 @@ +--- +aliases: +title: 'k6/x/dns' +description: 'k6 DNS extension API' +weight: 11 +--- + +# k6/x/dns + +The `k6/x/dns` module enables DNS resolution testing in k6, allowing you to resolve DNS names to IP addresses using custom DNS servers or the system's default DNS configuration. This module is particularly useful for testing DNS server performance, validating DNS configurations, and incorporating DNS resolution into your load testing scenarios. + +The DNS module is implemented as an official extension and is available natively in k6, requiring no additional installation or build steps thanks to native extensions support. + +## Key features + +- The [`dns.resolve()`](https://grafana.com/docs/k6//javascript-api/k6-x-dns/resolve) function resolves DNS names using a specified DNS server, allowing you to test custom DNS configurations and server performance. +- The [`dns.lookup()`](https://grafana.com/docs/k6//javascript-api/k6-x-dns/lookup) function resolves DNS names using the system's default DNS servers, providing a way to test standard DNS resolution behavior. +- Support for A (IPv4) and AAAA (IPv6) record types. +- Automatic metrics collection for DNS resolution performance analysis. + +### Use cases + +- Load testing DNS servers to ensure they can handle high query volumes. +- Validating DNS configurations in staging and production environments. +- Testing DNS failover mechanisms and redundancy. +- Incorporating DNS resolution time into overall application performance testing. + +## API + +| Function/Object | Description | +| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [resolve](https://grafana.com/docs/k6//javascript-api/k6-x-dns/resolve) | Resolves a DNS name to IP addresses using a specified DNS server. | +| [lookup](https://grafana.com/docs/k6//javascript-api/k6-x-dns/lookup) | Resolves a DNS name to IP addresses using the system's default DNS servers. | + +## Metrics + +The extension automatically generates the following metrics: + +- `dns_resolutions`: Counter tracking the number of DNS resolution attempts. +- `dns_resolution_duration`: Trend measuring DNS resolution response times. +- `dns_lookups`: Counter tracking the number of DNS lookup attempts. +- `dns_lookup_duration`: Trend measuring DNS lookup response times. + +## Examples + +### Basic DNS resolution with custom server + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; + +export default async function () { + // Resolve k6.io using Cloudflare's DNS server + const ips = await dns.resolve('k6.io', 'A', '1.1.1.1:53'); + console.log('k6.io resolves to:', ips); +} +``` + +{{< /code >}} + +### DNS lookup using system defaults + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; + +export default async function () { + // Resolve k6.io using system DNS servers + const ips = await dns.lookup('k6.io'); + console.log('k6.io resolves to:', ips); +} +``` + +{{< /code >}} + +### Comprehensive DNS testing + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; + +export const options = { + vus: 10, + duration: '30s', +}; + +export default async function () { + // Test both IPv4 and IPv6 resolution + const ipv4Results = await dns.resolve('example.com', 'A', '8.8.8.8:53'); + const ipv6Results = await dns.resolve('example.com', 'AAAA', '[2606:4700:4700::1111]:53'); + + // Test system DNS + const systemResults = await dns.lookup('example.com'); + + // Validate results + expect(ipv4Results.length).toBeGreaterThan(0); + expect(ipv6Results.length).toBeGreaterThan(0); + expect(systemResults.length).toBeGreaterThan(0); +} +``` + +{{< /code >}} + diff --git a/docs/sources/k6/next/javascript-api/k6-x-dns/lookup.md b/docs/sources/k6/next/javascript-api/k6-x-dns/lookup.md new file mode 100644 index 0000000000..6101e68dab --- /dev/null +++ b/docs/sources/k6/next/javascript-api/k6-x-dns/lookup.md @@ -0,0 +1,252 @@ +--- +title: 'lookup( hostname )' +description: 'resolve a DNS name to IP addresses using system DNS servers' +weight: 30 +--- + +# lookup( hostname ) + +The `dns.lookup` function performs DNS resolution using the system's default DNS configuration and returns a promise that resolves to an array of IP addresses. This function is useful, for instance, for testing standard DNS resolution behavior and comparing it with custom DNS server results. + +## Parameters + +| Parameter | Type | Description | +| :-------- | :----- | :------------------------------------------------------------------------ | +| hostname | string | The domain name to resolve (e.g., "example.com", "k6.io") | + +## Returns + +A promise resolving to an array of strings, where each string is an IP address that the domain name resolves to. The function returns the same IP addresses that would be returned by the system's standard DNS resolution mechanism. + +## Examples + +### Basic lookup + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; + +export default async function () { + // Resolve using system DNS servers + const addresses = await dns.lookup('k6.io'); + console.log('k6.io resolves to:', addresses); + // Output: k6.io resolves to: ["104.21.7.127", "172.67.154.74"] +} +``` + +{{< /code >}} + +### Comparing system vs custom DNS + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; + +export default async function () { + const domain = 'example.com'; + + // Get results from system DNS + const systemResults = await dns.lookup(domain); + + // Get results from Google's DNS + const googleResults = await dns.resolve(domain, 'A', '8.8.8.8:53'); + + console.log('System DNS results:', systemResults); + console.log('Google DNS results:', googleResults); + + // Check if both methods return results + expect(systemResults.length).toBeGreaterThan(0); + expect(googleResults.length).toBeGreaterThan(0); + + // Compare results (they might differ due to different DNS configurations) + const hasCommonAddress = systemResults.some(ip => googleResults.includes(ip)); + expect(hasCommonAddress).toBeTruthy(); +} +``` + +{{< /code >}} + +### Testing DNS consistency + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; + +export const options = { + vus: 1, + iterations: 10, +}; + +export default async function () { + const domain = 'k6.io'; + + try { + const results = await dns.lookup(domain); + + expect(results.length).toBeGreaterThan(0); + expect(results.every(ip => + /^\d+\.\d+\.\d+\.\d+$/.test(ip) || /^[0-9a-fA-F:]+$/.test(ip) + )).toBeTruthy(); + + console.log(`Iteration ${__ITER}: ${domain} -> ${results.join(', ')}`); + } catch (error) { + console.error('DNS lookup failed:', error); + } +} +``` + +{{< /code >}} + +### Load testing with system DNS + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; +import { sleep } from 'k6'; +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; +import { Trend, Rate } from 'k6/metrics'; + +const lookupDuration = new Trend('dns_lookup_duration_custom'); +const successRate = new Rate('dns_lookup_success_rate'); + +export const options = { + vus: 10, + duration: '60s', +}; + +const domains = [ + 'k6.io', + 'example.com', + 'google.com', + 'github.com', + 'stackoverflow.com', +]; + +export default async function () { + const domain = domains[Math.floor(Math.random() * domains.length)]; + const startTime = Date.now(); + + try { + const results = await dns.lookup(domain); + const duration = Date.now() - startTime; + + lookupDuration.add(duration); + successRate.add(true); + + expect(results.length).toBeGreaterThan(0); + + console.log(`${domain} resolved in ${duration}ms to ${results.length} addresses`); + } catch (error) { + const duration = Date.now() - startTime; + lookupDuration.add(duration); + successRate.add(false); + + console.error(`Failed to resolve ${domain}: ${error.message}`); + } + + sleep(1); +} +``` + +{{< /code >}} + +### Validating DNS configuration + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; + +export default async function () { + const testDomains = [ + 'k6.io', + 'grafana.com', + 'example.com', + ]; + + for (const domain of testDomains) { + try { + const results = await dns.lookup(domain); + + expect(results.length).toBeGreaterThan(0); + expect(results.every(ip => { + // Basic IPv4/IPv6 validation + return /^\d+\.\d+\.\d+\.\d+$/.test(ip) || /^[0-9a-fA-F:]+$/.test(ip); + })).toBeTruthy(); + + console.log(`✓ ${domain}: ${results.join(', ')}`); + } catch (error) { + console.error(`✗ ${domain}: ${error.message}`); + } + } +} +``` + +{{< /code >}} + +## Error handling + +The `lookup` function may throw errors in the following cases: + +- Invalid hostname format +- DNS resolution timeout +- No DNS servers configured on the system +- Network connectivity issues + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; + +export default async function () { + try { + const results = await dns.lookup('nonexistent.invalid.domain.test'); + console.log('Unexpected success:', results); + } catch (error) { + console.log('Expected DNS lookup error:', error.message); + } + + // Test with invalid hostname format + try { + const results = await dns.lookup(''); + console.log('Unexpected success with empty hostname:', results); + } catch (error) { + console.log('Expected error for empty hostname:', error.message); + } +} +``` + +{{< /code >}} + +## Metrics + +When using `dns.lookup`, the following metrics are automatically generated: + +- `dns_lookups`: Counter incremented for each lookup attempt +- `dns_lookup_duration`: Trend measuring the time taken for DNS lookup + +These metrics help you monitor DNS performance using your system's DNS configuration. + +## Notes on Usage + +- **System dependency**: Results depend on your system's DNS configuration (resolv.conf on Unix systems, network settings on Windows) +- **Caching**: System DNS resolution may be cached, which can affect performance measurements +- **Comparison baseline**: Use `lookup` as a baseline when comparing performance against custom DNS servers +- **Network environment**: Results may vary between different network environments and configurations +- **IPv4/IPv6**: The function returns both IPv4 and IPv6 addresses if available, depending on your system's configuration + +## Comparison with resolve() + +| Feature | `dns.lookup()` | `dns.resolve()` | +|---------|----------------|-----------------| +| DNS server | System default | Custom specified | +| Configuration | Uses system settings | Full control | +| Caching | May use system cache | Direct server query | +| Use case | Standard resolution testing | Custom DNS server testing | \ No newline at end of file diff --git a/docs/sources/k6/next/javascript-api/k6-x-dns/resolve.md b/docs/sources/k6/next/javascript-api/k6-x-dns/resolve.md new file mode 100644 index 0000000000..018e5db43d --- /dev/null +++ b/docs/sources/k6/next/javascript-api/k6-x-dns/resolve.md @@ -0,0 +1,171 @@ +--- +title: 'resolve( query, recordType, nameserver )' +description: 'resolve a DNS name to IP addresses using a specified DNS server' +weight: 20 +--- + +# resolve( query, recordType, nameserver ) + +The `dns.resolve` function performs DNS resolution using a specified DNS server and returns a promise that resolves to an array of IP addresses. This function allows you to test specific DNS servers, validate DNS configurations, and measure DNS resolution performance under load. + +## Parameters + +| Parameter | Type | Description | +| :--------- | :----- | :-------------------------------------------------------------------------------------------------------------- | +| query | string | The domain name to resolve (e.g., "example.com", "k6.io") | +| recordType | string | The DNS record type to query. Supported values: "A" (IPv4 addresses), "AAAA" (IPv6 addresses) | +| nameserver | string | The DNS server to use for resolution in the format "host:port" (e.g., "8.8.8.8:53", "[2606:4700:4700::1111]:53") | + +## Returns + +A promise resolving to an array of strings, where each string is an IP address that the domain name resolves to. For A records, these will be IPv4 addresses. For AAAA records, these will be IPv6 addresses. + +## Examples + +### Basic A record resolution + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; + +export default async function () { + // Resolve IPv4 addresses using Google's DNS server + const ipv4Addresses = await dns.resolve('k6.io', 'A', '8.8.8.8:53'); + console.log('k6.io IPv4 addresses:', ipv4Addresses); + // Output: k6.io IPv4 addresses: ["104.21.7.127", "172.67.154.74"] +} +``` + +{{< /code >}} + +### AAAA record resolution + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; + +export default async function () { + // Resolve IPv6 addresses using Cloudflare's IPv6 DNS server + const ipv6Addresses = await dns.resolve('k6.io', 'AAAA', '[2606:4700:4700::1111]:53'); + console.log('k6.io IPv6 addresses:', ipv6Addresses); + // Output: k6.io IPv6 addresses: ["2606:4700:3033::6815:77f", "2606:4700:3030::ac43:9a4a"] +} +``` + +{{< /code >}} + +### Testing multiple DNS servers + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; + +export const options = { + vus: 5, + duration: '30s', +}; + +const dnsServers = [ + '8.8.8.8:53', // Google DNS + '1.1.1.1:53', // Cloudflare DNS + '9.9.9.9:53', // Quad9 DNS +]; + +export default async function () { + const domain = 'example.com'; + + for (const server of dnsServers) { + try { + const results = await dns.resolve(domain, 'A', server); + + expect(results.length).toBeGreaterThan(0); + expect(results.every(ip => /^\d+\.\d+\.\d+\.\d+$/.test(ip))).toBeTruthy(); + + console.log(`${server} resolved ${domain} to:`, results); + } catch (error) { + console.error(`Failed to resolve ${domain} using ${server}:`, error); + } + } +} +``` + +{{< /code >}} + +### Performance comparison + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; +import { Trend } from 'k6/metrics'; + +const resolutionTime = new Trend('custom_dns_resolution_time'); + +export const options = { + vus: 1, + iterations: 100, +}; + +export default async function () { + const startTime = Date.now(); + + try { + const results = await dns.resolve('k6.io', 'A', '8.8.8.8:53'); + const duration = Date.now() - startTime; + + resolutionTime.add(duration); + + console.log(`Resolution took ${duration}ms, found ${results.length} addresses`); + } catch (error) { + console.error('DNS resolution failed:', error); + } +} +``` + +{{< /code >}} + +## Error handling + +The `resolve` function may throw errors in the following cases: + +- Invalid domain name format +- Unsupported record type +- DNS server unreachable or timeout +- DNS server returns an error response + +{{< code >}} + +```javascript +import dns from 'k6/x/dns'; + +export default async function () { + try { + const results = await dns.resolve('nonexistent.invalid', 'A', '8.8.8.8:53'); + console.log('Unexpected success:', results); + } catch (error) { + console.log('Expected DNS resolution error:', error.message); + } +} +``` + +{{< /code >}} + +## Metrics + +When using `dns.resolve`, the following metrics are automatically generated: + +- `dns_resolutions`: Counter incremented for each resolution attempt +- `dns_resolution_duration`: Trend measuring the time taken for DNS resolution + +These metrics help you monitor DNS performance and identify potential bottlenecks in your testing scenarios. + +## Notes on Usage + +- **Nameserver format**: Always specify the port number (typically 53 for DNS) in the nameserver parameter +- **Record type support**: Currently supports "A" and "AAAA" record types; additional types may be added in future versions +- **Timeout behavior**: DNS queries have built-in timeouts; consider this when designing load tests +- **Load testing**: Use multiple VUs and iterations to properly test DNS server performance under load \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c90b492571..a6e019f4fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -682,17 +682,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1095,20 +1084,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -5949,14 +5924,6 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "optional": true, - "peer": true - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6231,17 +6198,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",