Skip to content
This repository was archived by the owner on Aug 19, 2019. It is now read-only.

Conversation

@bmoyles0117
Copy link
Contributor

No description provided.

Copy link
Contributor

@supriyagarg supriyagarg left a comment

Choose a reason for hiding this comment

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

Minor comments.

const bool KubernetesReader::IsConfigured() const {
try {
json::value raw_node = QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes");
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be useful to minimize output by using fieldSelector (or limit=0 if that works)

https://v1-8.docs.kubernetes.io/docs/api-reference/v1.8/#list-474

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've limited it to just the node we're running on.

src/docker.cc Outdated

const bool DockerReader::IsConfigured() const {
try {
QueryDocker(std::string(kDockerEndpointPath) + "/json?all=true");
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be useful to minimize output by using limit / filter options:
https://docs.docker.com/engine/api/v1.24/#31-containers

Copy link
Contributor

Choose a reason for hiding this comment

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

There's a DockerContainerFilter config option — you should probably be checking that...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

Copy link
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

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

Do we need to verify the InstanceUpdater as well?

src/docker.cc Outdated

const bool DockerReader::IsConfigured() const {
try {
QueryDocker(std::string(kDockerEndpointPath) + "/json?all=true");
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a DockerContainerFilter config option — you should probably be checking that...

src/docker.cc Outdated
DockerReader::DockerReader(const MetadataAgentConfiguration& config)
: config_(config), environment_(config) {}

const bool DockerReader::IsConfigured() const {
Copy link
Contributor

Choose a reason for hiding this comment

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

const bool is redundant. Just use bool.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd call this ValidateConfiguration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

src/docker.cc Outdated
}

void DockerUpdater::start() {
if (!reader_.IsConfigured()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's define a virtual VerifyConfiguration function in PollingMetadataUpdater, make it return true by default, call it from start in the superclass, and override it here. We might also have to give each updater object a name, so we can identify it in the logs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

src/docker.cc Outdated
return;
}

if (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.

config()

src/reader.h Outdated

class MetadataReader {
public:
virtual bool ValidateConfiguration() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no reason to have a superclass for all readers, actually. I would keep this independently in KubernetesReader and DockerReader. Let's just revert 89d6cf8.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reader is the thing that's actually interacting with the endpoints. I'll talk with you offline about this to build additional context together with you, but when I tried implementing the validation in the updater it just resulted in transferred responsibility down to the reader.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's ok for each updater to pass the responsibility down to individual readers — that's why I suggested having a ValidateConfiguration function in each reader that needs it. I'm simply saying that it's not necessary to have the validation in the reader, and we should abstract that away. However, the polling updater needs to perform the validation before starting the polling threads, so let's document that in a virtual function.
Happy to discuss offline.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed and addressed offline.

src/kubernetes.h Outdated
// A Kubernetes metadata query function.
std::vector<MetadataUpdater::ResourceMetadata> MetadataQuery() const;

// Validates that the reader is configured properly.
Copy link
Contributor

Choose a reason for hiding this comment

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

How about just:

// Validates the relevant configuration and returns true if it's correct.

? Also in other files.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

src/docker.cc Outdated
config_.DockerContainerFilter().empty()
? "" : "&" + config_.DockerContainerFilter());

QueryDocker(std::string(kDockerEndpointPath) + "/json?all=true&limit=1" + container_filter);
Copy link
Contributor

Choose a reason for hiding this comment

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

Careful -- config_.DockerContainerFilter() may already include a limit. Should investigate which one takes precedence -- you may have to move your &limit=1 to the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great catch, I did some local testing and it appears that the docker server discards the second value, when two limits are sent in the URL. We'll leave it as-is for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a comment here...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

bool KubernetesReader::ValidateConfiguration() const {
try {
json::value raw_node = QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode());
Copy link
Contributor

Choose a reason for hiding this comment

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

Should also validate config_.KubernetesPodLabelSelector().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This interaction is just validating that we can communicate with the master, adding a secondary request for pods is redundant, and would be more symptomatic of a failure inside of kubernetes, or a transient failure between requests.

Copy link
Contributor

Choose a reason for hiding this comment

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

If so, then this is redundant, since CurrentNode issues two QueryMaster calls. So just calling CurrentNode is more than enough. Or you can call QueryMaster with a filter for one (random) node...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should continue to query for something like nodes, as the node name can be overridden by configuration, which would prevent the master interaction at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, as discussed, it makes sense to issue a QueryMaster on something that doesn't depend on the CurrentNode value, and keep CurrentNode as a secondary check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

bool KubernetesReader::ValidateConfiguration() const {
try {
json::value raw_node = QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode());
Copy link
Contributor

Choose a reason for hiding this comment

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

CurrentNode() may be empty if, e.g., the API token is invalid (so you'd get 403s). Should we check that as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great point, done.

src/updater.cc Outdated

void PollingMetadataUpdater::start() {
bool PollingMetadataUpdater::start() {
if (!reader_->ValidateConfiguration()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's just make ValidateConfiguration a virtual method in PollingMetadataUpdater.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will address as a part of the conversation for the reader above.

src/updater.h Outdated

// Starts updating.
virtual void start() = 0;
virtual bool start() = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why bool? If the configuration is invalid, a start is a no-op, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I needed a signal for the KubernetesUpdater to know that the parent updater had started successfully, before firing off the watch threads. The two options I thought of were return a bool or raise an exception in the PollingMetadataUpdater start. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

If it weren't for the desire to share a reader object, and the possible need to validate the watch and polling options together, I would have been considering splitting the watch and the poller into separate updaters.
I've already suggested using composition over inheritance here. But we could also have a StartThreads virtual function that actually starts the poller thread and is called from the (non-virtual) start after validation succeeds.
Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed and addressed offline.

src/updater.cc Outdated
MetadataUpdater::MetadataUpdater(MetadataAgent* store)
: store_(store) {}
MetadataUpdater::MetadataUpdater(MetadataAgent* store, MetadataReader* reader, std::string name)
: store_(store),
Copy link
Contributor

Choose a reason for hiding this comment

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

You can put all of these on one line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/updater.h Outdated
};

MetadataUpdater(MetadataAgent* store);
MetadataUpdater(MetadataAgent* store, MetadataReader* reader, std::string name);
Copy link
Contributor

Choose a reason for hiding this comment

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

const std::string& name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/updater.h Outdated
public:
PollingMetadataUpdater(
MetadataAgent* store, double period_s,
MetadataAgent* store, MetadataReader* reader, std::string name, double period_s,
Copy link
Contributor

Choose a reason for hiding this comment

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

const std::string& name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor Author

@bmoyles0117 bmoyles0117 left a comment

Choose a reason for hiding this comment

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

Thanks for the feedback, PTAL.

void KubernetesUpdater::start() {
PollingMetadataUpdater::start();
bool KubernetesUpdater::start() {
if(!PollingMetadataUpdater::start()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

return false;
}

if (config().KubernetesUseWatch()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it makes sense to choose one or the other, but then I'm not sure if the contract of the KubernetesUpdater gets a little blurred, as it extends a PolliingMetadataUpdater, but only acts on that functionality conditionally. It's almost as though there should be a KubernetesPollingUpdater, and a KubernetesWatchUpdater which is controlled by configuration when the updaters are all started. WDYT?

src/kubernetes.h Outdated
// A Kubernetes metadata query function.
std::vector<MetadataUpdater::ResourceMetadata> MetadataQuery() const;

// Validates that the reader is configured properly.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

src/updater.cc Outdated
MetadataUpdater::MetadataUpdater(MetadataAgent* store)
: store_(store) {}
MetadataUpdater::MetadataUpdater(MetadataAgent* store, MetadataReader* reader, std::string name)
: store_(store),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/updater.cc Outdated

void PollingMetadataUpdater::start() {
bool PollingMetadataUpdater::start() {
if (!reader_->ValidateConfiguration()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will address as a part of the conversation for the reader above.

src/updater.h Outdated
};

MetadataUpdater(MetadataAgent* store);
MetadataUpdater(MetadataAgent* store, MetadataReader* reader, std::string name);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/updater.h Outdated
public:
PollingMetadataUpdater(
MetadataAgent* store, double period_s,
MetadataAgent* store, MetadataReader* reader, std::string name, double period_s,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

bool KubernetesReader::ValidateConfiguration() const {
try {
json::value raw_node = QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great point, done.

src/docker.cc Outdated
config_.DockerContainerFilter().empty()
? "" : "&" + config_.DockerContainerFilter());

QueryDocker(std::string(kDockerEndpointPath) + "/json?all=true&limit=1" + container_filter);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great catch, I did some local testing and it appears that the docker server discards the second value, when two limits are sent in the URL. We'll leave it as-is for now.

Copy link
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

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

FWIW, I'm happy to do the proposed refactoring in a separate PR if you're willing to deal with merge conflicts.

src/reader.h Outdated

class MetadataReader {
public:
virtual bool ValidateConfiguration() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

It's ok for each updater to pass the responsibility down to individual readers — that's why I suggested having a ValidateConfiguration function in each reader that needs it. I'm simply saying that it's not necessary to have the validation in the reader, and we should abstract that away. However, the polling updater needs to perform the validation before starting the polling threads, so let's document that in a virtual function.
Happy to discuss offline.

src/updater.h Outdated

// Starts updating.
virtual void start() = 0;
virtual bool start() = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

If it weren't for the desire to share a reader object, and the possible need to validate the watch and polling options together, I would have been considering splitting the watch and the poller into separate updaters.
I've already suggested using composition over inheritance here. But we could also have a StartThreads virtual function that actually starts the poller thread and is called from the (non-virtual) start after validation succeeds.
Thoughts?

bool KubernetesReader::ValidateConfiguration() const {
try {
json::value raw_node = QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode());
Copy link
Contributor

Choose a reason for hiding this comment

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

If so, then this is redundant, since CurrentNode issues two QueryMaster calls. So just calling CurrentNode is more than enough. Or you can call QueryMaster with a filter for one (random) node...

return false;
}

if (config().KubernetesUseWatch()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I've considered it, but there's an advantage of sharing a reader.
Perhaps the right approach is to factor out a poller that takes a callback, rather than embedding the contract into the hierarchy (i.e., composition over inheritance).

src/docker.cc Outdated
config_.DockerContainerFilter().empty()
? "" : "&" + config_.DockerContainerFilter());

QueryDocker(std::string(kDockerEndpointPath) + "/json?all=true&limit=1" + container_filter);
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a comment here...

@bmoyles0117 bmoyles0117 changed the title Add validation to only initiate pollers if configured properly. Add validation to only initiate updaters if they're configured properly. Feb 28, 2018
bool KubernetesReader::ValidateConfiguration() const {
try {
json::value raw_node = QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode());
Copy link
Contributor

Choose a reason for hiding this comment

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

Right, as discussed, it makes sense to issue a QueryMaster on something that doesn't depend on the CurrentNode value, and keep CurrentNode as a secondary check.

return false;
}

if (config().KubernetesUseWatch()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Of course.

pod_watch_thread_ =
std::thread(&KubernetesReader::WatchPods, &reader_, cb);
} else {
PollingMetadataUpdater::StartUpdater();
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a comment that polling and watch are mutually exclusive (with watch taking precedence).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

[Optional] How about putting it right above this line? Just say something like // Only try to poll if watch is disabled..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

bool KubernetesReader::ValidateConfiguration() const {
return ValidatePodConnectivity() && ValidateNodeConnectivity();
Copy link
Contributor

Choose a reason for hiding this comment

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

We might also fail if both KubernetesUseWatch and KubernetesUpdaterIntervalSeconds are enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree and disagree with this. Being that the other polling updaters are not considered valid / invalid based on the polling interval, I think we should consolidate this to whether or not the pollers are able to communicate with endpoints, not whether or not you have a poll interval > 0.

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 this depends on whether you want to get an error message when poll interval < 0 or just silently avoid polling. Personally, I prefer the former (and reserve poll interval == 0 for "do not poll").

Perhaps we should implement ValidateConfiguration in PollingMetadataUpdater to verify the polling interval for all polling updaters, and invoke PollingMetadataUpdater::ValidateConfiguraiton here before doing updater-specific validation?..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Totally on board with this, updated.


bool KubernetesReader::ValidatePodConnectivity() const {
try {
QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

This implicitly validates the KubernetesEndpointHost configuration option. We could also make it use KubernetesPodLabelSelector.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm concerned about using the pod selector, as we do this same "just check for a single thing" for nodes. I don't think this function should turn into a unit test, strictly validating that we're able to get "any" pods and nodes should suffice as a sanity check that this updater is configured properly.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was proposing doing the same thing as for Docker, namely adding the pod selector here to have the API check the syntax, but also overriding the limit to 1. Otherwise a user misconfiguration (a bad pod selector) could result in failing queries on every poll, which is exactly what we want to avoid.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, if we're doing this for docker we might as well do it here. It's not completely the same as how we actually invoke this later, as it's combined with the node name later on, but at least we can validate the label selector itself. Done.

src/instance.h Outdated
: reader_(server->config()), PollingMetadataUpdater(
server, server->config().InstanceUpdaterIntervalSeconds(),
server,
"InstanceUpdater",
Copy link
Contributor

Choose a reason for hiding this comment

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

[Optional] You can put this on the previous line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/kubernetes.h Outdated
: reader_(server->config()), PollingMetadataUpdater(
server, server->config().KubernetesUpdaterIntervalSeconds(),
server,
"KubernetesUpdater",
Copy link
Contributor

Choose a reason for hiding this comment

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

[Optional] You can put this on the previous line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/kubernetes.h Outdated
void WatchPods(MetadataUpdater::UpdateCallback callback) const;

private:
bool ValidatePodConnectivity() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

Comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed these helpers.

return FindTopLevelOwner(ns, GetOwner(ns, ref->As<json::Object>()));
}

bool KubernetesReader::ValidatePodConnectivity() const {
Copy link
Contributor

Choose a reason for hiding this comment

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

[Optional] I feel like making those into separate functions is overkill - we don't intend to reuse them. Can we not just have blocks within ValidateConfiguration that do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I personally prefer smaller easily testable functions, but being that I'm not doing the relevant unit testing that goes with that statement, I'll streamline it.

src/updater.h Outdated
store_->UpdateMetadata(result.resource, std::move(result.metadata));
}

std::string name_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why make this protected?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Converted to private.

@bmoyles0117 bmoyles0117 changed the title Add validation to only initiate updaters if they're configured properly. Add validation to only start updaters if they're configured properly. Feb 28, 2018
src/docker.cc Outdated
// A limit may exist in the container_filter, however, the docker API only
// uses the first limit provided in the query params.
(void) QueryDocker(std::string(kDockerEndpointPath) +
"/json?all=true&limit=1" + container_filter);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be lined up with std::string (after the opening parenthesis).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

bool KubernetesReader::ValidateConfiguration() const {
try {
(void) QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

Fits on the previous line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Column count hits 81 on previous line.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused -- I count 80:

    (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/nodes?limit=1");

(void) QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes?limit=1");

return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This will cause an early return, so the rest of the function is dead code. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Overly ambitious copy / paste :P

try {
(void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1");

return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto here, though this one is less of an issue. But since we return true at the end anyway, let's just get rid of this one (and the blank line) to avoid confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

const std::string node_name = CurrentNode();
if (node_name.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we just call CurrentNode().empty() and avoid the node_name local?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

try {
(void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

We should try to use KubernetesPodLabelSelector to see if it gets rejected (bad syntax, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm really not completely comfortable with this, as technically they could pass in garbage for a node name as well. I strongly feel the intention of this code should not be a unit test, but rather a confirmation that the endpoint is available under normal scenarios. What we have so far is better than nothing, and I would argue we're going too far down a path with too many assumptions by incorporating user-defined values as a part of this logic.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should validate all user-supplied configuration. That said, I'm ok with this being done in a later PR.

pod_watch_thread_ =
std::thread(&KubernetesReader::WatchPods, &reader_, cb);
} else {
PollingMetadataUpdater::StartUpdater();
Copy link
Contributor

Choose a reason for hiding this comment

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

[Optional] How about putting it right above this line? Just say something like // Only try to poll if watch is disabled..

}

try {
(void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

We should validate all user-supplied configuration. That said, I'm ok with this being done in a later PR.

bool KubernetesReader::ValidateConfiguration() const {
try {
(void) QueryMaster(
std::string(kKubernetesEndpointPath) + "/nodes?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused -- I count 80:

    (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/nodes?limit=1");

Copy link
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

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

LGTM :shipit:

}

try {
(void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

For the future: by now this check is probably redundant, as CurrentNode() invokes this endpoint too. But we can fix that later.

Copy link
Contributor

Choose a reason for hiding this comment

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

On second thought, since you're waiting for a response from @supriyagarg anyway, can you just move this above the CurrentNode() check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

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

I'm only requesting that the CurrentNode change be done now. The other two (pod label selector and poll interval) can be done later, though if they're easy to make, I'd recommend doing them anyway while you're waiting for approval.

}

try {
(void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

On second thought, since you're waiting for a response from @supriyagarg anyway, can you just move this above the CurrentNode() check?

}

bool KubernetesReader::ValidateConfiguration() const {
return ValidatePodConnectivity() && ValidateNodeConnectivity();
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 this depends on whether you want to get an error message when poll interval < 0 or just silently avoid polling. Personally, I prefer the former (and reserve poll interval == 0 for "do not poll").

Perhaps we should implement ValidateConfiguration in PollingMetadataUpdater to verify the polling interval for all polling updaters, and invoke PollingMetadataUpdater::ValidateConfiguraiton here before doing updater-specific validation?..


bool KubernetesReader::ValidatePodConnectivity() const {
try {
QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1");
Copy link
Contributor

Choose a reason for hiding this comment

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

I was proposing doing the same thing as for Docker, namely adding the pod selector here to have the API check the syntax, but also overriding the limit to 1. Otherwise a user misconfiguration (a bad pod selector) could result in failing queries on every poll, which is exactly what we want to avoid.

Copy link
Contributor

@supriyagarg supriyagarg left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

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

Some issues with the newly added code.

? "" : "&" + config_.KubernetesPodLabelSelector());

(void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1" +
pod_label_selector);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question as for Docker -- can we confirm that the first limit=1 will take precedence over whatever is in the selector?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

pod_label_selector does not imply passing a limit, it implies passing labels, if a user is passing a limit there, that's a hack, and should be validated elsewhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree that it's a hack, but a limit=500000 in the (user-specified) selector should not cause us to suddenly grab all the pods in the cluster. Hence I was just asking whether this limit=1 will override it where it is now, or whether it should be at the end.

That said, I've just verified that the first occurrence of limit takes precedence, so we're good.

void KubernetesUpdater::start() {
PollingMetadataUpdater::start();
bool KubernetesUpdater::ValidateConfiguration() const {
if (!PollingMetadataUpdater::ValidateConfiguration()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this not also be in DockerUpdater and InstanceUpdater?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forgot about DockerUpdater, InstanceUpdater does not override this method.

src/updater.cc Outdated

void PollingMetadataUpdater::start() {
bool PollingMetadataUpdater::ValidateConfiguration() const {
return period_ > seconds::zero();
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be >= -- 0 is a perfectly valid value (it means "don't poll")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think in the future when we use composition, this statement isn't true, but for what you're saying makes sense, updated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Depends on what you mean by "use composition". If it's in the context of "instead of inheritance", then that's orthogonal. If in the context of "composition of options", then you're probably right.
In any case, this looks good now.

if (config().VerboseLogging()) {
LOG(INFO) << "Timer locked";
}
if (period_ > seconds::zero()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Still need this, because we want 0 to turn off polling without an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@igorpeshansky igorpeshansky left a comment

Choose a reason for hiding this comment

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

LGTM :shipit:

? "" : "&" + config_.KubernetesPodLabelSelector());

(void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1" +
pod_label_selector);
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree that it's a hack, but a limit=500000 in the (user-specified) selector should not cause us to suddenly grab all the pods in the cluster. Hence I was just asking whether this limit=1 will override it where it is now, or whether it should be at the end.

That said, I've just verified that the first occurrence of limit takes precedence, so we're good.

src/updater.cc Outdated

void PollingMetadataUpdater::start() {
bool PollingMetadataUpdater::ValidateConfiguration() const {
return period_ > seconds::zero();
Copy link
Contributor

Choose a reason for hiding this comment

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

Depends on what you mean by "use composition". If it's in the context of "instead of inheritance", then that's orthogonal. If in the context of "composition of options", then you're probably right.
In any case, this looks good now.

@bmoyles0117 bmoyles0117 merged commit 6d7718c into master Mar 1, 2018
@igorpeshansky igorpeshansky deleted the bmoyles0117-pollers-inspect-env branch March 10, 2018 19:49
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants