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

Conversation

@ACEmilG
Copy link
Contributor

@ACEmilG ACEmilG commented Mar 23, 2018

No description provided.

EXPECT_EQ("", config.InstanceId());
EXPECT_EQ("", config.InstanceZone());
}
static void ParseConfiguration(MetadataAgentConfiguration& config, const std::string& input) {
Copy link
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
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.

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

#include <string>

namespace google {
int main(int ac, char** av);
Copy link
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
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
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.

namespace google {
int main(int ac, char** av);

namespace google {
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 keep the blank line between the namespace and the class.


void ParseConfigFile(const std::string& filename);
void ParseConfiguration(std::istream& input);
void ParseConfigurationStream(std::istream& input);
Copy link
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
Contributor Author

Choose a reason for hiding this comment

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

Sure, reverted.

EXPECT_EQ("limit=30", config.DockerContainerFilter());
EXPECT_EQ(0, config.KubernetesUpdaterIntervalSeconds());
EXPECT_EQ("https://kubernetes.default.svc",
config.KubernetesEndpointHost());
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.

Fixed

EXPECT_EQ("", config.InstanceResourceType());
EXPECT_EQ(60, config.DockerUpdaterIntervalSeconds());
EXPECT_EQ("unix://%2Fvar%2Frun%2Fdocker.sock/",
config.DockerEndpointHost());
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.

Fixed

VerifyDefaultConfig();
TEST(MetadataAgentConfigurationTest, EmptyConfig) {
std::istringstream stream("");
MetadataAgentConfiguration config(stream);
Copy link
Contributor

Choose a reason for hiding this comment

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

I would just inline the stream constructor, i.e.:

  MetadataAgentConfiguration config(std::istringstream(""));

Copy link
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
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.

"MetadataReporterPurgeDeleted: true\n"
"MetadataReporterUserAgent: \"foobar/foobaz\"\n"
);
MetadataAgentConfiguration config(stream);
Copy link
Contributor

Choose a reason for hiding this comment

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

I would 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
Contributor Author

Choose a reason for hiding this comment

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

As above


private:
friend class MetadataAgentConfigurationTest;
friend int ::main(int ac, char** av);
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 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
Contributor Author

Choose a reason for hiding this comment

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

Agreed, changed.

#include <string>

namespace google {
int main(int ac, char** av);
Copy link
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.

#include <mutex>
#include <string>

int main(int ac, char** av);
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 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);
Copy link
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. 😄

VerifyDefaultConfig();
TEST(MetadataAgentConfigurationTest, EmptyConfig) {
std::istringstream stream("");
MetadataAgentConfiguration config(stream);
Copy link
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
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
MetadataAgentConfiguration config_;
};

static const std::string IsolationPath(const std::string& test_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 is superfluous here.

MetadataAgentConfiguration config_;
};

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

Choose a reason for hiding this comment

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

IsolationPathConfig

MetadataAgentConfiguration config_;
};

static const std::string IsolationPath(const std::string& test_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 not just have this return std::istringstream?

Copy link
Contributor

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)
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 not sure what's happening here, why do we need two constructors?

Copy link
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
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.

MetadataAgentConfiguration config_;
};

static std::istringstream IsolationPathConfig(const std::string& test_name) {
Copy link
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
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?

MetadataAgentConfiguration config_;
};

static std::istringstream IsolationPathConfig(const std::string& test_name) {
Copy link
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
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
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