app/controllers/katello/api/v2/repositories_controller.rb in katello-3.6.0.1.rc2 vs app/controllers/katello/api/v2/repositories_controller.rb in katello-3.7.0.rc1

- old
+ new

@@ -1,28 +1,39 @@ module Katello class Api::V2::RepositoriesController < Api::V2::ApiController # rubocop:disable Metrics/ClassLength include Katello::Concerns::FilteredAutoCompleteSearch + 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 + before_action :find_optional_organization, :only => [:index, :auto_complete_search] before_action :find_product, :only => [:index, :auto_complete_search] before_action :find_product_for_create, :only => [:create] before_action :find_organization_from_product, :only => [:create] before_action :find_repository, :only => [:show, :update, :destroy, :sync, :export, :remove_content, :upload_content, :republish, :import_uploads, :gpg_key_content] before_action :find_content, :only => :remove_content before_action :find_organization_from_repo, :only => [:update] - before_action :find_gpg_key, :only => [:create, :update] + before_action :only => [:create, :update] { find_content_credential CONTENT_CREDENTIAL_GPG_KEY_TYPE } + before_action :only => [:create, :update] { find_content_credential CONTENT_CREDENTIAL_SSL_CA_CERT_TYPE } + before_action :only => [:create, :update] { find_content_credential CONTENT_CREDENTIAL_SSL_CLIENT_CERT_TYPE } + before_action :only => [:create, :update] { find_content_credential CONTENT_CREDENTIAL_SSL_CLIENT_KEY_TYPE } before_action :error_on_rh_product, :only => [:create] before_action :error_on_rh_repo, :only => [:destroy] skip_before_action :authorize, :only => [:sync_complete, :gpg_key_content] skip_before_action :check_content_type, :only => [:upload_content] def_param_group :repo do param :url, String, :desc => N_("repository source url") param :gpg_key_id, :number, :desc => N_("id of the gpg key that will be assigned to the new repository") + param :ssl_ca_cert_id, :number, :desc => N_("Idenifier of the SSL CA Cert") + param :ssl_client_cert_id, :number, :desc => N_("Identifier of the SSL Client Cert") + param :ssl_client_key_id, :number, :desc => N_("Identifier of the SSL Client Key") param :unprotected, :bool, :desc => N_("true if this repository can be published via HTTP") param :checksum_type, String, :desc => N_("checksum of the repository, currently 'sha1' & 'sha256' are supported.") param :docker_upstream_name, String, :desc => N_("name of the upstream docker repository") param :download_policy, ["immediate", "on_demand", "background"], :desc => N_("download policy for yum repos (either 'immediate', 'on_demand', or 'background')") param :mirror_on_sync, :bool, :desc => N_("true if this repository when synced has to be mirrored from the source and stale rpms removed.") @@ -33,10 +44,11 @@ param :ostree_upstream_sync_depth, :number, :desc => N_("if a custom sync policy is chosen for ostree repositories then a 'depth' value must be provided.") param :deb_releases, String, :desc => N_("comma separated list of releases to be synched from deb-archive") param :deb_components, String, :desc => N_("comma separated list of repo components to be synched from deb-archive") param :deb_architectures, String, :desc => N_("comma separated list of architectures to be synched from deb-archive") param :ignore_global_proxy, :bool, :desc => N_("if true, will ignore the globally configured proxy when syncing.") + param :ignorable_content, Array, :desc => N_("List of content units to ignore while syncing a yum repository. Must be subset of %s") % Repository::IGNORABLE_CONTENT_UNIT_TYPES.join(",") end def_param_group :repo_create do param :label, String, :required => false param :product_id, :number, :required => true, :desc => N_("Product the repository belongs to") @@ -63,12 +75,32 @@ param :name, String, :desc => N_("name of the repository"), :required => false param :available_for, String, :desc => N_("interpret specified object to return only Repositories that can be associated with specified object. Only 'content_view' is supported."), :required => false param_group :search, Api::V2::ApiController def index + base_args = [index_relation.distinct, :name, :asc] options = {:includes => [:gpg_key, :product, :environment]} - respond(:collection => scoped_search(index_relation.distinct, :name, :asc, options)) + + respond_to do |format| + format.csv do + options[:csv] = true + repos = scoped_search(*base_args, options) + csv_response(repos, + [:id, :name, :label, :content_type, :arch, :url, :major, :minor, + :cp_label, :content_label, :pulp_id, :container_repository_name, + :download_policy, 'relative_path', 'product.id', 'product.name', + 'environment_id'], + ['Id', 'Name', 'label', 'Content Type', 'Arch', 'Url', 'Major', 'Minor', + 'Candlepin Label', '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 def index_relation query = Repository.readable query = index_relation_product(query) @@ -140,42 +172,32 @@ api :POST, "/repositories", N_("Create a custom repository") param :name, String, :required => true param_group :repo_create param_group :repo - def create # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity + def create repo_params = repository_params unless RepositoryTypeManager.creatable_by_user?(repo_params[:content_type]) msg = _("Invalid params provided - content_type must be one of %s") % RepositoryTypeManager.creatable_repository_types.keys.join(",") fail HttpErrors::UnprocessableEntity, msg end - gpg_key = @product.gpg_key - unless repo_params[:gpg_key_id].blank? - gpg_key = @gpg_key - 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 - repository = @product.add_repo(Hash[repo_params.slice(:label, :name, :url, :content_type, :arch, :unprotected, :gpg_key, :checksum_type, :download_policy).to_h.map { |k, v| [k.to_sym, v] }]) - repository.docker_upstream_name = repo_params[:docker_upstream_name] if repo_params[:docker_upstream_name] - repository.mirror_on_sync = ::Foreman::Cast.to_bool(repo_params[:mirror_on_sync]) if repo_params.key?(:mirror_on_sync) - repository.ignore_global_proxy = ::Foreman::Cast.to_bool(repo_params[:ignore_global_proxy]) if repo_params.key?(:ignore_global_proxy) - repository.verify_ssl_on_sync = ::Foreman::Cast.to_bool(repo_params[:verify_ssl_on_sync]) if repo_params.key?(:verify_ssl_on_sync) - repository.upstream_username = repo_params[:upstream_username] if repo_params.key?(:upstream_username) - repository.upstream_password = repo_params[:upstream_password] if repo_params.key?(:upstream_password) - if repository.ostree? - repository.ostree_upstream_sync_policy = repo_params[:ostree_upstream_sync_policy] - repository.ostree_upstream_sync_depth = repo_params[:ostree_upstream_sync_depth] - end - if repository.deb? - repository.deb_releases = repo_params[:deb_releases] if repo_params[:deb_releases] - repository.deb_components = repo_params[:deb_components] if repo_params[:deb_components] - repository.deb_architectures = repo_params[:deb_architectures] if repo_params[:deb_architectures] - end + repo_params[:ssl_ca_cert] = ssl_ca_cert + repo_params[:ssl_client_cert] = ssl_client_cert + repo_params[:ssl_client_key] = ssl_client_key + + repository = construct_repo_from_params(repo_params) sync_task(::Actions::Katello::Repository::Create, repository, false, true) repository = Repository.find(repository.id) respond_for_show(:resource => repository) end @@ -246,10 +268,12 @@ end end fail HttpErrors::BadRequest, _("Repository content type must be 'yum' to export.") unless @repository.content_type == 'yum' + fail HttpErrors::BadRequest, _("On demand repositories cannot be exported.") if @repository.download_policy == ::Runcible::Models::YumImporter::DOWNLOAD_ON_DEMAND + task = async_task(::Actions::Katello::Repository::Export, [@repository], ::Foreman::Cast.to_bool(params[:export_to_iso]), params[:since].try(:to_datetime), params[:iso_mb_size], @repository.pulp_id) @@ -317,11 +341,11 @@ api :POST, "/repositories/:id/upload_content", N_("Upload content into the repository") param :id, :number, :required => true, :desc => N_("repository ID") param :content, File, :required => true, :desc => N_("Content files to upload. Can be a single file or array of files.") def upload_content - fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload Docker content.") if @repository.docker? + fail Katello::Errors::InvalidRepositoryContent, _("Cannot upload Container Image content.") if @repository.docker? filepaths = Array.wrap(params[:content]).compact.collect do |content| {path: content.path, filename: content.original_filename} end @@ -349,41 +373,44 @@ param 'sync_capsule', :bool, :desc => N_("Whether or not to sync an external capsule after upload. Default: true") param :uploads, Array, :desc => N_("Array of uploads to import") do param 'id', String, :required => true param 'size', String param 'checksum', String - param 'name', String, :desc => N_("Needs to only be set for file repositories") + param 'name', String, :desc => N_("Needs to only be set for file repositories or docker tags") + param 'digest', String, :desc => N_("Needs to only be set for docker tags") end def import_uploads generate_metadata = ::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['upload_ids'].empty? && params['uploads'].empty? fail HttpErrors::BadRequest, _('No upload param specified. Either uploads or upload_ids (deprecated) is required.') end uploads = (params[:uploads] || []).map do |upload| - upload.permit(:id, :size, :checksum, :name).to_h + upload.permit(:id, :size, :checksum, :name, :digest).to_h end if params.key?(:upload_ids) ::Foreman::Deprecation.api_deprecation_warning("The parameter upload_ids will be removed in Katello 3.3. Please update to use the uploads parameter.") params[:upload_ids].each { |upload_id| uploads << {'id' => upload_id} } end upload_ids = uploads.map { |upload| upload['id'] } unit_keys = uploads.map do |upload| - if @repository.file? + if @repository.file? || @repository.docker? upload.except('id') else upload.except('id').except('name') end end + unit_type_id = unit_keys[0] && unit_keys[0].include?('digest') ? 'docker_tag' : @repository.unit_type_id + begin task = send(async ? :async_task : :sync_task, ::Actions::Katello::Repository::ImportUpload, - @repository, upload_ids, :unit_keys => unit_keys, + @repository, upload_ids, :unit_type_id => unit_type_id, :unit_keys => unit_keys, :generate_metadata => generate_metadata, :sync_capsule => sync_capsule) respond_for_async(resource: task) rescue => e raise HttpErrors::BadRequest, e.message end @@ -415,26 +442,68 @@ def find_repository @repository = Repository.find(params[:id]) end - def find_gpg_key - if params[:gpg_key_id] - @gpg_key = GpgKey.readable.where(:id => params[:gpg_key_id], :organization_id => @organization).first - fail HttpErrors::NotFound, _("Couldn't find gpg key '%s'") % params[:gpg_key_id] if @gpg_key.nil? + def find_content_credential(content_type) + credential_id = "#{content_type}_id".to_sym + credential_var = "@#{content_type}" + + if params[credential_id] + credential_value = GpgKey.readable.where(:id => params[credential_id], :organization_id => @organization).first + instance_variable_set(credential_var, credential_value) + if instance_variable_get(credential_var).nil? + fail HttpErrors::NotFound, _("Couldn't find %{content_type} with id '%{id}'") % { :content_type => content_type, :id => params[credential_id] } + end end end def repository_params keys = [:download_policy, :mirror_on_sync, :arch, :verify_ssl_on_sync, :upstream_password, :upstream_username, :ostree_upstream_sync_depth, :ostree_upstream_sync_policy, :ignore_global_proxy, - :deb_releases, :deb_components, :deb_architectures + :deb_releases, :deb_components, :deb_architectures, {:ignorable_content => []} ] + keys += [:label, :content_type] if params[:action] == "create" if params[:action] == 'create' || @repository.custom? - keys += [:url, :gpg_key_id, :unprotected, :name, :checksum_type, :docker_upstream_name] + keys += [:url, :gpg_key_id, :ssl_ca_cert_id, :ssl_client_cert_id, :ssl_client_key_id, :unprotected, :name, :checksum_type, :docker_upstream_name] end params.require(:repository).permit(*keys).to_h + end + + def get_content_credential(repo_params, content_type) + credential_value = @product.send(content_type) + + unless repo_params["#{content_type}_id".to_sym].blank? + credential_value = instance_variable_get("@#{content_type}") + end + + credential_value + end + + def construct_repo_from_params(repo_params) + repository = @product.add_repo(Hash[repo_params.slice(:label, :name, :url, :content_type, :arch, :unprotected, + :gpg_key, :ssl_ca_cert, :ssl_client_cert, :ssl_client_key, + :checksum_type, :download_policy).to_h.map { |k, v| [k.to_sym, v] }]) + repository.docker_upstream_name = repo_params[:docker_upstream_name] if repo_params[:docker_upstream_name] + repository.mirror_on_sync = ::Foreman::Cast.to_bool(repo_params[:mirror_on_sync]) if repo_params.key?(:mirror_on_sync) + repository.ignore_global_proxy = ::Foreman::Cast.to_bool(repo_params[:ignore_global_proxy]) if repo_params.key?(:ignore_global_proxy) + repository.verify_ssl_on_sync = ::Foreman::Cast.to_bool(repo_params[:verify_ssl_on_sync]) if repo_params.key?(:verify_ssl_on_sync) + repository.upstream_username = repo_params[:upstream_username] if repo_params.key?(:upstream_username) + repository.upstream_password = repo_params[:upstream_password] if repo_params.key?(:upstream_password) + repository.ignorable_content = repo_params[:ignorable_content] if repository.yum? && repo_params.key?(:ignorable_content) + + if repository.ostree? + repository.ostree_upstream_sync_policy = repo_params[:ostree_upstream_sync_policy] + repository.ostree_upstream_sync_depth = repo_params[:ostree_upstream_sync_depth] + end + if repository.deb? + repository.deb_releases = repo_params[:deb_releases] if repo_params[:deb_releases] + repository.deb_components = repo_params[:deb_components] if repo_params[:deb_components] + repository.deb_architectures = repo_params[:deb_architectures] if repo_params[:deb_architectures] + end + + repository end def error_on_rh_product fail HttpErrors::BadRequest, _("Red Hat products cannot be manipulated.") if @product.redhat? end