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

Support for publicly accessible configuration parsing to support testing#88

Merged
ACEmilG merged 10 commits intomasterfrom
ACEmilG-configuration-parsing
Mar 27, 2018
Merged

Support for publicly accessible configuration parsing to support testing#88
ACEmilG merged 10 commits intomasterfrom
ACEmilG-configuration-parsing

Conversation

@ACEmilG
Copy link
Copy Markdown
Contributor

@ACEmilG ACEmilG commented Mar 23, 2018

No description provided.

Comment thread test/configuration_unittest.cc Outdated
EXPECT_EQ("", config.InstanceId());
EXPECT_EQ("", config.InstanceZone());
}
static void ParseConfiguration(MetadataAgentConfiguration& config, const std::string& input) {
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor

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.

Comment thread 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);
Copy link
Copy Markdown
Contributor

@igorpeshansky igorpeshansky Mar 23, 2018

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"
  ));

?

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

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)?

Copy link
Copy Markdown
Contributor Author

@ACEmilG ACEmilG Mar 26, 2018

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.

Copy link
Copy Markdown
Contributor Author

@ACEmilG ACEmilG Mar 26, 2018

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.

Copy link
Copy Markdown
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 stylistic things.

Comment thread src/configuration.h Outdated
#include <string>

namespace google {
int main(int ac, char** av);
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

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 ‘::’

Copy link
Copy Markdown
Contributor

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.

Comment thread src/configuration.h
namespace google {
int main(int ac, char** av);

namespace google {
Copy link
Copy Markdown
Contributor

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.

Comment thread src/configuration.h Outdated

void ParseConfigFile(const std::string& filename);
void ParseConfiguration(std::istream& input);
void ParseConfigurationStream(std::istream& input);
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure, reverted.

Comment thread test/configuration_unittest.cc Outdated
EXPECT_EQ("limit=30", config.DockerContainerFilter());
EXPECT_EQ(0, config.KubernetesUpdaterIntervalSeconds());
EXPECT_EQ("https://kubernetes.default.svc",
config.KubernetesEndpointHost());
Copy link
Copy Markdown
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
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment thread test/configuration_unittest.cc Outdated
EXPECT_EQ("", config.InstanceResourceType());
EXPECT_EQ(60, config.DockerUpdaterIntervalSeconds());
EXPECT_EQ("unix://%2Fvar%2Frun%2Fdocker.sock/",
config.DockerEndpointHost());
Copy link
Copy Markdown
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
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment thread test/configuration_unittest.cc Outdated
VerifyDefaultConfig();
TEST(MetadataAgentConfigurationTest, EmptyConfig) {
std::istringstream stream("");
MetadataAgentConfiguration config(stream);
Copy link
Copy Markdown
Contributor

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(""));

Copy link
Copy Markdown
Contributor Author

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>’

Copy link
Copy Markdown
Contributor

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.

Comment thread test/configuration_unittest.cc Outdated
"MetadataReporterPurgeDeleted: true\n"
"MetadataReporterUserAgent: \"foobar/foobaz\"\n"
);
MetadataAgentConfiguration config(stream);
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As above

Comment thread src/configuration.h Outdated

private:
friend class MetadataAgentConfigurationTest;
friend int ::main(int ac, char** av);
Copy link
Copy Markdown
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 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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, changed.

Comment thread src/configuration.h Outdated
#include <string>

namespace google {
int main(int ac, char** av);
Copy link
Copy Markdown
Contributor

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.

Comment thread src/configuration.h Outdated
#include <mutex>
#include <string>

int main(int ac, char** av);
Copy link
Copy Markdown
Contributor

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**);

Comment thread src/configuration.h

void ParseConfigFile(const std::string& filename);
void ParseConfiguration(std::istream& input);
void ParseConfigFile(const std::string& filename);
Copy link
Copy Markdown
Contributor

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. 😄

Comment thread test/configuration_unittest.cc Outdated
VerifyDefaultConfig();
TEST(MetadataAgentConfigurationTest, EmptyConfig) {
std::istringstream stream("");
MetadataAgentConfiguration config(stream);
Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
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:

Don't forget to squash-and-merge.

@ACEmilG ACEmilG force-pushed the ACEmilG-configuration-parsing branch from f5b5926 to c0bf4ec Compare March 27, 2018 17:27
Comment thread test/health_checker_unittest.cc Outdated
MetadataAgentConfiguration config_;
};

static const std::string IsolationPath(const std::string& test_name) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

const is superfluous here.

Comment thread test/health_checker_unittest.cc Outdated
MetadataAgentConfiguration config_;
};

static const std::string IsolationPath(const std::string& test_name) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IsolationPathConfig

Comment thread test/health_checker_unittest.cc Outdated
MetadataAgentConfiguration config_;
};

static const std::string IsolationPath(const std::string& test_name) {
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1

Comment thread src/configuration.h
// 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)
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor

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.

@ACEmilG ACEmilG force-pushed the ACEmilG-configuration-parsing branch from edd9c4e to 7d08a61 Compare March 27, 2018 21:42
@ACEmilG ACEmilG force-pushed the ACEmilG-configuration-parsing branch from 8ead5cb to 72c3d58 Compare March 27, 2018 22:17
Copy link
Copy Markdown
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.

One more.

Comment thread test/health_checker_unittest.cc Outdated
MetadataAgentConfiguration config_;
};

static std::istringstream IsolationPathConfig(const std::string& test_name) {
Copy link
Copy Markdown
Contributor

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).

Copy link
Copy Markdown
Contributor

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?

Comment thread test/health_checker_unittest.cc Outdated
MetadataAgentConfiguration config_;
};

static std::istringstream IsolationPathConfig(const std::string& test_name) {
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
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:

Copy link
Copy Markdown
Contributor

@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.

LGTM

@ACEmilG ACEmilG merged commit ad9500c into master Mar 27, 2018
@ACEmilG ACEmilG deleted the ACEmilG-configuration-parsing branch March 27, 2018 23:02
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