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

fixes #7666, #7667, #7668 - errata mail notifications #4780

Merged
merged 3 commits into from Dec 8, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 47 additions & 0 deletions app/helpers/katello/errata_mailer_helper.rb
@@ -0,0 +1,47 @@
#
# Copyright 2014 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public
# License as published by the Free Software Foundation; either version
# 2 of the License (GPLv2) or (at your option) any later version.
# There is NO WARRANTY for this software, express or implied,
# including the implied warranties of MERCHANTABILITY,
# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
# have received a copy of GPLv2 along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.

module Katello
module ErrataMailerHelper
def content_host_errata_path(content_host)
uuid = Katello::System.find(content_host).uuid
"#{Setting[:foreman_url]}/content_hosts/#{uuid}/errata"
end

def content_view_environment_errata_path(content_view, environment)
version = Katello::ContentViewEnvironment.find_by_content_view_id_and_environment_id(content_view.id, environment.id).content_view_version_id
"#{Setting[:foreman_url]}/content_views/#{content_view.id}/versions/#{version}/errata"
end

def content_view_path(content_view)
"#{Setting[:foreman_url]}/content_views/#{content_view.id}/versions"
end

def erratum_path(erratum)
"#{Setting[:foreman_url]}/errata/#{erratum.uuid}/info"
end

def errata_count(host, errata_type)
available = host.available_errata.send(errata_type.to_sym).count
applicable = host.applicable_errata.send(errata_type.to_sym).count - available
Copy link
Member

Choose a reason for hiding this comment

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

Why reduce the applicable count by the available count?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It gives the user a better idea of what isn't available. Currently, if all errata are in the current environment, then it would look like this in the e-mail

Security: 5 (5)
Bugfix: 5 (5)
Enhancement: 5 (5)

I think it's more useful to know that I'm current and there's no action to take.

Copy link
Member

Choose a reason for hiding this comment

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

I must still be missing something. If I have 5 available errata, and 2 of those are applicable, won't I get:

2 - 5 = -3 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You would not have a situation like that: available is a subset of applicable.

The report shows available as the first number, and the second as applicable. So as an administrator, I get an e-mail that tells me my host has 3 available errata -- that I can apply yum update right now and get.

The second number is a heads up there's new content in the Library coming up the pipeline to my host, and I may want to check those out -- especially if it's security. It's not useful if that number includes available too.

"#{available} (#{applicable})"
end

def format_summary(summary)
summary.blank? ? summary : summary.gsub(/\n\n/, '<p>').gsub(/\n/, ' ').html_safe
end

def host_count(hosts, errata_type)
hosts.to_a.count { |host| host.available_errata.send(errata_type.to_sym).any? }
end
end
Copy link
Member

Choose a reason for hiding this comment

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

nitpick empty line at the bottom since you have one at the top.

Copy link
Contributor

Choose a reason for hiding this comment

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

👎

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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 a fan of putting a blank line before an end. Most rubyists don't do this (eg see rails).

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 didn't realize that was a thing...

I think I always put a space at the top, after the namespace(s), but never at the bottom.

Copy link
Contributor

Choose a reason for hiding this comment

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

The two styles I see most often are no blank lines at the top/bottom (rails, sinatra, etc) or a blank line at the top but not at the bottom. Rails use to include a mixture of both but it looks like they've since cleaned up their code by going with the former.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well looking around the Katello code, it's not consistent at all - so if we're going to hold people to a specific style here, we should enable a cop....

end
28 changes: 28 additions & 0 deletions app/lib/actions/katello/content_view/errata_mail.rb
@@ -0,0 +1,28 @@
#
# Copyright 2014 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public
# License as published by the Free Software Foundation; either version
# 2 of the License (GPLv2) or (at your option) any later version.
# There is NO WARRANTY for this software, express or implied,
# including the implied warranties of MERCHANTABILITY,
# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
# have received a copy of GPLv2 along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.

module Actions
module Katello
module ContentView
class ErrataMail < Actions::EntryAction
def plan(content_view, environment)
plan_self(:content_view => content_view.id, :environment => environment.id)
end

def run
MailNotification[:katello_promote_errata].deliver(:content_view => input[:content_view],
:environment => input[:environment])
end
end
end
end
end
2 changes: 2 additions & 0 deletions app/lib/actions/katello/content_view/promote.rb
Expand Up @@ -37,6 +37,8 @@ def plan(version, environment, is_force = false)
repos_to_delete(version, environment).each do |repo|
plan_action(Repository::Destroy, repo)
end

plan_action(ContentView::ErrataMail, version.content_view, environment)
end

plan_action(ContentView::UpdateEnvironment, version.content_view, environment)
Expand Down
1 change: 1 addition & 0 deletions app/lib/actions/katello/content_view/publish.rb
Expand Up @@ -44,6 +44,7 @@ def plan(content_view, description = "")
end

plan_action(ContentView::UpdateEnvironment, content_view, library)
plan_action(ContentView::ErrataMail, content_view, library)
plan_self(history_id: history.id, content_view_id: content_view.id,
environment_id: library.id, user_id: ::User.current.id)
end
Expand Down
33 changes: 33 additions & 0 deletions app/lib/actions/katello/repository/errata_mail.rb
@@ -0,0 +1,33 @@
#
# Copyright 2014 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public
# License as published by the Free Software Foundation; either version
# 2 of the License (GPLv2) or (at your option) any later version.
# There is NO WARRANTY for this software, express or implied,
# including the implied warranties of MERCHANTABILITY,
# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
# have received a copy of GPLv2 along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.

module Actions
module Katello
module Repository
class ErrataMail < Actions::EntryAction
def plan(repo)
last_updated = repo.repository_errata.order('updated_at ASC').last.try(:updated_at) || Time.now
plan_self(:repo => repo.id, :last_updated => last_updated.to_s)
end

def run
::User.current = ::User.anonymous_admin
MailNotification[:katello_sync_errata].deliver(:repo => input[:repo], :last_updated => input[:last_updated])
end

def finalize
::User.current = nil
end
end
end
end
end
1 change: 1 addition & 0 deletions app/lib/actions/katello/repository/sync.rb
Expand Up @@ -43,6 +43,7 @@ def plan(repo, pulp_sync_task_id = nil)
plan_action(Katello::Foreman::ContentUpdate, repo.environment, repo.content_view)
plan_action(Katello::Repository::CorrectChecksum, repo)
plan_action(Katello::Repository::UpdateMedia, repo)
plan_action(Katello::Repository::ErrataMail, repo)
end
plan_self(:id => repo.id, :sync_result => sync_task.output)
plan_action(Pulp::Repository::RegenerateApplicability, :pulp_id => repo.pulp_id)
Expand Down
73 changes: 73 additions & 0 deletions app/mailers/katello/errata_mailer.rb
@@ -0,0 +1,73 @@
#
# Copyright 2014 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public
# License as published by the Free Software Foundation; either version
# 2 of the License (GPLv2) or (at your option) any later version.
# There is NO WARRANTY for this software, express or implied,
# including the implied warranties of MERCHANTABILITY,
# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
# have received a copy of GPLv2 along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.

module Katello
Copy link
Member

Choose a reason for hiding this comment

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

Tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Missed this earlier, but we do tend to put copyright notices at the top of all of our files.

class ErrataMailer < ApplicationMailer
helper :'katello/errata_mailer'

def host_errata(options)
return unless (user = User.find(options[:user]))

@content_hosts = Katello::System.authorized_as(user, :view_content_hosts).reject { |host| host.applicable_errata.empty? }

set_locale_for user

mail(:to => user.mail,
:subject => _("Katello Host Advisory"),
:date => Time.zone.now)
end

def sync_errata(options)
return unless (@repo = Katello::Repository.find(options[:repo])) && (last_updated = options[:last_updated].to_datetime)

recipients = User.all.select do |user|
user.receives?(:katello_sync_errata) && user.can?(:view_products, @repo.product)
end

fail Errors::NotFound, N_("No recipients found for %s sync report") % @repo.name unless recipients.any?

all_errata = Katello::Erratum.where(:id => @repo.repository_errata.where('katello_repository_errata.updated_at > ?', last_updated).pluck(:erratum_id))

@errata_counts = errata_counts(all_errata)
@errata = all_errata.take(100).group_by(&:errata_type)

group_mail(recipients, :subject => (_("Katello Sync Summary for %s") % @repo.name))
end

def promote_errata(options)
return unless (content_view = Katello::ContentView.find(options[:content_view])) &&
(environment = Katello::KTEnvironment.find(options[:environment]))

recipients = User.all.select do |user|
user.receives?(:katello_promote_errata) && user.can?(:view_content_views, content_view)
end

fail Errors::NotFound, N_("No recipients found for %s promotion summary") % content_view.name unless recipients.any?

@content_view = content_view
@environment = environment

@content_hosts = Katello::System.where(:environment_id => environment.id, :content_view_id => content_view.id)

group_mail(recipients, :subject => (_("Katello Promotion Summary for %{content_view}") % {:content_view => content_view.name}))
end

private

def errata_counts(errata)
counts = {:total => errata.count}
counts.merge(Hash[[:security, :bugfix, :enhancement].collect do |errata_type|
[errata_type, errata.send(errata_type).count]
end])
end
end
end
29 changes: 29 additions & 0 deletions app/views/katello/errata_mailer/_erratum.html.erb
@@ -0,0 +1,29 @@
<table class="errata" style="border: 1px solid #cccccc; padding: 5px; background-color: #ffffff; border-collapse: collapse; text-align: justify; width: 100%;" bgcolor="#ffffff">
<tr>
<td width="18%" style="text-align: left; border-collapse: collapse; background-image: linear-gradient(#f5f5f5, #e8e8e8); padding: 4px; border: 1px solid #cccccc;" align="left">
<b><%= _("Errata ID") %></b>
</td>

<td width="32%" style="border-collapse: collapse; text-align: left; background-color: #FFFFFF; padding: 4px; border: 1px solid #cccccc;" align="center" bgcolor="#FFFFFF">
<%= link_to erratum.errata_id, erratum_path(erratum) %>
</td>

<td width="18%" style="text-align: left; border-collapse: collapse; background-image: linear-gradient(#f5f5f5, #e8e8e8); padding: 4px; border: 1px solid #cccccc;" align="left">
<b><%= _("Severity") %></b>
</td>

<td width="32%" style="border-collapse: collapse; text-align: left; background-color: #FFFFFF; padding: 4px; border: 1px solid #cccccc;" align="center" bgcolor="#FFFFFF">
<%= erratum.severity.blank? ? "-" : erratum.severity %>
</td>
</tr>

<tr>
<td colspan="1" style="text-align: left; border-collapse: collapse; background-image: linear-gradient(#f5f5f5, #e8e8e8); padding: 4px; border: 1px solid #cccccc;" valign="top" align="left">
<b><%= _("Summary") %></b>
</td>

<td colspan="3" style="border-collapse: collapse; text-align: left; background-color: #FFFFFF; padding: 4px; border: 1px solid #cccccc;" align="center" bgcolor="#FFFFFF">
<%= format_summary(erratum.summary) %>
</td>
</tr>
</table>
24 changes: 24 additions & 0 deletions app/views/katello/errata_mailer/_host_dashboard.html.erb
@@ -0,0 +1,24 @@
<h4><%= title %></h4>

<div align="center" style="margin: 10px 0px;">
<table class="summary" style="border: 1px solid; padding: 5px; background-color: #ffffff; border-collapse: collapse; text-align: center; width: 100%;" bgcolor="#ffffff">
<tr>
<td style="width: 33%; padding: 5px; border: 1px solid #ccc;">
<b class="counter" style="color: #6bb5df;"><%= host_count(content_hosts, :security) %></b>

<p class="summary-title" style="font-size: 80%; font-weight: bold; margin: 5px; padding: 0px;"><%= _("Security").upcase %></p>
</td>
<td style="width: 33%; padding: 5px; border: 1px solid #ccc;">
<b class="counter" style="color: #6bb5df;"><%= host_count(content_hosts, :bugfix) %></b>

<p class="summary-title" style="font-size: 80%; font-weight: bold; margin: 5px; padding: 0px;"><%= _("Bugfix").upcase %></p>
</td>
<td style="width: 33%; padding: 5px; border: 1px solid #ccc;">
<b class="counter" style="color: #6bb5df;"><%= host_count(content_hosts, :enhancement) %></b>

<p class="summary-title" style="font-size: 80%; font-weight: bold; margin: 5px; padding: 0px;"><%= _("Enhancement").upcase %></p>
</td>
</tr>
</table>
</div>

83 changes: 83 additions & 0 deletions app/views/katello/errata_mailer/host_errata.html.erb
@@ -0,0 +1,83 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #3f3f3f; background-color: #f1f1f1;">
<body>
<style type="text/css">
a, a:visited {
color: #6bb5df !important;
}

a:hover {
color: #CCCCCC !important;
}
</style>
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #3f3f3f; background-color: #f1f1f1;">
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder about all of these inline styles and how they will affect the ability to easily change the styling of these emails in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're a bit stuck there, it's the only option for some very popular web mail providers (e.g. Gmail). We're lucky they're supporting CSS at all these days.

I did break some things out as partials, but even then, the styles are in-line on those tables too.

It could all be abstracted away to helpers or something, but the nature of e-mail notifications is that I don't expect the number of e-mail types to ever become more than a handful.

Copy link
Contributor

Choose a reason for hiding this comment

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

True, I'm fine with using helpers in the future if we ever need to change these or add more. Don't want to create a premature abstraction.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah that looks nicer. At this point though, I want to get this in for the Katello 2.1 RC so will have to do it as a separate PR later.

<table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable" bgcolor="#f1f1f1">
<tr>
<td align="center" valign="top">
<table border="0" id="emailContainer" style="max-width: 600px;">
<tr>
<td align="center" valign="top">
<div align="left">
<h2 style="font-weight: normal;"><b>KATELLO</b> <%= _("Host Advisory").upcase %></h2>
</div>
<table border="0" width="100%" id="emailHeader" style="background-color: #e1e2e3;" bgcolor="#e1e2e3">
<tr>
<td align="left" valign="top" style="padding: 0px 20px;">
<%= render :partial => 'host_dashboard', :locals => {:content_hosts => @content_hosts, :title => _("Hosts with Available Errata")} %>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" valign="top">
<table border="0" width="100%" id="emailBody" style="background-color: #fff; margin-top: 10px; padding: 0px 0px 15px;" bgcolor="#fff">
<tr>
<td align="center" valign="top" style="padding: 0px 20px 5px;">

<% @content_hosts.group_by(&:organization).each do |org, hosts| %>
<h3 style="text-align: left; margin: 10px 0px 5px;" align="left"><%= org %></h3>

<p style="text-align: left; font-size: 75%; margin: 5px;" align="left">
<%= _("The following hosts have errata that apply to them: ") %>
</p>
<table class="errata" style="border: 1px solid #cccccc; padding: 5px; background-color: #ffffff; border-collapse: collapse; text-align: justify; width: 100%;" bgcolor="#ffffff">
<tr>
<th style="text-align: left; border-collapse: collapse; background-image: linear-gradient(#f5f5f5, #e8e8e8); padding: 4px; border: 1px solid #cccccc;" align="left"><%= _("Host") %></th>
<th style="border-collapse: collapse; background-image: linear-gradient(#f5f5f5, #e8e8e8); text-align: center; padding: 4px; border: 1px solid #cccccc;" align="center"><%= _("Security") %></th>
<th style="border-collapse: collapse; background-image: linear-gradient(#f5f5f5, #e8e8e8); text-align: center; padding: 4px; border: 1px solid #cccccc;" align="center"><%= _("Bugfix") %></th>
<th style="border-collapse: collapse; background-image: linear-gradient(#f5f5f5, #e8e8e8); text-align: center; padding: 4px; border: 1px solid #cccccc;" align="center"><%= _("Enhancement") %></th>
</tr>
<% hosts.each do |host| %>
<tr>
<td style="text-align: left; border-collapse: collapse; background-color: #FFFFFF; padding: 4px; border: 1px solid #cccccc;" width="48%" align="left" bgcolor="#FFFFFF"><%= link_to host, content_host_errata_path(host) %></td>
<td width="18%" style="border-collapse: collapse; text-align: center; background-color: #FFFFFF; padding: 4px; border: 1px solid #cccccc;" align="center" bgcolor="#FFFFFF"><%= errata_count(host, :security) %></td>
<td width="18%" style="border-collapse: collapse; text-align: center; background-color: #FFFFFF; padding: 4px; border: 1px solid #cccccc;" align="center" bgcolor="#FFFFFF"><%= errata_count(host, :bugfix) %></td>
<td width="18%" style="border-collapse: collapse; text-align: center; background-color: #FFFFFF; padding: 4px; border: 1px solid #cccccc;" align="center" bgcolor="#FFFFFF"><%= errata_count(host, :enhancement) %></td>
</tr>
<% end -%>
</table>
<% end -%>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" valign="top">
<table border="0" cellpadding="20" cellspacing="0" width="100%" id="emailFooter" style="background-color: #e1e2e3;" bgcolor="#e1e2e3">
<tr>
<td align="left" valign="top" style="font-size: 82%; padding: 0px 20px;">
<p><%= _("Note: The number in parentheses reflects all applicable errata from the Library environment that are unavailable to the host. You will need to promote this content to the relevant content view in order to make it available.") %></p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</body>
</html>
15 changes: 15 additions & 0 deletions app/views/katello/errata_mailer/host_errata.text.erb
@@ -0,0 +1,15 @@
<%= _("The following hosts have errata that apply to them: ") %>

<% @content_hosts.group_by(&:organization).each do |org, hosts| %>

<%= _("Organization") %>: <%= org %>

<% hosts.each do |host| %>
<%= host %>:
<%= _("Security") %>: <%= errata_count(host, :security) %>
<%= _("Bugfix") %>: <%= errata_count(host, :bugfix) %>
<%= _("Enhancement") %>: <%= errata_count(host, :enhancement) %>
<% end -%>
<% end -%>

<%= _("Note: The number in parentheses reflects all applicable errata from the Library environment that are unavailable to the host. You will need to promote this content to the relevant content view in order to make it available.") %>