mirror of https://github.com/grpc/grpc.git
parent
333a1dc35b
commit
e52d87b53d
18 changed files with 2458 additions and 7 deletions
@ -0,0 +1,6 @@ |
||||
%YAML 1.2 |
||||
--- | |
||||
<%! |
||||
import json |
||||
%> |
||||
${json.dumps(lb_interop_test_scenarios, indent=4, sort_keys=True)} |
@ -0,0 +1,109 @@ |
||||
#!/usr/bin/env python2.7 |
||||
# Copyright 2015 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
|
||||
|
||||
import argparse |
||||
import subprocess |
||||
import os |
||||
import tempfile |
||||
import sys |
||||
import time |
||||
import signal |
||||
import yaml |
||||
|
||||
argp = argparse.ArgumentParser(description='Runs a DNS server for LB interop tests') |
||||
argp.add_argument('-l', '--grpclb_ips', default=None, type=str, |
||||
help='Comma-separated list of IP addresses of balancers') |
||||
argp.add_argument('-f', '--fallback_ips', default=None, type=str, |
||||
help='Comma-separated list of IP addresses of fallback servers') |
||||
argp.add_argument('-c', '--cause_no_error_no_data_for_balancer_a_record', |
||||
default=False, action='store_const', const=True, |
||||
help=('Used for testing the case in which the grpclb ' |
||||
'balancer A record lookup results in a DNS NOERROR response ' |
||||
'but with no ANSWER section i.e. no addresses')) |
||||
args = argp.parse_args() |
||||
|
||||
balancer_records = [] |
||||
grpclb_ips = args.grpclb_ips.split(',') |
||||
if grpclb_ips[0]: |
||||
for ip in grpclb_ips: |
||||
balancer_records.append({ |
||||
'TTL': '2100', |
||||
'data': ip, |
||||
'type': 'A', |
||||
}) |
||||
fallback_records = [] |
||||
fallback_ips = args.fallback_ips.split(',') |
||||
if fallback_ips[0]: |
||||
for ip in fallback_ips: |
||||
fallback_records.append({ |
||||
'TTL': '2100', |
||||
'data': ip, |
||||
'type': 'A', |
||||
}) |
||||
records_config_yaml = { |
||||
'resolver_tests_common_zone_name': |
||||
'test.google.fr.', |
||||
'resolver_component_tests': [{ |
||||
'records': { |
||||
'_grpclb._tcp.server': [ |
||||
{ |
||||
'TTL': '2100', |
||||
'data': '0 0 12000 balancer', |
||||
'type': 'SRV' |
||||
}, |
||||
], |
||||
'balancer': |
||||
balancer_records, |
||||
'server': |
||||
fallback_records, |
||||
} |
||||
}] |
||||
} |
||||
if args.cause_no_error_no_data_for_balancer_a_record: |
||||
balancer_records = records_config_yaml[ |
||||
'resolver_component_tests'][0]['records']['balancer'] |
||||
assert not balancer_records |
||||
# Insert a TXT record at the balancer.test.google.fr. domain. |
||||
# This TXT record won't actually be resolved or used by gRPC clients; |
||||
# inserting this record is just a way get the balancer.test.google.fr. |
||||
# A record queries to return NOERROR DNS responses that also have no |
||||
# ANSWER section, in order to simulate this failure case. |
||||
balancer_records.append({ |
||||
'TTL': '2100', |
||||
'data': 'arbitrary string that wont actually be resolved', |
||||
'type': 'TXT', |
||||
}) |
||||
# Generate the actual DNS server records config file |
||||
records_config_path = tempfile.mktemp() |
||||
with open(records_config_path, 'w') as records_config_generated: |
||||
records_config_generated.write(yaml.dump(records_config_yaml)) |
||||
|
||||
with open(records_config_path, 'r') as records_config_generated: |
||||
sys.stderr.write('===== DNS server records config: =====\n') |
||||
sys.stderr.write(records_config_generated.read()) |
||||
sys.stderr.write('======================================\n') |
||||
|
||||
# Run the DNS server |
||||
# Note that we need to add the extra |
||||
# A record for metadata.google.internal in order for compute engine |
||||
# OAuth creds and ALTS creds to work. |
||||
# TODO(apolcyn): should metadata.google.internal always resolve |
||||
# to 169.254.169.254? |
||||
subprocess.check_output([ |
||||
'/var/local/git/grpc/test/cpp/naming/utils/dns_server.py', '--port=53', |
||||
'--records_config_path', records_config_path, |
||||
'--add_a_record=metadata.google.internal:169.254.169.254', |
||||
]) |
@ -0,0 +1,34 @@ |
||||
# Copyright 2015 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
|
||||
FROM golang:1.10 |
||||
|
||||
RUN apt-get update && apt-get install -y \ |
||||
dnsutils \ |
||||
git \ |
||||
vim \ |
||||
curl \ |
||||
python-pip \ |
||||
python-yaml \ |
||||
make && apt-get clean |
||||
|
||||
RUN ln -s /usr/local/go/bin/go /usr/local/bin |
||||
|
||||
# Install Python packages from PyPI |
||||
RUN pip install --upgrade pip==10.0.1 |
||||
RUN pip install virtualenv |
||||
RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 |
||||
|
||||
# Define the default command. |
||||
CMD ["bash"] |
@ -0,0 +1,35 @@ |
||||
#!/bin/bash |
||||
# Copyright 2015 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
# |
||||
# Gets a built Go interop server, fake balancer server, and python |
||||
# DNS server into a base image. |
||||
set -e |
||||
|
||||
# Clone just the grpc-go source code without any dependencies. |
||||
# We are cloning from a local git repo that contains the right revision |
||||
# to test instead of using "go get" to download from Github directly. |
||||
git clone --recursive /var/local/jenkins/grpc-go src/google.golang.org/grpc |
||||
|
||||
# Get all gRPC Go dependencies |
||||
(cd src/google.golang.org/grpc && make deps && make testdeps) |
||||
|
||||
# Build the interop server and fake balancer |
||||
(cd src/google.golang.org/grpc/interop/server && go install) |
||||
(cd src/google.golang.org/grpc/interop/fake_grpclb && go install) |
||||
|
||||
# Clone the grpc/grpc repo to get the python DNS server. |
||||
# Hack: we don't need to init submodules for the scripts we need. |
||||
mkdir -p /var/local/git/grpc |
||||
git clone /var/local/jenkins/grpc /var/local/git/grpc |
@ -0,0 +1,33 @@ |
||||
#!/bin/bash |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
|
||||
# Source this rc script to prepare the environment for interop builds |
||||
# This rc script must be used in the root directory of gRPC |
||||
|
||||
export LANG=en_US.UTF-8 |
||||
|
||||
# Download Docker images from DockerHub |
||||
export DOCKERHUB_ORGANIZATION=grpctesting |
||||
|
||||
git submodule update --init |
||||
|
||||
# Set up gRPC-Go and gRPC-Java to test |
||||
git clone --recursive https://github.com/grpc/grpc-go ./../grpc-go |
||||
git clone --recursive https://github.com/grpc/grpc-java ./../grpc-java |
||||
|
||||
# TODO(apolcyn): move to kokoro image? |
||||
virtualenv env |
||||
source env/bin/activate |
||||
pip install twisted |
@ -0,0 +1,26 @@ |
||||
#!/bin/bash |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
|
||||
set -ex |
||||
|
||||
export LANG=en_US.UTF-8 |
||||
|
||||
# Enter the gRPC repo root |
||||
cd $(dirname $0)/../../.. |
||||
|
||||
source tools/internal_ci/helper_scripts/prepare_build_linux_rc |
||||
source tools/internal_ci/helper_scripts/prepare_build_grpclb_interop_rc |
||||
|
||||
tools/run_tests/run_grpclb_interop_tests.py -l all --scenarios_file=tools/run_tests/generated/lb_interop_test_scenarios.json |
@ -0,0 +1,25 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
|
||||
# Config file for the internal CI (in protobuf text format) |
||||
|
||||
# Location of the continuous shell script in repository. |
||||
build_file: "grpc/tools/internal_ci/linux/grpc_run_grpclb_interop_tests.sh" |
||||
timeout_mins: 60 |
||||
action { |
||||
define_artifacts { |
||||
regex: "**/sponge_log.xml" |
||||
regex: "github/grpc/reports/**" |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,347 @@ |
||||
#!/usr/bin/env python2.7 |
||||
# Copyright 2015 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
"""Generates the appropriate JSON data for LB interop test scenarios.""" |
||||
|
||||
import json |
||||
import os |
||||
import yaml |
||||
|
||||
all_scenarios = [] |
||||
|
||||
# TODO(https://github.com/grpc/grpc-go/issues/2347): enable |
||||
# client_falls_back_because_no_backends_* scenarios for Java/Go. |
||||
|
||||
# TODO(https://github.com/grpc/grpc-java/issues/4887): enable |
||||
# *short_stream* scenarios for Java. |
||||
|
||||
# TODO(https://github.com/grpc/grpc-java/issues/4912): enable |
||||
# Java TLS tests involving TLS to the balancer. |
||||
|
||||
|
||||
def server_sec(transport_sec): |
||||
if transport_sec == 'google_default_credentials': |
||||
return 'alts', 'alts', 'tls' |
||||
return transport_sec, transport_sec, transport_sec |
||||
|
||||
|
||||
def generate_no_balancer_because_lb_a_record_returns_nx_domain(): |
||||
all_configs = [] |
||||
for transport_sec in [ |
||||
'insecure', 'alts', 'tls', 'google_default_credentials' |
||||
]: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
config = { |
||||
'name': |
||||
'no_balancer_because_lb_a_record_returns_nx_domain_%s' % |
||||
transport_sec, |
||||
'skip_langs': [], |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [], |
||||
'backend_configs': [], |
||||
'fallback_configs': [{ |
||||
'transport_sec': fallback_sec, |
||||
}], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
False, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_no_balancer_because_lb_a_record_returns_nx_domain() |
||||
|
||||
|
||||
def generate_no_balancer_because_lb_a_record_returns_no_data(): |
||||
all_configs = [] |
||||
for transport_sec in [ |
||||
'insecure', 'alts', 'tls', 'google_default_credentials' |
||||
]: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
config = { |
||||
'name': |
||||
'no_balancer_because_lb_a_record_returns_no_data_%s' % |
||||
transport_sec, |
||||
'skip_langs': [], |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [], |
||||
'backend_configs': [], |
||||
'fallback_configs': [{ |
||||
'transport_sec': fallback_sec, |
||||
}], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
True, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_no_balancer_because_lb_a_record_returns_no_data() |
||||
|
||||
|
||||
def generate_client_referred_to_backend(): |
||||
all_configs = [] |
||||
for balancer_short_stream in [True, False]: |
||||
for transport_sec in [ |
||||
'insecure', 'alts', 'tls', 'google_default_credentials' |
||||
]: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
skip_langs = [] |
||||
if transport_sec == 'tls': |
||||
skip_langs += ['java'] |
||||
if balancer_short_stream: |
||||
skip_langs += ['java'] |
||||
config = { |
||||
'name': |
||||
'client_referred_to_backend_%s_short_stream_%s' % |
||||
(transport_sec, balancer_short_stream), |
||||
'skip_langs': |
||||
skip_langs, |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}], |
||||
'backend_configs': [{ |
||||
'transport_sec': backend_sec, |
||||
}], |
||||
'fallback_configs': [], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
False, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_client_referred_to_backend() |
||||
|
||||
|
||||
def generate_client_referred_to_backend_fallback_broken(): |
||||
all_configs = [] |
||||
for balancer_short_stream in [True, False]: |
||||
for transport_sec in ['alts', 'tls', 'google_default_credentials']: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
skip_langs = [] |
||||
if transport_sec == 'tls': |
||||
skip_langs += ['java'] |
||||
if balancer_short_stream: |
||||
skip_langs += ['java'] |
||||
config = { |
||||
'name': |
||||
'client_referred_to_backend_fallback_broken_%s_short_stream_%s' |
||||
% (transport_sec, balancer_short_stream), |
||||
'skip_langs': |
||||
skip_langs, |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}], |
||||
'backend_configs': [{ |
||||
'transport_sec': backend_sec, |
||||
}], |
||||
'fallback_configs': [{ |
||||
'transport_sec': 'insecure', |
||||
}], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
False, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_client_referred_to_backend_fallback_broken() |
||||
|
||||
|
||||
def generate_client_referred_to_backend_multiple_backends(): |
||||
all_configs = [] |
||||
for balancer_short_stream in [True, False]: |
||||
for transport_sec in [ |
||||
'insecure', 'alts', 'tls', 'google_default_credentials' |
||||
]: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
skip_langs = [] |
||||
if transport_sec == 'tls': |
||||
skip_langs += ['java'] |
||||
if balancer_short_stream: |
||||
skip_langs += ['java'] |
||||
config = { |
||||
'name': |
||||
'client_referred_to_backend_multiple_backends_%s_short_stream_%s' |
||||
% (transport_sec, balancer_short_stream), |
||||
'skip_langs': |
||||
skip_langs, |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}], |
||||
'backend_configs': [{ |
||||
'transport_sec': backend_sec, |
||||
}, { |
||||
'transport_sec': backend_sec, |
||||
}, { |
||||
'transport_sec': backend_sec, |
||||
}, { |
||||
'transport_sec': backend_sec, |
||||
}, { |
||||
'transport_sec': backend_sec, |
||||
}], |
||||
'fallback_configs': [], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
False, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_client_referred_to_backend_multiple_backends() |
||||
|
||||
|
||||
def generate_client_falls_back_because_no_backends(): |
||||
all_configs = [] |
||||
for balancer_short_stream in [True, False]: |
||||
for transport_sec in [ |
||||
'insecure', 'alts', 'tls', 'google_default_credentials' |
||||
]: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
skip_langs = ['go', 'java'] |
||||
if transport_sec == 'tls': |
||||
skip_langs += ['java'] |
||||
if balancer_short_stream: |
||||
skip_langs += ['java'] |
||||
config = { |
||||
'name': |
||||
'client_falls_back_because_no_backends_%s_short_stream_%s' % |
||||
(transport_sec, balancer_short_stream), |
||||
'skip_langs': |
||||
skip_langs, |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}], |
||||
'backend_configs': [], |
||||
'fallback_configs': [{ |
||||
'transport_sec': fallback_sec, |
||||
}], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
False, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_client_falls_back_because_no_backends() |
||||
|
||||
|
||||
def generate_client_falls_back_because_balancer_connection_broken(): |
||||
all_configs = [] |
||||
for transport_sec in ['alts', 'tls', 'google_default_credentials']: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
skip_langs = [] |
||||
if transport_sec == 'tls': |
||||
skip_langs = ['java'] |
||||
config = { |
||||
'name': |
||||
'client_falls_back_because_balancer_connection_broken_%s' % |
||||
transport_sec, |
||||
'skip_langs': |
||||
skip_langs, |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [{ |
||||
'transport_sec': 'insecure', |
||||
'short_stream': False, |
||||
}], |
||||
'backend_configs': [], |
||||
'fallback_configs': [{ |
||||
'transport_sec': fallback_sec, |
||||
}], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
False, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_client_falls_back_because_balancer_connection_broken() |
||||
|
||||
|
||||
def generate_client_referred_to_backend_multiple_balancers(): |
||||
all_configs = [] |
||||
for balancer_short_stream in [True, False]: |
||||
for transport_sec in [ |
||||
'insecure', 'alts', 'tls', 'google_default_credentials' |
||||
]: |
||||
balancer_sec, backend_sec, fallback_sec = server_sec(transport_sec) |
||||
skip_langs = [] |
||||
if transport_sec == 'tls': |
||||
skip_langs += ['java'] |
||||
if balancer_short_stream: |
||||
skip_langs += ['java'] |
||||
config = { |
||||
'name': |
||||
'client_referred_to_backend_multiple_balancers_%s_short_stream_%s' |
||||
% (transport_sec, balancer_short_stream), |
||||
'skip_langs': |
||||
skip_langs, |
||||
'transport_sec': |
||||
transport_sec, |
||||
'balancer_configs': [ |
||||
{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}, |
||||
{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}, |
||||
{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}, |
||||
{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}, |
||||
{ |
||||
'transport_sec': balancer_sec, |
||||
'short_stream': balancer_short_stream, |
||||
}, |
||||
], |
||||
'backend_configs': [ |
||||
{ |
||||
'transport_sec': backend_sec, |
||||
}, |
||||
], |
||||
'fallback_configs': [], |
||||
'cause_no_error_no_data_for_balancer_a_record': |
||||
False, |
||||
} |
||||
all_configs.append(config) |
||||
return all_configs |
||||
|
||||
|
||||
all_scenarios += generate_client_referred_to_backend_multiple_balancers() |
||||
|
||||
print(yaml.dump({ |
||||
'lb_interop_test_scenarios': all_scenarios, |
||||
})) |
@ -0,0 +1,609 @@ |
||||
#!/usr/bin/env python |
||||
# Copyright 2015 gRPC authors. |
||||
# |
||||
# Licensed 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. |
||||
"""Run interop (cross-language) tests in parallel.""" |
||||
|
||||
from __future__ import print_function |
||||
|
||||
import argparse |
||||
import atexit |
||||
import itertools |
||||
import json |
||||
import multiprocessing |
||||
import os |
||||
import re |
||||
import subprocess |
||||
import sys |
||||
import tempfile |
||||
import time |
||||
import uuid |
||||
import six |
||||
import traceback |
||||
|
||||
import python_utils.dockerjob as dockerjob |
||||
import python_utils.jobset as jobset |
||||
import python_utils.report_utils as report_utils |
||||
|
||||
# Docker doesn't clean up after itself, so we do it on exit. |
||||
atexit.register(lambda: subprocess.call(['stty', 'echo'])) |
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..')) |
||||
os.chdir(ROOT) |
||||
|
||||
_FALLBACK_SERVER_PORT = 443 |
||||
_BALANCER_SERVER_PORT = 12000 |
||||
_BACKEND_SERVER_PORT = 8080 |
||||
|
||||
_TEST_TIMEOUT = 30 |
||||
|
||||
_FAKE_SERVERS_SAFENAME = 'fake_servers' |
||||
|
||||
# Use a name that's verified by the test certs |
||||
_SERVICE_NAME = 'server.test.google.fr' |
||||
|
||||
|
||||
class CXXLanguage: |
||||
|
||||
def __init__(self): |
||||
self.client_cwd = '/var/local/git/grpc' |
||||
self.safename = 'cxx' |
||||
|
||||
def client_cmd(self, args): |
||||
return ['bins/opt/interop_client'] + args |
||||
|
||||
def global_env(self): |
||||
# 1) Set c-ares as the resolver, to |
||||
# enable grpclb. |
||||
# 2) Turn on verbose logging. |
||||
# 3) Set the ROOTS_PATH env variable |
||||
# to the test CA in order for |
||||
# GoogleDefaultCredentials to be |
||||
# able to use the test CA. |
||||
return { |
||||
'GRPC_DNS_RESOLVER': |
||||
'ares', |
||||
'GRPC_VERBOSITY': |
||||
'DEBUG', |
||||
'GRPC_TRACE': |
||||
'client_channel,glb', |
||||
'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH': |
||||
'/var/local/git/grpc/src/core/tsi/test_creds/ca.pem', |
||||
} |
||||
|
||||
def __str__(self): |
||||
return 'c++' |
||||
|
||||
|
||||
class JavaLanguage: |
||||
|
||||
def __init__(self): |
||||
self.client_cwd = '/var/local/git/grpc-java' |
||||
self.safename = str(self) |
||||
|
||||
def client_cmd(self, args): |
||||
# Take necessary steps to import our test CA into |
||||
# the set of test CA's that the Java runtime of the |
||||
# docker container will pick up, so that |
||||
# Java GoogleDefaultCreds can use it. |
||||
pem_to_der_cmd = ('openssl x509 -outform der ' |
||||
'-in /external_mount/src/core/tsi/test_creds/ca.pem ' |
||||
'-out /tmp/test_ca.der') |
||||
keystore_import_cmd = ( |
||||
'keytool -import ' |
||||
'-keystore /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts ' |
||||
'-file /tmp/test_ca.der ' |
||||
'-deststorepass changeit ' |
||||
'-noprompt') |
||||
return [ |
||||
'bash', '-c', ('{pem_to_der_cmd} && ' |
||||
'{keystore_import_cmd} && ' |
||||
'./run-test-client.sh {java_client_args}').format( |
||||
pem_to_der_cmd=pem_to_der_cmd, |
||||
keystore_import_cmd=keystore_import_cmd, |
||||
java_client_args=' '.join(args)) |
||||
] |
||||
|
||||
def global_env(self): |
||||
# 1) Enable grpclb |
||||
# 2) Enable verbose logging |
||||
return { |
||||
'JAVA_OPTS': |
||||
('-Dio.grpc.internal.DnsNameResolverProvider.enable_grpclb=true ' |
||||
'-Djava.util.logging.config.file=/var/local/grpc_java_logging/logconf.txt' |
||||
) |
||||
} |
||||
|
||||
def __str__(self): |
||||
return 'java' |
||||
|
||||
|
||||
class GoLanguage: |
||||
|
||||
def __init__(self): |
||||
self.client_cwd = '/go/src/google.golang.org/grpc/interop/client' |
||||
self.safename = str(self) |
||||
|
||||
def client_cmd(self, args): |
||||
# Copy the test CA file into the path that |
||||
# the Go runtime in the docker container will use, so |
||||
# that Go's GoogleDefaultCredentials can use it. |
||||
# See https://golang.org/src/crypto/x509/root_linux.go. |
||||
return [ |
||||
'bash', '-c', ('cp /external_mount/src/core/tsi/test_creds/ca.pem ' |
||||
'/etc/ssl/certs/ca-certificates.crt && ' |
||||
'/go/bin/client {go_client_args}' |
||||
).format(go_client_args=' '.join(args)) |
||||
] |
||||
|
||||
def global_env(self): |
||||
return { |
||||
'GRPC_GO_LOG_VERBOSITY_LEVEL': '3', |
||||
'GRPC_GO_LOG_SEVERITY_LEVEL': 'INFO' |
||||
} |
||||
|
||||
def __str__(self): |
||||
return 'go' |
||||
|
||||
|
||||
_LANGUAGES = { |
||||
'c++': CXXLanguage(), |
||||
'go': GoLanguage(), |
||||
'java': JavaLanguage(), |
||||
} |
||||
|
||||
|
||||
def docker_run_cmdline(cmdline, image, docker_args, cwd, environ=None): |
||||
"""Wraps given cmdline array to create 'docker run' cmdline from it.""" |
||||
# turn environ into -e docker args |
||||
docker_cmdline = 'docker run -i --rm=true'.split() |
||||
if environ: |
||||
for k, v in environ.items(): |
||||
docker_cmdline += ['-e', '%s=%s' % (k, v)] |
||||
return docker_cmdline + ['-w', cwd] + docker_args + [image] + cmdline |
||||
|
||||
|
||||
def _job_kill_handler(job): |
||||
assert job._spec.container_name |
||||
dockerjob.docker_kill(job._spec.container_name) |
||||
|
||||
|
||||
def transport_security_to_args(transport_security): |
||||
args = [] |
||||
if transport_security == 'tls': |
||||
args += ['--use_tls=true'] |
||||
elif transport_security == 'alts': |
||||
args += ['--use_tls=false', '--use_alts=true'] |
||||
elif transport_security == 'insecure': |
||||
args += ['--use_tls=false'] |
||||
elif transport_security == 'google_default_credentials': |
||||
args += ['--custom_credentials_type=google_default_credentials'] |
||||
else: |
||||
print('Invalid transport security option.') |
||||
sys.exit(1) |
||||
return args |
||||
|
||||
|
||||
def lb_client_interop_jobspec(language, |
||||
dns_server_ip, |
||||
docker_image, |
||||
transport_security='tls'): |
||||
"""Runs a gRPC client under test in a docker container""" |
||||
interop_only_options = [ |
||||
'--server_host=%s' % _SERVICE_NAME, |
||||
'--server_port=%d' % _FALLBACK_SERVER_PORT |
||||
] + transport_security_to_args(transport_security) |
||||
# Don't set the server host override in any client; |
||||
# Go and Java default to no override. |
||||
# We're using a DNS server so there's no need. |
||||
if language.safename == 'c++': |
||||
interop_only_options += ['--server_host_override=""'] |
||||
# Don't set --use_test_ca; we're configuring |
||||
# clients to use test CA's via alternate means. |
||||
interop_only_options += ['--use_test_ca=false'] |
||||
client_args = language.client_cmd(interop_only_options) |
||||
container_name = dockerjob.random_name( |
||||
'lb_interop_client_%s' % language.safename) |
||||
docker_cmdline = docker_run_cmdline( |
||||
client_args, |
||||
environ=language.global_env(), |
||||
image=docker_image, |
||||
cwd=language.client_cwd, |
||||
docker_args=[ |
||||
'--dns=%s' % dns_server_ip, |
||||
'--net=host', |
||||
'--name=%s' % container_name, |
||||
'-v', |
||||
'{grpc_grpc_root_dir}:/external_mount:ro'.format( |
||||
grpc_grpc_root_dir=ROOT), |
||||
]) |
||||
jobset.message( |
||||
'IDLE', |
||||
'docker_cmdline:\b|%s|' % ' '.join(docker_cmdline), |
||||
do_newline=True) |
||||
test_job = jobset.JobSpec( |
||||
cmdline=docker_cmdline, |
||||
shortname=('lb_interop_client:%s' % language), |
||||
timeout_seconds=_TEST_TIMEOUT, |
||||
kill_handler=_job_kill_handler) |
||||
test_job.container_name = container_name |
||||
return test_job |
||||
|
||||
|
||||
def fallback_server_jobspec(transport_security, shortname): |
||||
"""Create jobspec for running a fallback server""" |
||||
cmdline = [ |
||||
'bin/server', |
||||
'--port=%d' % _FALLBACK_SERVER_PORT, |
||||
] + transport_security_to_args(transport_security) |
||||
return grpc_server_in_docker_jobspec( |
||||
server_cmdline=cmdline, shortname=shortname) |
||||
|
||||
|
||||
def backend_server_jobspec(transport_security, shortname): |
||||
"""Create jobspec for running a backend server""" |
||||
cmdline = [ |
||||
'bin/server', |
||||
'--port=%d' % _BACKEND_SERVER_PORT, |
||||
] + transport_security_to_args(transport_security) |
||||
return grpc_server_in_docker_jobspec( |
||||
server_cmdline=cmdline, shortname=shortname) |
||||
|
||||
|
||||
def grpclb_jobspec(transport_security, short_stream, backend_addrs, shortname): |
||||
"""Create jobspec for running a balancer server""" |
||||
cmdline = [ |
||||
'bin/fake_grpclb', |
||||
'--backend_addrs=%s' % ','.join(backend_addrs), |
||||
'--port=%d' % _BALANCER_SERVER_PORT, |
||||
'--short_stream=%s' % short_stream, |
||||
'--service_name=%s' % _SERVICE_NAME, |
||||
] + transport_security_to_args(transport_security) |
||||
return grpc_server_in_docker_jobspec( |
||||
server_cmdline=cmdline, shortname=shortname) |
||||
|
||||
|
||||
def grpc_server_in_docker_jobspec(server_cmdline, shortname): |
||||
container_name = dockerjob.random_name(shortname) |
||||
environ = { |
||||
'GRPC_GO_LOG_VERBOSITY_LEVEL': '3', |
||||
'GRPC_GO_LOG_SEVERITY_LEVEL': 'INFO ', |
||||
} |
||||
docker_cmdline = docker_run_cmdline( |
||||
server_cmdline, |
||||
cwd='/go', |
||||
image=docker_images.get(_FAKE_SERVERS_SAFENAME), |
||||
environ=environ, |
||||
docker_args=['--name=%s' % container_name]) |
||||
jobset.message( |
||||
'IDLE', |
||||
'docker_cmdline:\b|%s|' % ' '.join(docker_cmdline), |
||||
do_newline=True) |
||||
server_job = jobset.JobSpec( |
||||
cmdline=docker_cmdline, shortname=shortname, timeout_seconds=30 * 60) |
||||
server_job.container_name = container_name |
||||
return server_job |
||||
|
||||
|
||||
def dns_server_in_docker_jobspec(grpclb_ips, fallback_ips, shortname, |
||||
cause_no_error_no_data_for_balancer_a_record): |
||||
container_name = dockerjob.random_name(shortname) |
||||
run_dns_server_cmdline = [ |
||||
'python', |
||||
'test/cpp/naming/utils/run_dns_server_for_lb_interop_tests.py', |
||||
'--grpclb_ips=%s' % ','.join(grpclb_ips), |
||||
'--fallback_ips=%s' % ','.join(fallback_ips), |
||||
] |
||||
if cause_no_error_no_data_for_balancer_a_record: |
||||
run_dns_server_cmdline.append( |
||||
'--cause_no_error_no_data_for_balancer_a_record') |
||||
docker_cmdline = docker_run_cmdline( |
||||
run_dns_server_cmdline, |
||||
cwd='/var/local/git/grpc', |
||||
image=docker_images.get(_FAKE_SERVERS_SAFENAME), |
||||
docker_args=['--name=%s' % container_name]) |
||||
jobset.message( |
||||
'IDLE', |
||||
'docker_cmdline:\b|%s|' % ' '.join(docker_cmdline), |
||||
do_newline=True) |
||||
server_job = jobset.JobSpec( |
||||
cmdline=docker_cmdline, shortname=shortname, timeout_seconds=30 * 60) |
||||
server_job.container_name = container_name |
||||
return server_job |
||||
|
||||
|
||||
def build_interop_image_jobspec(lang_safename, basename_prefix='grpc_interop'): |
||||
"""Creates jobspec for building interop docker image for a language""" |
||||
tag = '%s_%s:%s' % (basename_prefix, lang_safename, uuid.uuid4()) |
||||
env = { |
||||
'INTEROP_IMAGE': tag, |
||||
'BASE_NAME': '%s_%s' % (basename_prefix, lang_safename), |
||||
} |
||||
build_job = jobset.JobSpec( |
||||
cmdline=['tools/run_tests/dockerize/build_interop_image.sh'], |
||||
environ=env, |
||||
shortname='build_docker_%s' % lang_safename, |
||||
timeout_seconds=30 * 60) |
||||
build_job.tag = tag |
||||
return build_job |
||||
|
||||
|
||||
argp = argparse.ArgumentParser(description='Run interop tests.') |
||||
argp.add_argument( |
||||
'-l', |
||||
'--language', |
||||
choices=['all'] + sorted(_LANGUAGES), |
||||
nargs='+', |
||||
default=['all'], |
||||
help='Clients to run.') |
||||
argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int) |
||||
argp.add_argument( |
||||
'-s', |
||||
'--scenarios_file', |
||||
default=None, |
||||
type=str, |
||||
help='File containing test scenarios as JSON configs.') |
||||
argp.add_argument( |
||||
'-n', |
||||
'--scenario_name', |
||||
default=None, |
||||
type=str, |
||||
help=( |
||||
'Useful for manual runs: specify the name of ' |
||||
'the scenario to run from scenarios_file. Run all scenarios if unset.')) |
||||
argp.add_argument( |
||||
'--cxx_image_tag', |
||||
default=None, |
||||
type=str, |
||||
help=('Setting this skips the clients docker image ' |
||||
'build step and runs the client from the named ' |
||||
'image. Only supports running a one client language.')) |
||||
argp.add_argument( |
||||
'--go_image_tag', |
||||
default=None, |
||||
type=str, |
||||
help=('Setting this skips the clients docker image build ' |
||||
'step and runs the client from the named image. Only ' |
||||
'supports running a one client language.')) |
||||
argp.add_argument( |
||||
'--java_image_tag', |
||||
default=None, |
||||
type=str, |
||||
help=('Setting this skips the clients docker image build ' |
||||
'step and runs the client from the named image. Only ' |
||||
'supports running a one client language.')) |
||||
argp.add_argument( |
||||
'--servers_image_tag', |
||||
default=None, |
||||
type=str, |
||||
help=('Setting this skips the fake servers docker image ' |
||||
'build step and runs the servers from the named image.')) |
||||
argp.add_argument( |
||||
'--no_skips', |
||||
default=False, |
||||
type=bool, |
||||
nargs='?', |
||||
const=True, |
||||
help=('Useful for manual runs. Setting this overrides test ' |
||||
'"skips" configured in test scenarios.')) |
||||
argp.add_argument( |
||||
'--verbose', |
||||
default=False, |
||||
type=bool, |
||||
nargs='?', |
||||
const=True, |
||||
help='Increase logging.') |
||||
args = argp.parse_args() |
||||
|
||||
docker_images = {} |
||||
|
||||
build_jobs = [] |
||||
if len(args.language) and args.language[0] == 'all': |
||||
languages = _LANGUAGES.keys() |
||||
else: |
||||
languages = args.language |
||||
for lang_name in languages: |
||||
l = _LANGUAGES[lang_name] |
||||
# First check if a pre-built image was supplied, and avoid |
||||
# rebuilding the particular docker image if so. |
||||
if lang_name == 'c++' and args.cxx_image_tag: |
||||
docker_images[str(l.safename)] = args.cxx_image_tag |
||||
elif lang_name == 'go' and args.go_image_tag: |
||||
docker_images[str(l.safename)] = args.go_image_tag |
||||
elif lang_name == 'java' and args.java_image_tag: |
||||
docker_images[str(l.safename)] = args.java_image_tag |
||||
else: |
||||
# Build the test client in docker and save the fully |
||||
# built image. |
||||
job = build_interop_image_jobspec(l.safename) |
||||
build_jobs.append(job) |
||||
docker_images[str(l.safename)] = job.tag |
||||
|
||||
# First check if a pre-built image was supplied. |
||||
if args.servers_image_tag: |
||||
docker_images[_FAKE_SERVERS_SAFENAME] = args.servers_image_tag |
||||
else: |
||||
# Build the test servers in docker and save the fully |
||||
# built image. |
||||
job = build_interop_image_jobspec( |
||||
_FAKE_SERVERS_SAFENAME, basename_prefix='lb_interop') |
||||
build_jobs.append(job) |
||||
docker_images[_FAKE_SERVERS_SAFENAME] = job.tag |
||||
|
||||
if build_jobs: |
||||
jobset.message('START', 'Building interop docker images.', do_newline=True) |
||||
print('Jobs to run: \n%s\n' % '\n'.join(str(j) for j in build_jobs)) |
||||
num_failures, _ = jobset.run( |
||||
build_jobs, newline_on_success=True, maxjobs=args.jobs) |
||||
if num_failures == 0: |
||||
jobset.message( |
||||
'SUCCESS', 'All docker images built successfully.', do_newline=True) |
||||
else: |
||||
jobset.message( |
||||
'FAILED', 'Failed to build interop docker images.', do_newline=True) |
||||
sys.exit(1) |
||||
|
||||
|
||||
def wait_until_dns_server_is_up(dns_server_ip): |
||||
"""Probes the DNS server until it's running and safe for tests.""" |
||||
for i in range(0, 30): |
||||
print('Health check: attempt to connect to DNS server over TCP.') |
||||
tcp_connect_subprocess = subprocess.Popen([ |
||||
os.path.join(os.getcwd(), 'test/cpp/naming/utils/tcp_connect.py'), |
||||
'--server_host', dns_server_ip, '--server_port', |
||||
str(53), '--timeout', |
||||
str(1) |
||||
]) |
||||
tcp_connect_subprocess.communicate() |
||||
if tcp_connect_subprocess.returncode == 0: |
||||
print(('Health check: attempt to make an A-record ' |
||||
'query to DNS server.')) |
||||
dns_resolver_subprocess = subprocess.Popen( |
||||
[ |
||||
os.path.join(os.getcwd(), |
||||
'test/cpp/naming/utils/dns_resolver.py'), |
||||
'--qname', ('health-check-local-dns-server-is-alive.' |
||||
'resolver-tests.grpctestingexp'), |
||||
'--server_host', dns_server_ip, '--server_port', |
||||
str(53) |
||||
], |
||||
stdout=subprocess.PIPE) |
||||
dns_resolver_stdout, _ = dns_resolver_subprocess.communicate() |
||||
if dns_resolver_subprocess.returncode == 0: |
||||
if '123.123.123.123' in dns_resolver_stdout: |
||||
print(('DNS server is up! ' |
||||
'Successfully reached it over UDP and TCP.')) |
||||
return |
||||
time.sleep(0.1) |
||||
raise Exception(('Failed to reach DNS server over TCP and/or UDP. ' |
||||
'Exitting without running tests.')) |
||||
|
||||
|
||||
def shortname(shortname_prefix, shortname, index): |
||||
return '%s_%s_%d' % (shortname_prefix, shortname, index) |
||||
|
||||
|
||||
def run_one_scenario(scenario_config): |
||||
jobset.message('START', 'Run scenario: %s' % scenario_config['name']) |
||||
server_jobs = {} |
||||
server_addresses = {} |
||||
suppress_server_logs = True |
||||
try: |
||||
backend_addrs = [] |
||||
fallback_ips = [] |
||||
grpclb_ips = [] |
||||
shortname_prefix = scenario_config['name'] |
||||
# Start backends |
||||
for i in xrange(len(scenario_config['backend_configs'])): |
||||
backend_config = scenario_config['backend_configs'][i] |
||||
backend_shortname = shortname(shortname_prefix, 'backend_server', i) |
||||
backend_spec = backend_server_jobspec( |
||||
backend_config['transport_sec'], backend_shortname) |
||||
backend_job = dockerjob.DockerJob(backend_spec) |
||||
server_jobs[backend_shortname] = backend_job |
||||
backend_addrs.append('%s:%d' % (backend_job.ip_address(), |
||||
_BACKEND_SERVER_PORT)) |
||||
# Start fallbacks |
||||
for i in xrange(len(scenario_config['fallback_configs'])): |
||||
fallback_config = scenario_config['fallback_configs'][i] |
||||
fallback_shortname = shortname(shortname_prefix, 'fallback_server', |
||||
i) |
||||
fallback_spec = fallback_server_jobspec( |
||||
fallback_config['transport_sec'], fallback_shortname) |
||||
fallback_job = dockerjob.DockerJob(fallback_spec) |
||||
server_jobs[fallback_shortname] = fallback_job |
||||
fallback_ips.append(fallback_job.ip_address()) |
||||
# Start balancers |
||||
for i in xrange(len(scenario_config['balancer_configs'])): |
||||
balancer_config = scenario_config['balancer_configs'][i] |
||||
grpclb_shortname = shortname(shortname_prefix, 'grpclb_server', i) |
||||
grpclb_spec = grpclb_jobspec(balancer_config['transport_sec'], |
||||
balancer_config['short_stream'], |
||||
backend_addrs, grpclb_shortname) |
||||
grpclb_job = dockerjob.DockerJob(grpclb_spec) |
||||
server_jobs[grpclb_shortname] = grpclb_job |
||||
grpclb_ips.append(grpclb_job.ip_address()) |
||||
# Start DNS server |
||||
dns_server_shortname = shortname(shortname_prefix, 'dns_server', 0) |
||||
dns_server_spec = dns_server_in_docker_jobspec( |
||||
grpclb_ips, fallback_ips, dns_server_shortname, |
||||
scenario_config['cause_no_error_no_data_for_balancer_a_record']) |
||||
dns_server_job = dockerjob.DockerJob(dns_server_spec) |
||||
server_jobs[dns_server_shortname] = dns_server_job |
||||
# Get the IP address of the docker container running the DNS server. |
||||
# The DNS server is running on port 53 of that IP address. Note we will |
||||
# point the DNS resolvers of grpc clients under test to our controlled |
||||
# DNS server by effectively modifying the /etc/resolve.conf "nameserver" |
||||
# lists of their docker containers. |
||||
dns_server_ip = dns_server_job.ip_address() |
||||
wait_until_dns_server_is_up(dns_server_ip) |
||||
# Run clients |
||||
jobs = [] |
||||
for lang_name in languages: |
||||
# Skip languages that are known to not currently |
||||
# work for this test. |
||||
if not args.no_skips and lang_name in scenario_config.get( |
||||
'skip_langs', []): |
||||
jobset.message('IDLE', |
||||
'Skipping scenario: %s for language: %s\n' % |
||||
(scenario_config['name'], lang_name)) |
||||
continue |
||||
lang = _LANGUAGES[lang_name] |
||||
test_job = lb_client_interop_jobspec( |
||||
lang, |
||||
dns_server_ip, |
||||
docker_image=docker_images.get(lang.safename), |
||||
transport_security=scenario_config['transport_sec']) |
||||
jobs.append(test_job) |
||||
jobset.message('IDLE', 'Jobs to run: \n%s\n' % '\n'.join( |
||||
str(job) for job in jobs)) |
||||
num_failures, resultset = jobset.run( |
||||
jobs, newline_on_success=True, maxjobs=args.jobs) |
||||
report_utils.render_junit_xml_report(resultset, 'sponge_log.xml') |
||||
if num_failures: |
||||
suppress_server_logs = False |
||||
jobset.message( |
||||
'FAILED', |
||||
'Scenario: %s. Some tests failed' % scenario_config['name'], |
||||
do_newline=True) |
||||
else: |
||||
jobset.message( |
||||
'SUCCESS', |
||||
'Scenario: %s. All tests passed' % scenario_config['name'], |
||||
do_newline=True) |
||||
return num_failures |
||||
finally: |
||||
# Check if servers are still running. |
||||
for server, job in server_jobs.items(): |
||||
if not job.is_running(): |
||||
print('Server "%s" has exited prematurely.' % server) |
||||
suppress_failure = suppress_server_logs and not args.verbose |
||||
dockerjob.finish_jobs( |
||||
[j for j in six.itervalues(server_jobs)], |
||||
suppress_failure=suppress_failure) |
||||
|
||||
|
||||
num_failures = 0 |
||||
with open(args.scenarios_file, 'r') as scenarios_input: |
||||
all_scenarios = json.loads(scenarios_input.read()) |
||||
for scenario in all_scenarios: |
||||
if args.scenario_name: |
||||
if args.scenario_name != scenario['name']: |
||||
jobset.message('IDLE', |
||||
'Skipping scenario: %s' % scenario['name']) |
||||
continue |
||||
num_failures += run_one_scenario(scenario) |
||||
if num_failures == 0: |
||||
sys.exit(0) |
||||
else: |
||||
sys.exit(1) |
Loading…
Reference in new issue