-
Notifications
You must be signed in to change notification settings - Fork 73
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 usage report command #851
Conversation
scenario = run_scenario(Scenarios::Report::Generate.new({}, [:reports])).first | ||
|
||
# description can be used too | ||
report_data = scenario.steps.map(&:data).compact.reduce(&:merge).transform_keys(&:to_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.
does that take all reports, their .data
and merge those hashes? what if two reports accidentally define the same hash key?
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.
Yes. I think that's very unlikely as the keys are very descriptive. Is it worth printing a warning to STDERR? Or perhaps better, having a some sort of a test that check for all keys in all checks? That may be a bit harder though as some of them are generated dynamically. Perhaps each check could have some explicit prefix that could that be verified, but that's likely schema v2 thing.
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.
What if we add an add_data(key, value)
method to Report
, and it does something like
data[self.class.name+'_'+key] = value
I wouldn't block on this, but I think it's a potential source for hard to debug issues once a "not so descriptive" name leaks into the set.
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.
@adamruzicka @ShimShtein 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.
It would also save the transform_keys(to_s)
here, which makes the code more readable?
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 leave this for v2. While we're at it, we should maybe shuffle things around a bit, the report classes are currently named somewhat arbitrarily
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 you keep something big like this I would find it more readable to split it across lines:
report_data = scenario.
steps.
map(&:data).
compact.
reduce(&:merge).
transform_keys(&:to_s)
definitions/reports/inventory.rb
Outdated
feature(:foreman_database). | ||
query("select type, count(*) from hosts group by type"). |
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 Report
should have a query
itself, no need to call feature()
all the time
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.
nope, undefined method #query
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 said "should", not "does" :)
As in: we should implement a Report.query
and use that, instead of calling feature(:db).query
all the time ;)
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 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.
@ares did you plan to address this, given Adam has a patch for it?
This extends the check so it can hold any arbitrary data. That is then used in the generate report command, that finds all checks tagged with report tag, executes them and gather their data for the final YAML creation. This commit also includes two examples of metrics that uses database as the Data source.
With this new child class, report checks can share helpers easily. Also the parent Check class, which is used elsewhere, does not need to be extended by the data storage.
Earlier, all Reports were also considered Checks, so they showed up in health checks. This change now fully isolates Reports and Checks while all the code is shared.
68df50d
to
88dde7d
Compare
I address most of the comments, rebased the PR and added one more report definition around RBAC |
* Add MVP reports * Remove unused report
Can we get some basic tests for the new |
definitions/reports/rbac.rb
Outdated
# How many custom roles did you create and assigned to users? | ||
# rubocop:disable Layout/LineLength | ||
# rubocop:disable Metrics/MethodLength | ||
# rubocop:disable Metrics/AbcSize |
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.
Break this method up instead of disabling rubocop. This could also help with the comments listed in these files. You can use ruby to help, e.g.
def number_of_users
def number_of_non_admin_users
def number_of_custom_roles
end | ||
|
||
def run | ||
raise NotImplementedError |
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.
Fun fact I learned and stuck with me -- https://kirillshevch.medium.com/misusing-of-notimplementederror-in-ruby-531dd866f461
Yes this code base already does it, but sharing is caring.
All comments should be addressed atm, could you please re-review/merge? |
Are the remaining open points (method length in rbac.rb, NotImplementedError in report.rb, Report#query method and tests) planned to be addressed? |
I filed #976 to track all of those. Could we accept this PR as is and resolve those in additional PRs so that we can save the additional round-trip through @ares's fork? |
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 fine with this a a first round as suggested by Adam
count = sql_count("cached_user_roles WHERE cached_user_roles.role_id IN (#{role_ids.join(',')})") | ||
result["assigned_custom_roles_count"] = count |
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 returns an empty string and not 0 if there are no custom roles
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.
Apparently, the query we generate isn't valid if there are no custom roles. Good catch
> query
=> " SELECT COUNT(*) AS COUNT FROM cached_user_roles WHERE cached_user_roles.role_id IN ()"
> feature(:foreman_database).query(query)
CSV::MalformedCSVError: Illegal quoting in line 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.
Yeah, IN ()
is not valid:
foreman=# SELECT COUNT(*) AS COUNT FROM cached_user_roles WHERE cached_user_roles.role_id IN ();
ERROR: syntax error at or near ")"
LINE 1: ...ROM cached_user_roles WHERE cached_user_roles.role_id IN ();
^
end | ||
end | ||
|
||
def settings_fields |
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 settings (like db_pending_seed
, instance_id
and bcrypt_cost
) are always modified -- is it worth logging them here or should they be filtered?
scenario = run_scenario(Scenarios::Report::Generate.new({}, [:reports])).first | ||
|
||
# description can be used too | ||
report_data = scenario.steps.map(&:data).compact.reduce(&:merge).transform_keys(&:to_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.
This produces quite a lot of output, which you probably don't want in the case where you run this via cron (as otherwise you'll get a mail from cron on every invocation. You could add --cron
or --silent
to suppress these?
This is the PoC of generating yaml representing the usage of the instance. Test wit
Currently, the command searches for all ReportChecks, executes them and collects the data from them. Then it prints out the resulting YAML to stdout.