Skip to content

Commit

Permalink
ApiManager handles managed rollout_strategy (envoyproxy#375)
Browse files Browse the repository at this point in the history
* ApiManager handles managed rollout_strategy

* Fixed code formatting

* ConfigManager ignores the rollout when download was not successful

* Revert "ConfigManager ignores the rollout when download was not successful"

This reverts commit f6de9be8b2c9c25d8f62f56cb8c31a6b7602e0fb.

* Removed const directive
  • Loading branch information
mangchiandjjoe authored Jun 21, 2017
1 parent 8403b51 commit 352ed4a
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 17 deletions.
61 changes: 48 additions & 13 deletions contrib/endpoints/src/api_manager/api_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,7 @@ ApiManagerImpl::ApiManagerImpl(std::unique_ptr<ApiManagerEnvInterface> env,
content << config_file.rdbuf();
config_file.close();

std::string config_id;
if (AddConfig(content.str(), false, &config_id).ok()) {
list.push_back({config_id, round(item.second)});
} else {
global_context_->env()->LogError("Unable to handle service config: " +
item.first);

config_loading_status_ =
utils::Status(Code::ABORTED, "Invalid service config");
break;
}
list.push_back({content.str(), round(item.second)});
} else {
global_context_->env()->LogError(
"Failed to open an api service configuration file: " + item.first);
Expand All @@ -78,8 +68,7 @@ ApiManagerImpl::ApiManagerImpl(std::unique_ptr<ApiManagerEnvInterface> env,
}

if (config_loading_status_.code() == Code::UNAVAILABLE && list.size() > 0) {
DeployConfigs(std::move(list));
config_loading_status_ = utils::Status::OK;
config_loading_status_ = AddAndDeployConfigs(std::move(list), false);
} else {
service_context_map_.clear();
config_loading_status_ =
Expand All @@ -95,6 +84,33 @@ ApiManagerImpl::ApiManagerImpl(std::unique_ptr<ApiManagerEnvInterface> env,
check_workflow_->RegisterAll();
}

utils::Status ApiManagerImpl::AddAndDeployConfigs(
std::vector<std::pair<std::string, int>> &&configs, bool initialize) {
std::vector<std::pair<std::string, int>> list;
for (auto item : configs) {
std::string config_id;
if (AddConfig(item.first, initialize, &config_id).ok()) {
list.push_back({config_id, round(item.second)});
} else {
std::string msg = "Invalid service config";
global_context_->env()->LogError(msg);
return utils::Status(Code::ABORTED, msg);
}
}

if (list.size() == 0) {
std::string msg = "Invalid service config";
global_context_->env()->LogError(msg);
return utils::Status(Code::ABORTED, msg);
}

DeployConfigs(std::move(list));

global_context_->env()->LogInfo("New rollout was deployed");

return utils::Status::OK;
}

utils::Status ApiManagerImpl::AddConfig(const std::string &service_config,
bool initialize,
std::string *config_id) {
Expand Down Expand Up @@ -153,6 +169,25 @@ utils::Status ApiManagerImpl::Init() {
}
}

if (global_context_->rollout_strategy() == kConfigRolloutManaged) {
config_manager_.reset(new ConfigManager(
global_context_,
[this](const utils::Status &status,
std::vector<std::pair<std::string, int>> &&configs) {
if (status.ok()) {
AddAndDeployConfigs(std::move(configs), true);
}
}));

if (global_context_->server_config()->has_service_config_rollout()) {
config_manager_->set_current_rollout_id(global_context_->server_config()
->service_config_rollout()
.rollout_id());
}

config_manager_->Init();
}

return utils::Status::OK;
}

Expand Down
10 changes: 10 additions & 0 deletions contrib/endpoints/src/api_manager/api_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define API_MANAGER_API_MANAGER_IMPL_H_

#include "contrib/endpoints/include/api_manager/api_manager.h"
#include "contrib/endpoints/src/api_manager/config_manager.h"
#include "contrib/endpoints/src/api_manager/context/global_context.h"
#include "contrib/endpoints/src/api_manager/context/service_context.h"
#include "contrib/endpoints/src/api_manager/service_control/interface.h"
Expand Down Expand Up @@ -63,6 +64,11 @@ class ApiManagerImpl : public ApiManager {
// Use these configs according to the traffic percentage.
void DeployConfigs(std::vector<std::pair<std::string, int>> &&list);

// Add and deploy service configs. Return utils::Status::OK when everything
// is ok.
utils::Status AddAndDeployConfigs(
std::vector<std::pair<std::string, int>> &&configs, bool initialize);

// The check work flow.
std::shared_ptr<CheckWorkflow> check_workflow_;

Expand All @@ -79,6 +85,10 @@ class ApiManagerImpl : public ApiManager {
// A weighted service selector.
std::unique_ptr<WeightedSelector> service_selector_;

// A config manager will be initialized when server_config.rollout_strategy is
// set to "managed"
std::unique_ptr<ConfigManager> config_manager_;

// - Code::UNAVAILABLE Not initialized yet. The default value.
// - Code::OK Successfully initialized
// - Code::ABORTED Initialization was failed
Expand Down
142 changes: 142 additions & 0 deletions contrib/endpoints/src/api_manager/api_manager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,22 @@ const char kServerConfigWithPartialServiceConfigFailed[] = R"(
}
)";

const char kServerConfigWithManagedRolloutStrategy[] = R"(
{
"google_authentication_secret": "{}",
"metadata_server_config": {
"enabled": true,
"url": "http://localhost"
},
"service_config_rollout": {
traffic_percentages: {
"contrib/endpoints/src/api_manager/testdata/bookstore_service_config_1.json": 100
}
},
"rollout_strategy": "managed"
}
)";

const char kServerConfigWithNoServiceConfig[] = R"(
{
"google_authentication_secret": "{}",
Expand Down Expand Up @@ -114,12 +130,97 @@ const char kServiceConfig1[] = R"(
}
)";

const char kServiceConfig2[] = R"(
{
"name": "bookstore.test.appspot.com",
"title": "Bookstore",
"http": {
"rules": [
{
"selector": "EchoGetMessage",
"get": "/echo"
}
]
},
"usage": {
"rules": [
{
"selector": "EchoGetMessage",
"allowUnregisteredCalls": true
}
]
},
"control": {
"environment": "servicecontrol.googleapis.com"
},
"id": "2017-05-01r1"
}
)";

const char kRolloutsResponse1[] = R"(
{
"rollouts": [
{
"rolloutId": "2017-05-01r0",
"createTime": "2017-05-01T22:40:09.884Z",
"createdBy": "test_user@google.com",
"status": "SUCCESS",
"trafficPercentStrategy": {
"percentages": {
"2017-05-01r1": 100
}
},
"serviceName": "service_name_from_server_config"
}
]
}
)";

const char kServiceForStatistics[] =
"name: \"service-name\"\n"
"control: {\n"
" environment: \"http://127.0.0.1:8081\"\n"
"}\n";

// Simulate periodic timer event on creation
class MockPeriodicTimer : public PeriodicTimer {
public:
MockPeriodicTimer() {}
MockPeriodicTimer(std::function<void()> continuation)
: continuation_(continuation) {
continuation_();
}

virtual ~MockPeriodicTimer() {}
void Stop(){};

private:
std::function<void()> continuation_;
};

class MockTimerApiManagerEnvironment : public MockApiManagerEnvironment {
public:
MOCK_METHOD2(Log, void(LogLevel, const char *));
MOCK_METHOD1(MakeTag, void *(std::function<void(bool)>));

virtual std::unique_ptr<PeriodicTimer> StartPeriodicTimer(
std::chrono::milliseconds interval, std::function<void()> continuation) {
return std::unique_ptr<PeriodicTimer>(new MockPeriodicTimer(continuation));
}

MOCK_METHOD1(DoRunHTTPRequest, void(HTTPRequest *));
MOCK_METHOD1(DoRunGRPCRequest, void(GRPCRequest *));
virtual void RunHTTPRequest(std::unique_ptr<HTTPRequest> req) {
DoRunHTTPRequest(req.get());
}
virtual void RunGRPCRequest(std::unique_ptr<GRPCRequest> req) {
DoRunGRPCRequest(req.get());
}

private:
std::unique_ptr<PeriodicTimer> periodic_timer_;
};

class ApiManagerTest : public ::testing::Test {
protected:
ApiManagerTest() : callback_run_count_(0) {}
Expand Down Expand Up @@ -249,6 +350,47 @@ TEST_F(ApiManagerTest, InitializedByConfigManager) {
EXPECT_EQ("2017-05-01r0", service->service().id());
}

TEST_F(ApiManagerTest, ManagedRolloutStrategy) {
std::unique_ptr<MockTimerApiManagerEnvironment> env(
new ::testing::NiceMock<MockTimerApiManagerEnvironment>());

EXPECT_CALL(*env.get(), DoRunHTTPRequest(_))
.WillOnce(Invoke([this](HTTPRequest *req) {
ASSERT_EQ(
"https://servicemanagement.googleapis.com/v1/services/"
"bookstore.test.appspot.com/rollouts?filter=status=SUCCESS",
req->url());
req->OnComplete(Status::OK, {}, kRolloutsResponse1);
}))
.WillOnce(Invoke([this](HTTPRequest *req) {
ASSERT_EQ(
"https://servicemanagement.googleapis.com/v1/services/"
"bookstore.test.appspot.com/configs/2017-05-01r1",
req->url());
req->OnComplete(Status::OK, {}, kServiceConfig2);
}));

std::shared_ptr<ApiManagerImpl> api_manager(
std::dynamic_pointer_cast<ApiManagerImpl>(MakeApiManager(
std::move(env), kServerConfigWithManagedRolloutStrategy)));

EXPECT_TRUE(api_manager);
EXPECT_TRUE(api_manager->Enabled());
EXPECT_EQ("bookstore.test.appspot.com", api_manager->service_name());
EXPECT_EQ("2017-05-01r0", api_manager->service("2017-05-01r0").id());

api_manager->Init();

EXPECT_TRUE(api_manager->Enabled());
EXPECT_EQ("2017-05-01r0", api_manager->service("2017-05-01r0").id());

auto service = api_manager->SelectService();

EXPECT_TRUE(service);
EXPECT_EQ("bookstore.test.appspot.com", service->service_name());
EXPECT_EQ("2017-05-01r1", service->service().id());
}

TEST_F(ApiManagerTest, ServerConfigWithPartialServiceConfig) {
std::unique_ptr<MockApiManagerEnvironment> env(
new ::testing::NiceMock<MockApiManagerEnvironment>());
Expand Down
3 changes: 2 additions & 1 deletion contrib/endpoints/src/api_manager/config_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ void ConfigManager::FetchConfigs(
}

// Update ApiManager
rollout_apply_function_(utils::Status::OK, config_fetch_info->configs);
rollout_apply_function_(utils::Status::OK,
std::move(config_fetch_info->configs));
current_rollout_id_ = config_fetch_info->rollout_id;
}
});
Expand Down
5 changes: 2 additions & 3 deletions contrib/endpoints/src/api_manager/config_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ namespace {
// - Code::OK Successfully initialized
// - Code::ABORTED Initialization was failed
// configs - pairs of ServiceConfig in text and rollout percentage
typedef std::function<void(
const utils::Status& status,
const std::vector<std::pair<std::string, int>>& configs)>
typedef std::function<void(const utils::Status& status,
std::vector<std::pair<std::string, int>>&& configs)>
RolloutApplyFunction;

// Data structure to fetch configs from rollouts
Expand Down

0 comments on commit 352ed4a

Please sign in to comment.