Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rabbitmq_config resource #1639

Merged
merged 5 commits into from
Apr 12, 2017
Merged

add rabbitmq_config resource #1639

merged 5 commits into from
Apr 12, 2017

Conversation

arlimus
Copy link
Contributor

@arlimus arlimus commented Apr 7, 2017

rabbitmq_config

This PR introduces the rabbitmq_config resource. It parses the full configuration file and represents it internally as a hash that can be accessed for InSpec tests.

It may require a bit more alignment with other resources and some optimization. (also recognizing more shared patterns)

implications

There is 2 large implications with this move:

  1. Adding a complex parser
  2. Adding the parslet gem as a dependency

1. Adding a complex parser to the core may require more effort down the line. I'm happy with the way it turned out in the end, but I'm aware that the underlying mechanism still carries a number of corner-cases we must be mindful of. They may only apply to very few use-cases (or put differently: all config files I ever used have been tested well), but it may bite us. The best example of this was the inclusion of parsing bit-streams (took 50% of the parser code)

2. Adding parslet a 78k ruby lib without any additional dependencies (which we already have in our dependency tree! see edit below). The implication is to have this library available for other rather complex config files, including Nginx, Apache, HCL, and custom configs.

I investigated treetop, parslet, and citrus, all of which are great for this task. Of those, parslet is actively maintained with recent contributions a large community. It also functions fully in Ruby without creating dynamic classes.

This may become a great foundation for other parsers down the line. What do you think?

edit: Apparently TOML already requires parslet! The dependency is rather outdated (3years+). I went down a few versions to prevent conflicts and might take a stab at the toml lib.

@arlimus arlimus force-pushed the dr/rabbitmq_config branch 2 times, most recently from dfc925f to 52a6b0d Compare April 7, 2017 17:58
Copy link
Contributor

@chris-rock chris-rock left a comment

Choose a reason for hiding this comment

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

Thank you @arlimus This is awesome. I added a note regarding Windows support, since the configuration is also available on Windows machines

name 'rabbitmq_config'
desc 'Use the rabbitmq_config InSpec resource to test configuration data '\
'for the RabbitMQ service located in /etc/rabbitmq/rabbitmq.config on '\
'Linux and UNIX platforms.'
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason why Windows rabitmq configurations cannot be parsed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Not at all actually, just haven't implemented default paths for Windows yet.

'for the RabbitMQ service located in /etc/rabbitmq/rabbitmq.config on '\
'Linux and UNIX platforms.'
example "
describe rabbitmq_config.params('rabbit', 'ssl_listeners') do
Copy link
Contributor

Choose a reason for hiding this comment

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

I like params to be established as a pattern to retrieve all values for all configuration files

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes please 😁

"

def initialize(conf_path = nil)
@conf_path = conf_path || '/etc/rabbitmq/rabbitmq.config'
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we going to supports the default per OS? See https://www.rabbitmq.com/configure.html#configuration-file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great idea, let's add that in the next PR?

Copy link
Contributor

@adamleff adamleff left a comment

Choose a reason for hiding this comment

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

This is a heck of a change. Nothing I have is a blocker but it would probably good to add some more detail to the #to_s method as described. Otherwise, I say we ship it and see what we uncover in the field.

end

def to_s
'RabbitMQ Configuration'
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we include @conf_path here so it's clear in the output what config file we read from? Thinking about the use case where a user is reading multiple RabbitMQ config files. We do this pattern with mysql_conf, etc.

Copy link
Contributor Author

@arlimus arlimus Apr 12, 2017

Choose a reason for hiding this comment

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

Will do, thank you! (curious, I didn't see it in mysql_conf.rb, but it is used in pip, x509 etc)

private

def read_content
return @content if defined?(@content)
Copy link
Contributor

Choose a reason for hiding this comment

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

I've been bitten enough times by defined? not returning what I expect it to that I usually follow the pattern of return @content unless @content.nil? in these situations. Not a blocker, just something to consider...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I started to use this convention initially was to avoid queries to the system for files that might be nil. It typically happens with missing permissions, permission the file read may be attempted a large number of times. It is only meant as an optimization.

That said, this part as well as the pieces below really need to be abstracted away, since these steps are used frequently.

end

@content = file.content
if @content.empty? && !file.empty?
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious what situation would cause one of these to be true while the other is false...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, I copied this from another resource and will remove it.
We had this case in the past, when files would return an empty string due to missing permissions (and in line with serverspec behavior) on some transport connections. We seem to have unified this in train now, so it is not needed anymore. Thank you!

return @params = {} if read_content.nil?
lex = ErlangParser.new.parse(read_content)
tree = ErlangTransform.new.apply(lex)
@params = turn_to_hash(tree)
Copy link
Contributor

Choose a reason for hiding this comment

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

I imagine this is going to be a pretty common need... should we instead make a #to_h method inside the ErlangTransform class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea, we might need a small class in between. That hash-conversion only works with a Array-Tuple combination, which is typical for RabbitMQ files. If we use this in other places the syntax might be very different. I'll add it to another class that abstracts the parser and transformer away

Signed-off-by: Dominik Richter <dominik.richter@gmail.com>
@adamleff
Copy link
Contributor

Looks good to me, @arlimus! If @chris-rock is good with it, I'm good. Let's just squash down to a single commit (which we can do via GitHub squash-and-merge, you don't actually have to do it... and thank you for adding incremental commits, makes it SO much easier to review).

Copy link
Contributor

@chris-rock chris-rock left a comment

Choose a reason for hiding this comment

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

Cool @arlimus Lets add Windows support in another PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants