Class: Katello::ContentView

Inherits:
Model
  • Object
show all
Includes:
Foreman::ObservableModel, ForemanTasks::Concerns::ActionSubject, Authorization::ContentView, Ext::LabelFromName
Defined in:
app/models/katello/content_view.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Classes: Jail

Constant Summary collapse

CONTENT_DIR =
"content_views".freeze
IMPORT_LIBRARY =
"Import-Library".freeze
EXPORT_LIBRARY =
"Export-Library".freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Authorization::ContentView

#deletable?, #editable?, #promotable_or_removable?, #publishable?, #readable?

Methods included from Ext::LabelFromName

included, #label_not_changed, #setup_label_from_name

Methods inherited from Model

#destroy!

Class Method Details

.completer_scope_options(search) ⇒ Object



146
147
148
149
150
151
152
153
# File 'app/models/katello/content_view.rb', line 146

def self.completer_scope_options(search)
  if search.include?('content_views')
    # Don't autocomplete CCV names when searching for components
    { :value_filter => { :composite => false } }
  else
    {}
  end
end

.find_components_by_cv_name(_key, operator, value) ⇒ Object



138
139
140
141
142
143
144
# File 'app/models/katello/content_view.rb', line 138

def self.find_components_by_cv_name(_key, operator, value)
  kcv = Katello::ContentView.table_name
  kcvc = Katello::ContentViewComponent.table_name
  { :conditions => "#{kcv}.composite = 't' AND #{kcv}.id IN (SELECT #{kcvc}.composite_content_view_id FROM #{kcvc} WHERE #{kcvc}.content_view_id IN (SELECT #{kcv}.id FROM #{kcv} WHERE #{kcv}.name #{operator} ?))",
    :parameter => [value],
  }
end

.humanize_class_name(_name = nil) ⇒ Object



801
802
803
# File 'app/models/katello/content_view.rb', line 801

def self.humanize_class_name(_name = nil)
  _("Content Views")
end

.in_environment(env) ⇒ Object



176
177
178
179
# File 'app/models/katello/content_view.rb', line 176

def self.in_environment(env)
  joins(:content_view_environments).
    where("#{Katello::ContentViewEnvironment.table_name}.environment_id = ?", env.id)
end

.in_organization(org) ⇒ Object



185
186
187
# File 'app/models/katello/content_view.rb', line 185

def self.in_organization(org)
  where(organization_id: org.id) unless org.nil?
end

.published_with_repositories(root_repository) ⇒ Object



181
182
183
# File 'app/models/katello/content_view.rb', line 181

def self.published_with_repositories(root_repository)
  joins(:content_view_versions => :repositories).where("katello_repositories.root_id" => root_repository.id).uniq
end

Instance Method Details

#add_components(components_to_add) ⇒ Object

Adds content view components based on the input

:latest=> false, :latest=> true ..


227
228
229
230
231
# File 'app/models/katello/content_view.rb', line 227

def add_components(components_to_add)
  components_to_add.each do |cvc|
    content_view_components.build(cvc)
  end
end

#add_environment(env, version) ⇒ Object

Associate an environment with this content view. This can occur whenever a version of the view is promoted to an environment. It is necessary for candlepin to become aware that the view is available for consumers.



594
595
596
597
598
599
600
601
602
603
604
605
# File 'app/models/katello/content_view.rb', line 594

def add_environment(env, version)
  if self.content_view_environments.where(:environment_id => env.id).empty?
    label = generate_cp_environment_label(env)
    ContentViewEnvironment.create!(:name => label,
                                   :label => label,
                                   :cp_id => generate_cp_environment_id(env),
                                   :environment_id => env.id,
                                   :content_view => self,
                                   :content_view_version => version
    )
  end
end

#all_version_library_instancesObject

get the library instances of all repos within this view



508
509
510
511
512
# File 'app/models/katello/content_view.rb', line 508

def all_version_library_instances
  all_repos = all_version_repos.where(:library_instance_id => nil).pluck("#{Katello::Repository.table_name}.id")
  all_repos += all_version_repos.pluck(:library_instance_id)
  Repository.where(:id => all_repos)
end

#all_version_reposObject



402
403
404
405
# File 'app/models/katello/content_view.rb', line 402

def all_version_repos
  Repository.joins(:content_view_version).
    where("#{Katello::ContentViewVersion.table_name}.content_view_id" => self.id)
end

#as_json(options = {}) ⇒ Object

NOTE: this function will most likely become obsolete once we drop api v1



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'app/models/katello/content_view.rb', line 310

def as_json(options = {})
  result = self.attributes
  result['organization'] = self.organization.try(:name)
  result['environments'] = environments.map { |e| e.try(:name) }
  result['versions'] = versions.map(&:version)
  result['versions_details'] = versions.map do |v|
    {
      :version => v.version,
      :published => v.created_at.to_s,
      :environments => v.environments.map { |e| e.name },
    }
  end

  if options && options[:environment].present?
    result['repositories'] = repos(options[:environment]).map(&:name)
  end

  result
end

#audited_changes_present?Boolean

Returns:

  • (Boolean)


906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
# File 'app/models/katello/content_view.rb', line 906

def audited_changes_present?
  latest_version_created_at = latest_version_object.created_at
  cv_repository_ids = repositories.pluck(:id)

  audited_changes_like = ->(param) {
    Arel.sql("#{Audit.table_name}.audited_changes ilike '%#{param}%'")
  }

  table = Audit.arel_table
  repository_condition = table[:auditable_id].eq(id)
                                             .and(table[:auditable_type].eq('Katello::ContentView'))
                                             .and(audited_changes_like.call("repository_ids"))

  cv_repository_condition = table[:auditable_id].in(cv_repository_ids)
                                                .and(table[:auditable_type].eq('Katello::Repository'))
                                                .and(Arel.sql("(#{audited_changes_like.call("publication_href")} OR #{audited_changes_like.call("version_href")})"))

  content_view_filter_condition = table[:auditable_type].eq('Katello::ContentViewFilter').and(table[:associated_id].eq(id))

  filter_rule_condition = table[:associated_id].eq(id).and(table[:auditable_type].matches('%FilterRule%'))

  base_query = table[:created_at].gt(latest_version_created_at)

  final_query = base_query.and(repository_condition.or(cv_repository_condition).or(content_view_filter_condition).or(filter_rule_condition))

  Audit.where(final_query).exists?
end

#auto_publish_componentsObject



452
453
454
# File 'app/models/katello/content_view.rb', line 452

def auto_publish_components
  component_composites.where(latest: true).joins(:composite_content_view).where(self.class.table_name => { auto_publish: true })
end

#auto_publish_compositesObject



456
457
458
# File 'app/models/katello/content_view.rb', line 456

def auto_publish_composites
  Katello::ContentView.joins(:content_view_components).merge(auto_publish_components)
end

#blocking_taskObject



942
943
944
945
946
947
948
949
950
951
# File 'app/models/katello/content_view.rb', line 942

def blocking_task
  blocking_task_labels = [
    ::Actions::Katello::ContentView::Publish.name,
  ]
  ForemanTasks::Task::DynflowTask.where(:label => blocking_task_labels)
                                 .where.not(state: 'stopped')
                                 .for_resource(self)
                                 .order(:started_at)
                                 .last
end

#check_component_publishes!Object



699
700
701
702
703
704
705
# File 'app/models/katello/content_view.rb', line 699

def check_component_publishes!
  return unless composite?

  if ::Katello::ContentViewManager.running_component_publish_tasks(self).any?
    fail ::Katello::Errors::ConflictException, _("Cannot publish composite content view while its content views are being published. Please wait for component publishes to complete.")
  end
end

#check_composite_action_allowed!(env) ⇒ Object



724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
# File 'app/models/katello/content_view.rb', line 724

def check_composite_action_allowed!(env)
  if composite? && Setting['restrict_composite_view']
    if components.size != content_view_components.size
      fail _("Make sure all the component content views are published before publishing/promoting the composite content view. "\
           "This restriction is optional and can be modified in the Administrator -> Settings -> Content "\
            "page using the restrict_composite_view flag.")
    end

    env_ids = env.try(:pluck, 'id') || []
    env_ids << env.id unless env_ids.size > 0
    components.each do |component|
      component_environment_ids = component.environments.pluck('id')
      unless (env_ids - component_environment_ids).empty?
        fail _("The action requested on this composite view cannot be performed until all of the "\
               "component content view versions have been promoted to the target environment: %{env}.  "\
               "This restriction is optional and can be modified in the Administrator -> Settings -> Content "\
               "page using the restrict_composite_view flag.") %
               { :env => env.try(:pluck, 'name') || env.name }
      end
    end
  end
  true
end

#check_default_label_nameObject



568
569
570
571
572
# File 'app/models/katello/content_view.rb', line 568

def check_default_label_name
  if default? && !(name == 'Default Organization View' && label == 'Default_Organization_View')
    errors.add(:base, _("Name and label of default content view should not be changed"))
  end
end

#check_docker_conflictsObject



574
575
576
577
578
579
# File 'app/models/katello/content_view.rb', line 574

def check_docker_conflicts
  duplicate_docker_repos.each do |repo|
    msg = _("Container Image repo '%{repo}' is present in multiple component content views.") % { repo: repo.name }
    errors.add(:base, msg)
  end
end

#check_docker_repository_names!(environments) ⇒ Object



707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'app/models/katello/content_view.rb', line 707

def check_docker_repository_names!(environments)
  environments.each do |environment|
    repositories = []
    publish_repositories do |all_repositories|
      repositories += all_repositories.keep_if { |repository| repository.content_type == Katello::Repository::DOCKER_TYPE }
    end
    next if repositories.empty?

    error_messages = ::Katello::Validators::EnvironmentDockerRepositoriesValidator.validate_repositories(environment.registry_name_pattern, repositories)
    unless error_messages.empty?
      error_messages << _("Consider changing the Lifecycle Environment's Registry Name Pattern to something more specific.")
      fail error_messages.join("  ")
    end
  end
  true
end

#check_non_composite_auto_publishObject



562
563
564
565
566
# File 'app/models/katello/content_view.rb', line 562

def check_non_composite_auto_publish
  if !composite? && auto_publish
    errors.add(:base, _("Cannot set auto publish to a non-composite content view"))
  end
end

#check_non_composite_componentsObject



556
557
558
559
560
# File 'app/models/katello/content_view.rb', line 556

def check_non_composite_components
  if !composite? && components.present?
    errors.add(:base, _("Cannot add component versions to a non-composite content view"))
  end
end

#check_orphaned_content_facets!(environments: []) ⇒ Object



748
749
750
751
752
753
754
755
756
757
758
# File 'app/models/katello/content_view.rb', line 748

def check_orphaned_content_facets!(environments: [])
  Location.no_taxonomy_scope do
    User.as_anonymous_admin do
      ::Katello::Host::ContentFacet.with_content_views(self).with_environments(environments).each do |facet|
        unless facet.host
          fail _("Orphaned content facets for deleted hosts exist for the content view and environment. Please run rake task : katello:clean_orphaned_facets and try again!")
        end
      end
    end
  end
end

#check_ready_to_destroy!Object



780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
# File 'app/models/katello/content_view.rb', line 780

def check_ready_to_destroy!
  check_orphaned_content_facets!(environments: self.environments)
  errors = []

  dependencies = { environments: _("environments"),
                   hosts: _("hosts"),
                   activation_keys: _("activation keys"),
                   hostgroups: _("host groups"),
  }

  dependencies.each do |key, name|
    if (models = self.association(key).scope).any?
      errors << _("Cannot delete '%{view}' due to associated %{dependent}: %{names}.") %
        { view: self.name, dependent: name, names: models.map(&:name).join(", ") }
    end
  end

  fail errors.join(" ") if errors.any?
  return true
end

#check_ready_to_import!Object



656
657
658
659
660
# File 'app/models/katello/content_view.rb', line 656

def check_ready_to_import!
  fail _("Cannot import a composite content view") if composite?
  fail _("This Content View must be set to Import-only before performing an import") unless import_only?
  true
end

#check_ready_to_publish!(importing: false, syncable: false) ⇒ Object



662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
# File 'app/models/katello/content_view.rb', line 662

def check_ready_to_publish!(importing: false, syncable: false)
  fail _("User must be logged in.") if ::User.current.nil?
  fail _("Cannot publish default content view") if default?

  if importing
    check_ready_to_import!
  else
    fail _("Import-only content views can not be published directly") if import_only? && !syncable
    check_repositories_blocking_publish!
    check_composite_action_allowed!(organization.library)
    check_docker_repository_names!([organization.library])
    check_orphaned_content_facets!(environments: self.environments)
    check_scheduled_publish!
    check_component_publishes!
  end

  true
end

#check_remove_from_environment!(env) ⇒ Object



760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
# File 'app/models/katello/content_view.rb', line 760

def check_remove_from_environment!(env)
  check_orphaned_content_facets!(environments: [env])
  errors = []

  dependencies = { hosts: _("hosts"),
                   activation_keys: _("activation keys"),
                   hostgroups: _("host groups"),
  }

  dependencies.each do |key, name|
    if (models = self.association(key).scope.in_environments(env)).any?
      errors << _("Cannot remove '%{view}' from environment '%{env}' due to associated %{dependent}: %{names}.") %
        { view: self.name, env: env.name, dependent: name, names: models.map(&:name).join(", ") }
    end
  end

  fail errors.join(" ") if errors.any?
  return true
end

#check_repositories_blocking_publish!Object



681
682
683
684
685
686
687
688
689
# File 'app/models/katello/content_view.rb', line 681

def check_repositories_blocking_publish!
  blocking_tasks = repositories&.map { |repo| repo.blocking_task }&.compact

  if blocking_tasks&.any?
    errored_tasks = blocking_tasks.uniq.map { |task| "- #{Setting['foreman_url']}/foreman_tasks/tasks/#{task&.id}" }.join("\n")
    fail _("Pending tasks detected in repositories of this content view. Please wait for the tasks: " +
             errored_tasks + " before publishing.")
  end
end

#check_scheduled_publish!Object



691
692
693
694
695
696
697
# File 'app/models/katello/content_view.rb', line 691

def check_scheduled_publish!
  return unless composite?

  if ::Katello::ContentViewManager.scheduled_composite_publish?(self)
    fail ::Katello::Errors::ConflictException, _("A publish is already scheduled for this content view. Please wait for the scheduled publish to complete.")
  end
end

#component_idsObject



213
214
215
# File 'app/models/katello/content_view.rb', line 213

def component_ids
  components.map(&:id)
end

#component_ids=(component_version_ids_to_set) ⇒ Object

Warning this call wipes out existing associations And replaces them with the component version ids passed in.



241
242
243
244
245
246
247
248
249
# File 'app/models/katello/content_view.rb', line 241

def component_ids=(component_version_ids_to_set)
  content_view_components.destroy_all
  component_version_ids_to_set.each do |content_view_version_id|
    cvv = ContentViewVersion.find(content_view_version_id)
    content_view_components.build(:content_view_version => cvv,
                                  :latest => false,
                                  :composite_content_view => self)
  end
end

#component_repositoriesObject



485
486
487
# File 'app/models/katello/content_view.rb', line 485

def component_repositories
  components.map(&:archived_repos).flatten
end

#component_repository_idsObject



489
490
491
# File 'app/models/katello/content_view.rb', line 489

def component_repository_ids
  component_repositories.map(&:id)
end

#componentsObject



217
218
219
# File 'app/models/katello/content_view.rb', line 217

def components
  content_view_components.map(&:latest_version).compact.freeze
end

#components_with_repo(library_instance) ⇒ Object



448
449
450
# File 'app/models/katello/content_view.rb', line 448

def components_with_repo(library_instance)
  components.select { |component| component.repositories.where(:library_instance => library_instance).any? }
end

#composite_cv_components_changed?Boolean

Returns:

  • (Boolean)


836
837
838
839
840
841
842
843
844
# File 'app/models/katello/content_view.rb', line 836

def composite_cv_components_changed?
  return true unless latest_version_object
  published_component_version_ids = latest_version_object.components.pluck(:id) || []
  unpublished_component_version_ids = content_view_components.where(latest: false).pluck(:content_view_version_id) || []
  content_view_components.where(latest: true).each do |latest_component|
    unpublished_component_version_ids << latest_component.content_view&.latest_version_object&.id
  end
  published_component_version_ids.compact.uniq.sort != unpublished_component_version_ids.compact.uniq.sort
end

#content_host_countObject



209
210
211
# File 'app/models/katello/content_view.rb', line 209

def content_host_count
  hosts.size
end

#content_view_environment(environment) ⇒ Object



581
582
583
# File 'app/models/katello/content_view.rb', line 581

def content_view_environment(environment)
  self.content_view_environments.where(:environment_id => environment.try(:id)).first
end

#copy(new_name) ⇒ Object



286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'app/models/katello/content_view.rb', line 286

def copy(new_name)
  new_view = ContentView.new
  new_view.name = new_name
  new_view.attributes = self.attributes.slice("description", "organization_id", "default", "composite", "solve_dependencies")
  new_view.save!
  new_view.repositories = self.repositories

  copy_components(new_view)

  copy_filters(new_view)
  new_view.save!
  new_view
end

#copy_components(new_view) ⇒ Object



251
252
253
254
255
256
257
# File 'app/models/katello/content_view.rb', line 251

def copy_components(new_view)
  self.content_view_components.each do |cvc|
    component = cvc.dup
    component.composite_content_view = new_view
    new_view.content_view_components << component
  end
end

#copy_filters(new_view) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'app/models/katello/content_view.rb', line 259

def copy_filters(new_view)
  self.filters.each do |filter|
    new_filter = filter.dup
    new_filter.repositories = filter.repositories
    new_view.filters << new_filter

    case filter.type
    when ContentViewDebFilter.name
      filter.deb_rules.each do |rule|
        new_filter.deb_rules << rule.dup
      end
    when ContentViewPackageFilter.name
      filter.package_rules.each do |rule|
        new_filter.package_rules << rule.dup
      end
    when ContentViewPackageGroupFilter.name
      filter.package_group_rules.each do |rule|
        new_filter.package_group_rules << rule.dup
      end
    when ContentViewErratumFilter.name
      filter.erratum_rules.each do |rule|
        new_filter.erratum_rules << rule.dup
      end
    end
  end
end

#cp_environment_id(env) ⇒ Object



633
634
635
# File 'app/models/katello/content_view.rb', line 633

def cp_environment_id(env)
  ContentViewEnvironment.where(:content_view_id => self, :environment_id => env).first.try(:cp_id)
end

#cp_environment_label(env) ⇒ Object



629
630
631
# File 'app/models/katello/content_view.rb', line 629

def cp_environment_label(env)
  ContentViewEnvironment.where(:content_view_id => self, :environment_id => env).first.try(:label)
end

#create_new_version(major = next_version, minor = 0, components = self.components) ⇒ Object



637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
# File 'app/models/katello/content_view.rb', line 637

def create_new_version(major = next_version, minor = 0, components = self.components)
  version = ContentViewVersion.create!(:major => major,
                                       :minor => minor,
                                       :content_view => self,
                                       :components => components
  )

  # TODO: If a controller creates a new version and then uses latest_version_object, the old data is displayed.
  #       To prevent this, a 'reload' would currently be necessary, but this is not very performant.
  #       However, this is currently not a problem because after your create_new_version there is no immediate
  #       access to latest_version_object, but the ContentView object is first completely reloaded.
  #
  #       In Rails 7.1, individual connections can be reloaded:
  #       https://www.shakacode.com/blog/rails-7-1-allows-resetting-singular-associations/

  update(:next_version => major.to_i + 1) unless major.to_i < next_version
  version
end

#cv_repo_indexed_after_last_published?Boolean

Returns:

  • (Boolean)


851
852
853
# File 'app/models/katello/content_view.rb', line 851

def cv_repo_indexed_after_last_published?
  repositories.any? { |repo| repo.last_indexed && repo.last_indexed > latest_version_object.created_at }
end

#delete(from_env) ⇒ Object



521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
# File 'app/models/katello/content_view.rb', line 521

def delete(from_env)
  if from_env.library? && in_non_library_environment?
    fail Errors::ChangesetContentException, _("Cannot delete view while it exists in environments")
  end

  version = self.version(from_env)
  if version.nil?
    fail Errors::ChangesetContentException, _("Cannot delete from %s, view does not exist there.") % from_env.name
  end
  version = ContentViewVersion.find(version.id)

  if (foreman_env = Environment.find_by_katello_id(self.organization, from_env, self))
    foreman_env.destroy
  end

  version.delete(from_env)
  self.destroy if self.versions.empty?
end

#dependency_solving_changed?Boolean

Returns:

  • (Boolean)


934
935
936
# File 'app/models/katello/content_view.rb', line 934

def dependency_solving_changed?
  latest_version_object.applied_filters && solve_dependencies != latest_version_object.applied_filters['dependency_solving']
end

#duplicate_docker_reposObject



552
553
554
# File 'app/models/katello/content_view.rb', line 552

def duplicate_docker_repos
  duplicate_repositories.docker_type
end

#duplicate_repositoriesObject



544
545
546
547
548
549
550
# File 'app/models/katello/content_view.rb', line 544

def duplicate_repositories
  counts = repositories_to_publish.each_with_object(Hash.new(0)) do |repo, h|
    h[repo.library_instance_id] += 1
  end
  ids = counts.select { |_k, v| v > 1 }.keys
  Repository.where(:id => ids)
end

#duplicate_repositories_to_publishObject



443
444
445
446
# File 'app/models/katello/content_view.rb', line 443

def duplicate_repositories_to_publish
  return [] unless composite?
  repositories_to_publish_by_library_instance.select { |key, val| val.count > 1 && key.present? }.keys
end

#filtered?Boolean

Returns:

  • (Boolean)


938
939
940
# File 'app/models/katello/content_view.rb', line 938

def filtered?
  filters.present?
end

#generated?Boolean

Returns:

  • (Boolean)


305
306
307
# File 'app/models/katello/content_view.rb', line 305

def generated?
  !generated_for_none?
end

#generated_for_library?Boolean

Returns:

  • (Boolean)


205
206
207
# File 'app/models/katello/content_view.rb', line 205

def generated_for_library?
  generated_for_library_export? || generated_for_library_import? || generated_for_library_export_syncable?
end

#generated_for_repository?Boolean

Returns:

  • (Boolean)


201
202
203
# File 'app/models/katello/content_view.rb', line 201

def generated_for_repository?
  generated_for_repository_export? || generated_for_repository_import? || generated_for_repository_export_syncable?
end

#get_repo_clone(env, repo) ⇒ Object



514
515
516
517
518
519
# File 'app/models/katello/content_view.rb', line 514

def get_repo_clone(env, repo)
  lib_id = repo.library_instance_id || repo.id
  Repository.in_environment(env).where(:library_instance_id => lib_id).
    joins(:content_view_version).
    where("#{Katello::ContentViewVersion.table_name}.content_view_id" => self.id)
end

#historyObject



370
371
372
373
# File 'app/models/katello/content_view.rb', line 370

def history
  Katello::ContentViewHistory.joins(:content_view_version).where(
    "#{Katello::ContentViewVersion.table_name}.content_view_id" => self.id)
end

#in_environment?(env) ⇒ Boolean

Returns:

  • (Boolean)


338
339
340
# File 'app/models/katello/content_view.rb', line 338

def in_environment?(env)
  environments.include?(env)
end

#in_non_library_environment?Boolean

Returns:

  • (Boolean)


540
541
542
# File 'app/models/katello/content_view.rb', line 540

def in_non_library_environment?
  environments.where(:library => false).length > 0
end

#last_publish_task_success?Boolean

Returns:

  • (Boolean)


846
847
848
849
# File 'app/models/katello/content_view.rb', line 846

def last_publish_task_success?
  last_publish_result = latest_version_object&.history&.publish&.first&.task&.result
  return last_publish_result.present? && last_publish_result == 'success'
end

#last_taskObject



365
366
367
368
# File 'app/models/katello/content_view.rb', line 365

def last_task
  last_task_id = history.order(:created_at)&.last&.task_id
  last_task_id ? ForemanTasks::Task.find_by(id: last_task_id) : nil
end

#latest_versionObject



351
352
353
# File 'app/models/katello/content_view.rb', line 351

def latest_version
  latest_version_object.try(:version)
end

#latest_version_envObject



359
360
361
362
363
# File 'app/models/katello/content_view.rb', line 359

def latest_version_env
  environments = organization.readable_promotion_paths.flatten
  environments.insert(0, organization.library)
  environments.intersection(latest_version_object.try(:environments) || [])
end

#latest_version_idObject



355
356
357
# File 'app/models/katello/content_view.rb', line 355

def latest_version_id
  latest_version_object.try(:id)
end

#library_export?Boolean

Returns:

  • (Boolean)


197
198
199
# File 'app/models/katello/content_view.rb', line 197

def library_export?
  name.start_with? EXPORT_LIBRARY
end

#library_import?Boolean

Returns:

  • (Boolean)


193
194
195
# File 'app/models/katello/content_view.rb', line 193

def library_import?
  name == IMPORT_LIBRARY
end

#library_repo_idsObject



398
399
400
# File 'app/models/katello/content_view.rb', line 398

def library_repo_ids
  repos(self.organization.library).map { |r| r.library_instance_id }
end

#library_reposObject



394
395
396
# File 'app/models/katello/content_view.rb', line 394

def library_repos
  Repository.where(:id => library_repo_ids)
end

#needs_publish?Boolean

Returns:

  • (Boolean)


859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
# File 'app/models/katello/content_view.rb', line 859

def needs_publish?
  #Returns
  # True:
  #     a) When content/repo/filter change audit records exist
  #     b) CV hasn't ever been published
  #     c) CV dependency_solving != latest_version.applied_filters.dependency_solving
  #     d) If repo was indexed after cv publish. This can happen under 3 cases:
  #       i) Index runs because last index(before publish) had failed and repo is picked up for index even if pulp publication hasn't changed.
  #       ii) Complete sync runs or sync adds/removes new content (Already true because new pulp publication/version gets created)
  #       iii) repo.index_content is run. (This doesn't necessarily indicate contents changed. Corner case where we play safe and return true)
  # nil:
  #     a) When CV version creation audit is missing(Indicating audit cleanup)
  #     b) Version doesn't have audited_filters set indicating
  #     it was published before 4.9 upgrade when we started auditing changes on the CV.
  #     c) Last publish task failed leaving us with no way of knowing if all content in the version is correct.
  # False:
  #     a) No changes were detected via audits *and*
  #        Audit for CV publish exists (Audits haven't been cleaned up)
  #        *and* applied_filters field is set(Published after upgrade)
  #     b) Default, import only and generated CVs can not be published, hence these will always return false.
  #
  return false if unpublishable?
  return true unless latest_version_object
  return nil unless last_publish_task_success?
  return composite_cv_components_changed? if composite?
  # return true if the audit records clearly show we have unpublished changes
  return true if audited_changes_present?
  # return true if the dependency solving changed for CV between last publish and now
  return true if dependency_solving_changed?
  # return true if any child repo's indexed_at > last_version.created_at
  return true if cv_repo_indexed_after_last_published?
  # if we didn't return `true` already, either the audit records show that we don't need to publish, or we may
  # have insufficient data to make the determination (either audits were cleaned, or never got created at all).
  # first, check for the `create` audit record; its absence indicates that audits were cleaned some time after
  # the cv version was created (i.e. the first indeterminate state) so we return `nil` in that case.
  return nil unless latest_version_object&.audits&.where(action: "create")&.exists?
  # even when the `create` audit exists, the other audits could still be absent due to the latest cv version
  # being created prior to the tracking of the other audits that were added in katello 4.9 (i.e. the second indeterminate state).
  # We determine that using the `applied_filters` field. This field was added in Katello 4.9 and is set to nil for
  # all versions published before that upgrade.
  # If `applied_filters` is nil we can not deterministically rule out changes before the upgrade
  # not captured by newer content change and filter change audits.
  # If that field is not nil, the version was published after upgrade, hence we have all the information to rule out
  # any audited changes to the CV and we can deterministically return false
  latest_version_object.applied_filters.nil? ? nil : false
end

#on_demand_repositoriesObject



809
810
811
# File 'app/models/katello/content_view.rb', line 809

def on_demand_repositories
  repositories.on_demand
end

#products(env = nil) ⇒ Object



502
503
504
505
# File 'app/models/katello/content_view.rb', line 502

def products(env = nil)
  repos = repos(env)
  Product.joins(:repositories).where("#{Katello::Repository.table_name}.id" => repos.map(&:id)).distinct
end

#promoted?Boolean

Returns:

  • (Boolean)


300
301
302
303
# File 'app/models/katello/content_view.rb', line 300

def promoted?
  # if the view exists in more than 1 environment, it has been promoted
  self.environments.many?
end

#publish_repositories(override_components = nil) ⇒ Object



460
461
462
463
464
465
466
467
468
469
# File 'app/models/katello/content_view.rb', line 460

def publish_repositories(override_components = nil)
  repositories = composite? ? repositories_to_publish_by_library_instance(override_components).values : repositories_to_publish
  repositories.each do |repos|
    if repos.is_a? Array
      yield repos
    else
      yield [repos]
    end
  end
end


821
822
823
824
825
826
827
828
829
830
831
832
833
834
# File 'app/models/katello/content_view.rb', line 821

def related_composite_cvs
  content_views = []
  component_composites.each do |cv|
    cv_id = cv.composite_content_view_id
    cv_name = ContentView.find(cv_id).name
    content_views.push(
      {
        id: cv_id,
        name: cv_name,
      }
    )
  end
  content_views
end


813
814
815
816
817
818
819
# File 'app/models/katello/content_view.rb', line 813

def related_cv_count
  if composite
    content_view_components.length
  else
    component_composites.length
  end
end

#remove_components(components_to_remove) ⇒ Object

Removes selected content view components

1,2,34

> content view component ids/



235
236
237
# File 'app/models/katello/content_view.rb', line 235

def remove_components(components_to_remove)
  content_view_components.where(:id => components_to_remove).destroy_all
end

#remove_environment(env) ⇒ Object

Unassociate an environment from this content view. This can occur whenever a view is deleted from an environment. It is necessary to make candlepin aware that the view is no longer available for consumers.



610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
# File 'app/models/katello/content_view.rb', line 610

def remove_environment(env)
  # Do not remove the content view environment, if there is still a view
  # version in the environment.
  if self.versions.in_environment(env).blank?
    view_env = self.content_view_environments.where(:environment_id => env.id).first
    if view_env
      # Check for hostgroup dependencies before removal
      if view_env.hostgroup_content_facets.any?
        hostgroup_names = view_env.hostgroup_content_facets.map { |f| f.hostgroup.name }.join(", ")
        fail _("Cannot remove '%{view}' from lifecycle environment '%{env}' due to associated host groups: %{names}.") %
          { view: self.name, env: env.name, names: hostgroup_names }
      end
      unless view_env.destroy
        fail _("Failed to remove content view environment: %{errors}") % { errors: view_env.errors.full_messages.join(", ") }
      end
    end
  end
end

#repos(env = nil) ⇒ Object



385
386
387
388
389
390
391
392
# File 'app/models/katello/content_view.rb', line 385

def repos(env = nil)
  if env
    repo_ids = versions.flat_map { |version| version.repositories.in_environment(env) }.map(&:id)
  else
    repo_ids = versions.flat_map { |version| version.repositories }.map(&:id)
  end
  Repository.where(:id => repo_ids)
end

#repos_in_product(env, product) ⇒ Object



493
494
495
496
497
498
499
500
# File 'app/models/katello/content_view.rb', line 493

def repos_in_product(env, product)
  version = version(env)
  if version
    version.repositories.in_environment(env).in_product(product)
  else
    []
  end
end

#repositories_to_publish(override_components = nil) ⇒ Object



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'app/models/katello/content_view.rb', line 407

def repositories_to_publish(override_components = nil)
  if composite?
    components_to_publish = []
    components.each do |component|
      override_component = override_components&.detect do |override_cvv|
        override_cvv.content_view == component.content_view
      end

      if override_component
        components_to_publish << override_component
      else
        components_to_publish << component
      end
    end
    ids = components_to_publish.flat_map { |version| version.repositories.archived }.map(&:id)
    Repository.where(:id => ids)
  else
    repositories
  end
end

#repositories_to_publish_by_library_instance(override_components = nil) ⇒ Object



432
433
434
435
436
437
438
439
440
441
# File 'app/models/katello/content_view.rb', line 432

def repositories_to_publish_by_library_instance(override_components = nil)
  # retrieve the list of repositories in a hash, where the key
  # is the library instance id, and the value is an array
  # of the repositories for that instance.
  repositories_to_publish(override_components).inject({}) do |result, repo|
    result[repo.library_instance] ||= []
    result[repo.library_instance] << repo
    result
  end
end

#repositories_to_publish_idsObject



428
429
430
# File 'app/models/katello/content_view.rb', line 428

def repositories_to_publish_ids
  composite? ? repositories_to_publish.pluck(&:id) : repository_ids
end

#resulting_productsObject



381
382
383
# File 'app/models/katello/content_view.rb', line 381

def resulting_products
  (self.repositories.collect { |r| r.product }).uniq
end

#sorted_versionsObject



221
222
223
# File 'app/models/katello/content_view.rb', line 221

def sorted_versions
  versions.order('created_at DESC')
end

#to_sObject



189
190
191
# File 'app/models/katello/content_view.rb', line 189

def to_s
  name
end

#total_deb_package_count(env) ⇒ Object



334
335
336
# File 'app/models/katello/content_view.rb', line 334

def total_deb_package_count(env)
  Katello::Deb.in_repositories(self.repos(env)).count
end

#total_package_count(env) ⇒ Object



330
331
332
# File 'app/models/katello/content_view.rb', line 330

def total_package_count(env)
  Katello::Rpm.in_repositories(self.repos(env)).count
end

#unpublishable?Boolean

Returns:

  • (Boolean)


855
856
857
# File 'app/models/katello/content_view.rb', line 855

def unpublishable?
  default? || import_only? || generated? || rolling?
end

#update_cp_content(env) ⇒ Object



585
586
587
588
589
# File 'app/models/katello/content_view.rb', line 585

def update_cp_content(env)
  view_env = content_view_environment(env)

  view_env&.update_cp_content
end

#update_host_statuses(environment) ⇒ Object



471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'app/models/katello/content_view.rb', line 471

def update_host_statuses(environment)
  # update errata applicability counts for all hosts in the CV & LE
  Location.no_taxonomy_scope do
    User.as_anonymous_admin do
      ::Katello::Host::ContentFacet.with_content_views(self).with_environments(environment).each do |facet|
        facet.update_applicability_counts
        facet.update_errata_status
      rescue NoMethodError
        Rails.logger.warn _('Errata statuses not updated for deleted content facet with UUID %s') % facet.uuid
      end
    end
  end
end

#version(env) ⇒ Object



347
348
349
# File 'app/models/katello/content_view.rb', line 347

def version(env)
  self.versions.in_environment(env).order("#{Katello::ContentViewVersion.table_name}.id ASC").readonly(false).last
end

#version_countObject



805
806
807
# File 'app/models/katello/content_view.rb', line 805

def version_count
  content_view_versions.size
end

#version_environment(env) ⇒ Object



375
376
377
378
379
# File 'app/models/katello/content_view.rb', line 375

def version_environment(env)
  # TODO: rewrite this into SQL or use content_view_environment when that
  # points to environment
  version(env).content_view_version_environments.select { |cvve| cvve.environment_id == env.id }
end