-
Notifications
You must be signed in to change notification settings - Fork 11
Add validation to only start updaters if they're configured properly. #63
Conversation
supriyagarg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor comments.
src/kubernetes.cc
Outdated
| const bool KubernetesReader::IsConfigured() const { | ||
| try { | ||
| json::value raw_node = QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes"); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
igorpeshansky
left a comment
There was a problem hiding this 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"); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
config()
Updater::start returns a bool so we can signal an upstream failure.
src/reader.h
Outdated
|
|
||
| class MetadataReader { | ||
| public: | ||
| virtual bool ValidateConfiguration() const; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/kubernetes.cc
Outdated
| bool KubernetesReader::ValidateConfiguration() const { | ||
| try { | ||
| json::value raw_node = QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode()); |
There was a problem hiding this comment.
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().
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/kubernetes.cc
Outdated
| bool KubernetesReader::ValidateConfiguration() const { | ||
| try { | ||
| json::value raw_node = QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode()); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const std::string& name.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const std::string& name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
70449da to
5aa2078
Compare
bmoyles0117
left a comment
There was a problem hiding this 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.
src/kubernetes.cc
Outdated
| void KubernetesUpdater::start() { | ||
| PollingMetadataUpdater::start(); | ||
| bool KubernetesUpdater::start() { | ||
| if(!PollingMetadataUpdater::start()) { |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/kubernetes.cc
Outdated
| bool KubernetesReader::ValidateConfiguration() const { | ||
| try { | ||
| json::value raw_node = QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode()); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
igorpeshansky
left a comment
There was a problem hiding this 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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
src/kubernetes.cc
Outdated
| bool KubernetesReader::ValidateConfiguration() const { | ||
| try { | ||
| json::value raw_node = QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode()); |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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...
This reverts commit 89d6cf8.
AND pod connectivity for kubernetes.
src/kubernetes.cc
Outdated
| bool KubernetesReader::ValidateConfiguration() const { | ||
| try { | ||
| json::value raw_node = QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes/" + CurrentNode()); |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
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..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/kubernetes.cc
Outdated
| } | ||
|
|
||
| bool KubernetesReader::ValidateConfiguration() const { | ||
| return ValidatePodConnectivity() && ValidateNodeConnectivity(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?..
There was a problem hiding this comment.
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.
src/kubernetes.cc
Outdated
|
|
||
| bool KubernetesReader::ValidatePodConnectivity() const { | ||
| try { | ||
| QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed these helpers.
src/kubernetes.cc
Outdated
| return FindTopLevelOwner(ns, GetOwner(ns, ref->As<json::Object>())); | ||
| } | ||
|
|
||
| bool KubernetesReader::ValidatePodConnectivity() const { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why make this protected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converted to private.
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); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/kubernetes.cc
Outdated
| bool KubernetesReader::ValidateConfiguration() const { | ||
| try { | ||
| (void) QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes?limit=1"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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");
src/kubernetes.cc
Outdated
| (void) QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes?limit=1"); | ||
|
|
||
| return true; |
There was a problem hiding this comment.
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. :-)
There was a problem hiding this comment.
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
src/kubernetes.cc
Outdated
| try { | ||
| (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1"); | ||
|
|
||
| return true; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/kubernetes.cc
Outdated
| } | ||
|
|
||
| const std::string node_name = CurrentNode(); | ||
| if (node_name.empty()) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/kubernetes.cc
Outdated
| } | ||
|
|
||
| try { | ||
| (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1"); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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..
src/kubernetes.cc
Outdated
| } | ||
|
|
||
| try { | ||
| (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1"); |
There was a problem hiding this comment.
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.
src/kubernetes.cc
Outdated
| bool KubernetesReader::ValidateConfiguration() const { | ||
| try { | ||
| (void) QueryMaster( | ||
| std::string(kKubernetesEndpointPath) + "/nodes?limit=1"); |
There was a problem hiding this comment.
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");
igorpeshansky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM ![]()
src/kubernetes.cc
Outdated
| } | ||
|
|
||
| try { | ||
| (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
igorpeshansky
left a comment
There was a problem hiding this 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.
src/kubernetes.cc
Outdated
| } | ||
|
|
||
| try { | ||
| (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1"); |
There was a problem hiding this comment.
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?
src/kubernetes.cc
Outdated
| } | ||
|
|
||
| bool KubernetesReader::ValidateConfiguration() const { | ||
| return ValidatePodConnectivity() && ValidateNodeConnectivity(); |
There was a problem hiding this comment.
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?..
src/kubernetes.cc
Outdated
|
|
||
| bool KubernetesReader::ValidatePodConnectivity() const { | ||
| try { | ||
| QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1"); |
There was a problem hiding this comment.
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.
supriyagarg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
igorpeshansky
left a comment
There was a problem hiding this 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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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")
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
igorpeshansky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM ![]()
| ? "" : "&" + config_.KubernetesPodLabelSelector()); | ||
|
|
||
| (void) QueryMaster(std::string(kKubernetesEndpointPath) + "/pods?limit=1" + | ||
| pod_label_selector); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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.
No description provided.