Class: Katello::Api::V2::RepositoriesController

Inherits:
ApiController
  • Object
show all
Includes:
Concerns::FilteredAutoCompleteSearch
Defined in:
app/controllers/katello/api/v2/repositories_controller.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

INDEX_ACTIONS =
[:index, :auto_complete_search].freeze
CONTENT_CREDENTIAL_GPG_KEY_TYPE =
"gpg_key".freeze
CONTENT_CREDENTIAL_SSL_CA_CERT_TYPE =
"ssl_ca_cert".freeze
CONTENT_CREDENTIAL_SSL_CLIENT_CERT_TYPE =
"ssl_client_cert".freeze
CONTENT_CREDENTIAL_SSL_CLIENT_KEY_TYPE =
"ssl_client_key".freeze

Constants included from Concerns::FilteredAutoCompleteSearch

Concerns::FilteredAutoCompleteSearch::PAGE_SIZE

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Concerns::FilteredAutoCompleteSearch

#auto_complete_search

Methods inherited from ApiController

#empty_search_query?, #full_result_response, #resource_class, #scoped_search, #skip_session

Methods included from Rendering

#respond_for_async, #respond_for_bulk_async, #respond_for_create, #respond_for_destroy, #respond_for_index, #respond_for_show, #respond_for_status, #respond_for_update, #respond_with_template, #respond_with_template_collection, #respond_with_template_resource, #try_specific_collection_template, #try_specific_resource_template

Methods included from Katello::Api::Version2

#api_version

Class Method Details

.before_index_actionsObject



38
39
40
41
42
43
44
45
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 38

def self.before_index_actions
  # used by RH Cloud to delay the execution of find_optional_organization and find_product
  skip_before_action :find_optional_organization, :only => INDEX_ACTIONS
  skip_before_action :find_product, :only => INDEX_ACTIONS
  yield
  before_action :find_optional_organization, :only => INDEX_ACTIONS
  before_action :find_product, :only => INDEX_ACTIONS
end

Instance Method Details

#compareObject



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 256

def compare
  fail _("No content_view_version_ids provided") if params[:content_view_version_ids].empty?
  @versions = ContentViewVersion.readable.where(:id => params[:content_view_version_ids])
  if @versions.count != params[:content_view_version_ids].uniq.length
    missing = params[:content_view_version_ids] - @versions.pluck(:id)
    fail HttpErrors::NotFound, _("Couldn't find content view versions '%s'") % missing.join(',')
  end

  archived_version_repos = Katello::Repository.where(:content_view_version_id => @versions&.pluck(:id))&.archived
  repos = Katello::Repository.where(id: archived_version_repos&.pluck(:library_instance_id))
  repos = repos.where(:root_id => @repo.root_id) if @repo
  repositories = restrict_comparison(repos, @versions, params[:restrict_comparison])
  collection = scoped_search(repositories.distinct, :name, :asc)
  collection[:results] = collection[:results].map { |item| ContentViewVersionComparePresenter.new(item, @versions, @repo) }
  respond_for_index(:collection => collection)
end

#compare_same(collection, content_view_versions = nil) ⇒ Object



286
287
288
289
290
291
292
293
294
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 286

def compare_same(collection, content_view_versions = nil)
  same_repo_ids = []
  collection.each do |repo|
    if (content_view_versions&.pluck(:id)&.- repo.published_in_versions&.pluck(:id))&.empty?
      same_repo_ids << repo.id
    end
  end
  same_repo_ids
end

#content_typesObject



579
580
581
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 579

def content_types
  render :json => Katello::RepositoryTypeManager.enabled_content_types.map { |type| Katello::RepositoryTypeManager.find_content_type(type) }
end

#createObject



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 301

def create
  repo_params = repository_params
  unless RepositoryTypeManager.creatable_by_user?(repo_params[:content_type], false)
    msg = _("Invalid params provided - content_type must be one of %s") %
      RepositoryTypeManager.creatable_repository_types.keys.sort.join(",")
    fail HttpErrors::UnprocessableEntity, msg
  end

  if !repo_params[:url].nil? && URI(repo_params[:url]).userinfo
    fail "Do not include the username/password in the URL. Use the username/password settings instead."
  end

  gpg_key = get_content_credential(repo_params, CONTENT_CREDENTIAL_GPG_KEY_TYPE)
  ssl_ca_cert = get_content_credential(repo_params, CONTENT_CREDENTIAL_SSL_CA_CERT_TYPE)
  ssl_client_cert = get_content_credential(repo_params, CONTENT_CREDENTIAL_SSL_CLIENT_CERT_TYPE)
  ssl_client_key = get_content_credential(repo_params, CONTENT_CREDENTIAL_SSL_CLIENT_KEY_TYPE)

  repo_params[:label] = labelize_params(repo_params)
  repo_params[:arch] = repo_params[:arch] || 'noarch'
  repo_params[:url] = nil if repo_params[:url].blank?
  repo_params[:unprotected] = repo_params.key?(:unprotected) ? repo_params[:unprotected] : true
  repo_params[:gpg_key] = gpg_key
  repo_params[:ssl_ca_cert] = ssl_ca_cert
  repo_params[:ssl_client_cert] = ssl_client_cert
  repo_params[:ssl_client_key] = ssl_client_key

  if repo_params[:package_types]
    repo_params[:package_types] = repo_params[:package_types].map(& :strip)
  end

  repo_params[:mirroring_policy] ||= Katello::Repository::YUM_TYPE == repo_params[:content_type] ? Setting[:default_yum_mirroring_policy] : Setting[:default_non_yum_mirroring_policy]

  root = construct_repo_from_params(repo_params)
  sync_task(::Actions::Katello::Repository::CreateRoot, root)
  @repository = root.reload.library_instance
  respond_for_create(:resource => @repository)
end

#custom_index_relation(collection) ⇒ Object



47
48
49
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 47

def custom_index_relation(collection)
  collection.includes(:product)
end

#destroyObject



448
449
450
451
452
453
454
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 448

def destroy
  sync_task(::Actions::Katello::Repository::Destroy, @repository,
            remove_from_content_view_versions: ::Foreman::Cast.to_bool(params.fetch(:remove_from_content_view_versions, false)),
            delete_empty_repo_filters: ::Foreman::Cast.to_bool(params.fetch(:delete_empty_repo_filters, true))
            )
  respond_for_destroy
end

#gpg_key_contentObject



570
571
572
573
574
575
576
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 570

def gpg_key_content
  if @repository.root.gpg_key && @repository.root.gpg_key.content.present?
    render(:plain => @repository.root.gpg_key.content, :layout => false)
  else
    head(:not_found)
  end
end

#import_uploadsObject



529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 529

def import_uploads
   = ::Foreman::Cast.to_bool(params.fetch(:publish_repository, true))
  sync_capsule = ::Foreman::Cast.to_bool(params.fetch(:sync_capsule, true))
  async = ::Foreman::Cast.to_bool(params.fetch(:async, false))
  if params['uploads'].empty?
    fail HttpErrors::BadRequest, _('No uploads param specified. An array of uploads to import is required.')
  end

  uploads = (params[:uploads] || []).map do |upload|
    upload.permit(:id, :content_unit_id, :size, :checksum, :name, :digest).to_h
  end

  if @repository.content_type != 'docker' && uploads.first['checksum'].nil?
    fail HttpErrors::BadRequest, _('Checksum is a required parameter.')
  end

  if uploads.first['name'].nil?
    fail HttpErrors::BadRequest, _('Name is a required parameter.')
  end

  begin
    upload_args = {
      content_type: params[:content_type],
      generate_metadata: ,
      sync_capsule: sync_capsule,
    }
    upload_args.merge!(generic_content_type_import_upload_args)

    respond_for_async(resource: send(
      async ? :async_task : :sync_task,
      ::Actions::Katello::Repository::ImportUpload, @repository, uploads, upload_args))
  rescue => e
    raise HttpErrors::BadRequest, e.message
  end
end

#indexObject



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 124

def index
  unless params[:content_type].empty? || RepositoryTypeManager.find(params[:content_type])
    msg = _("Invalid params provided - content_type must be one of %s") %
      RepositoryTypeManager.enabled_repository_types.keys.sort.join(",")
    fail HttpErrors::UnprocessableEntity, msg
  end
  unless params[:with_content].empty? || RepositoryTypeManager.find_content_type(params[:with_content], true)
    msg = _("Invalid params provided - with_content must be one of %s") %
      RepositoryTypeManager.indexable_content_types.map(&:label).sort.join(",")
    fail HttpErrors::UnprocessableEntity, msg
  end
  base_args = [index_relation.distinct, :name, :asc]
  options = {:includes => [:environment, {:root => [:gpg_key, :product]}]}

  respond_to do |format|
    format.csv do
      options[:csv] = true
      repos = scoped_search(*base_args, options)
      csv_response(repos,
                   [:id, :name, :description, :label, :content_type, :arch, :url, :major, :minor,
                    :content_label, :pulp_id, :container_repository_name,
                    :download_policy, 'relative_path', 'product.id', 'product.name',
                    'environment_id'],
                   ['Id', 'Name', 'Description', 'label', 'Content Type', 'Arch', 'Url', 'Major', 'Minor',
                    'Content Label', 'Pulp Id', 'Container Repository Name', 'Download Policy', 'Relative Path',
                    'Product Id', 'Product Name',
                    'Environment Id'])
    end
    format.any do
      repos = scoped_search(*base_args, options)
      respond(:collection => repos)
    end
  end
end

#index_relationObject



159
160
161
162
163
164
165
166
167
168
169
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 159

def index_relation
  query = Repository.readable
  query = query.with_content(params[:with_content]) if params[:with_content]
  query = index_relation_product(query)
  query = query.with_type(params[:content_type]) if params[:content_type]
  query = query.where(:root_id => RootRepository.where(:name => params[:name])) if params[:name]
  query = query.where(:root_id => RootRepository.where(:label => params[:label])) if params[:label]
  query = index_relation_content_unit(query)
  query = index_relation_content_view(query)
  index_relation_environment(query)
end

#index_relation_content_unit(query) ⇒ Object

rubocop:disable Metrics/AbcSize



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 210

def index_relation_content_unit(query)
  if params[:deb_id]
    query = query.joins(:debs)
      .where("#{Deb.table_name}.id" => Deb.with_identifiers(params[:deb_id]))
  end

  if params[:erratum_id]
    query = query.joins(:errata)
      .where("#{Erratum.table_name}.id" => Erratum.with_identifiers(params[:erratum_id]))
  end

  if params[:rpm_id]
    query = query.joins(:rpms)
      .where("#{Rpm.table_name}.id" => Rpm.with_identifiers(params[:rpm_id]))
  end

  if params[:file_id]
    query = query.joins(:files)
      .where("#{FileUnit.table_name}.id" => FileUnit.with_identifiers(params[:file_id]))
  end

  if params[:ansible_collection_id]
    query = query.joins(:ansible_collections)
                .where("#{AnsibleCollection.table_name}.id" => AnsibleCollection.with_identifiers(params[:ansible_collection_id]))
  end

  if params[:modulemd_id]
    query = query.joins(:module_streams)
                .where("#{ModuleStream.table_name}.id" => ModuleStream.with_identifiers(params[:modulemd_id]))
  end

  generic_type_param = RepositoryTypeManager.generic_content_types.find { |type| params["#{type}_id".to_sym] }
  if generic_type_param
    query = query.joins(:generic_content_units)
                 .where("#{GenericContentUnit.table_name}.id" => GenericContentUnit.with_identifiers(params["#{generic_type_param}_id".to_sym]))
  end

  query
end

#index_relation_content_view(query) ⇒ Object



178
179
180
181
182
183
184
185
186
187
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 178

def index_relation_content_view(query)
  if params[:content_view_version_id]
    query = query.where(:content_view_version_id => params[:content_view_version_id])
    query = query.archived if ::Foreman::Cast.to_bool params[:archived]
    query = Katello::Repository.where(:id => query.select(:library_instance_id)) if params[:library]
  elsif params[:content_view_id]
    query = filter_by_content_view(query, params[:content_view_id], params[:environment_id], params[:available_for] == 'content_view')
  end
  query
end

#index_relation_environment(query) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 189

def index_relation_environment(query)
  if params[:environment_id] && !params[:library]
    query = query.where(:environment_id => params[:environment_id])
  elsif params[:environment_id] && params[:library]
    instances = query.where(:environment_id => params[:environment_id])
    instance_ids = instances.pluck(:library_instance_id).reject(&:blank?)
    instance_ids += instances.where(:library_instance_id => nil)
    query = Repository.where(:id => instance_ids)
  elsif (params[:library] && !params[:environment_id]) || (params[:environment_id].blank? && params[:content_view_version_id].blank? && params[:content_view_id].blank?)
    if params[:available_for] == 'content_view_version'
      query = query.where.not(:content_view_version_id => nil, :environment_id => nil)
    elsif @organization
      query = query.where(:content_view_version_id => @organization.default_content_view.versions.first.id)
    else
      query = query.in_default_view
    end
  end
  query
end

#index_relation_product(query) ⇒ Object



171
172
173
174
175
176
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 171

def index_relation_product(query)
  query = query.joins(:root => :product).where("#{Product.table_name}.organization_id" => @organization) if @organization
  query = query.joins(:root).where("#{RootRepository.table_name}.product_id" => @product.id) if @product
  query = query.joins(:root).where("#{RootRepository.table_name}.download_policy" => params[:download_policy]) if params[:download_policy]
  query
end

#reclaim_spaceObject



400
401
402
403
404
405
406
407
408
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 400

def reclaim_space
  if @repository.download_policy != ::Katello::RootRepository::DOWNLOAD_ON_DEMAND
    fail HttpErrors::BadRequest, _("Only On Demand repositories may have space reclaimed.")
  end
  task = async_task(::Actions::Pulp3::Repository::ReclaimSpace, @repository)
  respond_for_async :resource => task
rescue Errors::InvalidActionOptionError => e
  raise HttpErrors::BadRequest, e.message
end

#remove_contentObject



464
465
466
467
468
469
470
471
472
473
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 464

def remove_content
  unless params[:content_type].empty? || RepositoryTypeManager.removable_content_types.map(&:label).include?(params[:content_type])
    msg = _("Invalid params provided - content_type must be one of %s") %
      RepositoryTypeManager.removable_content_types.map(&:label).sort.join(",")
    fail HttpErrors::UnprocessableEntity, msg
  end
  sync_capsule = ::Foreman::Cast.to_bool(params.fetch(:sync_capsule, true))
  fail _("No content ids provided") if @content.blank?
  respond_for_async :resource => sync_task(::Actions::Katello::Repository::RemoveContent, @repository, @content, content_type: params[:content_type], sync_capsule: sync_capsule)
end

#repository_typesObject



341
342
343
344
345
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 341

def repository_types
  creatable = ::Foreman::Cast.to_bool(params[:creatable])
  repo_types = creatable ? RepositoryTypeManager.creatable_repository_types : RepositoryTypeManager.enabled_repository_types
  render :json => repo_types.values
end

#republishObject



350
351
352
353
354
355
356
357
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 350

def republish
  if @repository.mirroring_policy == Katello::RootRepository::MIRRORING_POLICY_COMPLETE && !::Foreman::Cast.to_bool(params[:force])
    fail HttpErrors::BadRequest, _("Metadata republishing is risky on 'Complete Mirroring' repositories. Change the mirroring policy and try again.
Alternatively, use the 'force' parameter to regenerate metadata locally. On the next sync, the upstream repository's metadata will overwrite local metadata for 'Complete Mirroring' repositories.")
  end
  task = async_task(::Actions::Katello::Repository::MetadataGenerate, @repository)
  respond_for_async :resource => task
end

#restrict_comparison(collection, content_view_versions = nil, compare = 'all') ⇒ Object



273
274
275
276
277
278
279
280
281
282
283
284
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 273

def restrict_comparison(collection, content_view_versions = nil, compare = 'all')
  case compare
  when 'same'
    same_repo_ids = compare_same(collection, content_view_versions)
    collection.where(id: same_repo_ids)
  when 'different'
    same_repo_ids = compare_same(collection, content_view_versions)
    collection.where.not(id: same_repo_ids)
  else
    collection
  end
end

#showObject



362
363
364
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 362

def show
  respond_for_show(:resource => @repository)
end

#syncObject



371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 371

def sync
  fail HttpErrors::BadRequest, _("attempted to sync a non-library repository.") unless @repository.library_instance?
  sync_options = {
    :skip_metadata_check => ::Foreman::Cast.to_bool(params[:skip_metadata_check]),
    :validate_contents => ::Foreman::Cast.to_bool(params[:validate_contents]),
    :incremental => ::Foreman::Cast.to_bool(params[:incremental]),
  }

  if @repository.url.blank?
    fail HttpErrors::BadRequest, _("attempted to sync without a feed URL")
  end

  task = async_task(::Actions::Katello::Repository::Sync, @repository, sync_options)
  respond_for_async :resource => task
rescue Errors::InvalidActionOptionError => e
  raise HttpErrors::BadRequest, e.message
end

#updateObject



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 415

def update
  repo_params = repository_params
  if !repo_params[:url].nil? && URI(repo_params[:url]).userinfo
    fail "Do not include the username/password in the URL. Use the username/password settings instead."
  end

  if repo_params[:package_types]
    repo_params[:package_types] = repo_params[:package_types].map(& :strip)
  end

  if @repository.generic?
    generic_remote_options = generic_remote_options_hash(repo_params)
    repo_params[:generic_remote_options] = generic_remote_options.to_json
    RepositoryTypeManager.generic_remote_options.each do |option|
      repo_params&.delete(option.name)
    end
  end

  additive_policy = ::Katello::RootRepository::MIRRORING_POLICY_ADDITIVE
  is_repo_param_additive = repo_params[:mirroring_policy] == additive_policy
  is_root_additive = @repository.root.mirroring_policy == additive_policy

  if @repository.yum? && !(is_repo_param_additive || is_root_additive)
    repo_params[:retain_package_versions_count] = nil
  end
  sync_task(::Actions::Katello::Repository::Update, @repository.root, repo_params)
  respond_for_show(:resource => @repository)
end

#upload_contentObject



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 479

def upload_content
  fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload Container Image content.") if @repository.docker?
  fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload Ansible collections.") if @repository.ansible_collection?
  unless params[:content_type].empty? || RepositoryTypeManager.uploadable_content_types.map(&:label).include?(params[:content_type])
    msg = _("Invalid params provided - content_type must be one of %s") %
      RepositoryTypeManager.uploadable_content_types.map(&:label).sort.join(",")
    fail HttpErrors::UnprocessableEntity, msg
  end

  filepaths = Array.wrap(params[:content]).compact.collect do |content|
    {path: content.path, filename: content.original_filename}
  end

  if !filepaths.blank?
    sync_task(::Actions::Katello::Repository::UploadFiles, @repository, filepaths, params[:content_type])
    render :json => {:status => "success", :filenames => filepaths.map { |item| item[:filename] }}
  else
    fail HttpErrors::BadRequest, _("No file uploaded")
  end
rescue Katello::Errors::InvalidRepositoryContent => error
  respond_for_exception(
    error,
    :status => :unprocessable_entity,
    :text => error.message,
    :errors => [error.message],
    :with_logging => true
  )
end

#verify_checksumObject



391
392
393
394
395
396
# File 'app/controllers/katello/api/v2/repositories_controller.rb', line 391

def verify_checksum
  task = async_task(::Actions::Katello::Repository::VerifyChecksum, @repository)
  respond_for_async :resource => task
rescue Errors::InvalidActionOptionError => e
  raise HttpErrors::BadRequest, e.message
end