Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b4bb682
Save progress
zasweq Oct 1, 2020
8df52a8
Save progress
zasweq Oct 2, 2020
7ee7b14
Save progress, can almost build
zasweq Oct 2, 2020
9e8d678
Almost builds
zasweq Oct 2, 2020
b977914
Merge branch 'master' into load-balancer-fuzz
zasweq Oct 2, 2020
1cfaa5d
Builds correctly
zasweq Oct 2, 2020
73c24ed
Got rid of unused imports
zasweq Oct 2, 2020
54d5b67
Save progress
zasweq Oct 5, 2020
d5f072f
Ready for PR, working with Asan-fuzzer
zasweq Oct 6, 2020
8c00f14
Got rid of logs
zasweq Oct 6, 2020
810eb49
Spelling
zasweq Oct 6, 2020
413214c
First round of comments, saving progress
zasweq Oct 6, 2020
b5619fe
Responded to comments and redesigned based on design discussion
zasweq Oct 6, 2020
c4a53c0
Clean up
zasweq Oct 6, 2020
6bcb394
Responded to Asra's comments
zasweq Oct 7, 2020
ea0264a
Save progress, responded to Alex's comments and some Harvey comments
zasweq Oct 8, 2020
e43bb2c
Responded to Harvey's comments
zasweq Oct 9, 2020
6e3c420
Style
zasweq Oct 9, 2020
a7d7e88
Responded to Harvey's comments
zasweq Oct 9, 2020
5c6bdaf
Spelling CI
zasweq Oct 9, 2020
92666c1
Changed update to use generated bytes, added util class
zasweq Oct 9, 2020
6ee35db
Responded to Asra's comments
zasweq Oct 12, 2020
a81eabe
Added logic for localities
zasweq Oct 13, 2020
31a5668
Fixed slow iterations
zasweq Oct 13, 2020
91e5738
Partially responded to comments
zasweq Oct 13, 2020
794918b
Clang Tidy
zasweq Oct 13, 2020
c1c8e29
Adi's refactor comment
zasweq Oct 13, 2020
741b3a0
Clang tidy and Harvey nit
zasweq Oct 14, 2020
52cbbd5
Responded to Asra's comments
zasweq Oct 14, 2020
4bec47d
Partially responded to comments, still have 0(N^2) and flags
zasweq Oct 15, 2020
11a7f74
Removed O(N^2) and added health flags
zasweq Oct 15, 2020
4675237
Responded to Asra's comments
zasweq Oct 16, 2020
d07db49
Spelling
zasweq Oct 16, 2020
ff8e22f
Responded to Harvey's comments
zasweq Oct 19, 2020
d71c5d1
Responded to comments
zasweq Oct 20, 2020
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
53 changes: 53 additions & 0 deletions test/common/upstream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,59 @@ envoy_cc_test(
],
)

envoy_cc_test_library(
name = "load_balancer_impl_test_lib",
srcs = [
"load_balancer_fuzz_test_utils.cc",
],
hdrs = [
"load_balancer_fuzz_test_utils.h",
],
deps = [
":utility_lib",
"//source/common/upstream:load_balancer_lib",
"//test/mocks:common_lib",
"//test/mocks/runtime:runtime_mocks",
"//test/mocks/upstream:cluster_info_mocks",
"//test/mocks/upstream:host_set_mocks",
"//test/mocks/upstream:load_balancer_context_mock",
"//test/mocks/upstream:priority_set_mocks",
"@envoy_api//envoy/config/cluster/v3:pkg_cc_proto",
],
)

envoy_cc_test_library(
name = "load_balancer_fuzz_lib",
srcs = ["load_balancer_fuzz.cc"],
hdrs = ["load_balancer_fuzz.h"],
deps = [
":load_balancer_fuzz_proto_cc_proto",
":load_balancer_impl_test_lib",
"//source/common/upstream:load_balancer_lib",
],
)

envoy_proto_library(
name = "load_balancer_fuzz_proto",
srcs = ["load_balancer_fuzz.proto"],
deps = [
"//test/fuzz:common_proto",
"@envoy_api//envoy/config/cluster/v3:pkg",
],
)

envoy_cc_fuzz_test(
name = "load_balancer_fuzz_test",
srcs = ["load_balancer_fuzz_test.cc"],
corpus = "//test/common/upstream:load_balancer_corpus",
deps = [
":load_balancer_fuzz_lib",
":load_balancer_fuzz_proto_cc_proto",
":utility_lib",
"//test/fuzz:utility_lib",
Comment thread
zasweq marked this conversation as resolved.
Outdated
],
)

envoy_cc_test(
name = "load_balancer_simulation_test",
srcs = ["load_balancer_simulation_test.cc"],
Expand Down
16 changes: 16 additions & 0 deletions test/common/upstream/load_balancer_corpus/random_NoHosts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions test/common/upstream/load_balancer_corpus/random_Normal

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 123 additions & 0 deletions test/common/upstream/load_balancer_fuzz.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "test/common/upstream/load_balancer_fuzz.h"

#include "test/common/upstream/utility.h"

namespace Envoy {
namespace Upstream {

// Anonymous namespace for helper functions
namespace {
std::vector<uint64_t>
constructByteVectorForRandom(test::common::upstream::LoadBalancerTestCase input) {
std::vector<uint64_t> byteVector;
for (int i = 0; i < input.bytestring_for_random_calls().size(); ++i) {
byteVector.push_back(input.bytestring_for_random_calls(i));
}
return byteVector;
}
} // namespace

// Required because super class constructor has logic
LoadBalancerFuzzBase::LoadBalancerFuzzBase() : LoadBalancerFuzzTestBase() {}

void LoadBalancerFuzzBase::initializeFixedHostSets(uint32_t num_hosts_in_priority_set,
uint32_t num_hosts_in_failover_set) {
// TODO: Cap on ports?
int port = 80;
for (uint32_t i = 0; i < num_hosts_in_priority_set; ++i) {
host_set_.hosts_.push_back(makeTestHost(info_, "tcp://127.0.0.1:" + std::to_string(port)));
++port;
}
for (uint32_t i = 0; i < num_hosts_in_failover_set; ++i) {
failover_host_set_.hosts_.push_back(
makeTestHost(info_, "tcp://127.0.0.1:" + std::to_string(port)));
++port;
}
// TODO: More than two hosts?
}

void LoadBalancerFuzzBase::initializeAndReplay(test::common::upstream::LoadBalancerTestCase input) {
// TODO: Keep this random instantiation even in load balancers that don't call into it, or do they
// all?
random_.bytestring_ = constructByteVectorForRandom(input);
Comment thread
zasweq marked this conversation as resolved.
Outdated
try {
initialize(input); // Initializes specific load balancers
} catch (EnvoyException& e) {
ENVOY_LOG_MISC(debug, "EnvoyException: {}", e.what());
return;
}
initializeFixedHostSets(input.num_hosts_in_priority_set(), input.num_hosts_in_failover_set());
replay(input);
}

// So, these should be shared amongst all of the types. Since logically, we're just setting the mock
// priority set to have certain values, we're doing the same thing across all of them here
void LoadBalancerFuzzBase::updateHealthFlagsForAHostSet(bool failover_host_set,
uint32_t num_healthy_hosts,
uint32_t num_degraded_hosts,
uint32_t num_excluded_hosts) {
MockHostSet& host_set = *priority_set_.getMockHostSet(int(failover_host_set));
uint32_t i = 0;
for (; i < num_healthy_hosts && i < host_set.hosts_.size(); ++i) {
host_set.healthy_hosts_.push_back(host_set.hosts_[i]);
}
for (; i < (num_healthy_hosts + num_degraded_hosts) && i < host_set.hosts_.size(); ++i) {
host_set.degraded_hosts_.push_back(host_set.hosts_[i]);
}

for (; i < (num_healthy_hosts + num_degraded_hosts + num_excluded_hosts) &&
i < host_set.hosts_.size();
++i) {
host_set.excluded_hosts_.push_back(host_set.hosts_[i]);
}

host_set.runCallbacks({}, {});
}

void LoadBalancerFuzzBase::replay(test::common::upstream::LoadBalancerTestCase input) {
constexpr auto max_actions = 64;
for (int i = 0; i < std::min(max_actions, input.actions().size()); ++i) {
const auto& event = input.actions(i);
ENVOY_LOG_MISC(trace, "Action: {}", event.DebugString());
switch (event.action_selector_case()) {
case test::common::upstream::LbAction::kUpdateHealthFlags: {
updateHealthFlagsForAHostSet(event.update_health_flags().failover_host_set(),
event.update_health_flags().num_degraded_hosts(),
event.update_health_flags().num_excluded_hosts());
break;
}
case test::common::upstream::LbAction::kPrefetch: {
prefetch();
break;
}
case test::common::upstream::LbAction::kChooseHost: {
chooseHost();
break;
}
default:
break;
}
}
}

RandomLoadBalancerFuzzTest::RandomLoadBalancerFuzzTest() : LoadBalancerFuzzBase() {}

void RandomLoadBalancerFuzzTest::initialize(test::common::upstream::LoadBalancerTestCase input) {
load_balancer_ = std::make_unique<RandomLoadBalancer>(priority_set_, nullptr, stats_, runtime_,
random_, input.common_lb_config());
}

// For random load balancing, a randomly generated uint64 gets modded against the hosts to choose
// from. This is not something an untrusted upstream can affect, and fuzzing must be deterministic,
// so the fuzzer generates a bytestring which represents the random calls.

// Logic specific for random load balancers
void RandomLoadBalancerFuzzTest::prefetch() {
// random() calls are handled by fake random
load_balancer_->peekAnotherHost(nullptr);
}

void RandomLoadBalancerFuzzTest::chooseHost() { load_balancer_->chooseHost(nullptr); }

} // namespace Upstream
} // namespace Envoy
53 changes: 53 additions & 0 deletions test/common/upstream/load_balancer_fuzz.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "common/upstream/load_balancer_impl.h"

#include "test/common/upstream/load_balancer_fuzz.pb.validate.h"
#include "test/common/upstream/load_balancer_fuzz_test_utils.h"

namespace Envoy {
namespace Upstream {

class LoadBalancerFuzzBase : public LoadBalancerFuzzTestBase {
Comment thread
zasweq marked this conversation as resolved.
Outdated
public:
LoadBalancerFuzzBase();

// Untrusted upstreams don't have the ability to change the host set size, so keep it constant
// over the fuzz iteration.
void initializeFixedHostSets(uint32_t num_hosts_in_priority_set,
uint32_t num_hosts_in_failover_set);

virtual void initialize(test::common::upstream::LoadBalancerTestCase input) PURE;
void initializeAndReplay(test::common::upstream::LoadBalancerTestCase input);
Comment thread
asraa marked this conversation as resolved.
Outdated
void updateHealthFlagsForAHostSet(bool failover_host_set, uint32_t num_healthy_hosts,
uint32_t num_degraded_hosts = 0,
uint32_t num_excluded_hosts = 0);
// These two actions have a lot of logic attached to them such as mocks, so you need to delegate
// these to specific logic per each specific load balancer. This makes sense, as the load
// balancing algorithms sometimes use other components which are tightly coupled into the
// algorithm logically.
virtual void prefetch() PURE;
virtual void chooseHost() PURE;
virtual ~LoadBalancerFuzzBase() = default;

private:
void replay(test::common::upstream::LoadBalancerTestCase input);
// TODO: Move load balancer here?
};

class RandomLoadBalancerFuzzTest : public LoadBalancerFuzzBase {
public:
RandomLoadBalancerFuzzTest();
void initialize(test::common::upstream::LoadBalancerTestCase input) override;
// Has mock logic for random
void prefetch() override;
void chooseHost() override;
~RandomLoadBalancerFuzzTest() = default;

std::unique_ptr<RandomLoadBalancer> load_balancer_;
};

// TODO: Since each load balancer has a specific config, this will need to be addressed in their
Comment thread
asraa marked this conversation as resolved.
Outdated
// specific base classes. We can do this with an extended proto, which also will contain specific
// logic for mocks

} // namespace Upstream
} // namespace Envoy
41 changes: 41 additions & 0 deletions test/common/upstream/load_balancer_fuzz.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
syntax = "proto3";

package test.common.upstream;

import "validate/validate.proto";
import "envoy/config/cluster/v3/cluster.proto";
import "google/protobuf/empty.proto";

message UpdateHealthFlags {
bool failover_host_set = 1;
Comment thread
zasweq marked this conversation as resolved.
Outdated
uint32 num_healthy_hosts = 2;
Comment thread
zasweq marked this conversation as resolved.
uint32 num_degraded_hosts = 3;
uint32 num_excluded_hosts = 4;
}

//Left as message in case specific load balancers need something generated for these actions
message ChooseHost {
}

message Prefetch {
}

message LbAction {
oneof action_selector {
option (validate.required) = true;
UpdateHealthFlags update_health_flags = 1;
Prefetch prefetch = 2;
ChooseHost choose_host = 3;
}
}

message LoadBalancerTestCase {
envoy.config.cluster.v3.Cluster.CommonLbConfig common_lb_config = 1
[(validate.rules).message.required = true];
repeated LbAction actions = 2;
//This is capped at the max port value on an ip address - 80
Comment thread
zasweq marked this conversation as resolved.
Outdated
int64 num_hosts_in_priority_set = 3 [(validate.rules).int64.lt = 65455];
Comment thread
zasweq marked this conversation as resolved.
Outdated
int64 num_hosts_in_failover_set = 4 [(validate.rules).int64.lt = 65455];
repeated int64 bytestring_for_random_calls = 5
Comment thread
zasweq marked this conversation as resolved.
Outdated
[(validate.rules).repeated = {min_items: 1, max_items: 50}];
Comment thread
zasweq marked this conversation as resolved.
Outdated
}
Loading