-
Notifications
You must be signed in to change notification settings - Fork 11
Support for publicly accessible configuration parsing to support testing #88
Conversation
test/configuration_unittest.cc
Outdated
| EXPECT_EQ("", config.InstanceId()); | ||
| EXPECT_EQ("", config.InstanceZone()); | ||
| } | ||
| static void ParseConfiguration(MetadataAgentConfiguration& config, const std::string& input) { |
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.
Instead of having this helper, is it possible to just call std::stringstream inline throughout? This way other tests don't try to recycle this method.
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 not, I would vote for ParseConfigurationString, ParseConfigurationStream, and rename ParseConfig to ParseConfigurationFile.
src/configuration.h
Outdated
| // value means that parsing succeeded, but all of the arguments were handled | ||
| // within the function and the program should exit with a success exit code. | ||
| int ParseArguments(int ac, char** av); | ||
| void ParseConfigurationStream(std::istream& input); |
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.
Instead, can we please just add a public constructor with a std::istream& parameter? Something like:
MetadataAgentConfiguration::MetadataAgentConfiguration(std::istream& input)
: MetadataAgentConfiguration() {
ParseConfiguration(input);
}and then construct the stream in the tests as:
const MetadataAgentConfiguration config(std::istringstream(
"ProjectId: TestProjectId\n"
"MetadataApiNumThreads: 13\n"
"MetadataReporterPurgeDeleted: true\n"
"MetadataReporterUserAgent: \"foobar/foobaz\"\n"
));?
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.
@igorpeshansky would using a constructor instead of a public method still allow us to dynamically reload configurations in the future? It would be nice if the metadata agent could pick up changes to the configuration on the disk and reload in the future, either via watching for changes on the file itself periodically, or by receiving a signal to reload the configuration. This could help us reduce the current negative impact of the metadata agent becoming unavailable during a restart, if the only intended change is a configuration update. Kubernetes already updates the files on disk when they're updated via ConfigMaps or Secrets, which would make this an awesome candidate. If you feel that a constructor / public method is just a matter of implementation, fine by me, as long as we are open to the possibility of dynamically reloading the configuration.
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.
Adding to this, I know both "allow us", but what are your thoughts on the topic as a whole, and which approach would be more idiomatic in your mind?
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 believe that such a functionality, when it does get added, should be encapsulated in the Configuration class. No external entity should be responsible for keeping the configuration up to date.
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.
@igorpeshansky I agree with that principle, however its not represented in the current implementation. Right now the configuration class has an empty constructor and requires the something external to call ParseArguments(int ac, char** av).
Perhaps we should have 2 constructors: MetadataAgentConfiguration(std::istream& input) and MetadataAgentConfiguration(int ac, char ** av)?
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 see, the problem is with error reporting. ParseArguments(int ac, char** av) needs to return the success mode to the caller. It does seem that this initialization needs to be done outside a constructor.
In the interest of moving things forward, I am going to implement the constructor as @igorpeshansky has described. Lets talk offline so I can better understand why this is the way to do 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.
Offline conclusion: We will make int ParseArguments(int ac, char** av) private, and int ::main(int ac, char** av); a friend of MetadataAgentConfiguration. This friendship indicates an encapsulation leak that will be refactored out at a later point.
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 stylistic things.
src/configuration.h
Outdated
| #include <string> | ||
|
|
||
| namespace google { | ||
| int main(int ac, char** av); |
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 shouldn't need to pre-declare it 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.
I ran into a compilation issue without this forward declaration. Specifically: ‘int main(int, char**)’ should have been declared inside ‘::’
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.
Yeah, you're right, it needs to know about the main in the global namespace.
| namespace google { | ||
| int main(int ac, char** av); | ||
|
|
||
| namespace google { |
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 keep the blank line between the namespace and the class.
src/configuration.h
Outdated
|
|
||
| void ParseConfigFile(const std::string& filename); | ||
| void ParseConfiguration(std::istream& input); | ||
| void ParseConfigurationStream(std::istream& input); |
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 renaming of private functions now seems gratuitous. Can we rename them back to their original names?
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.
Sure, reverted.
test/configuration_unittest.cc
Outdated
| EXPECT_EQ("limit=30", config.DockerContainerFilter()); | ||
| EXPECT_EQ(0, config.KubernetesUpdaterIntervalSeconds()); | ||
| EXPECT_EQ("https://kubernetes.default.svc", | ||
| config.KubernetesEndpointHost()); |
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.
Fixed
test/configuration_unittest.cc
Outdated
| EXPECT_EQ("", config.InstanceResourceType()); | ||
| EXPECT_EQ(60, config.DockerUpdaterIntervalSeconds()); | ||
| EXPECT_EQ("unix://%2Fvar%2Frun%2Fdocker.sock/", | ||
| config.DockerEndpointHost()); |
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.
Fixed
test/configuration_unittest.cc
Outdated
| VerifyDefaultConfig(); | ||
| TEST(MetadataAgentConfigurationTest, EmptyConfig) { | ||
| std::istringstream stream(""); | ||
| MetadataAgentConfiguration config(stream); |
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 would just inline the stream constructor, i.e.:
MetadataAgentConfiguration config(std::istringstream(""));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 seems to be some sort of compilation issue calling config with std::istringstream inline. Specifically: cannot bind non-const lvalue reference of type ‘std::istream& {aka std::basic_istream<char>&}’ to an rvalue of type ‘std::basic_istream<char>’
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.
Ah, we would need to add a delegating constructor with an rvalue reference:
class MetadataAgentConfiguration {
...
MetadataAgentConfiguration(std::istream&& input)
: MetadataAgentConfiguration(input) {}
...
};because a temporary std::istringstream is an rvalue reference, and we need to explicitly convert it to an lvalue.
test/configuration_unittest.cc
Outdated
| "MetadataReporterPurgeDeleted: true\n" | ||
| "MetadataReporterUserAgent: \"foobar/foobaz\"\n" | ||
| ); | ||
| MetadataAgentConfiguration config(stream); |
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 would just inline the stream constructor, i.e.:
MetadataAgentConfiguration config(std::istringstream(
"ProjectId: TestProjectId\n"
"MetadataApiNumThreads: 13\n"
"MetadataReporterPurgeDeleted: true\n"
"MetadataReporterUserAgent: \"foobar/foobaz\"\n"
));Also below.
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.
As above
src/configuration.h
Outdated
|
|
||
| private: | ||
| friend class MetadataAgentConfigurationTest; | ||
| friend int ::main(int ac, char** av); |
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 on why it's a friend. You also shouldn't need the parameter names for a friend declaration. So, e.g.:
friend int ::main(int, char**); // Calls ParseArguments.WDYT?
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.
Agreed, changed.
src/configuration.h
Outdated
| #include <string> | ||
|
|
||
| namespace google { | ||
| int main(int ac, char** av); |
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.
Yeah, you're right, it needs to know about the main in the global namespace.
src/configuration.h
Outdated
| #include <mutex> | ||
| #include <string> | ||
|
|
||
| int main(int ac, char** av); |
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 remove the parameter names here as well:
int main(int, char**);|
|
||
| void ParseConfigFile(const std::string& filename); | ||
| void ParseConfiguration(std::istream& input); | ||
| void ParseConfigFile(const std::string& filename); |
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.
Might as well keep them in the original order, too. 😄
test/configuration_unittest.cc
Outdated
| VerifyDefaultConfig(); | ||
| TEST(MetadataAgentConfigurationTest, EmptyConfig) { | ||
| std::istringstream stream(""); | ||
| MetadataAgentConfiguration config(stream); |
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.
Ah, we would need to add a delegating constructor with an rvalue reference:
class MetadataAgentConfiguration {
...
MetadataAgentConfiguration(std::istream&& input)
: MetadataAgentConfiguration(input) {}
...
};because a temporary std::istringstream is an rvalue reference, and we need to explicitly convert it to an lvalue.
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 ![]()
Don't forget to squash-and-merge.
…declaration of main
f5b5926 to
c0bf4ec
Compare
test/health_checker_unittest.cc
Outdated
| MetadataAgentConfiguration config_; | ||
| }; | ||
|
|
||
| static const std::string IsolationPath(const std::string& test_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 is superfluous here.
test/health_checker_unittest.cc
Outdated
| MetadataAgentConfiguration config_; | ||
| }; | ||
|
|
||
| static const std::string IsolationPath(const std::string& test_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.
IsolationPathConfig
test/health_checker_unittest.cc
Outdated
| MetadataAgentConfiguration config_; | ||
| }; | ||
|
|
||
| static const std::string IsolationPath(const std::string& test_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 not just have this return std::istringstream?
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.
+1
| // within the function and the program should exit with a success exit code. | ||
| int ParseArguments(int ac, char** av); | ||
| MetadataAgentConfiguration(std::istream& input); | ||
| MetadataAgentConfiguration(std::istream&& input) |
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 not sure what's happening here, why do we need two constructors?
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.
@ACEmilG added a comment with an explanation.
edd9c4e to
7d08a61
Compare
8ead5cb to
72c3d58
Compare
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.
One more.
test/health_checker_unittest.cc
Outdated
| MetadataAgentConfiguration config_; | ||
| }; | ||
|
|
||
| static std::istringstream IsolationPathConfig(const std::string& test_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.
Forgot this one. static is ok within a class, but for a free-standing function, it's preferred to have it in an anonymous namespace (style guide).
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.
static is no longer needed, right?
test/health_checker_unittest.cc
Outdated
| MetadataAgentConfiguration config_; | ||
| }; | ||
|
|
||
| static std::istringstream IsolationPathConfig(const std::string& test_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.
static is no longer needed, right?
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 ![]()
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.
LGTM
No description provided.