foreman_content 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
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 %>