foreman_content 0.1

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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +113 -0
  3. data/Rakefile +111 -0
  4. data/app/assets/javascripts/content/content.js +12 -0
  5. data/app/assets/stylesheets/content/application.css +13 -0
  6. data/app/controllers/content/api/repositories_controller.rb +27 -0
  7. data/app/controllers/content/gpg_keys_controller.rb +42 -0
  8. data/app/controllers/content/products_controller.rb +51 -0
  9. data/app/controllers/content/repositories_controller.rb +57 -0
  10. data/app/helpers/content/application_helper.rb +5 -0
  11. data/app/helpers/content/repositories_helper.rb +56 -0
  12. data/app/models/content/custom_repository_paths.rb +27 -0
  13. data/app/models/content/environment_product.rb +19 -0
  14. data/app/models/content/gpg_key.rb +32 -0
  15. data/app/models/content/host_product.rb +20 -0
  16. data/app/models/content/hostgroup_product.rb +19 -0
  17. data/app/models/content/operatingsystem_repository.rb +19 -0
  18. data/app/models/content/orchestration/pulp.rb +93 -0
  19. data/app/models/content/product.rb +28 -0
  20. data/app/models/content/product_operatingsystem.rb +19 -0
  21. data/app/models/content/remote/pulp/repository.rb +46 -0
  22. data/app/models/content/repository.rb +70 -0
  23. data/app/models/content/validators/content_validator.rb +25 -0
  24. data/app/models/content/validators/description_format.rb +24 -0
  25. data/app/models/content/validators/name_format.rb +40 -0
  26. data/app/models/content/validators/no_trailing_space.rb +27 -0
  27. data/app/models/setting/content.rb +42 -0
  28. data/app/overrides/add_host_conent_tab.rb +9 -0
  29. data/app/overrides/add_hostgroup_conent_tab.rb +9 -0
  30. data/app/overrides/add_os_conent_tab.rb +9 -0
  31. data/app/services/content/pulp_event_handler.rb +25 -0
  32. data/app/views/content/gpg_keys/_form.html.erb +8 -0
  33. data/app/views/content/gpg_keys/edit.html.erb +3 -0
  34. data/app/views/content/gpg_keys/index.html.erb +20 -0
  35. data/app/views/content/gpg_keys/new.html.erb +3 -0
  36. data/app/views/content/products/_form.html.erb +26 -0
  37. data/app/views/content/products/_form_tab.html.erb +3 -0
  38. data/app/views/content/products/_host_tab_pane.html.erb +5 -0
  39. data/app/views/content/products/_hostgroup_tab_pane.html.erb +5 -0
  40. data/app/views/content/products/_operatingsystem_tab_pane.html.erb +5 -0
  41. data/app/views/content/products/edit.html.erb +3 -0
  42. data/app/views/content/products/index.html.erb +27 -0
  43. data/app/views/content/products/new.html.erb +3 -0
  44. data/app/views/content/products/welcome.html.erb +8 -0
  45. data/app/views/content/repositories/_form.html.erb +30 -0
  46. data/app/views/content/repositories/edit.html.erb +3 -0
  47. data/app/views/content/repositories/index.html.erb +32 -0
  48. data/app/views/content/repositories/new.html.erb +3 -0
  49. data/app/views/content/repositories/show.html.erb +98 -0
  50. data/app/views/content/repositories/welcome.html.erb +8 -0
  51. data/config/routes.rb +36 -0
  52. data/db/migrate/20130702140034_create_content_repositories.rb +19 -0
  53. data/db/migrate/20130702162629_create_content_products.rb +10 -0
  54. data/db/migrate/20130709001120_create_content_gpg_keys.rb +10 -0
  55. data/db/migrate/20130717032320_create_content_environments_products.rb +9 -0
  56. data/db/migrate/20130722084911_create_content_operatingsystem_repositories.rb +9 -0
  57. data/db/migrate/20130723084911_create_content_hostgroup_products.rb +9 -0
  58. data/db/migrate/20130723124911_create_content_host_products.rb +9 -0
  59. data/db/migrate/20130729032320_create_content_product_operatingsystems.rb +9 -0
  60. data/lib/content/engine.rb +59 -0
  61. data/lib/content/version.rb +3 -0
  62. data/lib/content_environment.rb +13 -0
  63. data/lib/content_home_helper_patch.rb +23 -0
  64. data/lib/content_host.rb +55 -0
  65. data/lib/content_hostgroup.rb +22 -0
  66. data/lib/content_operatingsystem.rb +17 -0
  67. data/lib/content_redhat.rb +36 -0
  68. data/lib/content_taxonomy.rb +18 -0
  69. data/lib/foreman_content.rb +4 -0
  70. data/lib/pulp_configuration.rb +23 -0
  71. data/lib/tasks/content_tasks.rake +4 -0
  72. data/test/content_test.rb +7 -0
  73. data/test/fixtures/content/products.yml +11 -0
  74. data/test/fixtures/content/repositories.yml +11 -0
  75. data/test/integration/navigation_test.rb +10 -0
  76. data/test/test_helper.rb +23 -0
  77. data/test/unit/content/description_format_validator_test.rb +20 -0
  78. data/test/unit/content/name_format_validator_test.rb +28 -0
  79. data/test/unit/content/product_validation_test.rb +18 -0
  80. data/test/unit/content/repositories_test.rb +7 -0
  81. metadata +194 -0
@@ -0,0 +1,5 @@
1
+ module Content
2
+ module ApplicationHelper
3
+ include Rails.application.routes.url_helpers
4
+ end
5
+ end
@@ -0,0 +1,56 @@
1
+ module Content
2
+ module RepositoriesHelper
3
+
4
+ def sync_status repo
5
+ status = repo.sync_status
6
+ status = if status && status.first
7
+ status.first[:state]
8
+ else
9
+ details = repo.retrieve_with_details
10
+ details[:importers][0][:last_sync] if details && details[:importers] && details[:importers][0]
11
+ end
12
+ "#{time_ago_in_words(status)} ago" rescue status
13
+ rescue
14
+ '-'
15
+ end
16
+
17
+ def last_publish details
18
+ return '' unless details[:distributors] && details[:distributors].first && published = details[:distributors].first[:last_publish]
19
+ "#{time_ago_in_words published} ago" rescue ''
20
+ end
21
+
22
+ def last_sync details
23
+ return '' unless details[:importers] && details[:importers].first && synced = details[:importers].first[:last_sync]
24
+ "#{time_ago_in_words synced} ago" rescue ''
25
+ end
26
+
27
+ def sync_history_times history
28
+ return {} unless history && history.first && summary = history.first['summary']
29
+ return {} unless summary[:comps]
30
+ {
31
+ :comps => summary[:comps][:time_total_sec],
32
+ :errata => summary[:errata][:errata_time_total_sec],
33
+ :packages => summary[:packages][:time_total_sec]
34
+ }
35
+ end
36
+
37
+ def sync_history_metrics history
38
+ return {} unless history && last = history.first
39
+ {
40
+ :updated => last[:updated_count],
41
+ :removed => last[:removed_count],
42
+ :added => last[:added_count]
43
+ }
44
+ end
45
+
46
+ def sync_history_status history
47
+ return {} unless history && last = history.first
48
+ {
49
+ 'result' => last[:result],
50
+ 'message' => last[:error_message] || last['summary']['error'],
51
+ 'completed' => ("#{time_ago_in_words(last[:completed])} ago" rescue '')
52
+ }
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,27 @@
1
+ module Content
2
+ module CustomRepositoryPaths
3
+ def repo_path_from_content_path(environment, content_path)
4
+ content_path = content_path.sub(/^\//, "")
5
+ path_prefix = [environment.organization.label, environment.label].join("/")
6
+ "#{path_prefix}/#{content_path}"
7
+ end
8
+
9
+ # repo path for custom product repos (RH repo paths are derived from
10
+ # content url)
11
+ def custom_repo_path(org_label, environment_label, product_label, repo_label)
12
+ prefix = [org_label, environment_label].map { |x| x.gsub(/[^-\w]/, "_") }.join("/")
13
+ prefix + custom_content_path(product_label, repo_label)
14
+ end
15
+
16
+ def custom_content_path(product_label, repo_label)
17
+ parts = []
18
+ # We generate repo path only for custom product content. We add this
19
+ # constant string to avoid collisions with RH content. RH content url
20
+ # begins usually with something like "/content/dist/rhel/...".
21
+ # There we prefix custom content/repo url with "/custom/..."
22
+ parts << "custom"
23
+ parts += [product_label, repo_label]
24
+ "/" + parts.map { |x| x.gsub(/[^-\w]/, "_") }.join("/")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright 2013 Red Hat, Inc.
3
+ #
4
+ # This software is licensed to you under the GNU General Public
5
+ # License as published by the Free Software Foundation; either version
6
+ # 2 of the License (GPLv2) or (at your option) any later version.
7
+ # There is NO WARRANTY for this software, express or implied,
8
+ # including the implied warranties of MERCHANTABILITY,
9
+ # NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
10
+ # have received a copy of GPLv2 along with this software; if not, see
11
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12
+ module Content
13
+ class EnvironmentProduct < ActiveRecord::Base
14
+ belongs_to :product
15
+ belongs_to :environment
16
+ validates_presence_of :product_id, :environment_id
17
+ validates_uniqueness_of :product_id, :scope => :environment_id
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ #
2
+ # Copyright 2013 Red Hat, Inc.
3
+ #
4
+ # This software is licensed to you under the GNU General Public
5
+ # License as published by the Free Software Foundation; either version
6
+ # 2 of the License (GPLv2) or (at your option) any later version.
7
+ # There is NO WARRANTY for this software, express or implied,
8
+ # including the implied warranties of MERCHANTABILITY,
9
+ # NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
10
+ # have received a copy of GPLv2 along with this software; if not, see
11
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12
+
13
+ module Content
14
+ class GpgKey < ActiveRecord::Base
15
+ include ::Taxonomix
16
+ MAX_CONTENT_LENGTH = 100000
17
+
18
+ has_many :repositories
19
+ has_many :products
20
+
21
+ validates :name, :presence => true
22
+ validates_with Validators::NameFormat, :attributes => :name
23
+ validates :content, :presence => true
24
+ validates_with Validators::ContentValidator, :attributes => :content
25
+ validates_length_of :content, :maximum => MAX_CONTENT_LENGTH
26
+
27
+ validates_uniqueness_of :name, :message => N_("Label has already been taken")
28
+
29
+ scoped_search :on => :name
30
+
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # Copyright 2013 Red Hat, Inc.
3
+ #
4
+ # This software is licensed to you under the GNU General Public
5
+ # License as published by the Free Software Foundation; either version
6
+ # 2 of the License (GPLv2) or (at your option) any later version.
7
+ # There is NO WARRANTY for this software, express or implied,
8
+ # including the implied warranties of MERCHANTABILITY,
9
+ # NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
10
+ # have received a copy of GPLv2 along with this software; if not, see
11
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12
+ module Content
13
+ class HostProduct < ActiveRecord::Base
14
+ belongs_to :product
15
+ belongs_to :host
16
+ validates_presence_of :product_id, :host_id
17
+ validates_uniqueness_of :product_id, :scope => :host_id
18
+
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright 2013 Red Hat, Inc.
3
+ #
4
+ # This software is licensed to you under the GNU General Public
5
+ # License as published by the Free Software Foundation; either version
6
+ # 2 of the License (GPLv2) or (at your option) any later version.
7
+ # There is NO WARRANTY for this software, express or implied,
8
+ # including the implied warranties of MERCHANTABILITY,
9
+ # NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
10
+ # have received a copy of GPLv2 along with this software; if not, see
11
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12
+ module Content
13
+ class HostgroupProduct < ActiveRecord::Base
14
+ belongs_to :product
15
+ belongs_to :hostgroup
16
+ validates_presence_of :product_id, :hostgroup_id
17
+ validates_uniqueness_of :product_id, :scope => :hostgroup_id
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright 2013 Red Hat, Inc.
3
+ #
4
+ # This software is licensed to you under the GNU General Public
5
+ # License as published by the Free Software Foundation; either version
6
+ # 2 of the License (GPLv2) or (at your option) any later version.
7
+ # There is NO WARRANTY for this software, express or implied,
8
+ # including the implied warranties of MERCHANTABILITY,
9
+ # NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
10
+ # have received a copy of GPLv2 along with this software; if not, see
11
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12
+ module Content
13
+ class OperatingsystemRepository < ActiveRecord::Base
14
+ belongs_to :operatingsystem
15
+ belongs_to :repository
16
+ validates_presence_of :operatingsystem_id, :repository_id
17
+ validates_uniqueness_of :operatingsystem_id, :scope => :repository_id
18
+ end
19
+ end
@@ -0,0 +1,93 @@
1
+ module Content::Orchestration::Pulp
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ after_validation :queue_pulp
6
+ before_destroy :queue_pulp_destroy unless Rails.env == "test"
7
+ end
8
+
9
+ def orchestration_errors?
10
+ errors.empty?
11
+ end
12
+
13
+ protected
14
+
15
+ def queue_pulp
16
+ return unless pulp? and errors.empty?
17
+ new_record? ? queue_pulp_create : queue_pulp_update
18
+ end
19
+
20
+ private
21
+
22
+ def queue_pulp_create
23
+ logger.debug "Scheduling new Pulp Repository"
24
+ queue.create(:name => _("Create Pulp Repository for %s") % self, :priority => 10,
25
+ :action => [self, :set_pulp_repo])
26
+ queue.create(:name => _("Sync Pulp Repository %s") % self, :priority => 20,
27
+ :action => [self, :set_sync_pulp_repo])
28
+ end
29
+
30
+ def queue_pulp_update
31
+ end
32
+
33
+ def set_pulp_repo
34
+ Runcible::Extensions::Repository.create_with_importer_and_distributors(pulp_id,
35
+ pulp_importer,
36
+ [pulp_distributor],
37
+ { :display_name => relative_path,
38
+ :description => description })
39
+ rescue RestClient::BadRequest => e
40
+ raise (JSON.parse e.response)['error_message']
41
+ end
42
+
43
+ def del_pulp_repo
44
+ delete
45
+ end
46
+
47
+ def set_sync_pulp_repo
48
+ sync
49
+ end
50
+
51
+ def del_sync_pulp_repo
52
+ status = sync_status
53
+ return if status.blank? || status == ::PulpSyncStatus::Status::NOT_SYNCED
54
+ Runcible::Resources::Task.cancel(status.uuid)
55
+ end
56
+
57
+ def pulp_importer
58
+ options = {
59
+ :feed_url => feed
60
+ }
61
+
62
+ case content_type
63
+ when Content::Repository::YUM_TYPE, Content::Repository::KICKSTART_TYPE
64
+ Runcible::Extensions::YumImporter.new(options)
65
+ when Content::Repository::FILE_TYPE
66
+ Runcible::Extensions::IsoImporter.new(options)
67
+ else
68
+ raise "Unexpected repo type %s" % content_type
69
+ end
70
+ end
71
+
72
+ def pulp_distributor
73
+ case content_type
74
+ when Content::Repository::YUM_TYPE, Content::Repository::KICKSTART_TYPE
75
+ Runcible::Extensions::YumDistributor.new(relative_path, unprotected, true,
76
+ { :protected => true, :id => pulp_id,
77
+ :auto_publish => true })
78
+ when Content::Repository::FILE_TYPE
79
+ dist = Runcible::Extensions::IsoDistributor.new(true, true)
80
+ dist.auto_publish = true
81
+ dist
82
+ else
83
+ raise "Unexpected repo type %s" % content_type
84
+ end
85
+ end
86
+
87
+ def queue_pulp_destroy
88
+ return unless pulp? and errors.empty?
89
+ logger.debug _("Scheduling removal of Pulp Repository %s") % name
90
+ queue.create(:name => _("Delete Pulp repository for %s") % name, :priority => 50,
91
+ :action => [self, :del_pulp_repo])
92
+ end
93
+ end
@@ -0,0 +1,28 @@
1
+ module Content
2
+ class Product < ActiveRecord::Base
3
+ include ::Taxonomix
4
+
5
+ has_many :repositories
6
+ has_many :environment_products, :dependent => :destroy, :uniq=>true
7
+ has_many :environments, :through => :environment_products
8
+
9
+ has_many :product_operatingsystems, :dependent => :destroy, :uniq=>true
10
+ has_many :operatingsystems, :through => :product_operatingsystems
11
+
12
+ has_many :hostgroup_products, :dependent => :destroy, :uniq=>true
13
+ has_many :hostgroups, :through => :hostgroup_products
14
+
15
+ has_many :host_products, :dependent => :destroy, :uniq=>true
16
+ has_many :hosts, :through => :host_products
17
+
18
+ validates_with Validators::DescriptionFormat, :attributes => :description
19
+ validates :name, :presence => true
20
+ validates_with Validators::NameFormat, :attributes => :name
21
+ scoped_search :on => :name
22
+ scoped_search :in => :repositories, :on => :name, :rename => :repository, :complete_value => :true
23
+
24
+ def sync
25
+ self.repositories.map{ |repo| repo.sync }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright 2013 Red Hat, Inc.
3
+ #
4
+ # This software is licensed to you under the GNU General Public
5
+ # License as published by the Free Software Foundation; either version
6
+ # 2 of the License (GPLv2) or (at your option) any later version.
7
+ # There is NO WARRANTY for this software, express or implied,
8
+ # including the implied warranties of MERCHANTABILITY,
9
+ # NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
10
+ # have received a copy of GPLv2 along with this software; if not, see
11
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12
+ module Content
13
+ class ProductOperatingsystem < ActiveRecord::Base
14
+ belongs_to :product
15
+ belongs_to :operatingsystem
16
+ validates_presence_of :product_id, :operatingsystem_id
17
+ validates_uniqueness_of :product_id, :scope => :operatingsystem_id
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ module Content
2
+ module Remote
3
+ module Pulp
4
+ module Repository
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ after_initialize :initialize_pulp
9
+ end
10
+
11
+ def pulp?
12
+ @use_pulp ||= Setting.use_pulp and enabled?
13
+ end
14
+
15
+ def sync
16
+ Runcible::Resources::Repository.sync(pulp_id)
17
+ end
18
+
19
+ def retrieve_with_details
20
+ return unless pulp? && pulp_id
21
+ Runcible::Resources::Repository.retrieve(pulp_id, {:details => true})
22
+ end
23
+
24
+ def sync_status
25
+ return unless pulp? && pulp_id
26
+ Runcible::Extensions::Repository.sync_status(pulp_id)
27
+ end
28
+
29
+ def sync_history
30
+ return unless pulp? && pulp_id
31
+ Runcible::Extensions::Repository.sync_history(pulp_id)
32
+ end
33
+
34
+ def delete
35
+ Runcible::Resources::Repository.delete(pulp_id)
36
+ end
37
+
38
+ protected
39
+ def initialize_pulp
40
+ self.pulp_id ||= Foreman.uuid.gsub("-", '')
41
+ self.relative_path ||= custom_repo_path("acme_org", "library", product.name, name) if name
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,70 @@
1
+ require 'content/orchestration/pulp'
2
+ # TODO: split into a custom and red hat repositories:
3
+ # as handling of repo creation/updates is different between them
4
+ module Content
5
+ class Repository < ActiveRecord::Base
6
+ include CustomRepositoryPaths
7
+ include Content::Remote::Pulp::Repository
8
+ include ::Orchestration
9
+ include Content::Orchestration::Pulp
10
+
11
+ YUM_TYPE = 'yum'
12
+ KICKSTART_TYPE = 'kickstart'
13
+ FILE_TYPE = 'iso'
14
+ TYPES = [YUM_TYPE, KICKSTART_TYPE, FILE_TYPE]
15
+
16
+ REPO_PREFIX = '/pulp/repos/'
17
+
18
+ belongs_to :product
19
+ belongs_to :gpg_key
20
+ belongs_to :architecture
21
+ has_many :operatingsystem_repositories, :dependent => :destroy, :uniq => true
22
+ has_many :operatingsystems, :through => :operatingsystem_repositories
23
+
24
+ validates :product, :presence => true
25
+ validates :name, :presence => true
26
+ validates_uniqueness_of :name, :scope => :product_id
27
+ validates_inclusion_of :content_type,
28
+ :in => TYPES,
29
+ :allow_blank => false,
30
+ :message => (_("Please select content type from one of the following: %s") % TYPES.join(', '))
31
+
32
+ scoped_search :on => [:name, :enabled], :complete_value => :true
33
+ scoped_search :in => :architecture, :on => :name, :rename => :architecture, :complete_value => :true
34
+ scoped_search :in => :operatingsystems, :on => :name, :rename => :os, :complete_value => :true
35
+ scoped_search :in => :product, :on => :name, :rename => :product, :complete_value => :true
36
+
37
+ # Repositories filtered by operating system architecture and environment.
38
+ # architecture_id is nil for noarch repositories.
39
+ scope :available_for_host, lambda { |host|
40
+ joins(:operatingsystems => :operatingsystem_repositories , :product => :environments).
41
+ where(:architecture_id => [nil, host.architecture_id],
42
+ :content_operatingsystem_repositories => { :operatingsystem_id => host.operatingsystem_id},
43
+ :content_environment_products => { :environment_id => host.environment_id }).
44
+ uniq}
45
+
46
+ #
47
+ scope :attached_to_host, lambda { |host|
48
+ where(:product_id => host.all_product_ids).available_for_host(host)
49
+ }
50
+
51
+ scope :kickstart, where(:content_type => KICKSTART_TYPE)
52
+ scope :yum, where(:content_type => YUM_TYPE)
53
+
54
+ def update_cache
55
+ nil
56
+ end
57
+
58
+ def full_path
59
+ pulp_url = URI.parse(Setting.pulp_url)
60
+ scheme = (unprotected ? 'http' : 'https')
61
+ port = (pulp_url.port == 443 || pulp_url.port == 80 ? "" : ":#{pulp_url.port}")
62
+ "#{scheme}://#{pulp_url.host}#{port}#{REPO_PREFIX}#{relative_path}"
63
+ end
64
+
65
+ # The label is used as a repository label in a yum repo file.
66
+ def to_label
67
+ "#{product.name}-#{name}".parameterize
68
+ end
69
+ end
70
+ end