changeset 1350:fda743e92b31

Tests: upstream random tests.
author Sergey Kandaurov <pluknet@nginx.com>
date Fri, 15 Jun 2018 13:59:08 +0300
parents 99a83f7e7755
children 81579df2ba8c
files lib/Test/Nginx.pm stream_upstream_random.t upstream_random.t
diffstat 3 files changed, 581 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/lib/Test/Nginx.pm
+++ b/lib/Test/Nginx.pm
@@ -155,6 +155,8 @@ sub has_module($) {
 			=> '(?s)^(?!.*--without-http_upstream_ip_hash_module)',
 		upstream_least_conn
 			=> '(?s)^(?!.*--without-http_upstream_least_conn_mod)',
+		upstream_random
+			=> '(?s)^(?!.*--without-http_upstream_random_module)',
 		upstream_keepalive
 			=> '(?s)^(?!.*--without-http_upstream_keepalive_modu)',
 		upstream_zone
@@ -186,6 +188,8 @@ sub has_module($) {
 			=> '(?s)^(?!.*--without-stream_upstream_hash_module)',
 		stream_upstream_least_conn
 			=> '(?s)^(?!.*--without-stream_upstream_least_conn_m)',
+		stream_upstream_random
+			=> '(?s)^(?!.*--without-stream_upstream_random_modul)',
 		stream_upstream_zone
 			=> '(?s)^(?!.*--without-stream_upstream_zone_module)',
 	);
new file mode 100644
--- /dev/null
+++ b/stream_upstream_random.t
@@ -0,0 +1,309 @@
+#!/usr/bin/perl
+
+# (C) Sergey Kandaurov
+# (C) Nginx, Inc.
+
+# Tests for upstream random balancer module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx qw/ :DEFAULT http_end /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()
+	->has(qw/stream stream_upstream_zone stream_upstream_random/)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+worker_processes 2;
+
+events {
+}
+
+stream {
+    upstream u {
+        zone z 1m;
+        random;
+        server 127.0.0.1:8081;
+        server 127.0.0.1:8082;
+    }
+
+    upstream lc {
+        zone lc 1m;
+        random two;
+        server 127.0.0.1:8081;
+        server 127.0.0.1:8082;
+    }
+
+    upstream w {
+        zone w 1m;
+        random two least_conn;
+        server 127.0.0.1:8081;
+        server 127.0.0.1:8082 weight=2;
+    }
+
+    upstream mc {
+        zone mc 1m;
+        random;
+        server 127.0.0.1:8081 max_conns=2;
+        server 127.0.0.1:8082 max_conns=1;
+    }
+
+    upstream mc2 {
+        zone mc 1m;
+        random two;
+        server 127.0.0.1:8081 max_conns=2;
+        server 127.0.0.1:8082 max_conns=1;
+    }
+
+    upstream one {
+        random;
+        server 127.0.0.1:8081;
+    }
+
+    upstream two {
+        random two;
+        server 127.0.0.1:8081;
+    }
+
+    upstream zone {
+        zone z 1m;
+        random;
+        server 127.0.0.1:8081;
+    }
+
+    upstream ztwo {
+        zone z 1m;
+        random two;
+        server 127.0.0.1:8081;
+    }
+
+    upstream fail {
+        zone fail 1m;
+        random;
+        server 127.0.0.1:8096;
+        server 127.0.0.1:8083 down;
+        server 127.0.0.1:8082;
+    }
+
+    upstream fail2 {
+        zone fail2 1m;
+        random two;
+        server 127.0.0.1:8096;
+        server 127.0.0.1:8083 down;
+        server 127.0.0.1:8082;
+    }
+
+    server {
+        listen      127.0.0.1:8080;
+        proxy_pass  u;
+    }
+
+    server {
+        listen      127.0.0.1:8083;
+        proxy_pass  lc;
+    }
+
+    server {
+        listen      127.0.0.1:8084;
+        proxy_pass  w;
+    }
+
+    server {
+        listen      127.0.0.1:8085;
+        proxy_pass  mc;
+    }
+
+    server {
+        listen      127.0.0.1:8086;
+        proxy_pass  mc2;
+    }
+
+    server {
+        listen      127.0.0.1:8087;
+        proxy_pass  one;
+    }
+
+    server {
+        listen      127.0.0.1:8088;
+        proxy_pass  two;
+    }
+
+    server {
+        listen      127.0.0.1:8089;
+        proxy_pass  zone;
+    }
+
+    server {
+        listen      127.0.0.1:8090;
+        proxy_pass  ztwo;
+    }
+
+    proxy_connect_timeout 1s;
+
+    server {
+        listen      127.0.0.1:8091;
+        proxy_pass  fail;
+    }
+
+    server {
+        listen      127.0.0.1:8092;
+        proxy_pass  fail2;
+    }
+}
+
+EOF
+
+$t->run_daemon(\&http_daemon, port(8081));
+$t->run_daemon(\&http_daemon, port(8082));
+$t->try_run('no upstream random')->plan(12);
+
+$t->waitforsocket('127.0.0.1:' . port(8081));
+$t->waitforsocket('127.0.0.1:' . port(8082));
+
+###############################################################################
+
+my @ports = my ($port1, $port2) = (port(8081), port(8082));
+
+like(get(8080, '/'), qr/X-Port: ($port1|$port2)/, 'random');
+like(get(8083, '/'), qr/X-Port: ($port1|$port2)/, 'random two');
+
+my $s = get(8083, '/w', start => 1, sleep => 0.2);
+my $r = get(8083, '/');
+my ($p) = http_end($s) =~ /X-Port: (\d+)/;
+like($r, qr/X-Port: (?!$p)/, 'random wait');
+
+SKIP: {
+skip 'long test', 3 unless $ENV{TEST_NGINX_UNSAFE};
+
+is(parallel(8084, '/w', 3), "$port1: 1, $port2: 2", 'random weight');
+
+is(parallel(8085, '/w', 4), "$port1: 2, $port2: 1", 'max_conns');
+is(parallel(8086, '/w', 4), "$port1: 2, $port2: 1", 'max_conns two');
+
+}
+
+# single variants
+
+like(get(8087, '/'), qr/X-Port: $port1/, 'single one');
+like(get(8088, '/'), qr/X-Port: $port1/, 'single two');
+like(get(8089, '/'), qr/X-Port: $port1/, 'zone one');
+like(get(8090, '/'), qr/X-Port: $port1/, 'zone two');
+
+like(many(8091, '/', 10), qr/$port2: 10/, 'failures');
+like(many(8092, '/', 10), qr/$port2: 10/, 'failures two');
+
+###############################################################################
+
+sub get {
+	my ($port, $uri, %opts) = @_;
+	my $s = IO::Socket::INET->new(
+		Proto => 'tcp',
+		PeerAddr => '127.0.0.1',
+		PeerPort => port($port),
+	)
+		or die "Can't connect to nginx: $!\n";
+
+	http_get($uri, socket => $s, %opts);
+}
+
+sub many {
+	my ($port, $uri, $count, %opts) = @_;
+	my %ports;
+
+	for (1 .. $count) {
+		if (get($port, $uri) =~ /X-Port: (\d+)/) {
+			$ports{$1} = 0 unless defined $ports{$1};
+			$ports{$1}++;
+		}
+
+		select undef, undef, undef, $opts{delay} if $opts{delay};
+	}
+
+	my @keys = map { my $p = $_; grep { $p == $_ } keys %ports } @ports;
+	return join ', ', map { $_ . ": " . $ports{$_} } @keys;
+}
+
+sub parallel {
+	my ($port, $uri, $n) = @_;
+	my %ports;
+
+	my @s = map { get($port, $uri, start => 1, sleep => 0.1) } (1 .. $n);
+
+	for (@s) {
+		if (http_end($_) =~ /X-Port: (\d+)/) {
+			$ports{$1} = 0 unless defined $ports{$1};
+			$ports{$1}++;
+		}
+	}
+
+	my @keys = map { my $p = $_; grep { $p == $_ } keys %ports } @ports;
+	return join ', ', map { $_ . ": " . $ports{$_} } @keys;
+}
+
+###############################################################################
+
+sub http_daemon {
+	my ($port) = @_;
+
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1',
+		LocalPort => $port,
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	local $SIG{PIPE} = 'IGNORE';
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $headers = '';
+		my $uri = '';
+
+		while (<$client>) {
+			$headers .= $_;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
+
+		if ($uri eq '/w') {
+			Test::Nginx::log_core('||', "$port: sleep(2.5)");
+			select undef, undef, undef, 2.5;
+		}
+
+		if ($uri eq '/close' && $port == port(8081)) {
+			next;
+		}
+
+		Test::Nginx::log_core('||', "$port: response, 200");
+		print $client <<EOF;
+HTTP/1.1 200 OK
+Connection: close
+X-Port: $port
+
+OK
+EOF
+
+		close $client;
+	}
+}
+
+###############################################################################
new file mode 100644
--- /dev/null
+++ b/upstream_random.t
@@ -0,0 +1,268 @@
+#!/usr/bin/perl
+
+# (C) Sergey Kandaurov
+# (C) Nginx, Inc.
+
+# Tests for upstream random balancer module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx qw/ :DEFAULT http_end /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy upstream_zone upstream_random/)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+worker_processes 2;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    upstream u {
+        zone z 1m;
+        random;
+        server 127.0.0.1:8081;
+        server 127.0.0.1:8082;
+        server 127.0.0.1:8083 down;
+    }
+
+    upstream lc {
+        zone lc 1m;
+        random two;
+        server 127.0.0.1:8081;
+        server 127.0.0.1:8082;
+        server 127.0.0.1:8083 down;
+    }
+
+    upstream w {
+        zone w 1m;
+        random two least_conn;
+        server 127.0.0.1:8081;
+        server 127.0.0.1:8082 weight=2;
+    }
+
+    upstream mc {
+        zone mc 1m;
+        random;
+        server 127.0.0.1:8081 max_conns=2;
+        server 127.0.0.1:8082 max_conns=1;
+    }
+
+    upstream mc2 {
+        zone mc 1m;
+        random two;
+        server 127.0.0.1:8081 max_conns=2;
+        server 127.0.0.1:8082 max_conns=1;
+    }
+
+    upstream one {
+        random;
+        server 127.0.0.1:8081;
+    }
+
+    upstream two {
+        random two;
+        server 127.0.0.1:8081;
+    }
+
+    upstream zone {
+        zone z 1m;
+        random;
+        server 127.0.0.1:8081;
+    }
+
+    upstream ztwo {
+        zone z 1m;
+        random two;
+        server 127.0.0.1:8081;
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            proxy_pass http://u;
+        }
+
+        location /lc/ {
+            proxy_pass http://lc/;
+        }
+
+        location /w {
+            proxy_pass http://w;
+        }
+
+        location /mc/ {
+            proxy_pass http://mc/;
+        }
+
+        location /mc2/ {
+            proxy_pass http://mc2/;
+        }
+
+        location /one {
+            proxy_pass http://one;
+        }
+
+        location /two {
+            proxy_pass http://two;
+        }
+
+        location /zone {
+            proxy_pass http://zone;
+        }
+
+        location /ztwo {
+            proxy_pass http://ztwo;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&http_daemon, port(8081));
+$t->run_daemon(\&http_daemon, port(8082));
+$t->try_run('no upstream random')->plan(12);
+
+$t->waitforsocket('127.0.0.1:' . port(8081));
+$t->waitforsocket('127.0.0.1:' . port(8082));
+
+###############################################################################
+
+my @ports = my ($port1, $port2) = (port(8081), port(8082));
+
+like(http_get('/'), qr/X-Port: ($port1|$port2)/, 'random');
+like(http_get('/lc/'), qr/X-Port: ($port1|$port2)/, 'random two');
+
+my $s = http_get('/lc/w', start => 1, sleep => 0.2);
+my $r = http_get('/lc/');
+my ($p) = http_end($s) =~ /X-Port: (\d+)/;
+like($r, qr/X-Port: (?!$p)/, 'random wait');
+
+SKIP: {
+skip 'long test', 3 unless $ENV{TEST_NGINX_UNSAFE};
+
+is(parallel('/w', 3), "$port1: 1, $port2: 2", 'random weight');
+
+is(parallel('/mc/w', 4), "$port1: 2, $port2: 1", 'max_conns');
+is(parallel('/mc2/w', 4), "$port1: 2, $port2: 1", 'max_conns two');
+
+}
+
+# single variants
+
+like(http_get('/one'), qr/X-Port: $port1/, 'single one');
+like(http_get('/two'), qr/X-Port: $port1/, 'single two');
+like(http_get('/zone'), qr/X-Port: $port1/, 'zone one');
+like(http_get('/ztwo'), qr/X-Port: $port1/, 'zone two');
+
+like(many('/close', 10), qr/$port2: 10/, 'failures');
+like(many('/lc/close', 10), qr/$port2: 10/, 'failures two');
+
+###############################################################################
+
+sub many {
+	my ($uri, $count, %opts) = @_;
+	my %ports;
+
+	for (1 .. $count) {
+		if (http_get($uri) =~ /X-Port: (\d+)/) {
+			$ports{$1} = 0 unless defined $ports{$1};
+			$ports{$1}++;
+		}
+
+		select undef, undef, undef, $opts{delay} if $opts{delay};
+	}
+
+	my @keys = map { my $p = $_; grep { $p == $_ } keys %ports } @ports;
+	return join ', ', map { $_ . ": " . $ports{$_} } @keys;
+}
+
+sub parallel {
+	my ($uri, $n) = @_;
+	my %ports;
+
+	my @s = map { http_get($uri, start => 1, sleep => 0.1) } (1 .. $n);
+
+	for (@s) {
+		if (http_end($_) =~ /X-Port: (\d+)/) {
+			$ports{$1} = 0 unless defined $ports{$1};
+			$ports{$1}++;
+		}
+	}
+
+	my @keys = map { my $p = $_; grep { $p == $_ } keys %ports } @ports;
+	return join ', ', map { $_ . ": " . $ports{$_} } @keys;
+}
+
+###############################################################################
+
+sub http_daemon {
+	my ($port) = @_;
+
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1',
+		LocalPort => $port,
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	local $SIG{PIPE} = 'IGNORE';
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $headers = '';
+		my $uri = '';
+
+		while (<$client>) {
+			$headers .= $_;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
+
+		if ($uri eq '/w') {
+			Test::Nginx::log_core('||', "$port: sleep(2.5)");
+			select undef, undef, undef, 2.5;
+		}
+
+		if ($uri eq '/close' && $port == port(8081)) {
+			next;
+		}
+
+		Test::Nginx::log_core('||', "$port: response, 200");
+		print $client <<EOF;
+HTTP/1.1 200 OK
+Connection: close
+X-Port: $port
+
+OK
+EOF
+
+		close $client;
+	}
+}
+
+###############################################################################