foreman_templates 5.0.0 → 5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e20876391aee6555fefc6346da21ac4dd54e9e9
4
- data.tar.gz: 1cee72d5d2ec7e8e509791f50f7bd7ae2d168b6c
3
+ metadata.gz: 3889bfa2270632f8e0bfacc4a35a1a85c3eca1e6
4
+ data.tar.gz: 797795544b68cbb4b2d408e8293e6689d1e3ff02
5
5
  SHA512:
6
- metadata.gz: 28f7610e8b1b4b3aa1cff6a228db20d3ac4d8835265dd6d646a50d0c3ebe536e2548bf3e9184e72af7096f92a40b226a46303f1c13aad1f322018c3260f4e7ef
7
- data.tar.gz: 7e492ef86762d568446c9c1859957be35d38af649b998352c64f10fb401172773e0e4af6ad2b1d7d8b24d0851f80781fffed89c90f77539d1c77bc390642a35c
6
+ metadata.gz: da4517d69b72c25c3065db6f0afba3621b6165c9c7a8e317a52e0a4c24a81b369fac520cc50ea102ea7accc8b9934452ef38b4a6e270e15d28c2a606e4105688
7
+ data.tar.gz: ae821ae58dedaaa29a83e41924b1ac30383c0d40e827c6d039397ac7bfd95f7923c94d418fcc1f89bcc59a020ae3b58b385db90bcb71353420384e8b31d4ba01
@@ -1,15 +1,17 @@
1
1
  module Api
2
2
  module V2
3
3
  class TemplateController < ::Api::V2::BaseController
4
-
5
4
  api :POST, "/template/import/", N_("Initiate Import")
6
- param :repo, String, :required => false, :desc => N_("Import templates from a different repo.")
5
+ param :verbose, :bool, :required => false, :desc => N_("Set verbosity of import")
6
+ param :repo, String, :required => false, :desc => N_("Override the default repo from settings.")
7
7
  param :branch, String, :required => false, :desc => N_("Branch in Git repo.")
8
8
  param :prefix, String, :required => false, :desc => N_("The string all imported templates should begin with.")
9
9
  param :dirname, String, :required => false, :desc => N_("The directory within the git tree containing the templates.")
10
- param :filter, String, :required => false, :desc => N_("Import names matching this regex (case-insensitive; snippets are not filtered).")
10
+ param :filter, String, :required => false, :desc => N_("Import templates with names matching this regex (case-insensitive; snippets are not filtered).")
11
11
  param :negate, :bool, :required => false, :desc => N_("Negate the prefix (for purging).")
12
- param :associate, String, :required => false, :desc => N_("Associate to OS's, Locations & Organizations. Options are: always, new or never.")
12
+ param :associate, Setting::TemplateSync.associate_types.keys, :required => false, :desc => N_("Associate to OS's, Locations & Organizations. Options are: always, new or never.")
13
+ param :force, :bool, :required => false, :desc => N_("Update templates that are locked")
14
+
13
15
  def import
14
16
  results = ForemanTemplates::TemplateImporter.new({
15
17
  verbose: params['verbose'],
@@ -20,8 +22,33 @@ module Api
20
22
  filter: params['filter'],
21
23
  associate: params['associate'],
22
24
  negate: params['negate'],
25
+ force: params['force']
23
26
  }).import!
24
- render :json => {:message => results}
27
+ render :json => { :message => results }
28
+ end
29
+
30
+ api :POST, "/template/export", N_("Initiate Export")
31
+ param :verbose, :bool, :required => false, :desc => N_("Set verbosity of export")
32
+ param :repo, String, :required => false, :desc => N_("Override the default repo from settings")
33
+ param :branch, String, :required => false, :desc => N_("Branch in Git repo.")
34
+ param :filter, String, :required => false, :desc => N_("Export templates with names matching this regex (case-insensitive; snippets are not filtered).")
35
+ param :negate, :bool, :required => false, :desc => N_("Negate the prefix (for purging).")
36
+ param :metadata_export_mode, Setting::TemplateSync.metadata_export_mode_types.keys, :required => false, :desc => N_("Specify how to handle metadata")
37
+ param :dir, String, :required => false, :desc => N_("The directory within Git repo")
38
+ def export
39
+ ForemanTemplates::TemplateExporter.new({
40
+ verbose: params['verbose'],
41
+ repo: params['repo'],
42
+ branch: params['branch'],
43
+ filter: params['filter'],
44
+ negate: params['negate'],
45
+ metadata_export_mode: params['metadata_export_mode'],
46
+ dir: params['dir']
47
+ }).export!
48
+ render :json => { :message => _('Success') }
49
+ rescue => e
50
+ logger.debug e
51
+ render :json => { :message => (_('Something went wrong during export: %s') % e.message) }, :status => 500
25
52
  end
26
53
  end
27
54
  end
@@ -1,60 +1,94 @@
1
- module ForemanTemplates::ProvisioningTemplateImport
2
- extend ActiveSupport::Concern
3
-
4
- module ClassMethods
5
- def import!(name, text, metadata)
6
- # Check for snippet type
7
- return import_snippet!(name, text) if metadata['snippet'] || metadata['kind'] == 'snippet'
8
-
9
- # Get template type
10
- kind = TemplateKind.find_by_name(metadata['kind'])
11
- raise NoKindError unless kind
12
-
13
- # Data
14
- template = ProvisioningTemplate.where(:name => name).first_or_initialize
15
- data = {
16
- :template => text,
17
- :snippet => false,
18
- :template_kind_id => kind.id
19
- }
20
- oses = map_metadata(metadata, 'oses')
21
- locations = map_metadata(metadata, 'locations')
22
- organizations = map_metadata(metadata, 'organizations')
23
-
24
- # Printout helpers
25
- c_or_u = template.new_record? ? 'Created' : 'Updated'
26
- id_string = ('id' + template.id) rescue ''
27
-
28
- if (metadata['associate'] == 'new' && template.new_record?) || (metadata['associate'] == 'always')
29
- data[:operatingsystem_ids] = oses.map(&:id)
30
- data[:location_ids] = locations.map(&:id)
31
- data[:organization_ids] = organizations.map(&:id)
1
+ module ForemanTemplates
2
+ module ProvisioningTemplateImport
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def import!(name, text, metadata, force = false)
7
+ # Check for snippet type
8
+ return import_snippet!(name, text, force) if metadata['snippet'] || metadata['kind'] == 'snippet'
9
+
10
+ # Get template type
11
+ kind = TemplateKind.find_by(name: metadata['kind'])
12
+ raise NoKindError unless kind
13
+
14
+ # Data
15
+ template = ProvisioningTemplate.where(:name => name).first_or_initialize
16
+ data = {
17
+ :template => text,
18
+ :snippet => false,
19
+ :template_kind_id => kind.id
20
+ }
21
+ oses = map_metadata(metadata, 'oses')
22
+ locations = map_metadata(metadata, 'locations')
23
+ organizations = map_metadata(metadata, 'organizations')
24
+
25
+ # Printout helpers
26
+ c_or_u = template.new_record? ? 'Creating' : 'Updating'
27
+ id_string = template.new_record? ? '' : "id #{template.id}"
28
+ if template.locked? && !template.new_record? && !force
29
+ return { :diff => nil,
30
+ :status => false,
31
+ :result => "Skipping Template #{id_string}:#{name} - template is locked" }
32
+ end
33
+
34
+ associate_metadata data, template, metadata, oses, organizations, locations
35
+
36
+ diff = nil
37
+ status = nil
38
+ if template_changed?(data, template)
39
+ diff = create_diff(data, template)
40
+ template.ignore_locking do
41
+ status = template.update_attributes(data)
42
+ end
43
+ result = build_associations_result c_or_u, id_string, name, oses, organizations, locations
44
+ else
45
+ status = true
46
+ result = " No change to Template #{id_string}:#{name}"
47
+ end
48
+
49
+ { :diff => diff, :status => status, :result => result, :errors => template.errors }
50
+ end
51
+
52
+ def associate_metadata(data, template, metadata, oses, organizations, locations)
53
+ if (metadata['associate'] == 'new' && template.new_record?) || (metadata['associate'] == 'always')
54
+ data[:operatingsystem_ids] = oses.map(&:id)
55
+ data[:location_ids] = locations.map(&:id)
56
+ data[:organization_ids] = organizations.map(&:id)
57
+ end
58
+ data
59
+ end
60
+
61
+ def build_associations_result(c_or_u, id_string, name, oses, organizations, locations)
62
+ res = " #{c_or_u} Template #{id_string}:#{name}"
63
+ res += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
64
+ res += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
65
+ res += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
66
+ res
67
+ end
68
+
69
+ def create_diff(data, template)
70
+ if template_content_changed?(template.template, data[:template])
71
+ Diffy::Diff.new(
72
+ template.template,
73
+ data[:template],
74
+ :include_diff_info => true
75
+ ).to_s(:color)
76
+ else
77
+ nil
78
+ end
79
+ end
80
+
81
+ def template_content_changed?(template_template, data_template)
82
+ template_template != data_template
83
+ end
84
+
85
+ def associations_changed?(data)
86
+ !(data[:operatingsystem_ids] || data[:location_ids] || data[:organization_ids]).nil?
32
87
  end
33
88
 
34
- if data[:template] != template.template
35
- diff = Diffy::Diff.new(
36
- template.template,
37
- data[:template],
38
- :include_diff_info => true
39
- ).to_s(:color)
40
- status = template.update_attributes(data)
41
- result = " #{c_or_u} Template #{id_string}:#{name}"
42
- result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
43
- result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
44
- result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
45
- elsif data[:operatingsystem_ids] || data[:location_ids] || data[:organization_ids]
46
- diff = nil
47
- status = template.update_attributes(data)
48
- result = " #{c_or_u} Template Associations #{id_string}:#{name}"
49
- result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
50
- result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
51
- result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
52
- else
53
- diff = nil
54
- status = true
55
- result = " No change to Template #{id_string}:#{name}"
89
+ def template_changed?(data, template)
90
+ template_content_changed?(template.template, data[:template]) || associations_changed?(data)
56
91
  end
57
- { :diff => diff, :status => status, :result => result }
58
92
  end
59
93
  end
60
94
  end
@@ -1,57 +1,89 @@
1
- module ForemanTemplates::PtableImport
2
- extend ActiveSupport::Concern
3
-
4
- module ClassMethods
5
- def import!(name, text, metadata)
6
- # Check for snippet type
7
- return import_snippet!(name, text) if metadata['snippet']
8
-
9
- # Data
10
- ptable = Ptable.where(:name => name).first_or_initialize
11
- data = {
12
- :layout => text
13
- }
14
- oses = map_metadata(metadata, 'oses')
15
- locations = map_metadata(metadata, 'locations')
16
- organizations = map_metadata(metadata, 'organizations')
17
-
18
- # Printout helpers
19
- c_or_u = ptable.new_record? ? 'Created' : 'Updated'
20
- id_string = ('id' + ptable.id) rescue ''
21
-
22
- if (metadata['associate'] == 'new' && ptable.new_record?) || (metadata['associate'] == 'always')
23
- data[:operatingsystem_ids] = oses.map(&:id)
24
- data[:os_family] = oses.map(&:family).uniq.first
25
- data[:location_ids] = locations.map(&:id)
26
- data[:organization_ids] = organizations.map(&:id)
1
+ module ForemanTemplates
2
+ module PtableImport
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def import!(name, text, metadata, force = false)
7
+ # Check for snippet type
8
+ return import_snippet!(name, text, force) if metadata['snippet']
9
+
10
+ # Data
11
+ ptable = Ptable.where(:name => name).first_or_initialize
12
+ data = {
13
+ :layout => text
14
+ }
15
+ oses = map_metadata(metadata, 'oses')
16
+ locations = map_metadata(metadata, 'locations')
17
+ organizations = map_metadata(metadata, 'organizations')
18
+
19
+ # Printout helpers
20
+ c_or_u = ptable.new_record? ? 'Creating' : 'Updating'
21
+ id_string = ptable.new_record? ? '' : "id #{ptable.id}"
22
+ if ptable.locked? && !ptable.new_record? && !force
23
+ return { :diff => nil,
24
+ :status => false,
25
+ :result => "Skipping Partition Table #{id_string}:#{name} - partition table is locked" }
26
+ end
27
+
28
+ associate_metadata data, ptable, metadata, oses, organizations, locations
29
+
30
+ diff = nil
31
+ status = nil
32
+ if ptable_changed?(data, ptable)
33
+ diff = create_diff(data, ptable)
34
+ ptable.ignore_locking do
35
+ status = ptable.update_attributes(data)
36
+ end
37
+ result = build_associations_result c_or_u, id_string, name, oses, organizations, locations
38
+ else
39
+ status = true
40
+ result = " No change to Ptable #{id_string}:#{name}"
41
+ end
42
+ { :diff => diff, :status => status, :result => result, :errors => ptable.errors }
43
+ end
44
+
45
+ def associate_metadata(data, ptable, metadata, oses, organizations, locations)
46
+ if (metadata['associate'] == 'new' && ptable.new_record?) || (metadata['associate'] == 'always')
47
+ data[:operatingsystem_ids] = oses.map(&:id)
48
+ data[:os_family] = oses.map(&:family).uniq.first
49
+ data[:location_ids] = locations.map(&:id)
50
+ data[:organization_ids] = organizations.map(&:id)
51
+ end
52
+ data
53
+ end
54
+
55
+ def ptable_content_changed?(data_layout, ptable_layout)
56
+ data_layout != ptable_layout
57
+ end
58
+
59
+ def associations_changed?(data)
60
+ !(data[:os_family] || data[:location_ids] || data[:organization_ids]).nil?
61
+ end
62
+
63
+ def create_diff(data, ptable)
64
+ if ptable_content_changed?(data[:layout], ptable.layout)
65
+ Diffy::Diff.new(
66
+ ptable.layout,
67
+ data[:layout],
68
+ :include_diff_info => true
69
+ ).to_s(:color)
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ def build_associations_result(c_or_u, id_string, name, oses, organizations, locations)
76
+ res = " #{c_or_u} Ptable #{id_string}:#{name}"
77
+ res += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" unless oses.empty?
78
+ res += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
79
+ res += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
80
+ res += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
81
+ res
27
82
  end
28
83
 
29
- if data[:layout] != ptable.layout
30
- diff = Diffy::Diff.new(
31
- ptable.layout,
32
- data[:layout],
33
- :include_diff_info => true
34
- ).to_s(:color)
35
- status = ptable.update_attributes(data)
36
- result = " #{c_or_u} Ptable #{id_string}:#{name}"
37
- result += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" unless oses.empty?
38
- result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
39
- result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
40
- result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
41
- elsif data[:os_family] || data[:location_ids] || data[:organization_ids]
42
- diff = nil
43
- status = ptable.update_attributes(data)
44
- result = " #{c_or_u} Ptable Associations #{id_string}:#{name}"
45
- result += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" unless oses.empty?
46
- result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
47
- result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
48
- result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
49
- else
50
- diff = nil
51
- status = true
52
- result = " No change to Ptable #{id_string}:#{name}"
84
+ def ptable_changed?(data, ptable)
85
+ ptable_content_changed?(data[:layout], ptable.layout) || associations_changed?(data)
53
86
  end
54
- { :diff => diff, :status => status, :result => result }
55
87
  end
56
88
  end
57
89
  end
@@ -1,53 +1,64 @@
1
- module ForemanTemplates::TemplateImport
2
- extend ActiveSupport::Concern
1
+ module ForemanTemplates
2
+ module TemplateImport
3
+ extend ActiveSupport::Concern
3
4
 
4
- module ClassMethods
5
- def import_snippet!(name, text)
6
- # Data
7
- snippet = self.where(:name => name).first_or_initialize
8
- data = {
9
- :template => text,
10
- :snippet => true
11
- }
5
+ module ClassMethods
6
+ def import_snippet!(name, text, force = false)
7
+ # Data
8
+ snippet = self.where(:name => name).first_or_initialize
9
+ data = {
10
+ :template => text,
11
+ :snippet => true
12
+ }
12
13
 
13
- # Printout helpers
14
- c_or_u = snippet.new_record? ? 'Created' : 'Updated'
15
- id_string = ('id' + snippet.id) rescue ''
14
+ # Printout helpers
15
+ c_or_u = snippet.new_record? ? 'Creating' : 'Updating'
16
+ id_string = snippet.new_record? ? '' : "id #{snippet.id}"
16
17
 
17
- if data[:template] != snippet.template
18
- diff = Diffy::Diff.new(
19
- snippet.template,
20
- data[:template],
21
- :include_diff_info => true
22
- ).to_s(:color)
23
- status = snippet.update_attributes(data)
24
- result = " #{c_or_u} Snippet #{id_string}:#{name}"
25
- else
26
- diff = nil
27
- status = true
28
- result = " No change to Snippet #{id_string}:#{name}"
18
+ if snippet.locked? && !snippet.new_record? && !force
19
+ return { :diff => nil,
20
+ :status => false,
21
+ :result => "Skipping snippet #{id_string}:#{name} - template is locked" }
22
+ end
23
+
24
+ status = nil
25
+ if data[:template] != snippet.template
26
+ diff = Diffy::Diff.new(
27
+ snippet.template,
28
+ data[:template],
29
+ :include_diff_info => true
30
+ ).to_s(:color)
31
+ snippet.ignore_locking do
32
+ status = snippet.update_attributes(data)
33
+ end
34
+ result = " #{c_or_u} Snippet #{id_string}:#{name}"
35
+ else
36
+ diff = nil
37
+ status = true
38
+ result = " No change to Snippet #{id_string}:#{name}"
39
+ end
40
+ { :diff => diff, :status => status, :result => result, :errors => snippet.errors }
29
41
  end
30
- { :diff => diff, :status => status, :result => result }
31
- end
32
42
 
33
- def map_metadata(metadata, param)
34
- if metadata[param]
35
- case param
36
- when 'oses'
37
- metadata[param].map do |os|
38
- Operatingsystem.all.map { |db| db.to_label =~ /^#{os}/ ? db : nil }
39
- end.flatten.compact
40
- when 'locations'
41
- metadata[param].map do |loc|
42
- Location.all.map { |db| db.name =~ /^#{loc}/ ? db : nil }
43
- end.flatten.compact
44
- when 'organizations'
45
- metadata[param].map do |org|
46
- Organization.all.map { |db| db.name =~ /^#{org}/ ? db : nil }
47
- end.flatten.compact
43
+ def map_metadata(metadata, param)
44
+ if metadata[param]
45
+ case param
46
+ when 'oses'
47
+ metadata[param].map do |os|
48
+ Operatingsystem.all.map { |db| db.to_label =~ /^#{os}/ ? db : nil }
49
+ end.flatten.compact
50
+ when 'locations'
51
+ metadata[param].map do |loc|
52
+ User.current.my_locations.map { |db| db.name =~ /^#{loc}/ ? db : nil }
53
+ end.flatten.compact
54
+ when 'organizations'
55
+ metadata[param].map do |org|
56
+ User.current.my_organizations.map { |db| db.name =~ /^#{org}/ ? db : nil }
57
+ end.flatten.compact
58
+ end
59
+ else
60
+ []
48
61
  end
49
- else
50
- []
51
62
  end
52
63
  end
53
64
  end
@@ -1,54 +1,56 @@
1
- class Setting::TemplateSync < Setting
2
- def self.associate_types
3
- {
4
- 'always' => _('Always'),
5
- 'new' => _('New'),
6
- 'never' => _('Never')
7
- }
8
- end
9
-
10
- def self.metadata_export_mode_types
11
- {
12
- 'refresh' => _('Refresh'),
13
- 'keep' => _('Keep'),
14
- 'remove' => _('Remove')
15
- }
16
- end
17
-
18
-
19
- def self.load_defaults
20
- return unless super
21
-
22
- %w(template_sync_filter template_sync_branch).each { |s| Setting::BLANK_ATTRS << s }
1
+ class Setting
2
+ class TemplateSync < ::Setting
3
+ def self.associate_types
4
+ {
5
+ 'always' => _('Always'),
6
+ 'new' => _('New'),
7
+ 'never' => _('Never')
8
+ }
9
+ end
23
10
 
24
- self.transaction do
25
- [
26
- self.set('template_sync_verbose', N_('Choose verbosity for Rake task importing templates'), false, N_('Verbosity')),
27
- self.set('template_sync_associate', N_('Associate templates to OS'), 'new', N_('Associate'), nil, { :collection => Proc.new { self.associate_types } }),
28
- self.set('template_sync_prefix', N_('The string all imported templates should begin with'), "Community ", N_('Prefix')),
29
- self.set('template_sync_dirname', N_('The directory within the Git repo containing the templates'), '/', N_('Dirname')),
30
- self.set('template_sync_filter', N_('Import or export names matching this regex (case-insensitive; snippets are not filtered)'), nil, N_('Filter')),
31
- self.set('template_sync_repo', N_('Default Git repo to sync from'), 'https://github.com/theforeman/community-templates.git', N_('Repo')),
32
- self.set('template_sync_negate', N_('Negate the prefix (for purging) / filter (for importing/exporting)'), false, N_('Negate')),
33
- self.set('template_sync_branch', N_('Default branch in Git repo'), nil, N_('Branch')),
34
- self.set('template_sync_metadata_export_mode', N_('Default metadata export mode, refresh re-renders metadata, keep will keep existing metadata, remove exports template withou metadata'), 'refresh', N_('Metadata export mode'), nil, { :collection => Proc.new { self.metadata_export_mode_types } })
35
- ].compact.each { |s| self.create! s.update(:category => "Setting::TemplateSync") }
11
+ def self.metadata_export_mode_types
12
+ {
13
+ 'refresh' => _('Refresh'),
14
+ 'keep' => _('Keep'),
15
+ 'remove' => _('Remove')
16
+ }
36
17
  end
37
18
 
38
- true
39
- end
19
+ def self.load_defaults
20
+ return unless super
21
+
22
+ %w(template_sync_filter template_sync_branch).each { |s| Setting::BLANK_ATTRS << s }
23
+
24
+ self.transaction do
25
+ [
26
+ self.set('template_sync_verbose', N_('Choose verbosity for Rake task importing templates'), false, N_('Verbosity')),
27
+ self.set('template_sync_associate', N_('Associate templates to OS'), 'new', N_('Associate'), nil, { :collection => Proc.new { self.associate_types } }),
28
+ self.set('template_sync_prefix', N_('The string all imported templates should begin with'), "Community ", N_('Prefix')),
29
+ self.set('template_sync_dirname', N_('The directory within the Git repo containing the templates'), '/', N_('Dirname')),
30
+ self.set('template_sync_filter', N_('Import or export names matching this regex (case-insensitive; snippets are not filtered)'), nil, N_('Filter')),
31
+ self.set('template_sync_repo', N_('Default Git repo to sync from'), 'https://github.com/theforeman/community-templates.git', N_('Repo')),
32
+ self.set('template_sync_negate', N_('Negate the prefix (for purging) / filter (for importing/exporting)'), false, N_('Negate')),
33
+ self.set('template_sync_branch', N_('Default branch in Git repo'), nil, N_('Branch')),
34
+ self.set('template_sync_metadata_export_mode', N_('Default metadata export mode, refresh re-renders metadata, keep will keep existing metadata, remove exports template withou metadata'), 'refresh', N_('Metadata export mode'), nil, { :collection => Proc.new { self.metadata_export_mode_types } }),
35
+ self.set('template_sync_force', N_('Should importing overwrite locked templates?'), false, N_('Force import')),
36
+ ].compact.each { |s| self.create! s.update(:category => "Setting::TemplateSync") }
37
+ end
38
+
39
+ true
40
+ end
40
41
 
41
- def validate_template_sync_associate(record)
42
- values = record.class.associate_types.keys
43
- if record.value && !values.include?(record.value)
44
- record.errors[:base] << (_("template_sync_associate must be one of %s") % values.join(', '))
42
+ def validate_template_sync_associate(record)
43
+ values = record.class.associate_types.keys
44
+ if record.value && !values.include?(record.value)
45
+ record.errors[:base] << (_("template_sync_associate must be one of %s") % values.join(', '))
46
+ end
45
47
  end
46
- end
47
48
 
48
- def validate_template_sync_metadata_export_mode(record)
49
- values = record.class.metadata_export_mode_types.keys
50
- if record.value && !values.include?(record.value)
51
- record.errors[:base] << (_("template_sync_metadata_export_mode must be one of %s") % values.join(', '))
49
+ def validate_template_sync_metadata_export_mode(record)
50
+ values = record.class.metadata_export_mode_types.keys
51
+ if record.value && !values.include?(record.value)
52
+ record.errors[:base] << (_("template_sync_metadata_export_mode must be one of %s") % values.join(', '))
53
+ end
52
54
  end
53
55
  end
54
56
  end
@@ -14,7 +14,7 @@ module ForemanTemplates
14
14
  end
15
15
  end
16
16
 
17
- def repond_to_missing?(method, include_private = false)
17
+ def respond_to_missing?(method, include_private = false)
18
18
  self.class.setting_overrides.include?(method)
19
19
  end
20
20
 
@@ -1,3 +1,5 @@
1
+ require 'shellwords'
2
+
1
3
  module ForemanTemplates
2
4
  class TemplateExporter < Action
3
5
  def self.setting_overrides
@@ -43,7 +45,7 @@ module ForemanTemplates
43
45
 
44
46
  status = git_repo.status
45
47
  if status.added.any? || status.changed.any? || status.deleted.any? || status.untracked.any?
46
- logger.debug "committing changes in cloned repo"
48
+ logger.debug 'committing changes in cloned repo'
47
49
  git_repo.commit "Templates export made by Foreman user #{User.current.try(:login) || User::ANONYMOUS_ADMIN}"
48
50
 
49
51
  logger.debug "pushing to branch #{branch} at origin #{@repo}"
@@ -70,7 +72,7 @@ module ForemanTemplates
70
72
  end
71
73
 
72
74
  def get_template_filename(template)
73
- template.name.downcase.tr(' ', '_') + '.erb'
75
+ Shellwords.escape(template.name.downcase.tr(' /', '_') + '.erb')
74
76
  end
75
77
 
76
78
  def get_dump_dir(template)
@@ -105,6 +107,5 @@ module ForemanTemplates
105
107
  raise "Unknown metadata export mode #{@metadata_export_mode}"
106
108
  end
107
109
  end
108
-
109
110
  end
110
111
  end
@@ -1,24 +1,18 @@
1
- class NoKindError < Exception; end
2
- class MissingKindError < Exception; end
1
+ class NoKindError < RuntimeError; end
2
+ class MissingKindError < RuntimeError; end
3
3
 
4
4
  module ForemanTemplates
5
5
  class TemplateImporter < Action
6
6
  attr_accessor :metadata, :name, :text
7
7
 
8
8
  def self.setting_overrides
9
- super + %i(associate)
9
+ super + %i(associate force)
10
10
  end
11
11
 
12
12
  def initialize(args = {})
13
13
  super
14
- # Rake hands off strings, not booleans, and "false" is true...
15
- if @verbose.is_a?(String)
16
- @verbose = if @verbose == 'false'
17
- false
18
- else
19
- true
20
- end
21
- end
14
+ @verbose = parse_bool(@verbose)
15
+ @force = parse_bool(@force)
22
16
  end
23
17
 
24
18
  def import!
@@ -69,23 +63,23 @@ module ForemanTemplates
69
63
  if @filter
70
64
  matching = name.match(/#{@filter}/i)
71
65
  matching = !matching if @negate
72
- next if !matching
66
+ next unless matching
73
67
  end
74
68
 
75
69
  begin
76
- # Expects a return of { :diff, :status, :result }
70
+ # Expects a return of { :diff, :status, :result, :errors }
77
71
  data = if metadata['model'].present?
78
- metadata['model'].constantize.import!(name, text, metadata)
72
+ metadata['model'].constantize.import!(name, text, metadata, @force)
79
73
  else
80
74
  # For backwards-compat before "model" metadata was added
81
75
  case metadata['kind']
82
76
  when 'ptable'
83
- Ptable.import!(name, text, metadata)
77
+ Ptable.import!(name, text, metadata, @force)
84
78
  when 'job_template'
85
79
  # TODO: update REX templates to have `model` and delete this
86
80
  update_job_template(name, text)
87
81
  else
88
- ProvisioningTemplate.import!(name, text, metadata)
82
+ ProvisioningTemplate.import!(name, text, metadata, @force)
89
83
  end
90
84
  end
91
85
 
@@ -93,12 +87,12 @@ module ForemanTemplates
93
87
  data[:diff] = calculate_diff(data[:old], data[:new])
94
88
  end
95
89
 
96
- if data[:status] == true && @verbose
90
+ if @verbose
97
91
  result_lines << data[:result]
98
92
  result_lines << data[:diff] unless data[:diff].nil?
99
- elsif data[:status] == false
100
- result_lines << "Template \"#{name}\": #{data[:result]}"
101
93
  end
94
+ result_lines << status_to_text(data[:status], name)
95
+ result_lines << data[:errors] unless data[:errors].empty?
102
96
  rescue MissingKindError
103
97
  result_lines << " Skipping: '#{name}' - No template kind or model detected"
104
98
  next
@@ -136,10 +130,12 @@ module ForemanTemplates
136
130
  puts 'Deprecation warning: JobTemplate support is moving to the Remote Execution plugin'
137
131
  puts "- please add 'model: JobTemplate' to the metadata in '#{file}' to call the right method"
138
132
 
139
- return {
140
- :status => false,
141
- :result => 'Skipping job template import, remote execution plugin is not installed.'
142
- } unless defined?(JobTemplate)
133
+ unless defined?(JobTemplate)
134
+ return {
135
+ :status => false,
136
+ :result => 'Skipping job template import, remote execution plugin is not installed.'
137
+ }
138
+ end
143
139
  template = JobTemplate.import(
144
140
  text.sub(/^name: .*$/, "name: #{name}"),
145
141
  :update => true
@@ -167,5 +163,21 @@ module ForemanTemplates
167
163
  template.destroy
168
164
  end
169
165
  end # :purge
166
+
167
+ private
168
+
169
+ def parse_bool(bool_name)
170
+ bool_name.is_a?(String) ? bool_name != 'false' : bool_name
171
+ end
172
+
173
+ def status_to_text(status, name)
174
+ msg = "#{name} - import "
175
+ msg << if status
176
+ "success"
177
+ else
178
+ 'failure'
179
+ end
180
+ msg
181
+ end
170
182
  end
171
183
  end
data/config/routes.rb CHANGED
@@ -1,13 +1,12 @@
1
1
  Rails.application.routes.draw do
2
-
3
- namespace :api, :defaults => {:format => 'json'} do
4
- scope "(:apiv)", :module => :v2, :defaults => {:apiv => 'v2'}, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2) do
5
- resources :templates, :controller => :template, :only => :import do
2
+ namespace :api, :defaults => { :format => 'json' } do
3
+ scope "(:apiv)", :module => :v2, :defaults => { :apiv => 'v2' }, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2) do
4
+ resources :templates, :controller => :template, :only => [] do
6
5
  collection do
7
6
  post 'import'
7
+ post 'export'
8
8
  end
9
9
  end
10
10
  end
11
11
  end
12
-
13
12
  end
@@ -23,6 +23,9 @@ module ForemanTemplates
23
23
  permission :import_templates, {
24
24
  :"api/v2/template" => [:import]
25
25
  }, :resource_type => 'Template'
26
+ permission :export_templates, {
27
+ :"api/v2/template" => [:export]
28
+ }, :resource_type => 'Template'
26
29
  end
27
30
  add_all_permissions_to_default_roles
28
31
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanTemplates
2
- VERSION = '5.0.0'.freeze
2
+ VERSION = '5.0.1'.freeze
3
3
  end
@@ -6,13 +6,13 @@ namespace :templates do
6
6
  ActiveSupport::Deprecation.warn('templates:sync task has been renamed to templates:import and will be removed in a future version')
7
7
  end
8
8
  # Available options:
9
- #* verbose => Print extra information during the run [false]
10
- #* repo => Sync templates from a different Git repo [https://github.com/theforeman/community-templates]
11
- #* branch => Branch in Git repo [default branch]
12
- #* prefix => The string all imported templates should begin with [Community ]
13
- #* dirname => The directory within the git tree containing the templates [/]
14
- #* filter => Import names matching this regex (case-insensitive; snippets are not filtered)
15
- #* associate => Associate to OS's, Locations & Organizations. Options are: always, new or never [new]
9
+ # * verbose => Print extra information during the run [false]
10
+ # * repo => Sync templates from a different Git repo [https://github.com/theforeman/community-templates]
11
+ # * branch => Branch in Git repo [default branch]
12
+ # * prefix => The string all imported templates should begin with [Community ]
13
+ # * dirname => The directory within the git tree containing the templates [/]
14
+ # * filter => Import names matching this regex (case-insensitive; snippets are not filtered)
15
+ # * associate => Associate to OS's, Locations & Organizations. Options are: always, new or never [new]
16
16
 
17
17
  User.current = User.anonymous_admin
18
18
 
@@ -54,9 +54,9 @@ namespace :templates do
54
54
  User.current = User.anonymous_admin
55
55
 
56
56
  ForemanTemplates::TemplateImporter.new({
57
- #* negate => negate query [false]
58
- #* prefix => The string all templates to purge should ( or not ) begin with [Community ]
59
- #* verbose => Print extra information during the run [false]
57
+ # * negate => negate query [false]
58
+ # * prefix => The string all templates to purge should ( or not ) begin with [Community ]
59
+ # * verbose => Print extra information during the run [false]
60
60
  negate: ENV['negate'],
61
61
  prefix: ENV['prefix'],
62
62
  verbose: ENV['verbose'],
@@ -69,7 +69,6 @@ namespace :templates do
69
69
  ForemanTemplates::Cleaner.new.clean_up!
70
70
  puts 'Clean up finished, you can now remove the plugin from your system'
71
71
  end
72
-
73
72
  end
74
73
 
75
74
  # Tests
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_templates
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Sutcliffe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-25 00:00:00.000000000 Z
11
+ date: 2017-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diffy