katello 4.13.0.rc1 → 4.13.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of katello might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/controllers/katello/api/registry/registry_proxies_controller.rb +334 -23
- data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +8 -0
- data/app/controllers/katello/api/v2/repositories_controller.rb +1 -1
- data/app/lib/actions/katello/capsule_content/sync_capsule.rb +7 -2
- data/app/lib/actions/katello/organization/manifest_delete.rb +6 -1
- data/app/lib/actions/katello/repository/create.rb +17 -11
- data/app/lib/actions/katello/repository/create_root.rb +4 -2
- data/app/lib/actions/katello/repository/discover.rb +11 -4
- data/app/lib/actions/katello/upstream_subscriptions/bind_entitlement.rb +1 -1
- data/app/lib/actions/pulp3/orchestration/orphan_cleanup/remove_orphans.rb +1 -0
- data/app/lib/actions/pulp3/orphan_cleanup/purge_completed_tasks.rb +15 -0
- data/app/lib/actions/pulp3/repository/create_publication.rb +4 -0
- data/app/lib/katello/repo_discovery.rb +4 -190
- data/app/lib/katello/resources/discovery/container.rb +127 -0
- data/app/lib/katello/resources/discovery/yum.rb +95 -0
- data/app/lib/katello/util/http_helper.rb +15 -0
- data/app/models/732bd3db9f64c621c64d2be4f2a838727aac0845.patch +61 -0
- data/app/models/katello/content_view.rb +2 -0
- data/app/models/katello/glue/pulp/repos.rb +8 -1
- data/app/models/katello/repository.rb +5 -1
- data/app/models/katello/repository.rb.bak +978 -0
- data/app/models/katello/root_repository.rb +14 -2
- data/app/models/katello/trace_status.rb +1 -1
- data/app/services/katello/pulp3/api/core.rb +8 -0
- data/app/services/katello/pulp3/api/docker.rb +4 -0
- data/app/services/katello/pulp3/content_view_version/import_validator.rb.bak +166 -0
- data/app/services/katello/pulp3/content_view_version/importable_repositories.rb.bak +164 -0
- data/app/services/katello/pulp3/repository/yum.rb +1 -6
- data/app/services/katello/repository_type.rb +1 -1
- data/app/views/foreman/smart_proxies/_content_tab.html.erb +3 -1
- data/config/initializers/monkeys.rb +0 -1
- data/db/migrate/20240520142245_add_container_push_props_to_repo.rb +7 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/checksum.service.js +6 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +3 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +0 -3
- data/lib/katello/plugin.rb +12 -0
- data/lib/katello/repository_types/docker.rb +1 -0
- data/lib/katello/repository_types/yum.rb +1 -0
- data/lib/katello/tasks/update_repository_expiry.rake +114 -0
- data/lib/katello/version.rb +1 -1
- data/lib/katello.rb +0 -2
- data/locale/bn/katello.po.time_stamp +0 -0
- data/locale/bn_IN/katello.po.time_stamp +0 -0
- data/locale/ca/katello.po.time_stamp +0 -0
- data/locale/cs/katello.po.time_stamp +0 -0
- data/locale/cs_CZ/katello.po.time_stamp +0 -0
- data/locale/de/katello.po.time_stamp +0 -0
- data/locale/de_AT/katello.po.time_stamp +0 -0
- data/locale/de_DE/katello.po.time_stamp +0 -0
- data/locale/el/katello.po.time_stamp +0 -0
- data/locale/en/katello.po.time_stamp +0 -0
- data/locale/en_GB/katello.po.time_stamp +0 -0
- data/locale/en_US/katello.po.time_stamp +0 -0
- data/locale/es/katello.po.time_stamp +0 -0
- data/locale/et_EE/katello.po.time_stamp +0 -0
- data/locale/fr/katello.po.time_stamp +0 -0
- data/locale/gl/katello.po.time_stamp +0 -0
- data/locale/gu/katello.po.time_stamp +0 -0
- data/locale/he_IL/katello.po.time_stamp +0 -0
- data/locale/hi/katello.po.time_stamp +0 -0
- data/locale/id/katello.po.time_stamp +0 -0
- data/locale/it/katello.po.time_stamp +0 -0
- data/locale/ja/katello.po.time_stamp +0 -0
- data/locale/ka/katello.po.time_stamp +0 -0
- data/locale/kn/katello.po.time_stamp +0 -0
- data/locale/ko/katello.po.time_stamp +0 -0
- data/locale/ml_IN/katello.po.time_stamp +0 -0
- data/locale/mr/katello.po.time_stamp +0 -0
- data/locale/nl_NL/katello.po.time_stamp +0 -0
- data/locale/or/katello.po.time_stamp +0 -0
- data/locale/pa/katello.po.time_stamp +0 -0
- data/locale/pl/katello.po.time_stamp +0 -0
- data/locale/pl_PL/katello.po.time_stamp +0 -0
- data/locale/pt/katello.po.time_stamp +0 -0
- data/locale/pt_BR/katello.po.time_stamp +0 -0
- data/locale/ro/katello.po.time_stamp +0 -0
- data/locale/ro_RO/katello.po.time_stamp +0 -0
- data/locale/ru/katello.po.time_stamp +0 -0
- data/locale/sl/katello.po.time_stamp +0 -0
- data/locale/sv_SE/katello.po.time_stamp +0 -0
- data/locale/ta/katello.po.time_stamp +0 -0
- data/locale/ta_IN/katello.po.time_stamp +0 -0
- data/locale/te/katello.po.time_stamp +0 -0
- data/locale/tr/katello.po.time_stamp +0 -0
- data/locale/vi/katello.po.time_stamp +0 -0
- data/locale/vi_VN/katello.po.time_stamp +0 -0
- data/locale/zh/katello.po.time_stamp +0 -0
- data/locale/zh_CN/katello.po.time_stamp +0 -0
- data/locale/zh_TW/katello.po.time_stamp +0 -0
- data/package.json +0 -1
- data/webpack/components/Content/ContentTable.js +0 -1
- data/webpack/components/Content/__tests__/__snapshots__/ContentTable.test.js.snap +0 -1
- data/webpack/global_test_setup.js.bak +59 -0
- data/webpack/scenes/ModuleStreams/ModuleStreamsPage.js +33 -39
- data/webpack/scenes/ModuleStreams/__tests__/ModuleStreamPage.test.js +4 -2
- data/webpack/scenes/ModuleStreams/__tests__/__snapshots__/ModuleStreamsTable.test.js.snap +0 -1
- data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +4 -2
- metadata +87 -28
- data/lib/monkeys/anemone.rb +0 -33
- data/webpack/utils/__tests__/useParamsWithHash.test.js +0 -22
- data/webpack/utils/paramsFromHash.js +0 -16
- data/webpack/utils/useUrlParams.js +0 -14
@@ -0,0 +1,978 @@
|
|
1
|
+
module Katello
|
2
|
+
# rubocop:disable Metrics/ClassLength
|
3
|
+
class Repository < Katello::Model
|
4
|
+
audited
|
5
|
+
|
6
|
+
#pulp uses pulp id to sync with 'yum_distributor' on the end
|
7
|
+
PULP_ID_MAX_LENGTH = 220
|
8
|
+
|
9
|
+
validates_lengths_from_database
|
10
|
+
before_destroy :assert_deletable
|
11
|
+
before_create :downcase_pulp_id
|
12
|
+
|
13
|
+
include ForemanTasks::Concerns::ActionSubject
|
14
|
+
include Glue::Candlepin::Repository
|
15
|
+
include Glue::Pulp::Repo
|
16
|
+
|
17
|
+
include Glue
|
18
|
+
include Authorization::Repository
|
19
|
+
include Katello::Engine.routes.url_helpers
|
20
|
+
|
21
|
+
include ERB::Util
|
22
|
+
include ::ScopedSearchExtensions
|
23
|
+
|
24
|
+
AUDIT_SYNC_ACTION = 'sync'.freeze
|
25
|
+
|
26
|
+
DEB_TYPE = 'deb'.freeze
|
27
|
+
YUM_TYPE = 'yum'.freeze
|
28
|
+
FILE_TYPE = 'file'.freeze
|
29
|
+
DOCKER_TYPE = 'docker'.freeze
|
30
|
+
OSTREE_TYPE = 'ostree'.freeze
|
31
|
+
ANSIBLE_COLLECTION_TYPE = 'ansible_collection'.freeze
|
32
|
+
GENERIC_TYPE = 'generic'.freeze
|
33
|
+
|
34
|
+
EXPORTABLE_TYPES = [YUM_TYPE, FILE_TYPE, ANSIBLE_COLLECTION_TYPE].freeze
|
35
|
+
|
36
|
+
define_model_callbacks :sync, :only => :after
|
37
|
+
|
38
|
+
belongs_to :root, :inverse_of => :repositories, :class_name => "Katello::RootRepository"
|
39
|
+
belongs_to :environment, :inverse_of => :repositories, :class_name => "Katello::KTEnvironment"
|
40
|
+
belongs_to :library_instance, :class_name => "Katello::Repository", :inverse_of => :library_instances_inverse
|
41
|
+
has_many :library_instances_inverse,
|
42
|
+
:class_name => 'Katello::Repository',
|
43
|
+
:dependent => :restrict_with_exception,
|
44
|
+
:foreign_key => :library_instance_id
|
45
|
+
|
46
|
+
has_one :product, :through => :root
|
47
|
+
|
48
|
+
has_many :content_view_repositories, :class_name => "Katello::ContentViewRepository",
|
49
|
+
:dependent => :destroy, :inverse_of => :repository
|
50
|
+
has_many :content_views, :through => :content_view_repositories
|
51
|
+
|
52
|
+
has_many :repository_errata, :class_name => "Katello::RepositoryErratum", :dependent => :delete_all
|
53
|
+
has_many :errata, :through => :repository_errata
|
54
|
+
|
55
|
+
has_many :repository_rpms, :class_name => "Katello::RepositoryRpm", :dependent => :delete_all
|
56
|
+
has_many :rpms, :through => :repository_rpms
|
57
|
+
|
58
|
+
has_many :repository_srpms, :class_name => "Katello::RepositorySrpm", :dependent => :delete_all
|
59
|
+
has_many :srpms, :through => :repository_srpms
|
60
|
+
|
61
|
+
has_many :repository_generic_content_units, :class_name => "Katello::RepositoryGenericContentUnit", :dependent => :delete_all
|
62
|
+
has_many :generic_content_units, :through => :repository_generic_content_units
|
63
|
+
|
64
|
+
has_many :repository_file_units, :class_name => "Katello::RepositoryFileUnit", :dependent => :delete_all
|
65
|
+
has_many :files, :through => :repository_file_units, :source => :file_unit
|
66
|
+
alias_attribute :file_units, :files
|
67
|
+
|
68
|
+
has_many :repository_docker_manifests, :class_name => "Katello::RepositoryDockerManifest", :dependent => :delete_all
|
69
|
+
has_many :docker_manifests, :through => :repository_docker_manifests
|
70
|
+
|
71
|
+
has_many :repository_docker_manifest_lists, :class_name => "Katello::RepositoryDockerManifestList", :dependent => :delete_all
|
72
|
+
has_many :docker_manifest_lists, :through => :repository_docker_manifest_lists
|
73
|
+
|
74
|
+
has_many :yum_metadata_files, :dependent => :destroy, :class_name => "Katello::YumMetadataFile"
|
75
|
+
|
76
|
+
has_many :repository_docker_tags, :class_name => "Katello::RepositoryDockerTag", :dependent => :delete_all
|
77
|
+
has_many :docker_tags, :through => :repository_docker_tags
|
78
|
+
|
79
|
+
has_many :repository_docker_meta_tags, :class_name => "Katello::RepositoryDockerMetaTag", :dependent => :delete_all
|
80
|
+
has_many :docker_meta_tags, :through => :repository_docker_meta_tags
|
81
|
+
|
82
|
+
has_many :repository_debs, :class_name => "Katello::RepositoryDeb", :dependent => :delete_all
|
83
|
+
has_many :debs, :through => :repository_debs
|
84
|
+
|
85
|
+
has_many :content_facet_repositories, :class_name => "Katello::ContentFacetRepository", :dependent => :delete_all
|
86
|
+
has_many :content_facets, :through => :content_facet_repositories
|
87
|
+
|
88
|
+
has_many :repository_package_groups, :class_name => "Katello::RepositoryPackageGroup", :dependent => :delete_all
|
89
|
+
has_many :package_groups, :through => :repository_package_groups
|
90
|
+
|
91
|
+
has_many :kickstart_content_facets, :class_name => "Katello::Host::ContentFacet", :foreign_key => :kickstart_repository_id,
|
92
|
+
:inverse_of => :kickstart_repository, :dependent => :nullify
|
93
|
+
|
94
|
+
has_many :kickstart_hostgroup_content_facets, :class_name => "Katello::Hostgroup::ContentFacet", :foreign_key => :kickstart_repository_id,
|
95
|
+
:inverse_of => :kickstart_repository, :dependent => :nullify
|
96
|
+
|
97
|
+
has_many :kickstart_hostgroups, :class_name => "::Hostgroup", :through => :kickstart_hostgroup_content_facets
|
98
|
+
|
99
|
+
has_many :repository_module_streams, class_name: "Katello::RepositoryModuleStream", dependent: :delete_all
|
100
|
+
has_many :module_streams, through: :repository_module_streams
|
101
|
+
|
102
|
+
has_many :repository_ansible_collections, :class_name => "Katello::RepositoryAnsibleCollection", :dependent => :delete_all
|
103
|
+
has_many :ansible_collections, :through => :repository_ansible_collections
|
104
|
+
has_many :repository_content_view_filters, :class_name => "Katello::RepositoryContentViewFilter", :dependent => :delete_all
|
105
|
+
has_many :filters, :through => :repository_content_view_filters
|
106
|
+
|
107
|
+
belongs_to :content_view_version, :inverse_of => :repositories, :class_name => "Katello::ContentViewVersion"
|
108
|
+
has_many :distribution_references, :class_name => 'Katello::Pulp3::DistributionReference',
|
109
|
+
:dependent => :destroy, :inverse_of => :repository
|
110
|
+
|
111
|
+
has_many :smart_proxy_sync_histories, :class_name => "::Katello::SmartProxySyncHistory", :inverse_of => :repository, :dependent => :delete_all
|
112
|
+
|
113
|
+
has_many :smart_proxy_alternate_content_sources, :class_name => 'Katello::SmartProxyAlternateContentSource', :inverse_of => :repository, :dependent => :nullify
|
114
|
+
|
115
|
+
validates_with Validators::ContainerImageNameValidator, :attributes => :container_repository_name, :allow_blank => false, :if => :docker?
|
116
|
+
validates :container_repository_name, :if => :docker?, :uniqueness => {message: ->(object, _data) do
|
117
|
+
_("for repository '%{name}' is not unique and cannot be created in '%{env}'. Its Container Repository Name (%{container_name}) conflicts with an existing repository. Consider changing the Lifecycle Environment's Registry Name Pattern to something more specific.") %
|
118
|
+
{name: object.name, container_name: object.container_repository_name, :env => object.environment.name}
|
119
|
+
end}
|
120
|
+
|
121
|
+
before_validation :set_pulp_id
|
122
|
+
before_validation :set_container_repository_name, :if => :docker?
|
123
|
+
|
124
|
+
scope :has_url, -> { joins(:root).where.not("#{RootRepository.table_name}.url" => nil) }
|
125
|
+
scope :on_demand, -> { joins(:root).where("#{RootRepository.table_name}.download_policy" => ::Katello::RootRepository::DOWNLOAD_ON_DEMAND) }
|
126
|
+
scope :immediate, -> { joins(:root).where("#{RootRepository.table_name}.download_policy" => ::Katello::RootRepository::DOWNLOAD_IMMEDIATE) }
|
127
|
+
scope :non_immediate, -> { joins(:root).where.not("#{RootRepository.table_name}.download_policy" => ::Katello::RootRepository::DOWNLOAD_IMMEDIATE) }
|
128
|
+
scope :in_default_view, -> { joins(:content_view_version => :content_view).where("#{Katello::ContentView.table_name}.default" => true) }
|
129
|
+
scope :in_non_default_view, -> { joins(:content_view_version => :content_view).where("#{Katello::ContentView.table_name}.default" => false) }
|
130
|
+
scope :deb_type, -> { with_type(DEB_TYPE) }
|
131
|
+
scope :yum_type, -> { with_type(YUM_TYPE) }
|
132
|
+
scope :file_type, -> { with_type(FILE_TYPE) }
|
133
|
+
scope :docker_type, -> { with_type(DOCKER_TYPE) }
|
134
|
+
scope :ostree_type, -> { with_type(OSTREE_TYPE) }
|
135
|
+
scope :ansible_collection_type, -> { with_type(ANSIBLE_COLLECTION_TYPE) }
|
136
|
+
scope :generic_type, -> { with_type(Katello::RepositoryTypeManager.enabled_repository_types.select { |_, v| v.pulp3_service_class == Katello::Pulp3::Repository::Generic }.keys) }
|
137
|
+
scope :non_archived, -> { where('environment_id is not NULL') }
|
138
|
+
scope :archived, -> { where('environment_id is NULL') }
|
139
|
+
scope :in_published_environments, -> { in_content_views(Katello::ContentView.non_default).where.not(:environment_id => nil) }
|
140
|
+
scope :order_by_root, ->(attr) { joins(:root).order("#{Katello::RootRepository.table_name}.#{attr}") }
|
141
|
+
scope :with_content, ->(content) { joins(Katello::RepositoryTypeManager.find_content_type(content).model_class.repository_association_class.name.demodulize.underscore.pluralize.to_sym).distinct }
|
142
|
+
scope :by_rpm_count, -> { left_joins(:repository_rpms).group(:id).order("count(katello_repository_rpms.id) ASC") } # smallest count first
|
143
|
+
scope :exportable, -> { with_type(EXPORTABLE_TYPES) }
|
144
|
+
scope :syncable_exportable, -> { with_type([YUM_TYPE, FILE_TYPE]) }
|
145
|
+
|
146
|
+
scope :immediate_or_none, -> do
|
147
|
+
immediate.or(where("#{RootRepository.table_name}.download_policy" => nil)).
|
148
|
+
or(where("#{RootRepository.table_name}.download_policy" => ""))
|
149
|
+
end
|
150
|
+
scope :redhat, -> { joins(:product => :provider).where("#{Provider.table_name}.provider_type": Provider::REDHAT) }
|
151
|
+
scope :custom, -> { joins(:product => :provider).where.not("#{Provider.table_name}.provider_type": Provider::REDHAT) }
|
152
|
+
scope :library, -> { where(library_instance_id: nil) }
|
153
|
+
|
154
|
+
scoped_search :on => :name, :relation => :root, :complete_value => true
|
155
|
+
scoped_search :rename => :product, :on => :name, :relation => :product, :complete_value => true
|
156
|
+
scoped_search :rename => :product_id, :on => :id, :relation => :product
|
157
|
+
scoped_search :on => :content_type, :relation => :root, :complete_value => -> do
|
158
|
+
Katello::RepositoryTypeManager.enabled_repository_types.keys.index_by { |value| value.to_sym }
|
159
|
+
end
|
160
|
+
scoped_search :on => :content_view_id, :relation => :content_view_repositories, :validator => ScopedSearch::Validators::INTEGER, :only_explicit => true
|
161
|
+
scoped_search :on => :distribution_version, :complete_value => true
|
162
|
+
scoped_search :on => :distribution_arch, :complete_value => true
|
163
|
+
scoped_search :on => :distribution_family, :complete_value => true
|
164
|
+
scoped_search :on => :distribution_variant, :complete_value => true
|
165
|
+
scoped_search :on => :distribution_bootable, :complete_value => true
|
166
|
+
scoped_search :on => :redhat, :complete_value => { :true => true, :false => false }, :ext_method => :search_by_redhat
|
167
|
+
scoped_search :on => :container_repository_name, :complete_value => true
|
168
|
+
scoped_search :on => :description, :relation => :root, :only_explicit => true
|
169
|
+
scoped_search :on => :download_policy, :relation => :root, :only_explicit => true
|
170
|
+
scoped_search :on => :name, :relation => :product, :rename => :product_name
|
171
|
+
scoped_search :on => :id, :relation => :product, :rename => :product_id, :only_explicit => true
|
172
|
+
scoped_search :on => :label, :relation => :root, :complete_value => true, :only_explicit => true
|
173
|
+
scoped_search :on => :content_label, :ext_method => :search_by_content_label
|
174
|
+
|
175
|
+
delegate :product, :redhat?, :custom?, :to => :root
|
176
|
+
delegate :yum?, :docker?, :deb?, :file?, :ostree?, :ansible_collection?, :generic?, :to => :root
|
177
|
+
delegate :name, :label, :docker_upstream_name, :url, :download_concurrency, :to => :root
|
178
|
+
|
179
|
+
delegate :name, :created_at, :updated_at, :major, :minor, :gpg_key_id, :gpg_key, :arch, :label, :url, :unprotected,
|
180
|
+
:content_type, :product_id, :checksum_type, :docker_upstream_name, :mirroring_policy,
|
181
|
+
:download_policy, :verify_ssl_on_sync, :"verify_ssl_on_sync?", :upstream_username, :upstream_password,
|
182
|
+
:upstream_authentication_token, :deb_releases,
|
183
|
+
:deb_components, :deb_architectures, :ssl_ca_cert_id, :ssl_ca_cert, :ssl_client_cert, :ssl_client_cert_id,
|
184
|
+
:ssl_client_key_id, :os_versions, :ssl_client_key, :ignorable_content, :description, :include_tags, :exclude_tags,
|
185
|
+
:docker_tags_whitelist, :ansible_collection_requirements, :ansible_collection_auth_url, :ansible_collection_auth_token,
|
186
|
+
:http_proxy_policy, :http_proxy_id, :to => :root
|
187
|
+
|
188
|
+
delegate :content_id, to: :root, allow_nil: true
|
189
|
+
delegate :repository_type, to: :root
|
190
|
+
|
191
|
+
def self.with_type(content_type)
|
192
|
+
joins(:root).where("#{RootRepository.table_name}.content_type" => content_type)
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.for_products(products)
|
196
|
+
joins(:root).where("#{Katello::RootRepository.table_name}.product_id" => products)
|
197
|
+
end
|
198
|
+
|
199
|
+
def to_label
|
200
|
+
name
|
201
|
+
end
|
202
|
+
|
203
|
+
def backend_service(smart_proxy, force_pulp3 = false)
|
204
|
+
if force_pulp3 || smart_proxy.pulp3_support?(self)
|
205
|
+
@service ||= Katello::Pulp3::Repository.instance_for_type(self, smart_proxy)
|
206
|
+
else
|
207
|
+
@service ||= Katello::Pulp::Repository.instance_for_type(self, smart_proxy)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def backend_content_service(smart_proxy)
|
212
|
+
backend_service(smart_proxy).content_service
|
213
|
+
end
|
214
|
+
|
215
|
+
def backend_content_unit_service(smart_proxy, content_unit_type)
|
216
|
+
backend_service(smart_proxy).content_service(content_unit_type)
|
217
|
+
end
|
218
|
+
|
219
|
+
def organization
|
220
|
+
if self.environment
|
221
|
+
self.environment.organization
|
222
|
+
else
|
223
|
+
self.content_view.organization
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def organization_id
|
228
|
+
organization&.id
|
229
|
+
end
|
230
|
+
|
231
|
+
def audit_sync
|
232
|
+
write_audit(action: AUDIT_SYNC_ACTION, comment: _('Successfully synchronized.'), audited_changes: {})
|
233
|
+
end
|
234
|
+
|
235
|
+
def set_pulp_id
|
236
|
+
return if self.pulp_id
|
237
|
+
|
238
|
+
if self.content_view.default?
|
239
|
+
items = [SecureRandom.uuid]
|
240
|
+
elsif self.environment
|
241
|
+
items = [organization.id, content_view.label, environment.label, library_instance.pulp_id]
|
242
|
+
else
|
243
|
+
version = self.content_view_version.version.gsub('.', '_')
|
244
|
+
items = [organization.id, content_view.label, "v#{version}", library_instance.pulp_id]
|
245
|
+
end
|
246
|
+
self.pulp_id = items.join('-')
|
247
|
+
self.pulp_id = SecureRandom.uuid if self.pulp_id.length > PULP_ID_MAX_LENGTH
|
248
|
+
end
|
249
|
+
|
250
|
+
def set_container_repository_name
|
251
|
+
self.container_repository_name = Repository.safe_render_container_name(self)
|
252
|
+
end
|
253
|
+
|
254
|
+
def content_view
|
255
|
+
self.content_view_version.content_view
|
256
|
+
end
|
257
|
+
|
258
|
+
def library_instance?
|
259
|
+
self.content_view.default?
|
260
|
+
end
|
261
|
+
|
262
|
+
def self.undisplayable_types
|
263
|
+
[::Katello::Repository::CANDLEPIN_DOCKER_TYPE]
|
264
|
+
end
|
265
|
+
|
266
|
+
def self.in_organization(org)
|
267
|
+
where("#{Repository.table_name}.environment_id" => org.kt_environments.pluck("#{KTEnvironment.table_name}.id"))
|
268
|
+
end
|
269
|
+
|
270
|
+
def self.in_environment(env_id)
|
271
|
+
where(environment_id: env_id)
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.in_product(prod)
|
275
|
+
where(:root_id => RootRepository.where(product_id: prod))
|
276
|
+
end
|
277
|
+
|
278
|
+
def self.in_content_views(views)
|
279
|
+
joins(:content_view_version)
|
280
|
+
.where("#{Katello::ContentViewVersion.table_name}.content_view_id" => views.map(&:id))
|
281
|
+
end
|
282
|
+
|
283
|
+
def self.feed_ca_cert(url)
|
284
|
+
file = feed_ca_file(url)
|
285
|
+
File.read(file) if file
|
286
|
+
end
|
287
|
+
|
288
|
+
def self.feed_ca_file(url)
|
289
|
+
::Katello::Resources::CDN::CdnResource.ca_file if ::Katello::Resources::CDN::CdnResource.redhat_cdn?(url)
|
290
|
+
end
|
291
|
+
|
292
|
+
def soft_copy_of_library?
|
293
|
+
return false if self.version_href.nil?
|
294
|
+
self.version_href.starts_with?(self.library_instance.backend_service(SmartProxy.pulp_primary).repository_reference.repository_href)
|
295
|
+
end
|
296
|
+
|
297
|
+
def archive?
|
298
|
+
self.environment.nil?
|
299
|
+
end
|
300
|
+
|
301
|
+
def using_mirrored_metadata?
|
302
|
+
self.yum? && self.library_instance? && self.root.mirroring_policy == Katello::RootRepository::MIRRORING_POLICY_COMPLETE
|
303
|
+
end
|
304
|
+
|
305
|
+
def in_default_view?
|
306
|
+
content_view_version&.default_content_view?
|
307
|
+
end
|
308
|
+
|
309
|
+
def on_demand?
|
310
|
+
root.download_policy == ::Katello::RootRepository::DOWNLOAD_ON_DEMAND
|
311
|
+
end
|
312
|
+
|
313
|
+
def immediate?
|
314
|
+
root.download_policy == ::Katello::RootRepository::DOWNLOAD_IMMEDIATE
|
315
|
+
end
|
316
|
+
|
317
|
+
def yum_gpg_key_url
|
318
|
+
# if the repo has a gpg key return a url to access it
|
319
|
+
if self.root.gpg_key.try(:content).present?
|
320
|
+
"../..#{gpg_key_content_api_repository_url(self, :only_path => true)}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def product_type
|
325
|
+
redhat? ? "redhat" : "custom"
|
326
|
+
end
|
327
|
+
|
328
|
+
def content_counts
|
329
|
+
content_counts = {}
|
330
|
+
RepositoryTypeManager.defined_repository_types[content_type].content_types_to_index.each do |content_type|
|
331
|
+
if content_type&.model_class::CONTENT_TYPE == DockerTag::CONTENT_TYPE
|
332
|
+
content_counts[DockerTag::CONTENT_TYPE] = docker_tags.count
|
333
|
+
elsif content_type&.model_class::CONTENT_TYPE == GenericContentUnit::CONTENT_TYPE
|
334
|
+
content_counts[content_type.content_type] = content_type&.model_class&.in_repositories(self)&.where(:content_type => content_type.content_type)&.count
|
335
|
+
else
|
336
|
+
content_counts[content_type.label] = content_type&.model_class&.in_repositories(self)&.count
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
content_counts['module_stream'] = content_counts.delete('modulemd') if content_counts.key?('modulemd')
|
341
|
+
content_counts
|
342
|
+
end
|
343
|
+
|
344
|
+
def published_in_versions
|
345
|
+
Katello::ContentViewVersion.with_repositories(self.library_instances_inverse)
|
346
|
+
.where(content_view_id: Katello::ContentView.ignore_generated).distinct
|
347
|
+
end
|
348
|
+
|
349
|
+
def self.errata_with_package_counts(repo)
|
350
|
+
repository_rpm = Katello::RepositoryRpm.table_name
|
351
|
+
repository_errata = Katello::RepositoryErratum.table_name
|
352
|
+
rpm = Katello::Rpm.table_name
|
353
|
+
errata = Katello::Erratum.table_name
|
354
|
+
erratum_package = Katello::ErratumPackage.table_name
|
355
|
+
::Katello::Erratum.joins(
|
356
|
+
"INNER JOIN #{erratum_package} on #{erratum_package}.erratum_id = #{errata}.id",
|
357
|
+
"INNER JOIN #{repository_errata} on #{repository_errata}.erratum_id = #{errata}.id",
|
358
|
+
"INNER JOIN #{rpm} on #{rpm}.filename = #{erratum_package}.filename",
|
359
|
+
"INNER JOIN #{repository_rpm} on #{repository_rpm}.rpm_id = #{rpm}.id").
|
360
|
+
where("#{repository_rpm}.repository_id" => repo.id).
|
361
|
+
where("#{repository_errata}.repository_id" => repo.id).
|
362
|
+
group("#{errata}.id").count
|
363
|
+
end
|
364
|
+
|
365
|
+
def self.errata_with_module_stream_counts(repo)
|
366
|
+
repository_errata = Katello::RepositoryErratum.table_name
|
367
|
+
errata = Katello::Erratum.table_name
|
368
|
+
erratum_package = Katello::ErratumPackage.table_name
|
369
|
+
repository_module_stream = Katello::RepositoryModuleStream.table_name
|
370
|
+
msep = ::Katello::ModuleStreamErratumPackage.table_name
|
371
|
+
::Katello::Erratum.joins(
|
372
|
+
"INNER JOIN #{erratum_package} on #{erratum_package}.erratum_id = #{errata}.id",
|
373
|
+
"INNER JOIN #{msep} on #{msep}.erratum_package_id = #{erratum_package}.id",
|
374
|
+
"INNER JOIN #{repository_errata} on #{repository_errata}.erratum_id = #{errata}.id",
|
375
|
+
"INNER JOIN #{repository_module_stream} on #{repository_module_stream}.module_stream_id = #{msep}.module_stream_id").
|
376
|
+
where("#{repository_module_stream}.repository_id" => repo.id).
|
377
|
+
where("#{repository_errata}.repository_id" => repo.id).
|
378
|
+
group("#{errata}.id").count
|
379
|
+
end
|
380
|
+
|
381
|
+
def fetch_package_errata_to_keep
|
382
|
+
errata_counts = ::Katello::Repository.errata_with_package_counts(self)
|
383
|
+
if errata_counts.any?
|
384
|
+
errata_counts_in_library = ::Katello::Repository.errata_with_package_counts(library_instance)
|
385
|
+
errata_counts.keep_if { |id| errata_counts[id] == errata_counts_in_library[id] }
|
386
|
+
errata_counts.keys
|
387
|
+
else
|
388
|
+
[]
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
def fetch_module_errata_to_filter
|
393
|
+
errata_counts = ::Katello::Repository.errata_with_module_stream_counts(self)
|
394
|
+
errata_counts_in_library = ::Katello::Repository.errata_with_module_stream_counts(library_instance)
|
395
|
+
if errata_counts_in_library.any?
|
396
|
+
errata_counts_in_library.keep_if { |id| errata_counts[id] != errata_counts_in_library[id] }
|
397
|
+
errata_counts_in_library.keys
|
398
|
+
else
|
399
|
+
[]
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def partial_errata
|
404
|
+
return [] if library_instance?
|
405
|
+
|
406
|
+
partial_errata = self.errata
|
407
|
+
errata_to_keep = fetch_package_errata_to_keep - fetch_module_errata_to_filter
|
408
|
+
|
409
|
+
if errata_to_keep.any?
|
410
|
+
partial_errata = self.errata.where("#{Katello::Erratum.table_name}.id NOT IN (?)", errata_to_keep)
|
411
|
+
end
|
412
|
+
|
413
|
+
partial_errata
|
414
|
+
end
|
415
|
+
|
416
|
+
def remove_partial_errata!
|
417
|
+
found = partial_errata.to_a
|
418
|
+
yield(found) if block_given?
|
419
|
+
self.repository_errata.where(:erratum_id => found.map(&:id)).delete_all
|
420
|
+
found
|
421
|
+
end
|
422
|
+
|
423
|
+
def siblings
|
424
|
+
content_view_version.archived_repos.where.not(:id => id)
|
425
|
+
end
|
426
|
+
|
427
|
+
def clones
|
428
|
+
self.root.repositories.where.not(:id => library_instance_id || id)
|
429
|
+
end
|
430
|
+
|
431
|
+
def all_instances
|
432
|
+
self.root.repositories
|
433
|
+
end
|
434
|
+
|
435
|
+
def group
|
436
|
+
all_instances
|
437
|
+
end
|
438
|
+
|
439
|
+
def full_path(smart_proxy = nil, force_http = false)
|
440
|
+
pulp_uri = URI.parse(smart_proxy ? smart_proxy.url : ::SmartProxy.pulp_primary.url)
|
441
|
+
scheme = force_http ? 'http' : 'https'
|
442
|
+
if docker?
|
443
|
+
"#{pulp_uri.host.downcase}/#{container_repository_name}"
|
444
|
+
elsif ansible_collection?
|
445
|
+
"#{scheme}://#{pulp_uri.host.downcase}/pulp_ansible/galaxy/#{relative_path}/api/"
|
446
|
+
else
|
447
|
+
"#{scheme}://#{pulp_uri.host.downcase}/pulp/content/#{relative_path}/"
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def to_hash(content_source = nil, force_http = false)
|
452
|
+
{id: id, name: label, url: full_path(content_source, force_http)}
|
453
|
+
end
|
454
|
+
|
455
|
+
#is the repo cloned in the specified environment
|
456
|
+
def cloned_in?(env)
|
457
|
+
!get_clone(env).nil?
|
458
|
+
end
|
459
|
+
|
460
|
+
def promoted?
|
461
|
+
if environment&.library?
|
462
|
+
self.clones.any?
|
463
|
+
else
|
464
|
+
false
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def get_clone(env)
|
469
|
+
if self.content_view.default
|
470
|
+
# this repo is part of a default content view
|
471
|
+
Repository.in_environment(env).clones.
|
472
|
+
joins(:content_view_version => :content_view).where("#{Katello::ContentView.table_name}.default" => true).first
|
473
|
+
else
|
474
|
+
# this repo is part of a content view that was published from a user created view
|
475
|
+
self.content_view.get_repo_clone(env, self).first
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
# Returns true if the pulp_task_id was triggered by the last synchronization
|
480
|
+
# action for the repository. Dynflow action handles the synchronization
|
481
|
+
# by it's own so no need to synchronize it again in this callback. Since the
|
482
|
+
# callbacks are run just after synchronization is finished, it should be enough
|
483
|
+
# to check for the last synchronization task.
|
484
|
+
def dynflow_handled_last_sync?(pulp_task_id)
|
485
|
+
task = ForemanTasks::Task::DynflowTask.for_action(::Actions::Katello::Repository::Sync).
|
486
|
+
for_resource(self).order(:started_at).last
|
487
|
+
return task && task.main_action.pulp_task_id == pulp_task_id
|
488
|
+
end
|
489
|
+
|
490
|
+
def generate_content_path
|
491
|
+
path = content.content_url
|
492
|
+
root.substitutions.each do |key, value|
|
493
|
+
path = path.gsub("$#{key}", value) if value
|
494
|
+
end
|
495
|
+
path
|
496
|
+
end
|
497
|
+
|
498
|
+
def library_instance_or_self
|
499
|
+
self.library_instance || self
|
500
|
+
end
|
501
|
+
|
502
|
+
def generate_repo_path(content_path = nil)
|
503
|
+
_org, _content, content_path = library_instance_or_self.relative_path.split("/", 3) if content_path.blank?
|
504
|
+
content_path = content_path.sub(%r|^/|, '')
|
505
|
+
if self.environment
|
506
|
+
cve = ContentViewEnvironment.where(:environment_id => self.environment,
|
507
|
+
:content_view_id => self.content_view).first
|
508
|
+
"#{organization.label}/#{cve.label}/#{content_path}"
|
509
|
+
else
|
510
|
+
"#{organization.label}/#{ContentView::CONTENT_DIR}/#{self.content_view.label}/#{self.content_view_version.version}/#{content_path}"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def generate_docker_repo_path
|
515
|
+
org = self.organization.label.downcase
|
516
|
+
if self.environment
|
517
|
+
cve = ContentViewEnvironment.where(:environment_id => self.environment,
|
518
|
+
:content_view_id => self.content_view).first
|
519
|
+
view = self.content_view.label
|
520
|
+
product = self.product.label
|
521
|
+
env = cve.label.split('/').first
|
522
|
+
"#{org}-#{env.downcase}-#{view}-#{product}-#{self.root.label}"
|
523
|
+
else
|
524
|
+
"#{org}-#{self.content_view.label}-#{self.content_view_version.version}-#{self.root.product.label}-#{self.root.label}"
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
def packages_without_errata
|
529
|
+
if errata_filenames.any?
|
530
|
+
self.rpms.where("#{Rpm.table_name}.filename NOT in (?)", errata_filenames)
|
531
|
+
else
|
532
|
+
self.rpms
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
def module_streams_without_errata
|
537
|
+
module_stream_errata = Katello::ModuleStreamErratumPackage.joins(:erratum_package => {:erratum => :repository_errata})
|
538
|
+
.where("#{RepositoryErratum.table_name}.repository_id" => self.id)
|
539
|
+
.pluck("#{Katello::ModuleStreamErratumPackage.table_name}.module_stream_id")
|
540
|
+
if module_stream_errata.any?
|
541
|
+
self.module_streams.where("#{ModuleStream.table_name}.id NOT in (?)", module_stream_errata)
|
542
|
+
else
|
543
|
+
self.module_streams
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
def self.with_errata(errata)
|
548
|
+
joins(:repository_errata).where("#{Katello::RepositoryErratum.table_name}.erratum_id" => errata)
|
549
|
+
end
|
550
|
+
|
551
|
+
def errata_filenames
|
552
|
+
Katello::ErratumPackage.joins(:erratum => :repository_errata).
|
553
|
+
where("#{RepositoryErratum.table_name}.repository_id" => self.id).pluck("#{Katello::ErratumPackage.table_name}.filename")
|
554
|
+
end
|
555
|
+
|
556
|
+
# TODO: break up method
|
557
|
+
def build_clone(options)
|
558
|
+
to_env = options[:environment]
|
559
|
+
version = options[:version]
|
560
|
+
content_view = options[:content_view] || to_env.default_content_view
|
561
|
+
to_version = version || content_view.version(to_env)
|
562
|
+
|
563
|
+
fail _("Cannot clone into the Default Content View") if content_view.default?
|
564
|
+
|
565
|
+
if to_env && version
|
566
|
+
fail "Cannot clone into both an environment and a content view version archive"
|
567
|
+
end
|
568
|
+
|
569
|
+
if to_version.nil?
|
570
|
+
fail _("View %{view} has not been promoted to %{env}") %
|
571
|
+
{:view => content_view.name, :env => to_env.name}
|
572
|
+
end
|
573
|
+
|
574
|
+
if to_env && self.clones.in_content_views([content_view]).in_environment(to_env).any?
|
575
|
+
fail _("Repository has already been cloned to %{cv_name} in environment %{to_env}") %
|
576
|
+
{:to_env => to_env, :cv_name => content_view.name}
|
577
|
+
end
|
578
|
+
|
579
|
+
if self.yum?
|
580
|
+
if self.library_instance?
|
581
|
+
checksum_type = root.checksum_type || pulp_scratchpad_checksum_type
|
582
|
+
else
|
583
|
+
checksum_type = self.saved_checksum_type
|
584
|
+
end
|
585
|
+
end
|
586
|
+
clone = Repository.new(:environment => to_env,
|
587
|
+
:library_instance => library_instance_or_self,
|
588
|
+
:root => self.root,
|
589
|
+
:content_view_version => to_version,
|
590
|
+
:saved_checksum_type => checksum_type)
|
591
|
+
|
592
|
+
clone.relative_path = clone.docker? ? clone.generate_docker_repo_path : clone.generate_repo_path
|
593
|
+
clone
|
594
|
+
end
|
595
|
+
|
596
|
+
def self.synced_on_capsule(smart_proxy)
|
597
|
+
smart_proxy.smart_proxy_sync_histories.map { |sph| sph.repository unless sph.finished_at.nil? }
|
598
|
+
end
|
599
|
+
|
600
|
+
def clear_smart_proxy_sync_histories(smart_proxy = nil)
|
601
|
+
if smart_proxy
|
602
|
+
self.smart_proxy_sync_histories.where(:smart_proxy_id => smart_proxy.id).try(:delete_all)
|
603
|
+
else
|
604
|
+
self.smart_proxy_sync_histories.delete_all
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
def create_smart_proxy_sync_history(smart_proxy)
|
609
|
+
clear_smart_proxy_sync_histories(smart_proxy)
|
610
|
+
sp_history_args = {
|
611
|
+
:smart_proxy_id => smart_proxy.id,
|
612
|
+
:repository_id => self.id,
|
613
|
+
:started_at => Time.now
|
614
|
+
}
|
615
|
+
sp_history = ::Katello::SmartProxySyncHistory.create sp_history_args
|
616
|
+
sp_history.save!
|
617
|
+
sp_history.id
|
618
|
+
end
|
619
|
+
|
620
|
+
def latest_sync_audit
|
621
|
+
self.audits.where(:action => AUDIT_SYNC_ACTION).order(:created_at).last
|
622
|
+
end
|
623
|
+
|
624
|
+
def cancel_dynflow_sync
|
625
|
+
if latest_dynflow_sync
|
626
|
+
plan = latest_dynflow_sync.execution_plan
|
627
|
+
|
628
|
+
plan.steps.each_pair do |_number, step|
|
629
|
+
if step.cancellable? && step.is_a?(Dynflow::ExecutionPlan::Steps::RunStep)
|
630
|
+
::ForemanTasks.dynflow.world.event(plan.id, step.id, Dynflow::Action::Cancellable::Cancel)
|
631
|
+
end
|
632
|
+
end
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
def latest_dynflow_sync
|
637
|
+
@latest_dynflow_sync ||= ForemanTasks::Task::DynflowTask.where(:label => ::Actions::Katello::Repository::Sync.name).
|
638
|
+
for_resource(self).order(:started_at).last
|
639
|
+
end
|
640
|
+
|
641
|
+
# returns other instances of this repo with the same library
|
642
|
+
# equivalent of repo
|
643
|
+
def environmental_instances(view)
|
644
|
+
self.all_instances.non_archived.in_content_views([view])
|
645
|
+
end
|
646
|
+
|
647
|
+
def archived_instance
|
648
|
+
if self.environment_id.nil? || self.library_instance_id.nil?
|
649
|
+
self
|
650
|
+
else
|
651
|
+
self.content_view_version.archived_repos.where(:root_id => self.root_id).first
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def requires_yum_clone_distributor?
|
656
|
+
self.yum? && self.environment_id && !self.in_default_view?
|
657
|
+
end
|
658
|
+
|
659
|
+
def url?
|
660
|
+
root.url.present?
|
661
|
+
end
|
662
|
+
|
663
|
+
def related_resources
|
664
|
+
self.product
|
665
|
+
end
|
666
|
+
|
667
|
+
def node_syncable?
|
668
|
+
environment
|
669
|
+
end
|
670
|
+
|
671
|
+
def self.smart_proxy_syncable
|
672
|
+
joins(:content_view_version => :content_view).
|
673
|
+
merge(ContentView.ignore_generated).
|
674
|
+
where.not(environment_id: nil)
|
675
|
+
end
|
676
|
+
|
677
|
+
def exist_for_environment?(environment, content_view, attribute = nil)
|
678
|
+
if environment.present? && content_view.in_environment?(environment)
|
679
|
+
repos = content_view.version(environment).repos(environment)
|
680
|
+
|
681
|
+
repos.any? do |repo|
|
682
|
+
not_self = (repo.id != self.id)
|
683
|
+
same_product = (repo.product.id == self.product.id)
|
684
|
+
|
685
|
+
repo_exists = same_product && not_self
|
686
|
+
|
687
|
+
if repo_exists && attribute
|
688
|
+
same_attribute = repo.send(attribute) == self.send(attribute)
|
689
|
+
repo_exists = same_attribute
|
690
|
+
end
|
691
|
+
|
692
|
+
repo_exists
|
693
|
+
end
|
694
|
+
else
|
695
|
+
false
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
def units_for_removal(ids, type_class = nil)
|
700
|
+
removable_unit_association = unit_type_for_removal(type_class)
|
701
|
+
table_name = removable_unit_association.table_name
|
702
|
+
is_integer = Integer(ids.first) rescue false #assume all ids are either integers or not
|
703
|
+
|
704
|
+
if is_integer
|
705
|
+
removable_unit_association.where("#{table_name}.id in (?)", ids)
|
706
|
+
else
|
707
|
+
removable_unit_association.where("#{table_name}.pulp_id in (?)", ids)
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
def self.import_distributions
|
712
|
+
self.all.each do |repo|
|
713
|
+
repo.import_distribution_data
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
def import_distribution_data(target_repo = nil)
|
718
|
+
if target_repo
|
719
|
+
self.update!(
|
720
|
+
:distribution_version => target_repo.distribution_version,
|
721
|
+
:distribution_arch => target_repo.distribution_arch,
|
722
|
+
:distribution_family => target_repo.distribution_family,
|
723
|
+
:distribution_variant => target_repo.distribution_variant,
|
724
|
+
:distribution_bootable => target_repo.distribution_bootable
|
725
|
+
)
|
726
|
+
else
|
727
|
+
self.backend_service(SmartProxy.pulp_primary).import_distribution_data
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
def distribution_information
|
732
|
+
{
|
733
|
+
distribution_version: self.distribution_version,
|
734
|
+
distribution_arch: self.distribution_arch,
|
735
|
+
distribution_family: self.distribution_family,
|
736
|
+
distribution_variant: self.distribution_variant,
|
737
|
+
distribution_bootable: self.distribution_bootable
|
738
|
+
}
|
739
|
+
end
|
740
|
+
|
741
|
+
# deleteable? is already taken by the authorization mixin
|
742
|
+
def destroyable?(remove_from_content_view_versions = false)
|
743
|
+
if self.environment.try(:library?) && self.content_view.default?
|
744
|
+
if self.environment.organization.being_deleted?
|
745
|
+
return true
|
746
|
+
elsif self.custom? && self.deletable?(remove_from_content_view_versions)
|
747
|
+
return true
|
748
|
+
elsif !self.custom? && self.redhat_deletable?(remove_from_content_view_versions)
|
749
|
+
return true
|
750
|
+
else
|
751
|
+
errors.add(:base, _("Repository cannot be deleted since it has already been included in a published Content View. " \
|
752
|
+
"Please delete all Content View versions containing this repository before attempting to delete it."))
|
753
|
+
|
754
|
+
return false
|
755
|
+
end
|
756
|
+
end
|
757
|
+
return true
|
758
|
+
end
|
759
|
+
|
760
|
+
def sync_hook
|
761
|
+
run_callbacks :sync do
|
762
|
+
logger.debug "custom hook after_sync on #{name} will be executed if defined."
|
763
|
+
true
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
def rabl_path
|
768
|
+
"katello/api/v2/#{self.class.to_s.demodulize.tableize}/show"
|
769
|
+
end
|
770
|
+
|
771
|
+
def assert_deletable
|
772
|
+
throw :abort unless destroyable?
|
773
|
+
end
|
774
|
+
|
775
|
+
def docker_meta_tag_count
|
776
|
+
DockerMetaTag.in_repositories(self.id).count
|
777
|
+
end
|
778
|
+
|
779
|
+
# a primary repository actually has content (rpms, errata, etc) in the pulp repository. For these repositories, we can use the YumDistributor
|
780
|
+
# to generate metadata and can index the content from pulp, or for the case of content view archives without filters, can also use the YumCloneDistributor
|
781
|
+
#
|
782
|
+
def primary?
|
783
|
+
!self.yum? || # non-yum repos
|
784
|
+
self.in_default_view? || # default content view repos
|
785
|
+
(self.archive? && !self.content_view.composite) || # non-composite content view archive repos
|
786
|
+
(self.archive? && self.content_view.composite? && self.component_source_repositories.count > 1) # composite archive repo with more than 1 source repository
|
787
|
+
end
|
788
|
+
|
789
|
+
# a link repository has no content in the pulp repository and serves as a shell. It will always be empty. Only the YumCloneDistributor can be used
|
790
|
+
# to publish yum metadata, and it cannot be indexed from pulp, but must have its indexed associations copied from another repository (its target).
|
791
|
+
def link?
|
792
|
+
!primary?
|
793
|
+
end
|
794
|
+
|
795
|
+
# A link (empty repo) points to a target (a repository that actually has units in pulp). Target repos are always archive repos of a content view version (a repo with no environment)
|
796
|
+
# But for composite view versions, an archive repo, usually won't be a primary (but might be if multple components contain the same repo)
|
797
|
+
def target_repository
|
798
|
+
fail _("This is not a linked repository") if primary?
|
799
|
+
return nil if self.archived_instance.nil?
|
800
|
+
|
801
|
+
#this is an environment repo, and the archived_instance is a primary (not always true with composite)
|
802
|
+
if self.environment_id? && self.archived_instance.primary?
|
803
|
+
self.archived_instance
|
804
|
+
elsif self.environment_id #this is an environment repo, but a composite who's archived_instance isn't a primary
|
805
|
+
self.archived_instance.target_repository || self.archived_instance #to archived_instance if nil
|
806
|
+
else #must be a composite archive repo, with only one component repo
|
807
|
+
self.component_source_repositories.first
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
def component_source_repositories
|
812
|
+
#find other copies of this repositories, in the CV version's components, that are in the 'archive'
|
813
|
+
Katello::Repository.where(:content_view_version_id => self.content_view_version.components, :environment_id => nil,
|
814
|
+
:root_id => self.root_id)
|
815
|
+
end
|
816
|
+
|
817
|
+
def self.linked_repositories
|
818
|
+
to_return = []
|
819
|
+
Katello::Repository.yum_type.in_non_default_view.find_each do |repo|
|
820
|
+
to_return << repo if repo.link?
|
821
|
+
end
|
822
|
+
to_return
|
823
|
+
end
|
824
|
+
|
825
|
+
def self.search_by_redhat(_key, operator, value)
|
826
|
+
value = value == 'true'
|
827
|
+
value = !value if operator == '<>'
|
828
|
+
|
829
|
+
product_ids = Katello::Product.redhat.select(:id)
|
830
|
+
root_ids = Katello::RootRepository.where(:product_id => product_ids).pluck(:id)
|
831
|
+
if product_ids.empty?
|
832
|
+
{:conditions => "1=0"}
|
833
|
+
else
|
834
|
+
operator = value ? 'IN' : 'NOT IN'
|
835
|
+
{:conditions => "#{Katello::Repository.table_name}.root_id #{operator} (#{root_ids.join(',')})"}
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
def self.search_by_content_label(_key, operator, value)
|
840
|
+
conditions = sanitize_sql_for_conditions(["label #{operator} ?", value_to_sql(operator, value)])
|
841
|
+
contents = Katello::Content.where(conditions).pluck(:cp_content_id)
|
842
|
+
root_ids = Katello::RootRepository.where(:content_id => contents).pluck(:id)
|
843
|
+
if root_ids.empty?
|
844
|
+
{ :conditions => "1=0" }
|
845
|
+
else
|
846
|
+
{ :conditions => "#{Katello::Repository.table_name}.root_id IN (#{root_ids.join(',')})" }
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
def self.safe_render_container_name(repository, pattern = nil)
|
851
|
+
if (pattern && !pattern.blank?) || (repository.environment && !repository.environment.registry_name_pattern.empty?)
|
852
|
+
pattern ||= repository.environment.registry_name_pattern
|
853
|
+
allowed_methods = {}
|
854
|
+
allowed_vars = {}
|
855
|
+
scope_variables = {repository: repository, organization: repository.organization, product: repository.product,
|
856
|
+
lifecycle_environment: repository.environment, content_view: repository.content_view_version.content_view,
|
857
|
+
content_view_version: repository.content_view_version}
|
858
|
+
box = Safemode::Box.new(repository, allowed_methods)
|
859
|
+
erb = ERB.new(pattern)
|
860
|
+
pattern = box.eval(erb.src, allowed_vars, scope_variables)
|
861
|
+
return Repository.clean_container_name(pattern)
|
862
|
+
elsif repository.content_view.default?
|
863
|
+
items = [repository.organization.label, repository.product.label, repository.label]
|
864
|
+
elsif repository.environment
|
865
|
+
items = [repository.organization.label, repository.environment.label, repository.content_view.label, repository.product.label, repository.label]
|
866
|
+
else
|
867
|
+
items = [repository.organization.label, repository.content_view.label, repository.content_view_version.version, repository.product.label, repository.label]
|
868
|
+
end
|
869
|
+
Repository.clean_container_name(items.compact.join("-"))
|
870
|
+
end
|
871
|
+
|
872
|
+
def self.clean_container_name(name)
|
873
|
+
name.gsub(/[^-\/\w]/, "_").gsub(/_{3,}/, "_").gsub(/-_|^_+|_+$/, "").downcase.strip
|
874
|
+
end
|
875
|
+
|
876
|
+
def custom_repo_path
|
877
|
+
return custom_docker_repo_path if docker?
|
878
|
+
if [environment, product, root.label].any?(&:nil?)
|
879
|
+
return nil # can't generate valid path
|
880
|
+
end
|
881
|
+
prefix = [environment.organization.label, environment.label].map { |x| x.gsub(/[^-\w]/, "_") }.join("/")
|
882
|
+
prefix + root.custom_content_path
|
883
|
+
end
|
884
|
+
|
885
|
+
def custom_docker_repo_path
|
886
|
+
if [environment, product, root.label].any?(&:nil?)
|
887
|
+
return nil # can't generate valid path
|
888
|
+
end
|
889
|
+
parts = [environment.organization.label, product.label, root.label]
|
890
|
+
parts.map { |x| x.gsub(/[^-\w]/, "_") }.join("-").downcase
|
891
|
+
end
|
892
|
+
|
893
|
+
def copy_indexed_data(source_repository)
|
894
|
+
repository_type.content_types_to_index.each do |type|
|
895
|
+
type.model_class.copy_repository_associations(source_repository, self)
|
896
|
+
repository_type.index_additional_data_proc&.call(self, source_repository)
|
897
|
+
end
|
898
|
+
end
|
899
|
+
|
900
|
+
def index_linked_repo
|
901
|
+
if (base_repo = self.target_repository)
|
902
|
+
copy_indexed_data(base_repo)
|
903
|
+
else
|
904
|
+
Rails.logger.error("Cannot index #{self.id}, no target repository found.")
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
def index_content(options = {})
|
909
|
+
# set full_index to true if you want to force fetch all data from pulp
|
910
|
+
# This is automatically done for library instance repos
|
911
|
+
# However for non-library instance as in those belonging to a version
|
912
|
+
# by default we fetch only ids and match them with the library instance
|
913
|
+
# some times we want to force fetch all data even for non-library repos.
|
914
|
+
# Use the full_index for those times
|
915
|
+
|
916
|
+
full_index = options.fetch(:full_index, false)
|
917
|
+
source_repository = options.fetch(:source_repository, nil)
|
918
|
+
if self.yum? && !self.primary?
|
919
|
+
index_linked_repo
|
920
|
+
elsif source_repository && !repository_type.unique_content_per_repo
|
921
|
+
copy_indexed_data(source_repository)
|
922
|
+
else
|
923
|
+
repository_type.content_types_to_index.each do |type|
|
924
|
+
Katello::Logging.time("CONTENT_INDEX", data: {type: type.model_class}) do
|
925
|
+
Katello::ContentUnitIndexer.new(content_type: type, repository: self, optimized: !full_index).import_all
|
926
|
+
end
|
927
|
+
end
|
928
|
+
repository_type.index_additional_data_proc&.call(self)
|
929
|
+
end
|
930
|
+
true
|
931
|
+
end
|
932
|
+
|
933
|
+
def in_content_view?(content_view)
|
934
|
+
content_view.repositories.include? self
|
935
|
+
end
|
936
|
+
|
937
|
+
protected
|
938
|
+
|
939
|
+
def unit_type_for_removal(type_class = nil)
|
940
|
+
if type_class
|
941
|
+
Katello::RepositoryTypeManager.find_content_type(type_class).model_class
|
942
|
+
else
|
943
|
+
Katello::RepositoryTypeManager.find(self.content_type).default_managed_content_type.model_class
|
944
|
+
end
|
945
|
+
end
|
946
|
+
|
947
|
+
def downcase_pulp_id
|
948
|
+
# Docker doesn't support uppercase letters in repository names. Since the pulp_id
|
949
|
+
# is currently being used for the name, it will be downcased for this content type.
|
950
|
+
if self.content_type == Repository::DOCKER_TYPE
|
951
|
+
self.pulp_id = self.pulp_id.downcase
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
def remove_docker_content(manifests)
|
956
|
+
destroyable_manifests = manifests.select do |manifest|
|
957
|
+
manifest.repositories.empty? || manifest.docker_manifest_lists.empty?
|
958
|
+
end
|
959
|
+
# destroy any orphan docker manifests
|
960
|
+
destroyable_manifests.each do |manifest|
|
961
|
+
self.docker_manifests.delete(manifest)
|
962
|
+
manifest.destroy
|
963
|
+
end
|
964
|
+
DockerMetaTag.cleanup_tags
|
965
|
+
end
|
966
|
+
|
967
|
+
apipie :class, desc: "A class representing #{model_name.human} object" do
|
968
|
+
name 'Repository'
|
969
|
+
refs 'Repository'
|
970
|
+
sections only: %w[all additional]
|
971
|
+
prop_group :katello_basic_props, Katello::Model, meta: { friendly_name: 'Repository' }
|
972
|
+
property :docker_upstream_name, String, desc: 'Returns name of the upstream docker repository'
|
973
|
+
end
|
974
|
+
class Jail < ::Safemode::Jail
|
975
|
+
allow :name, :label, :docker_upstream_name, :content_counts
|
976
|
+
end
|
977
|
+
end
|
978
|
+
end
|