Skip to content
This repository was archived by the owner on Aug 19, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ This is the Stackdriver metadata agent.

1. Install runtime dependencies:

$ sudo apt-get install libyajl2 libboost-system1.55.0 libboost-thread1.55.0
$ sudo apt-get install libyajl2 libboost-program-options1.55.0 \
libboost-system1.55.0 libboost-thread1.55.0

2. Install build dependencies:

Expand All @@ -18,7 +19,8 @@ This is the Stackdriver metadata agent.

1. Install runtime dependencies (Ubuntu 16.04 special edition):

$ sudo apt-get install libssl1.0.0 libyajl2 libboost-system1.58.0 \
$ sudo apt-get install libssl1.0.0 libyajl2 \
libboost-program-options1.58.0 libboost-system1.58.0 \
libboost-thread1.58.0

2. Install build dependencies (Ubuntu 16.04 special edition):
Expand Down
2 changes: 1 addition & 1 deletion pkg/deb/debian/control.base
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Build-Depends: g++, cmake, dpkg-dev, libyajl-dev (>= 2.0), libssl-dev, libboost1

Package: stackdriver-metadata
Architecture: amd64
Depends: libyajl2, libboost-system1.55.0, libboost-thread1.55.0
Depends: libyajl2, libboost-program-options1.55.0 libboost-system1.55.0, libboost-thread1.55.0
Description: Stackdriver metadata collection daemon
The Stackdriver metadata daemon collects resource metadata and
sends it to the Stackdriver service.
2 changes: 1 addition & 1 deletion pkg/deb/debian/control.xenial
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Build-Depends: g++, cmake, dpkg-dev, libyajl-dev (>= 2.0), libssl-dev, libboost1

Package: stackdriver-metadata
Architecture: amd64
Depends: libssl1.0.0, libyajl2, libboost-system1.58.0, libboost-thread1.58.0
Depends: libssl1.0.0, libyajl2, libboost-program-options1.58.0, libboost-system1.58.0, libboost-thread1.58.0
Description: Stackdriver metadata collection daemon
The Stackdriver metadata daemon collects resource metadata and
sends it to the Stackdriver service.
4 changes: 2 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ SED_I=/usr/bin/env sed -i
CMAKE=cmake
CXXFLAGS=-std=c++11 -g -DENABLE_KUBERNETES_METADATA -I$(CPP_NETLIB_DIR) -I$(YAML_CPP_DIR)/include
LDFLAGS=-L$(CPP_NETLIB_LIBDIR) -L$(YAML_CPP_LIBDIR)
LDLIBS=-lcppnetlib-uri -lcppnetlib-client-connections -lboost_system \
-lboost_thread -lpthread -lyajl -lssl -lcrypto -lyaml-cpp
LDLIBS=-lcppnetlib-uri -lcppnetlib-client-connections -lboost_program_options \
-lboost_system -lboost_thread -lpthread -lyajl -lssl -lcrypto -lyaml-cpp
SED_EXTRA=

UNAME_S=$(shell uname -s)
Expand Down
76 changes: 51 additions & 25 deletions src/api_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,29 @@ void MetadataApiServer::Handler::operator()(const HttpServer::request& request,
static const std::string kPrefix = "/monitoredResource/";
// The format for the local metadata API request is:
// {host}:{port}/monitoredResource/{id}
LOG(INFO) << "Handler called: " << request.method
<< " " << request.destination
<< " headers: " << request.headers
<< " body: " << request.body;
if (agent_.config_.VerboseLogging()) {
LOG(INFO) << "Handler called: " << request.method
<< " " << request.destination
<< " headers: " << request.headers
<< " body: " << request.body;
}
if (request.method == "GET" && request.destination.find(kPrefix) == 0) {
std::string id = request.destination.substr(kPrefix.size());
const auto result = agent_.resource_map_.find(id);
if (result == agent_.resource_map_.end()) {
// TODO: This could be considered log spam.
// As we add more resource mappings, these will become less and less
// frequent, and could be promoted to ERROR.
LOG(WARNING) << "No matching resource for " << id;
if (agent_.config_.VerboseLogging()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the only way to do it? Seems like if conditionals to guard logging defeats the purpose of using log levels like INFO and DEBUG. Also elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the two are orthogonal. With verbose logging, we can get messages at all 4 levels (ERROR, WARNING, INFO, DEBUG). You're right that ideally this would be built into the logging framework itself, but that's a much larger undertaking.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we all agree here, I had spoken with @igorpeshansky offline and we came to the conclusion that the goal for now is to reduce log spam with a simple flag and implement full support for log levels later on down the road.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Alright. Deferring for now.

LOG(WARNING) << "No matching resource for " << id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider logging this at least once per resource instead of guarding by verbose logging, as I would agree repetitively spamming an undiscovered resource would be spammy, even though this would happen within the 60 second poll interval for newly created docker containers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging this once isn't something that's easy to do with the current logging framework.
Besides, this is a handler for the incoming requests, so it would log every time an agent wants to translate a resource, which is a lot more frequent than once a minute.

}
response = HttpServer::response::stock_reply(
HttpServer::response::not_found, "");
} else {
const MonitoredResource& resource = result->second;
LOG(INFO) << "Found resource for " << id << ": " << resource;
if (agent_.config_.VerboseLogging()) {
LOG(INFO) << "Found resource for " << id << ": " << resource;
}
response = HttpServer::response::stock_reply(
HttpServer::response::ok, resource.ToJSON()->ToString());
}
Expand Down Expand Up @@ -176,9 +182,13 @@ void MetadataReporter::ReportMetadata() {
std::this_thread::sleep_for(std::chrono::seconds(3));
// TODO: Do we need to be able to stop this?
while (true) {
LOG(INFO) << "Sending metadata request to server";
if (agent_.config_.VerboseLogging()) {
LOG(INFO) << "Sending metadata request to server";
}
SendMetadata(agent_.GetMetadataMap());
LOG(INFO) << "Metadata request sent successfully";
if (agent_.config_.VerboseLogging()) {
LOG(INFO) << "Metadata request sent successfully";
}
std::this_thread::sleep_for(period_);
}
//LOG(INFO) << "Metadata reporter exiting";
Expand All @@ -188,13 +198,16 @@ namespace {

void SendMetadataRequest(std::vector<json::value>&& entries,
const std::string& endpoint,
const std::string& auth_header) {
const std::string& auth_header,
bool verbose_logging) {
json::value update_metadata_request = json::object({
{"entries", json::array(std::move(entries))},
});

LOG(INFO) << "About to send request: POST " << endpoint
<< " " << *update_metadata_request;
if (verbose_logging) {
LOG(INFO) << "About to send request: POST " << endpoint
<< " " << *update_metadata_request;
}

http::client client;
http::client::request request(endpoint);
Expand All @@ -205,7 +218,9 @@ void SendMetadataRequest(std::vector<json::value>&& entries,
request << boost::network::header("Authorization", auth_header);
request << boost::network::body(request_body);
http::client::response response = client.post(request);
LOG(INFO) << "Server responded with " << body(response);
if (verbose_logging) {
LOG(INFO) << "Server responded with " << body(response);
}
// TODO: process response.
}

Expand All @@ -214,11 +229,15 @@ void SendMetadataRequest(std::vector<json::value>&& entries,
void MetadataReporter::SendMetadata(
std::map<MonitoredResource, MetadataAgent::Metadata>&& metadata) {
if (metadata.empty()) {
LOG(INFO) << "No data to send";
if (agent_.config_.VerboseLogging()) {
LOG(INFO) << "No data to send";
}
return;
}

LOG(INFO) << "Sending request to the server";
if (agent_.config_.VerboseLogging()) {
LOG(INFO) << "Sending request to the server";
}
const std::string project_id = environment_.NumericProjectId();
// The endpoint template is expected to be of the form
// "https://stackdriver.googleapis.com/.../projects/{{project_id}}/...".
Expand Down Expand Up @@ -261,15 +280,17 @@ void MetadataReporter::SendMetadata(
continue;
}
if (total_size + size > limit_bytes) {
SendMetadataRequest(std::move(entries), endpoint, auth_header);
SendMetadataRequest(std::move(entries), endpoint, auth_header,
agent_.config_.VerboseLogging());
entries.clear();
total_size = empty_size;
}
entries.emplace_back(std::move(metadata_entry));
total_size += size;
}
if (!entries.empty()) {
SendMetadataRequest(std::move(entries), endpoint, auth_header);
SendMetadataRequest(std::move(entries), endpoint, auth_header,
agent_.config_.VerboseLogging());
}
}

Expand All @@ -285,17 +306,22 @@ void MetadataAgent::UpdateResource(const std::vector<std::string>& resource_ids,
// TODO: How do we handle deleted resources?
// TODO: Do we care if the value was already there?
for (const std::string& id : resource_ids) {
LOG(INFO) << "Updating resource map '" << id << "'->" << resource;
if (config_.VerboseLogging()) {
LOG(INFO) << "Updating resource map '" << id << "'->" << resource;
}
resource_map_.emplace(id, resource);
}
LOG(INFO) << "Updating metadata map " << resource << "->{"
<< "version: " << entry.version << ", "
<< "is_deleted: " << entry.is_deleted << ", "
<< "created_at: " << rfc3339::ToString(entry.created_at) << ", "
<< "collected_at: " << rfc3339::ToString(entry.collected_at) << ", "
<< "metadata: " << *entry.metadata << ", "
<< "ignore: " << entry.ignore
<< "}";
if (config_.VerboseLogging()) {
LOG(INFO) << "Updating metadata map " << resource << "->{"
<< "version: " << entry.version << ", "
<< "is_deleted: " << entry.is_deleted << ", "
<< "created_at: " << rfc3339::ToString(entry.created_at) << ", "
<< "collected_at: " << rfc3339::ToString(entry.collected_at)
<< ", "
<< "metadata: " << *entry.metadata << ", "
<< "ignore: " << entry.ignore
<< "}";
}
// Force value update. The repeated search is inefficient, but shouldn't
// be a huge deal.
metadata_map_.erase(resource);
Expand Down
4 changes: 4 additions & 0 deletions src/api_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ class MetadataAgent {
// Starts serving.
void start();

const MetadataAgentConfiguration& config() const {
return config_;
}

private:
friend class MetadataApiServer;
friend class MetadataReporter;
Expand Down
42 changes: 39 additions & 3 deletions src/configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@

#include "configuration.h"

#include <boost/program_options.hpp>
#include <iostream>
#include <map>

#include <yaml-cpp/yaml.h>

namespace google {

namespace {
constexpr const char kConfigFileFlag[] = "config-file";

constexpr const char kDefaultProjectId[] = "";
constexpr const char kDefaultCredentialsFile[] = "";
Expand Down Expand Up @@ -51,6 +54,7 @@ constexpr const char kDefaultInstanceZone[] = "";
MetadataAgentConfiguration::MetadataAgentConfiguration()
: project_id_(kDefaultProjectId),
credentials_file_(kDefaultCredentialsFile),
verbose_logging_(false),
metadata_api_num_threads_(kMetadataApiDefaultNumThreads),
metadata_api_port_(kMetadataApiDefaultPort),
metadata_reporter_interval_seconds_(
Expand All @@ -71,9 +75,41 @@ MetadataAgentConfiguration::MetadataAgentConfiguration()
instance_id_(kDefaultInstanceId),
instance_zone_(kDefaultInstanceZone) {}

MetadataAgentConfiguration::MetadataAgentConfiguration(
const std::string& filename) : MetadataAgentConfiguration()
{
int MetadataAgentConfiguration::ParseArguments(int ac, char** av) {
std::string config_file;
boost::program_options::options_description flags_desc;
flags_desc.add_options()
("help,h", "Print help message")
("verbose,v", boost::program_options::bool_switch(&verbose_logging_),
"Enable verbose logging")
;
boost::program_options::options_description hidden_desc;
hidden_desc.add_options()
(kConfigFileFlag,
boost::program_options::value<std::string>(&config_file)
->default_value(""),
"Configuration file location")
;
boost::program_options::options_description all_desc;
all_desc.add(flags_desc).add(hidden_desc);
boost::program_options::positional_options_description positional_desc;
positional_desc.add(kConfigFileFlag, 1);
boost::program_options::variables_map flags;
boost::program_options::store(
boost::program_options::command_line_parser(ac, av)
.options(all_desc).positional(positional_desc).run(), flags);
boost::program_options::notify(flags);

if (flags.count("help")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this work if -h is passed in instead of --help?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it will.

std::cout << flags_desc << std::endl;
return 1;
}

ParseConfigFile(config_file);
return 0;
}

void MetadataAgentConfiguration::ParseConfigFile(const std::string& filename) {
std::lock_guard<std::mutex> lock(mutex_);
if (filename.empty()) return;

Expand Down
9 changes: 8 additions & 1 deletion src/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace google {
class MetadataAgentConfiguration {
public:
MetadataAgentConfiguration();
MetadataAgentConfiguration(const std::string& filename);
int ParseArguments(int ac, char** av);

// Shared configuration.
const std::string& ProjectId() const {
Expand All @@ -35,6 +35,10 @@ class MetadataAgentConfiguration {
std::lock_guard<std::mutex> lock(mutex_);
return credentials_file_;
}
bool VerboseLogging() const {
std::lock_guard<std::mutex> lock(mutex_);
return verbose_logging_;
}
// Metadata API server configuration options.
int MetadataApiNumThreads() const {
std::lock_guard<std::mutex> lock(mutex_);
Expand Down Expand Up @@ -102,9 +106,12 @@ class MetadataAgentConfiguration {
}

private:
void ParseConfigFile(const std::string& filename);

mutable std::mutex mutex_;
std::string project_id_;
std::string credentials_file_;
bool verbose_logging_;
int metadata_api_num_threads_;
int metadata_api_port_;
int metadata_reporter_interval_seconds_;
Expand Down
20 changes: 15 additions & 5 deletions src/docker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ DockerReader::DockerReader(const MetadataAgentConfiguration& config)

std::vector<PollingMetadataUpdater::ResourceMetadata>
DockerReader::MetadataQuery() const {
LOG(INFO) << "Docker Query called";
if (config_.VerboseLogging()) {
LOG(INFO) << "Docker Query called";
}
const std::string zone = environment_.InstanceZone();
const std::string docker_endpoint(config_.DockerEndpointHost() +
"v" + config_.DockerApiVersion() +
Expand All @@ -60,9 +62,13 @@ std::vector<PollingMetadataUpdater::ResourceMetadata>
try {
http::local_client::response list_response = client.get(list_request);
Timestamp collected_at = std::chrono::system_clock::now();
LOG(INFO) << "List response: " << body(list_response);
if (config_.VerboseLogging()) {
LOG(INFO) << "List response: " << body(list_response);
}
json::value parsed_list = json::Parser::FromString(body(list_response));
LOG(INFO) << "Parsed list: " << *parsed_list;
if (config_.VerboseLogging()) {
LOG(INFO) << "Parsed list: " << *parsed_list;
}
const json::Array* container_list = parsed_list->As<json::Array>();
for (const json::value& element : *container_list) {
try {
Expand All @@ -71,10 +77,14 @@ std::vector<PollingMetadataUpdater::ResourceMetadata>
// Inspect the container.
http::local_client::request inspect_request(docker_endpoint + "/" + id + "/json");
http::local_client::response inspect_response = client.get(inspect_request);
LOG(INFO) << "Inspect response: " << body(inspect_response);
if (config_.VerboseLogging()) {
LOG(INFO) << "Inspect response: " << body(inspect_response);
}
json::value parsed_metadata =
json::Parser::FromString(body(inspect_response));
LOG(INFO) << "Parsed metadata: " << *parsed_metadata;
if (config_.VerboseLogging()) {
LOG(INFO) << "Parsed metadata: " << *parsed_metadata;
}
const MonitoredResource resource("docker_container", {
{"location", zone},
{"container_id", id},
Expand Down
Loading