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
Fixes #19314 - Notification for subscr. expiring soon #6752
Conversation
class ExpireSoon | ||
class << self | ||
def deliver! | ||
binding.pry |
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.
Remove debugger entry point binding.pry.
class ExpireSoon | ||
class << self | ||
def deliver! | ||
binding.pry |
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.
Remove debugger entry point binding.pry.
@@ -0,0 +1,39 @@ | |||
module Katello |
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.
file does not belongs to this pr.
:subject => subscription, | ||
:initiator => User.anonymous_admin, | ||
:audience => Notification::AUDIENCE_ADMIN, | ||
:message => ::UINotifications::StringParser.new( |
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 have the expiry date of the subscription, maybe we should use it for the notification expiry date too?
class << self | ||
def deliver! | ||
subs_expiring_soon.each do |subscription| | ||
next if notification_already_exists?(subscription) |
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.
see comments on #6750 around these lines.
::Notification.create!( | ||
:subject => subscription, | ||
:initiator => User.anonymous_admin, | ||
:audience => Notification::AUDIENCE_ADMIN, |
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.
IMHO this should be the taxonomy users/admins?
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 agree, but how do you set this? AUDIENCE_TAXONOMY
is subject.user_ids
- if subject
is the subscription, it will not work. The subject
can't be the taxonomy
|
||
def subs_expiring_soon | ||
result = [] | ||
Katello::Subscription.unscoped.all.each do |subscription| |
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.
is there no better query than go over all? at least I would use select instead of the next statement below.
{ | ||
group: _('Proxies'), | ||
name: 'pulp_low_disk_space', | ||
message: _("%{subject}'s disk is %{percentage} full. Since this proxy is running Pulp, it needs disk space to publish content views. Please ensure the disk does not get full."), |
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.
not related to this pr.
@@ -0,0 +1,13 @@ | |||
namespace :proxy_disk_space do | |||
desc <<-END_DESC | |||
Check the proxy disk space based on Pulp API results and create notifications if |
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.
ditto
@@ -0,0 +1,13 @@ | |||
namespace :subscriptions do | |||
desc <<-END_DESC | |||
Check the proxy disk space based on Pulp API results and create notifications if |
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.
wrong description titles etc.
Updated so that:
cc @ohadlevy thanks for taking a look before |
I'm getting an error when running the rake task, i think because Subscription is not taxable:
|
@dLobatog any update here? |
lib/katello/scheduled_jobs.rb
Outdated
|
||
# Only create notifications if there isn't a scheduled job | ||
SendExpireSoonNotifications.perform_later if scheduled_job.blank? | ||
|
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 trailing blank lines detected.
|
||
def notification_already_exists?(subject) | ||
subs_expiration_notification = Notification.unscoped.find_by(:subject => subject) | ||
return false unless subs_expiration_notification.present? |
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.
Use if subs_expiration_notification.blank? instead of unless subs_expiration_notification.present?.
self.class.set(:wait => 12.hours).perform_later | ||
end | ||
|
||
def perform |
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.
Inconsistent indentation detected.
@@ -0,0 +1,9 @@ | |||
class SendExpireSoonNotifications < ActiveJob::Base | |||
after_perform do |
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.
Use 2 (not 3) spaces for indentation.
@@ -0,0 +1,9 @@ | |||
class SendExpireSoonNotifications < ActiveJob::Base |
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.
Jobs should subclass ApplicationJob.
d9f7977
to
51af787
Compare
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.
Updated, @jlsherrill @akofink , the job is scheduled every 12 hours with ActiveJob now
it seems rubocop is failing, could someone with permissions set WoC? since we don't have the output in jenkins anymore, I'm retriggering tests [test] |
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.
Rebased, @ares I think the only failure from hound is that "Jobs should subclass ApplicationJob." - I do agree (it's a convention, not really necessary) but I introduce that class in https://github.com/theforeman/foreman/pull/4240/files in Foreman itself, so at this moment it isn't available here to inherit, and I don't think it's a good idea to have a ApplicationJob class namespaced to be exclusive to Katello.
Best course would be to use ApplicationJob in here when the core PR is merged, but it shouldn't block this one.
this needs rebase again, ActiveJob is now in core too, but maybe it's better to wait until #6750 gets in |
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, it would be great if someone from katello could take a look |
::Notification.create!( | ||
:subject => subscription, | ||
:initiator => User.anonymous_admin, | ||
:audience => Notification::AUDIENCE_TAXONOMY, |
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 this require some update to foreman? I'm getting an error that this constant isn't defined:
NameError: uninitialized constant Notification::AUDIENCE_TAXONOMY
from /home/vagrant/git/foreman_hooks/lib/foreman_hooks/as_dependencies_hook.rb:4:in `load_missing_constant'
from /home/vagrant/git/katello/app/services/katello/ui_notifications/subscriptions/expire_soon.rb:13:in `block in deliver!'
from /home/vagrant/git/katello/app/services/katello/ui_notifications/subscriptions/expire_soon.rb:7:in `each'
from /home/vagrant/git/katello/app/services/katello/ui_notifications/subscriptions/expire_soon.rb:7:in `deliver!'
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, there's no such audience, I guess we need to find administrators of a given org for which there's no good option atm, @jlsherrill can we find a user who uploaded the manifest for the organization somehow?
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.
We don't track who has uploaded or refreshed a manifest. I would suggest notifying anyone who has access to do that for the given org via the 'import_manifest' permission
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.
Note to self, I'll make a method notification_recipient_ids
on the Subscription object, which contains all of the users with import_manifest
permissions. Changing the audience to Notification::AUDIENCE_SUBJECT
will also be required.
I think it's not easy to find easily such recipients. There are no relations like this, especially if you consider filtered permissions, where the assignment might be scoped e.g. with "name ~ my". Perhaps we could only search for user who don't have such permission filtered. |
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 I've updated this to use the same permission checking as we do on the API to upload a manifest (e.g: if you can upload a manifest to this organization, you get the notification).
I do realize it's a relatively expensive operation, but I don't see any other way of dealing with it. The task ought to run only twice a day though, so I hope that is not a problem. Another advantage is that by leveraging allowed_to?
vs writing complicated permissions logic here, if the permissions model change in Foreman core & that method changes, we're covered and this should work - which wouldn't if I write custom logic here.
@dLobatog I don't think you need to write custom logic, it's fine to ignore filtering conditions, but I believe it should call |
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 I could use can?
but as you say it would mean ignoring specific permissions 'search' scope. With .allowed_to?
, I'm able to pass the specific object I want to check the permission for, like:
user.allowed_to?(
:controller => 'katello/api/v2/subscriptions',
:action => 'import',
:organization_id => organization_id
)
So it's more precise, I can know for sure the users who should be able to see the notification.
@dLobatog the allowed_to example that you used only verified that user has permission to access "katello/api/v2/subscriptions/import" route based on mapping in lib/katello/permissions/. The fact you pass |
group: N_('Subscriptions'), | ||
name: 'subs_expire_soon', | ||
message: N_('%{expiring_subs} subscriptions are going to expire soon in %{subject}. Please renew them before they expire to guarantee your hosts will continue receiving content.'), | ||
level: 'warning', |
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.
Avoid comma after the last item of a hash.
end | ||
|
||
|
||
def actions |
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.
Use empty lines between method definitions.
) | ||
end | ||
|
||
|
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.
Extra blank line detected.
def notification_already_exists?(subject) | ||
subs_expiration_notification = Notification.unscoped.find_by(:subject => subject) | ||
return false if subs_expiration_notification.blank? | ||
subs_expiration_notification.notification_blueprint == blueprint |
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.
Operator == used in void context.
group: N_('Subscriptions'), | ||
name: 'subs_expire_soon', | ||
message: N_('%{expiring_subs} subscriptions are going to expire soon in %{subject}. Please renew them before they expire to guarantee your hosts will continue receiving content.'), | ||
level: 'warning', |
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.
Avoid comma after the last item of a hash.
end | ||
|
||
|
||
def actions |
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.
Use empty lines between method definitions.
) | ||
end | ||
|
||
|
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.
Extra blank line detected.
def notification_already_exists?(subject) | ||
subs_expiration_notification = Notification.unscoped.find_by(:subject => subject) | ||
return false if subs_expiration_notification.blank? | ||
subs_expiration_notification.notification_blueprint == blueprint |
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.
Operator == used in void context.
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.
@jlsherrill @beav Updated, now it creates a notification per organization. It shows the # of subs about to expire in the notification, which links to the search page for that.
Unfortunately for admin users, this will mean that the following case may happen:
- admin is in org A, B, C
- admin chooses in the UI to be in org A. By design the /subscriptions page forces you to choose.
- admin gets notifications about all 3 organizations with subs about to expire.
- admin clicks on the link for the subs about to expire in A - it works well.
- admin clicks on the link for the subs about to expire in B - since admin is in org A in the UI, it will not show the correct subs. admin needs to change to org B in the UI, then click on the notification.
I hope it's not a dealbreaker, as it's an unlikely case, and moving the user away from the current organization by clicking on the notification seems like an anti-pattern.
[test katello] |
1 similar comment
[test katello] |
@dLobatog , Does this PR helps to show an expiration notification before 120 days? Does the notification depend upon the place where the foreman is being accessed (e.g for me, its IST time) ? |
@jyejare Yes, you may change the 120 to any other value in
|
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 @jyejare @jlsherrill @beav
I've updated the notification to include the number of days. I can't be more specific as I'm creating a notification per organization. This means there are multiple subscriptions there, which may vary in their expiring date, so I can't just say 'will expire in X days' as I did before with the 'per-subscription' notification.
To test this:
rake db:seed
Then on rake console
:
class Organization
def expiring_subscriptions
subscriptions
end
end
Organization.all.each { |o| Notification.where(:subject => o).destroy_all }
Katello::UINotifications::Subscriptions::ExpireSoon.deliver!
def notification_recipients_ids | ||
User.unscoped.all.find_all do |user| | ||
user.can?(:import_manifest, self) | ||
end.pluck(:id) |
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.
Avoid chaining a method call on a do...end block.
[test katello] |
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.
The only tests failing are React ones, seems that the snapshot needs to be updated for ListView, can't be caused by this one, probably theforeman/foreman@e26f63d
I was missing - https://github.com/Katello/katello/pull/7228/files on my branch, rebasing & pushing again.. |
e72d549
to
343e7b8
Compare
The notifications drawer should show a warning when some subscription is about to expire. "expiring_soon?" defined in the subscriptions.rb code says this is 120 days before it expires.
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.
@jyejare Thanks for the feedback, it definitely did NOT work for org admins, there was a bug in the notification_recipients_ids
method.
You also asked to configure the expire soon interval, so thanks for that too 😄 Can you check now? It should work 100% fine now.
|
||
def notification_recipients_ids | ||
users = User.unscoped.all.find_all do |user| | ||
user.can?(:import_manifest) && user.can?(:view_organizations, self) |
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.
@jyejare The problem I had was that previously I was calling user.can?(:import_manifest, self) here. This would not work, because import_manifest is a permission for Subscription, not for Organization. Hence, only admins would return 'true' for that.
tl;dr - It should be fine now for org admins
Tests Performed:
QE ACK. |
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.
Thanks @dLobatog !
Depends on #6750 and https://github.com/theforeman/foreman/pull/4240/files (this https://github.com/theforeman/foreman/pull/4240/files#diff-2190144d88f9376ea36f321f8f5af34dR71 allows to set actions with more freedom than the current structure).