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 #36807 - UX fixes and add update count action #10766

Merged
merged 5 commits into from
Oct 24, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def run
smart_proxy = ::SmartProxy.unscoped.find(input[:smart_proxy_id])
smart_proxy.update_content_counts!
end

def rescue_strategy
Dynflow::Action::Rescue::Skip
end
end
end
end
Expand Down
32 changes: 20 additions & 12 deletions app/models/katello/concerns/smart_proxy_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def alternate_content_sources
end

def update_content_counts!
# {:content_view_versions=>{87=>{:repositories=>{1=>{:rpms=>98, :module_streams=>9898}}}}}
# {:content_view_versions=>{87=>{:repositories=>{1=>{:metadata=>{},:counts=>{:rpms=>98, :module_streams=>9898}}}}}
new_content_counts = { content_view_versions: {} }
smart_proxy_helper = ::Katello::SmartProxyHelper.new(self)
repos = smart_proxy_helper.repositories_available_to_capsule
Expand All @@ -131,24 +131,30 @@ def update_content_counts!
repos.each do |repo|
repo_mirror_service = repo.backend_service(self).with_mirror_adapter
repo_content_counts = repo_mirror_service.latest_content_counts
translated_counts = {}
repo_content_counts.each do |name, count|
translated_counts = {metadata: {}, counts: {}}
translated_counts[:metadata] = {
env_id: repo.environment_id,
library_instance_id: repo.library_instance_or_self.id,
product_id: repo.product_id,
content_type: repo.content_type
}
repo_content_counts&.each do |name, count|
count = count[:count]
# Some content units in Pulp have the same model
if name == 'rpm.package' && repo.content_counts['srpm'] > 0
translated_counts['srpm'] = repo_mirror_service.count_by_pulpcore_type(::Katello::Pulp3::Srpm)
translated_counts['rpm'] = count - translated_counts['srpm']
translated_counts[:counts]['srpm'] = repo_mirror_service.count_by_pulpcore_type(::Katello::Pulp3::Srpm)
translated_counts[:counts]['rpm'] = count - translated_counts[:counts]['srpm']
elsif name == 'container.manifest' && repo.content_counts['docker_manifest_list'] > 0
translated_counts['docker_manifest_list'] = repo_mirror_service.count_by_pulpcore_type(::Katello::Pulp3::DockerManifestList)
translated_counts['docker_manifest'] = count - translated_counts['docker_manifest_list']
translated_counts[:counts]['docker_manifest_list'] = repo_mirror_service.count_by_pulpcore_type(::Katello::Pulp3::DockerManifestList)
translated_counts[:counts]['docker_manifest'] = count - translated_counts[:counts]['docker_manifest_list']
else
translated_counts[::Katello::Pulp3::PulpContentUnit.katello_name_from_pulpcore_name(name, repo)] = count
translated_counts[:counts][::Katello::Pulp3::PulpContentUnit.katello_name_from_pulpcore_name(name, repo)] = count
end
end
new_content_counts[:content_view_versions][repo.content_view_version_id] ||= { repositories: {}}
# Store counts on capsule of archived repos which are reused across environment copies
# of the archived repo corresponding to each environment CV version is promoted to.
new_content_counts[:content_view_versions][repo.content_view_version_id][:repositories][repo.content_view_version.archived_repos.find_by(library_instance_id: repo.library_instance_id)&.id] = translated_counts
new_content_counts[:content_view_versions][repo.content_view_version_id][:repositories][repo.id] = translated_counts
end
update(content_counts: new_content_counts)
end
Expand Down Expand Up @@ -424,10 +430,12 @@ def last_sync_audit
Audited::Audit.where(:auditable_id => self, :auditable_type => SmartProxy.name, action: "sync capsule").order(:created_at).last
end

def last_sync_time
task = sync_tasks.where.not(:ended_at => nil).where(:result => 'success').order(:ended_at).last
def last_sync_task
sync_tasks.where.not(:ended_at => nil).where(:result => 'success').order(:ended_at).last
end

task&.ended_at || last_sync_audit&.created_at&.to_s
def last_sync_time
last_sync_task&.ended_at || last_sync_audit&.created_at&.to_s
end

def environment_syncable?(env)
Expand Down
6 changes: 4 additions & 2 deletions app/services/katello/pulp3/repository_mirror.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def fetch_repository
end

def version_href
fetch_repository.latest_version_href
fetch_repository&.latest_version_href
sjha4 marked this conversation as resolved.
Show resolved Hide resolved
end

def publication_href
Expand Down Expand Up @@ -225,7 +225,9 @@ def count_by_pulpcore_type(service_class)
end

def latest_content_counts
api.repository_versions_api.read(version_href)&.content_summary&.present
version_pulp_href = version_href
return unless version_pulp_href
api.repository_versions_api.read(version_pulp_href)&.content_summary&.present
end

def pulp3_enabled_repo_types
Expand Down
2 changes: 1 addition & 1 deletion app/views/foreman/smart_proxies/_content_tab.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<%= javascript_include_tag *webpack_asset_paths('katello', extension: 'js') %>
<% @smartProxyId= @smart_proxy.id %>
<%= react_component('Content', smartProxyId: @smartProxyId,) %>
<%= react_component('Content', smartProxyId: @smartProxyId, organizationId: Organization.current&.id,) %>
22 changes: 18 additions & 4 deletions app/views/katello/api/v2/capsule_content/sync_status.json.rabl
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
object @capsule

attribute :last_sync_time
node :last_sync_words do |_object|
@capsule&.last_sync_time ? time_ago_in_words(Time.parse(@capsule&.last_sync_time&.to_s)) : nil
end

attribute :download_policy

Expand All @@ -15,6 +18,14 @@ child :last_failed_sync_tasks => :last_failed_sync_tasks do
extends 'foreman_tasks/api/tasks/show'
end

child :last_sync_task => :last_sync_task do
extends 'foreman_tasks/api/tasks/show'
end

node :content_counts do
@capsule.content_counts
end

child @lifecycle_environments => :lifecycle_environments do
extends 'katello/api/v2/common/identifier'
extends 'katello/api/v2/common/org_reference'
Expand All @@ -27,16 +38,17 @@ child @lifecycle_environments => :lifecycle_environments do
if @capsule.has_feature?(SmartProxy::PULP_NODE_FEATURE) || @capsule.has_feature?(SmartProxy::PULP3_FEATURE)
node :counts do |env|
{
:content_views => env.content_views.non_default.count,
:content_counts => @capsule.content_counts
:content_views => env.content_views.non_default.count
}
end

node :content_views do |env|
env.content_views.ignore_generated.map do |content_view|
cvv = ::Katello::ContentViewVersion.in_environment(env).find_by(:content_view => content_view)
attributes = {
:id => content_view.id,
:cvv_id => ::Katello::ContentViewVersion.in_environment(env).find_by(:content_view => content_view)&.id,
:cvv_id => cvv&.id,
:cvv_version => cvv&.version,
:label => content_view.label,
:name => content_view.name,
:composite => content_view.composite,
Expand All @@ -50,7 +62,9 @@ child @lifecycle_environments => :lifecycle_environments do
{
:id => repo.id,
:name => repo.name,
:library_id => repo.library_instance_id
:library_id => repo.library_instance_id,
:product_id => repo.product_id,
:content_type => repo.content_type
}
end
}
Expand Down
77 changes: 68 additions & 9 deletions test/models/concerns/smart_proxy_extensions_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,80 @@ def test_update_content_counts
python_service.expects(:latest_content_counts).once.returns(python_counts)
repos = [yum_repo, file_repo, ansible_repo, container_repo,
ostree_repo, deb_repo, python_repo]
yum_repo.content_view_version.expects(:archived_repos).returns(::Katello::Repository.where(id: [yum_repo, file_repo, ansible_repo, container_repo,
ostree_repo, deb_repo, python_repo]))
::Katello::SmartProxyHelper.any_instance.expects(:repositories_available_to_capsule).once.returns(repos)
@proxy.update_content_counts!
counts = @proxy.content_counts
expected_counts = { "content_view_versions" =>
{ yum_repo.content_view_version.id.to_s =>
{ "repositories" =>
{ yum_repo.id.to_s => { "erratum" => 4, "srpm" => 1, "rpm" => 31, "module_stream" => 7, "rpm.modulemd_defaults" => 3, "package_group" => 7, "rpm.packagecategory" => 1 },
file_repo.id.to_s => { "file" => 100 },
ansible_repo.id.to_s => { "ansible.collection" => 802 },
container_repo.id.to_s => { "container.blob" => 30, "docker_manifest_list" => 1, "docker_manifest" => 9, "docker_tag" => 5 },
ostree_repo.id.to_s => {"ostree_ref" => 30 },
deb_repo.id.to_s => { "deb" => 987 },
python_repo.id.to_s => { "python_package" => 42 }
{ yum_repo.id.to_s => {
"metadata" => {
"env_id" => yum_repo.environment.id,
"library_instance_id" => yum_repo.library_instance_or_self.id,
"product_id" => yum_repo.product_id,
"content_type" => yum_repo.content_type
},
"counts" => { "erratum" => 4, "srpm" => 1, "rpm" => 31, "module_stream" => 7, "rpm.modulemd_defaults" => 3, "package_group" => 7, "rpm.packagecategory" => 1 }
},
file_repo.id.to_s => {
"metadata" => {
"env_id" => file_repo.environment.id,
"library_instance_id" => file_repo.library_instance_or_self.id,
"product_id" => file_repo.product_id,
"content_type" => file_repo.content_type
},
"counts" =>
{ "file" => 100 }
},
ansible_repo.id.to_s => {
"metadata" => {
"env_id" => ansible_repo.environment.id,
"library_instance_id" => ansible_repo.library_instance_or_self.id,
"product_id" => ansible_repo.product_id,
"content_type" => ansible_repo.content_type},
"counts" =>
{ "ansible.collection" => 802 }
},
container_repo.id.to_s => {
"metadata" => {
"env_id" => container_repo.environment.id,
"library_instance_id" => container_repo.library_instance_or_self.id,
"product_id" => container_repo.product_id,
"content_type" => container_repo.content_type
},
"counts" =>
{ "container.blob" => 30, "docker_manifest_list" => 1, "docker_manifest" => 9, "docker_tag" => 5 }
},
ostree_repo.id.to_s => {
"metadata" => {
"env_id" => ostree_repo.environment.id,
"library_instance_id" => ostree_repo.library_instance_or_self.id,
"product_id" => ostree_repo.product_id,
"content_type" => ostree_repo.content_type
},
"counts" =>
{"ostree_ref" => 30 }
},
deb_repo.id.to_s => {
"metadata" => {
"env_id" => deb_repo.environment.id,
"library_instance_id" => deb_repo.library_instance_or_self.id,
"product_id" => deb_repo.product_id,
"content_type" => deb_repo.content_type
},
"counts" =>
{ "deb" => 987 }
},
python_repo.id.to_s => {
"metadata" => {
"env_id" => python_repo.environment.id,
"library_instance_id" => python_repo.library_instance_or_self.id,
"product_id" => python_repo.product_id,
"content_type" => python_repo.content_type
},
"counts" =>
{ "python_package" => 42 }
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion webpack/scenes/ContentViews/Details/Repositories/RepoIcon.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Tooltip } from '@patternfly/react-core';
import { BundleIcon, MiddlewareIcon, BoxIcon, CodeBranchIcon, FanIcon, TenantIcon } from '@patternfly/react-icons';
import { BundleIcon, MiddlewareIcon, BoxIcon, CodeBranchIcon, FanIcon, TenantIcon, AnsibleTowerIcon } from '@patternfly/react-icons';
import PropTypes from 'prop-types';

const RepoIcon = ({ type }) => {
Expand All @@ -10,6 +10,7 @@ const RepoIcon = ({ type }) => {
ostree: CodeBranchIcon,
file: TenantIcon,
deb: FanIcon,
ansible_collection: AnsibleTowerIcon,
};
const Icon = iconMap[type] || BoxIcon;

Expand Down
14 changes: 11 additions & 3 deletions webpack/scenes/SmartProxy/Content.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,24 @@ import React from 'react';
import PropTypes from 'prop-types';
import SmartProxyExpandableTable from './SmartProxyExpandableTable';

const Content = ({ smartProxyId }) => (
<SmartProxyExpandableTable smartProxyId={smartProxyId} />
const Content = ({ smartProxyId, organizationId }) => (
<SmartProxyExpandableTable smartProxyId={smartProxyId} organizationId={organizationId} />
);

Content.propTypes = {
smartProxyId: PropTypes.number,
smartProxyId: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string, // The API can sometimes return strings
]),
organizationId: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string, // The API can sometimes return strings
]),
};

Content.defaultProps = {
smartProxyId: null,
organizationId: null,
};

export default Content;
42 changes: 27 additions & 15 deletions webpack/scenes/SmartProxy/ExpandableCvDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import ContentViewIcon from '../ContentViews/components/ContentViewIcon';
import { useSet } from '../../components/Table/TableHooks';
import ExpandedSmartProxyRepositories from './ExpandedSmartProxyRepositories';

const ExpandableCvDetails = ({ contentViews, counts }) => {
const ExpandableCvDetails = ({ contentViews, contentCounts, envId }) => {
const columnHeaders = [
__('Content view'),
__('Version'),
__('Last published'),
__('Synced to smart proxy'),
__('Synced'),
];
const { content_counts: contentCounts } = counts;
// const { content_counts: contentCounts } = counts;
const expandedTableRows = useSet([]);
const tableRowIsExpanded = id => expandedTableRows.has(id);

Expand All @@ -38,18 +39,22 @@ const ExpandableCvDetails = ({ contentViews, counts }) => {
</Thead>
{contentViews.map((cv, rowIndex) => {
const {
id, name: cvName, composite, up_to_date: upToDate, cvv_id: version, repositories,
id, name: cvName, composite, up_to_date: upToDate,
cvv_id: versionId, cvv_version: version, repositories,
} = cv;
const upToDateVal = upToDate ? <CheckCircleIcon /> : <TimesCircleIcon />;
const isExpanded = tableRowIsExpanded(version);
const upToDateVal = upToDate ? <CheckCircleIcon style={{ color: 'green' }} /> : <TimesCircleIcon style={{ color: 'red' }} />;
const isExpanded = tableRowIsExpanded(versionId);
return (
<Tbody key={`${id} + ${version}`}isExpanded={isExpanded}>
<Tr key={version} ouiaId={cv.name}>
<Tbody key={`${id} + ${versionId}`}isExpanded={isExpanded}>
<Tr key={versionId} ouiaId={cv.name}>
<Td
aria-label={`expand-cv-${id}`}
style={{ paddingTop: 0 }}
expand={{
rowIndex,
isExpanded,
onToggle: (_event, _rInx, isOpen) => expandedTableRows.onToggle(isOpen, version),
onToggle: (_event, _rInx, isOpen) =>
expandedTableRows.onToggle(isOpen, versionId),
}}
/>
<Td>
Expand All @@ -58,14 +63,19 @@ const ExpandableCvDetails = ({ contentViews, counts }) => {
description={<a href={cv.default ? urlBuilder('products', '') : urlBuilder('content_views', '', id)}>{cvName}</a>}
/>
</Td>
<Td>
<a href={`/content_views/${id}#/versions/${versionId}/`}>{__('Version ')}{version}</a>
</Td>
<Td><LongDateTime date={cv.last_published} showRelativeTimeTooltip /></Td>
<Td>{upToDateVal}</Td>
</Tr>
<Tr key="child_row" ouiaId={`ContentViewTableRowChild-${id}`} isExpanded={isExpanded}>
<Td colSpan={12}>
<ExpandedSmartProxyRepositories
contentCounts={contentCounts?.content_view_versions[version]?.repositories}
contentCounts={contentCounts?.content_view_versions[versionId]?.repositories}
repositories={repositories}
syncedToCapsule={upToDate}
envId={envId}
/>
</Td>
</Tr>
Expand All @@ -80,16 +90,18 @@ const ExpandableCvDetails = ({ contentViews, counts }) => {

ExpandableCvDetails.propTypes = {
contentViews: PropTypes.arrayOf(PropTypes.shape({})),
counts: PropTypes.shape({
content_counts: PropTypes.shape({
content_view_versions: PropTypes.shape({}),
}),
contentCounts: PropTypes.shape({
content_view_versions: PropTypes.shape({}),
}),
envId: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string, // The API can sometimes return strings
]).isRequired,
};

ExpandableCvDetails.defaultProps = {
contentViews: [],
counts: {},
contentCounts: {},
};

export default ExpandableCvDetails;
Loading
Loading