foreman_content 0.3 → 0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -2
  3. data/app/assets/javascripts/content/content.js +23 -12
  4. data/app/assets/javascripts/content/repository.js +19 -0
  5. data/app/controllers/content/content_views_controller.rb +19 -6
  6. data/app/controllers/content/products_controller.rb +1 -1
  7. data/app/controllers/content/repositories_controller.rb +9 -3
  8. data/app/helpers/content/content_views_helper.rb +24 -4
  9. data/app/helpers/content/repositories_helper.rb +28 -0
  10. data/app/models/concerns/content/host_extensions.rb +1 -1
  11. data/app/models/concerns/content/operatingsystem_extensions.rb +1 -1
  12. data/app/models/concerns/content/orchestration/pulp.rb +1 -1
  13. data/app/models/concerns/content/orchestration/pulp/sync.rb +37 -2
  14. data/app/models/concerns/content/redhat_extensions.rb +19 -3
  15. data/app/models/concerns/content/repository_common.rb +13 -0
  16. data/app/models/content/content_view.rb +21 -15
  17. data/app/models/content/content_view_repository_clone.rb +2 -2
  18. data/app/models/content/product.rb +1 -1
  19. data/app/models/content/repository.rb +24 -8
  20. data/app/models/content/repository/operating_system.rb +11 -2
  21. data/app/models/content/repository/product.rb +8 -3
  22. data/app/models/content/repository_clone.rb +5 -10
  23. data/app/services/content/content_view_factory.rb +46 -25
  24. data/app/services/content/pulp/repository.rb +13 -0
  25. data/app/services/content/pulp/repository_sync_history.rb +8 -2
  26. data/app/views/content/content_views/{_step2.html.erb → _composite.html.erb} +1 -1
  27. data/app/views/content/content_views/_form.html.erb +17 -14
  28. data/app/views/content/content_views/index.html.erb +4 -3
  29. data/app/views/content/content_views/new.html.erb +1 -7
  30. data/app/views/content/content_views/show.erb +22 -0
  31. data/app/views/content/products/_form.html.erb +3 -9
  32. data/app/views/content/repositories/_fields.html.erb +9 -0
  33. data/app/views/content/repositories/_os_form.html.erb +6 -11
  34. data/app/views/content/repositories/_product_form.html.erb +7 -11
  35. data/app/views/content/repositories/_repository.html.erb +4 -0
  36. data/app/views/content/repositories/_sync_schedule.erb +10 -0
  37. data/app/views/content/repositories/edit.html.erb +2 -0
  38. data/app/views/content/repositories/index.html.erb +14 -16
  39. data/app/views/content/repositories/new.html.erb +2 -0
  40. data/app/views/content/repositories/show.html.erb +51 -37
  41. data/db/migrate/20130807121629_create_content_content_views.rb +1 -1
  42. data/db/migrate/20130828173006_change_content_view_index_name.rb +10 -0
  43. data/db/migrate/20130901070945_add_publish_boolean_to_content_repository.rb +6 -0
  44. data/db/migrate/20130902130445_change_repository_to_polymorphic.rb +42 -0
  45. data/db/migrate/20130902142326_add_polymorphic_repo_to_content_view_repository_clone.rb +8 -0
  46. data/lib/content/version.rb +1 -1
  47. metadata +14 -18
@@ -1,5 +1,5 @@
1
1
  class Content::ContentViewRepositoryClone < ActiveRecord::Base
2
2
  belongs_to :content_view
3
- belongs_to :repository_clone
4
- validate :content_view_id, :repository_clone_id, :presence => true
3
+ belongs_to :repository, :polymorphic => true
4
+ validate :content_view_id, :repository_id, :repository_type, :presence => true
5
5
  end
@@ -2,7 +2,7 @@ module Content
2
2
  class Product < ActiveRecord::Base
3
3
  include ::Taxonomix
4
4
 
5
- has_many :repositories
5
+ has_many :repositories, :as => :originator
6
6
  has_many :repository_clones, :through => :repositories
7
7
  has_many :content_views, :as => :originator
8
8
 
@@ -2,24 +2,29 @@ module Content
2
2
  class Repository < ActiveRecord::Base
3
3
  include Content::Orchestration::Pulp::Sync
4
4
  include Foreman::STI
5
+ include Content::RepositoryCommon
5
6
 
6
7
  YUM_TYPE = 'yum'
7
8
  KICKSTART_TYPE = 'kickstart'
8
9
  FILE_TYPE = 'iso'
9
10
  TYPES = [YUM_TYPE, KICKSTART_TYPE, FILE_TYPE]
10
11
 
11
- belongs_to :product, :class_name => 'Content::Product'
12
- belongs_to :operatingsystem
12
+ belongs_to :originator, :polymorphic => true
13
13
  belongs_to :gpg_key
14
14
  belongs_to :architecture
15
15
  has_many :repository_clones
16
+ has_many :content_view_repository_clones, :dependent => :destroy, :as => :repository
16
17
 
18
+ before_validation :set_originator_type
17
19
  before_destroy EnsureNotUsedBy.new(:repository_clones)
18
20
 
19
21
  validates_presence_of :type # can't create this object, only child
20
22
 
23
+ validates_presence_of :originator_id, :originator_type
24
+
21
25
  validates :name, :presence => true
22
- validates_uniqueness_of :name, :scope => [:operatingsystem_id, :product_id]
26
+ validates_uniqueness_of :name, :scope => [:originator_type, :originator_id]
27
+ validates :feed, :presence => true, :uniqueness => true
23
28
  validates_inclusion_of :content_type,
24
29
  :in => TYPES,
25
30
  :allow_blank => false,
@@ -27,19 +32,30 @@ module Content
27
32
 
28
33
  scoped_search :on => [:name, :enabled], :complete_value => :true
29
34
  scoped_search :in => :architecture, :on => :name, :rename => :architecture, :complete_value => :true
30
- scoped_search :in => :operatingsystem, :on => :name, :rename => :os, :complete_value => :true
31
- scoped_search :in => :product, :on => :name, :rename => :product, :complete_value => :true
35
+ belongs_to :search_operatingsystems, :class_name => 'Operatingsystem', :foreign_key => :originator_id,
36
+ :conditions => '"content_repositories"."originator_type" = "Operatingsystem"'
37
+ belongs_to :search_products, :class_name => 'Content::Product', :foreign_key => :originator_id,
38
+ :conditions => '"content_repositories"."originator_type" = "Content::Product"'
39
+ scoped_search :in => :search_products, :on => :name, :rename => :product,
40
+ :complete_value => :true, :only_explicit => true
41
+ scoped_search :in => :search_operatingsystems, :on => :name, :rename => :operatingsystem,
42
+ :complete_value => :true, :only_explicit => true
32
43
 
33
44
  scope :kickstart, where(:content_type => KICKSTART_TYPE)
34
45
  scope :yum, where(:content_type => YUM_TYPE)
35
46
 
47
+ scope :for_content_views, lambda { |ids|
48
+ joins(:content_view_repository_clones).
49
+ where('content_content_view_repository_clones' => {:content_view_id => ids})
50
+ }
51
+
36
52
  def content_types
37
53
  TYPES
38
54
  end
39
55
 
40
56
  # The label is used as a repository label in a yum repo file.
41
57
  def to_label
42
- "#{entity_name}-#{name}".parameterize
58
+ "#{entity_name}/#{name}"
43
59
  end
44
60
 
45
61
  #inhariters are expected to override this method
@@ -47,11 +63,11 @@ module Content
47
63
  ''
48
64
  end
49
65
 
50
- def publish content_view
66
+ def clone content_view
51
67
  repository_clones.create!(
52
68
  :content_views => [content_view],
53
69
  :name => self.name + "_clone",
54
- :relative_path => "content_views/#{to_label}/#{Foreman.uuid}"
70
+ :relative_path => "content_views/#{entity_name}/#{name}/#{Foreman.uuid}"
55
71
  )
56
72
  end
57
73
 
@@ -1,7 +1,6 @@
1
1
  module Content
2
2
  class Repository::OperatingSystem < Repository
3
-
4
- validates_presence_of :operatingsystem
3
+ alias_method :operatingsystem, :originator
5
4
 
6
5
  def self.model_name
7
6
  Repository.model_name
@@ -10,5 +9,15 @@ module Content
10
9
  def entity_name
11
10
  operatingsystem.to_label
12
11
  end
12
+
13
+ def description
14
+ entity_name
15
+ end
16
+
17
+ private
18
+
19
+ def set_originator_type
20
+ self.originator_type ||= 'Operatingsystem'
21
+ end
13
22
  end
14
23
  end
@@ -1,10 +1,9 @@
1
1
  module Content
2
2
  class Repository::Product < Repository
3
-
3
+ alias_method :product, :originator
4
4
  has_many :operatingsystem_repositories, :foreign_key => :repository_id, :dependent => :destroy, :uniq => true
5
5
  has_many :operatingsystems, :through => :operatingsystem_repositories
6
-
7
- validates_presence_of :product
6
+ delegate :description, :to => :product
8
7
 
9
8
  def self.model_name
10
9
  Repository.model_name
@@ -17,5 +16,11 @@ module Content
17
16
  def entity_name
18
17
  product.name
19
18
  end
19
+
20
+ private
21
+
22
+ def set_originator_type
23
+ self.originator_type = 'Content::Product'
24
+ end
20
25
  end
21
26
  end
@@ -1,29 +1,24 @@
1
1
  module Content
2
2
  class RepositoryClone < ActiveRecord::Base
3
3
  include Content::Orchestration::Pulp::Clone
4
-
5
- REPO_PREFIX = '/pulp/repos/'
4
+ include Content::RepositoryCommon
6
5
 
7
6
  belongs_to :repository
8
- has_many :content_view_repository_clones, :dependent => :destroy
7
+ has_many :content_view_repository_clones, :dependent => :destroy, :as => :repository
9
8
  has_many :content_views, :through => :content_view_repository_clones
10
9
  before_destroy EnsureNotUsedBy.new(:content_views)
11
10
 
12
11
  attr_accessible :description, :last_published, :name, :pulp_id, :relative_path, :status, :content_views
13
12
  validate :relative_path, :repository_id, :presence => true
14
13
 
15
- delegate :content_type, :architecture, :unprotected, :gpg_key, :product, :to => :repository
14
+ delegate :content_type, :architecture, :unprotected, :gpg_key, :product, :operatingsystem, :enabled, :to => :repository
16
15
 
17
16
  scope :for_content_views, lambda { |ids|
18
17
  joins(:content_view_repository_clones).
19
18
  where('content_content_view_repository_clones' => {:content_view_id => ids})
20
19
  }
21
20
 
22
- def full_path
23
- pulp_url = URI.parse(Setting.pulp_url)
24
- scheme = (unprotected ? 'http' : 'https')
25
- port = (pulp_url.port == 443 || pulp_url.port == 80 ? "" : ":#{pulp_url.port}")
26
- "#{scheme}://#{pulp_url.host}#{port}#{REPO_PREFIX}#{relative_path}"
27
- end
21
+ default_scope { includes(:repository) }
22
+
28
23
  end
29
24
  end
@@ -1,32 +1,11 @@
1
1
  module Content
2
2
  class ContentViewFactory
3
3
  include ActiveModel::Conversion
4
- extend ActiveModel::Naming
4
+ include ActiveModel::Validations
5
+ extend ActiveModel::Naming
5
6
 
6
- attr_accessor :type, :originator_id, :parent_cv, :product_cv, :os_cv
7
- # create a content view of a single product
8
- def self.create_product_content_view(product_id)
9
- product = Product.find_by_id(product_id)
10
- ContentView.new(:source_repositories => product.repositories, :originator => product)
11
- end
12
-
13
- # create a content view of an operating system
14
- def self.create_os_content_view(operatingsystem_id)
15
- os = Operatingsystem.find_by_id(operatingsystem_id)
16
- ContentView.new(:source_repositories => os.repositories, :originator => os)
17
- end
18
-
19
- # create a composite content view of a hostgroup (the hostgroup may have a list of products) and a parent
20
- # content view. The parent content view is one of the hostgroup parent content views.
21
- # A hostgroup may have number of content views representing different versions of that hostgroup content.
22
- def self.create_composite_content_view(options = {})
23
- factory = new(options)
24
- hostgroup = Hostgroup.find_by_id(factory.originator_id)
25
- clone_ids = Content::RepositoryClone.for_content_views([factory.product_cv, factory.os_cv]).
26
- pluck(:id)
27
- ContentView.new(:source_repositories=> [], :originator => hostgroup,
28
- :repository_clone_ids => clone_ids, :parent_id => factory.parent_cv)
29
- end
7
+ attr_accessor :originator_type, :originator_id, :parent_cv, :product_cv, :os_cv, :source_repositories
8
+ validates_presence_of :originator_type, :originator_id
30
9
 
31
10
  def initialize(attributes = {})
32
11
  attributes.each do |name, value|
@@ -37,5 +16,47 @@ module Content
37
16
  def persisted?
38
17
  false
39
18
  end
19
+
20
+ def content_view
21
+ case originator_type
22
+ when 'Hostgroup'
23
+ create_composite_content_view
24
+ else
25
+ ContentView.new(:repository_ids_to_clone => originator.repositories, :originator => originator)
26
+ end
27
+ end
28
+
29
+ def repositories
30
+ originator.repositories
31
+ end
32
+
33
+ private
34
+
35
+ def originator
36
+ originator ||= case originator_type
37
+ when 'Content::Product'
38
+ Product.find(originator_id)
39
+ when 'Operatingsystem'
40
+ Operatingsystem.find(originator_id)
41
+ when 'Hostgroup'
42
+ Hostgroup.find(originator_id)
43
+ else
44
+ raise _('Unknown type')
45
+ end
46
+ end
47
+
48
+ # create a composite content view of a hostgroup (the hostgroup may have a list of products) and a parent
49
+ # content view. The parent content view is one of the hostgroup parent content views.
50
+ # A hostgroup may have number of content views representing different versions of that hostgroup content.
51
+ def create_composite_content_view
52
+ clone_ids = Content::RepositoryClone.for_content_views([product_cv, os_cv]).pluck(:id)
53
+ source_ids = Content::Repository.for_content_views([product_cv, os_cv]).pluck(:id)
54
+ ContentView.new(:originator => originator,
55
+ :repository_clone_ids => clone_ids,
56
+ :repository_source_ids => source_ids,
57
+ :parent_id => parent_cv)
58
+ end
59
+
60
+
40
61
  end
41
62
  end
@@ -126,6 +126,19 @@ class Content::Pulp::Repository
126
126
  true
127
127
  end
128
128
 
129
+ def sync_schedule=(schedule)
130
+ type = Runcible::Extensions::YumImporter::ID
131
+ if schedule.present?
132
+ Runcible::Resources::RepositorySchedule.create(pulp_id, type, schedule)
133
+ else
134
+ Runcible::Extensions::Repository.remove_schedules(pulp_id, type)
135
+ end
136
+ end
137
+
138
+ def sync_schedule
139
+ Runcible::Resources::RepositorySchedule.list(pulp_id, Runcible::Extensions::YumImporter::ID)
140
+ end
141
+
129
142
  private
130
143
 
131
144
  def create_repository pulp_distributors = []
@@ -1,5 +1,5 @@
1
1
  class Content::Pulp::RepositorySyncHistory
2
- attr_reader :exception, :removed_count, :started, :completed, :summary,
2
+ attr_reader :exception, :removed_count, :started, :completed,
3
3
  :error_message, :added_count, :updated_count, :details, :result
4
4
 
5
5
  def initialize(attrs)
@@ -12,7 +12,7 @@ class Content::Pulp::RepositorySyncHistory
12
12
  {
13
13
  :comps => summary[:time_total_sec],
14
14
  :errata => summary[:errata_time_total_sec],
15
- :packages => summary[:packages][:time_total_sec]
15
+ :packages => (summary[:packages][:time_total_sec] rescue 0)
16
16
  }
17
17
  end
18
18
 
@@ -32,4 +32,10 @@ class Content::Pulp::RepositorySyncHistory
32
32
  }
33
33
  end
34
34
 
35
+ private
36
+
37
+ def summary
38
+ @summary ||= Hash.new(0)
39
+ end
40
+
35
41
  end
@@ -1,5 +1,5 @@
1
1
  <%= wizard_header 2, _("Select source type"), _("Select content"), _("Create") %>
2
- <%= form_for Content::ContentViewFactory.new, :url => hash_for_new_content_view_path, :class => 'form-horizontal well', :method => :get do |f| %>
2
+ <%= form_for @factory, :url => hash_for_new_content_view_path, :class => 'form-horizontal well', :method => :get do |f| %>
3
3
  <%= f.hidden_field :originator_id, :value => @hostgroup.id %>
4
4
  <%= f.hidden_field :originator_type, :value => @hostgroup.class.name %>
5
5
  <%= select_f f, :parent_cv, @hostgroup.parent.try(:content_views) || [], "id", "name",
@@ -1,3 +1,5 @@
1
+ <%= wizard_header 2, _('Select source type'), _('Select content'), _('Create') %>
2
+
1
3
  <%= form_for @content_view, :url => (@content_view.new_record? ? content_views_path : content_view_path(@content_view)) do |f| %>
2
4
  <%= base_errors_for @content_view %>
3
5
 
@@ -11,30 +13,31 @@
11
13
  <%= text_f f, :name, :value => @content_view.to_label %>
12
14
 
13
15
  <% if @content_view.new_record? %>
14
- <% @content_view.source_repositories.each do |repo| %>
15
- <%= f.hidden_field :source_repositories, :multiple => true, :value => repo.id %>
16
- <% end if @content_view.source_repositories %>
17
- <% @content_view.repository_clones.each do |repo| %>
18
- <%= f.hidden_field :repository_clone_ids, :multiple => true, :value => repo.id %>
19
- <% end if @content_view.repository_clones %>
20
-
21
16
  <%= f.hidden_field :originator_id, :value => @content_view.originator_id %>
22
17
  <%= f.hidden_field :originator_type, :value => @content_view.originator_type %>
23
18
  <% end %>
24
19
 
25
20
  <table class="table table-bordered">
26
21
  <tr>
22
+ <th></th>
27
23
  <th><%= _("Name") %></th>
28
24
  <th><%= _("State") %></th>
29
- <th><%= _("Last publish") %></th>
25
+ <th><%= f.object.kind_of?(Content::Repository) ? _('Last Sync') : _('Last Publish') %></th>
30
26
  <th><%= _("Content type") %></th>
31
27
  </tr>
32
- <% repositories(@content_view).each do |repository| %>
33
- <tr>
34
- <td><%= repository.name %></td>
35
- <td><%= repository.try(:state) %></td>
36
- <td><%= last_time(repository.last_published) %></td>
37
- <td><%= repository.content_type %></td>
28
+ <% repositories.each do |repository| %>
29
+ <tr class='repository_selection'>
30
+ <td>
31
+ <% if @content_view.new_record? %>
32
+ <%= check_box_tag nil, repository.id, true %>
33
+ <%= f.hidden_field :repository_ids_to_clone, :value => repository.id, :multiple => true, :id => "clone-#{repository.id}",
34
+ :disabled => true %>
35
+ <%= f.hidden_field :repository_source_ids, :value => repository.id, :multiple => true, :id => "latest-#{repository.id}",
36
+ :disabled => true %>
37
+ <%= select_tag(nil, options_for_type_selection(repository), {:class => 'cv-repo-selection span2', :id => "select-#{repository.id}"}) %>
38
+ <% end %>
39
+ </td>
40
+ <%= render :partial => 'content/repositories/repository', :locals => {:repository => repository} %>
38
41
  </tr>
39
42
  <% end %>
40
43
  </table>
@@ -2,7 +2,7 @@
2
2
  <% title_actions select_action_button( _('New content view'),
3
3
  display_link_if_authorized(_("Product view"), hash_for_new_content_view_path(:type=>'product')),
4
4
  display_link_if_authorized(_("Operating system view"), hash_for_new_content_view_path(:type=>'operatingsystem')),
5
- display_link_if_authorized(_("Hostgroup view"), hash_for_new_content_view_path(:type=>'hostgroup'))) %>
5
+ display_link_if_authorized(_("Composite hostgroup view"), hash_for_new_content_view_path(:type=>'hostgroup'))) %>
6
6
 
7
7
 
8
8
  <table class="table table-bordered table-striped">
@@ -14,11 +14,12 @@
14
14
  </tr>
15
15
  <% @content_views.each do |content_view| %>
16
16
  <tr>
17
- <td><%= link_to_if_authorized(h(content_view.name), hash_for_edit_content_view_path(content_view)) %></td>
17
+ <td><%= link_to_if_authorized(h(content_view.name), hash_for_content_view_path(content_view)) %></td>
18
18
  <td><%= @counter[content_view.id] || '0' %></td>
19
19
  <td><%= _("%s ago") % time_ago_in_words(content_view.created_at) %></td>
20
20
  <td><%= action_buttons(
21
- display_delete_if_authorized(hash_for_content_view_path(content_view), :confirm => "Delete #{content_view}?")
21
+ display_link_if_authorized(_("Edit"), hash_for_edit_content_view_path(content_view)),
22
+ display_delete_if_authorized(hash_for_content_view_path(content_view), :confirm => "Delete #{content_view}?")
22
23
  )%></td>
23
24
  </tr>
24
25
  <% end %>
@@ -2,10 +2,4 @@
2
2
 
3
3
  <% title _("New Content View") %>
4
4
 
5
- <% if @content_view %>
6
- <%= render :partial => 'form' %>
7
- <% elsif @hostgroup %>
8
- <%= render :partial => 'step2' %>
9
- <% else %>
10
- <%= render :partial => 'step1' %>
11
- <% end %>
5
+ <%= render :partial => step? %>
@@ -0,0 +1,22 @@
1
+ <% title @content_view.to_label %>
2
+
3
+ <%= title_actions(button_group(
4
+ link_to_if_authorized(_("Edit"), hash_for_edit_content_view_path(@content_view)),
5
+ display_delete_if_authorized(hash_for_content_view_path(@content_view), :class => 'btn-danger', :confirm => "Delete #{@content_view.name}?")
6
+ )) %>
7
+
8
+ <h3><%= _('Repositories') %></h3>
9
+ <table class="table table-bordered table-striped">
10
+ <tr>
11
+ <th><%= sort :name, :as => s_("Name") %></th>
12
+ <th><%= _("State") %></th>
13
+ <th><%= _("Last sync status") %></th>
14
+ <th><%= _("Content type") %></th>
15
+ </tr>
16
+ <% @content_view.repository_clones.each do |repository| %>
17
+ <tr>
18
+ <%= render :partial => 'content/repositories/repository', :locals => { :repository => repository } %>
19
+ </tr>
20
+ <% end %>
21
+ </table>
22
+
@@ -1,15 +1,9 @@
1
1
  <%= javascript 'content/content.js' %>
2
2
  <%= form_for @product, :url => (@product.new_record? ? products_path : product_path(@product)) do |f| %>
3
3
  <%= base_errors_for @product %>
4
- <ul class="nav nav-tabs" data-tabs="tabs">
5
- <li class="active"><a href="#primary" data-toggle="tab"><%= _("Product") %></a></li>
6
- </ul>
7
- <div class="tab-content">
8
- <div class="tab-pane active" id="primary">
9
- <%= text_f f, :name %>
10
- <%= textarea_f f, :description, :class => "input-xlarge" , :rows=> '3' %>
11
- </div>
12
4
 
13
- </div>
5
+ <%= text_f f, :name %>
6
+ <%= textarea_f f, :description, :class => "input-xlarge", :rows => '3' %>
7
+
14
8
  <%= submit_or_cancel f %>
15
9
  <% end %>