foreman_templates 2.1.0 → 3.0.0
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 +4 -4
- data/README.md +64 -7
- data/app/models/concerns/foreman_templates/provisioning_template_import.rb +62 -0
- data/app/models/concerns/foreman_templates/ptable_import.rb +54 -0
- data/app/models/concerns/foreman_templates/template_import.rb +33 -0
- data/app/services/foreman_templates/template_importer.rb +167 -0
- data/lib/foreman_templates/engine.rb +15 -7
- data/lib/foreman_templates/version.rb +1 -1
- data/lib/tasks/foreman_templates_tasks.rake +75 -0
- metadata +7 -3
- data/lib/templates.rake +0 -234
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab073792fefeb26fbe23885b712a9baa467753f0
|
4
|
+
data.tar.gz: c9b1d817144bd1fb43b7667a9e453000a4e047e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d746a74ce4346d98e062cd6df27c163d411474925d52ee6e54594fc68f16ccf9776fb849a701c0ef137e8136c8faf2d1ff6d915e39e8214e7dea3c004bea478
|
7
|
+
data.tar.gz: 7ee0097bbc39da25acd264c7932db97c789edc1e8e042ffd7af0f984ebbe20ba7dd1cab8281390bc9022b729e1c6686ef73d81be835bb1abfb8a6035a6986223
|
data/README.md
CHANGED
@@ -21,10 +21,12 @@ You can get the develop branch of the plugin by specifying your Gemfile in this
|
|
21
21
|
|
22
22
|
## Configuration
|
23
23
|
|
24
|
-
There is UI
|
24
|
+
There is no UI configuration at this time.
|
25
25
|
|
26
26
|
## Usage
|
27
27
|
|
28
|
+
### Import
|
29
|
+
|
28
30
|
The plugin provides a Rake task to import the templates. To use it, simply do
|
29
31
|
|
30
32
|
foreman-rake templates:sync
|
@@ -37,7 +39,7 @@ The importer will attempt to figure out the OS and Release the template refers t
|
|
37
39
|
this is a new template being created, and we can find a matching OS in Foreman, the
|
38
40
|
template will be automatically associated with the OS
|
39
41
|
|
40
|
-
|
42
|
+
#### Rake options
|
41
43
|
|
42
44
|
* verbose => Print extra information during the run [false]
|
43
45
|
* repo => Sync templates from a different Git repo [https://github.com/theforeman/community-templates]
|
@@ -51,9 +53,9 @@ The `branch` default will use *develop* if you're on Foreman-nightly; or the
|
|
51
53
|
matching *1.X-stable* branch for your version of Foreman (if it exists); or
|
52
54
|
finally it will remain on the default branch as a fallback.
|
53
55
|
|
54
|
-
|
56
|
+
#### Examples
|
55
57
|
|
56
|
-
Just import all the templates
|
58
|
+
Just import all the templates from the default repo
|
57
59
|
|
58
60
|
foreman-rake templates:sync
|
59
61
|
|
@@ -65,14 +67,69 @@ Import templates matching the name "Fedora"
|
|
65
67
|
|
66
68
|
foreman-rake templates:sync filter='fedora'
|
67
69
|
|
68
|
-
Import templates from a
|
70
|
+
Import templates from a subdirectory of a git repo:
|
69
71
|
|
70
72
|
foreman-rake templates:sync repo="http://github.com/GregSutcliffe/community-templates" dirname='/subdir'
|
71
73
|
|
74
|
+
### Purge
|
75
|
+
|
76
|
+
This task deletes matching templates from the Foreman DB
|
77
|
+
|
78
|
+
#### Rake options
|
79
|
+
|
80
|
+
* prefix => The string all templates to be purged should begin with [Community ]
|
81
|
+
* negate => Negate the search [false]
|
82
|
+
* verbose => Print extra information during the run [false]
|
83
|
+
|
84
|
+
#### Examples
|
85
|
+
|
86
|
+
Just purge all the templates the begin with 'Community '
|
87
|
+
|
88
|
+
foreman-rake templates:purge
|
89
|
+
|
90
|
+
Purge all templates that begin with 'Oops '
|
91
|
+
|
92
|
+
foreman-rake templates:purge prefix='Oops '
|
93
|
+
|
94
|
+
Purge all templates that do not begin with 'Community '
|
95
|
+
|
96
|
+
foreman-rake templates:purge negate=true
|
97
|
+
|
98
|
+
## Integration with other Foreman Plugins
|
99
|
+
|
100
|
+
`templates` will start processing a template by looking for a metadata entry of
|
101
|
+
`model`. If this is found, `templates` will call `import!` on this model.
|
102
|
+
|
103
|
+
That means it's possible for a plugin to define it's own handling of text and
|
104
|
+
metadata, relevant to the plugins own interests. The `import!` method will be
|
105
|
+
sent 3 arguments - the `name` of the template, the `text` of the template, and
|
106
|
+
a complete copy of the `metadata`.
|
107
|
+
|
108
|
+
As a trivial example for a random plugin, suppose `foreman_nosuchplugin` has
|
109
|
+
this code:
|
110
|
+
|
111
|
+
```
|
112
|
+
module ForemanNosuchplugin
|
113
|
+
class SomeTemplate
|
114
|
+
def self.import!(name, text, metadata)
|
115
|
+
File.open("/tmp/#{name}",'w') {|f| f.write text }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
Assuming a template had "model: SomeTemplate" in it's metadata, this would then
|
122
|
+
get written to a file in `/tmp`.
|
123
|
+
|
124
|
+
`templates` will expect the `import!` method to return a Hash, containing:
|
125
|
+
|
126
|
+
* `:status` (boolean),
|
127
|
+
* `:diff` (text, may be nil), or
|
128
|
+
* `:old` and `:new` (in which case this plugin will calculate the diff)
|
129
|
+
* :result` (text, may be nil).
|
130
|
+
|
72
131
|
## TODO
|
73
132
|
|
74
|
-
* Allow user to filter to a specific subset of templates
|
75
|
-
* Add associations by template family
|
76
133
|
* Add a button to the UI with Deface to run the rake task
|
77
134
|
|
78
135
|
## Copyright
|
@@ -0,0 +1,62 @@
|
|
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_oses(metadata)
|
21
|
+
|
22
|
+
# Printout helpers
|
23
|
+
c_or_u = template.new_record? ? 'Created' : 'Updated'
|
24
|
+
id_string = ('id' + template.id) rescue ''
|
25
|
+
|
26
|
+
if (metadata['associate'] == 'new' && template.new_record?) || (metadata['associate'] == 'always')
|
27
|
+
data[:operatingsystem_ids] = oses.map(&:id)
|
28
|
+
end
|
29
|
+
|
30
|
+
if data[:template] != template.template
|
31
|
+
diff = Diffy::Diff.new(
|
32
|
+
template.template,
|
33
|
+
data[:template],
|
34
|
+
:include_diff_info => true
|
35
|
+
).to_s(:color)
|
36
|
+
status = template.update_attributes(data)
|
37
|
+
result = " #{c_or_u} Template #{id_string}:#{name}"
|
38
|
+
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
39
|
+
elsif data[:operatingsystem_ids]
|
40
|
+
diff = nil
|
41
|
+
status = template.update_attributes(data)
|
42
|
+
result = " #{c_or_u} Template Associations #{id_string}:#{name}"
|
43
|
+
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
44
|
+
else
|
45
|
+
diff = nil
|
46
|
+
status = true
|
47
|
+
result = " No change to Template #{id_string}:#{name}"
|
48
|
+
end
|
49
|
+
{ :diff => diff, :status => status, :result => result }
|
50
|
+
end
|
51
|
+
|
52
|
+
def map_oses(metadata)
|
53
|
+
if metadata['oses']
|
54
|
+
metadata['oses'].map do |os|
|
55
|
+
Operatingsystem.all.map { |db| db.to_label =~ /^#{os}/ ? db : nil }
|
56
|
+
end.flatten.compact
|
57
|
+
else
|
58
|
+
[]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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 = { :layout => text }
|
12
|
+
oses = map_oses(metadata)
|
13
|
+
|
14
|
+
# Printout helpers
|
15
|
+
c_or_u = ptable.new_record? ? 'Created' : 'Updated'
|
16
|
+
id_string = ('id' + ptable.id) rescue ''
|
17
|
+
|
18
|
+
if (metadata['associate'] == 'new' && ptable.new_record?) || (metadata['associate'] == 'always')
|
19
|
+
data[:os_family] = oses.map(&:family).uniq.first
|
20
|
+
end
|
21
|
+
|
22
|
+
if data[:layout] != ptable.layout
|
23
|
+
diff = Diffy::Diff.new(
|
24
|
+
ptable.layout,
|
25
|
+
data[:layout],
|
26
|
+
:include_diff_info => true
|
27
|
+
).to_s(:color)
|
28
|
+
status = ptable.update_attributes(data)
|
29
|
+
result = " #{c_or_u} Ptable #{id_string}:#{name}"
|
30
|
+
elsif data[:os_family]
|
31
|
+
diff = nil
|
32
|
+
status = ptable.update_attributes(data)
|
33
|
+
result = " #{c_or_u} Ptable Associations #{id_string}:#{name}"
|
34
|
+
result += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" unless oses.empty?
|
35
|
+
else
|
36
|
+
diff = nil
|
37
|
+
status = true
|
38
|
+
result = " No change to Ptable #{id_string}:#{name}"
|
39
|
+
end
|
40
|
+
{ :diff => diff, :status => status, :result => result }
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO: DRY this, it's copied from ProvisioningTemplateImport
|
44
|
+
def map_oses(metadata)
|
45
|
+
if metadata['oses']
|
46
|
+
metadata['oses'].map do |os|
|
47
|
+
Operatingsystem.all.map { |db| db.to_label =~ /^#{os}/ ? db : nil }
|
48
|
+
end.flatten.compact
|
49
|
+
else
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ForemanTemplates::TemplateImport
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
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
|
+
}
|
12
|
+
|
13
|
+
# Printout helpers
|
14
|
+
c_or_u = snippet.new_record? ? 'Created' : 'Updated'
|
15
|
+
id_string = ('id' + snippet.id) rescue ''
|
16
|
+
|
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}"
|
29
|
+
end
|
30
|
+
{ :diff => diff, :status => status, :result => result }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
class NoKindError < Exception; end
|
2
|
+
class MissingKindError < Exception; end
|
3
|
+
|
4
|
+
module ForemanTemplates
|
5
|
+
class TemplateImporter
|
6
|
+
delegate :logger, :to => :Rails
|
7
|
+
attr_accessor :metadata, :name, :text
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
@verbose = args[:verbose] || false
|
11
|
+
@associate = args[:associate] || 'new'
|
12
|
+
@prefix = args[:prefix] || 'Community '
|
13
|
+
@dirname = args[:dirname] || '/'
|
14
|
+
@filter = args[:filter] || nil
|
15
|
+
@repo = args[:repo] || 'https://github.com/theforeman/community-templates.git'
|
16
|
+
@negate = args[:negate] || false
|
17
|
+
@branch = args[:branch] || false
|
18
|
+
|
19
|
+
# Rake hands off strings, not booleans, and "false" is true...
|
20
|
+
if @verbose.is_a?(String)
|
21
|
+
@verbose = if @verbose == 'false'
|
22
|
+
false
|
23
|
+
else
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def import!
|
30
|
+
# Check out the community templates to a temp location
|
31
|
+
@dir = Dir.mktmpdir
|
32
|
+
|
33
|
+
begin
|
34
|
+
gitrepo = Git.clone(@repo, @dir)
|
35
|
+
branch = @branch ? @branch : get_default_branch(gitrepo)
|
36
|
+
gitrepo.checkout(branch) if branch
|
37
|
+
|
38
|
+
return parse_files!
|
39
|
+
ensure
|
40
|
+
FileUtils.remove_entry_secure(@dir) if File.exist?(@dir)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_files!
|
45
|
+
result_lines = []
|
46
|
+
|
47
|
+
# Build a list of ERB files to parse
|
48
|
+
Dir["#{@dir}#{@dirname}/**/*.erb"].each do |template|
|
49
|
+
text = File.read(template)
|
50
|
+
result_lines << 'Parsing: ' + template.gsub(/#{@dir}#{@dirname}/, '') if @verbose
|
51
|
+
|
52
|
+
metadata = parse_metadata(text)
|
53
|
+
metadata['associate'] = @associate
|
54
|
+
|
55
|
+
# Get the name and filter
|
56
|
+
filename = template.split('/').last
|
57
|
+
title = filename.split('.').first
|
58
|
+
name = metadata['name'] || title
|
59
|
+
name = [@prefix, name].compact.join
|
60
|
+
next if @filter && !name.match(/#{@filter}/i)
|
61
|
+
|
62
|
+
raise MissingKindError unless metadata['kind']
|
63
|
+
|
64
|
+
begin
|
65
|
+
# Expects a return of { :diff, :status, :result }
|
66
|
+
data = if metadata['model'].present?
|
67
|
+
metadata['model'].constantize.import!(name, text, metadata)
|
68
|
+
else
|
69
|
+
# For backwards-compat before "model" metadata was added
|
70
|
+
case metadata['kind']
|
71
|
+
when 'ptable'
|
72
|
+
Ptable.import!(name, text, metadata)
|
73
|
+
when 'job_template'
|
74
|
+
# TODO: update REX templates to have `model` and delete this
|
75
|
+
update_job_template(name, text)
|
76
|
+
else
|
77
|
+
ProvisioningTemplate.import!(name, text, metadata)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if data[:diff].nil? && data[:old].present? && data[:new].present?
|
82
|
+
data[:diff] = calculate_diff(data[:old], data[:new])
|
83
|
+
end
|
84
|
+
|
85
|
+
if data[:status] == true && @verbose
|
86
|
+
result_lines << data[:result]
|
87
|
+
result_lines << data[:diff] unless data[:diff].nil?
|
88
|
+
elsif data[:status] == false
|
89
|
+
result_lines << "Template \"#{name}\": #{data[:result]}"
|
90
|
+
end
|
91
|
+
rescue MissingKindError
|
92
|
+
result_lines << " Skipping: '#{name}' - No template kind or model detected"
|
93
|
+
next
|
94
|
+
rescue NoKindError
|
95
|
+
result_lines << " Skipping: '#{name}' - Unknown template kind '#{metadata['kind']}'"
|
96
|
+
next
|
97
|
+
rescue NameError
|
98
|
+
result_lines << " Skipping: '#{name}' - Unknown template model '#{metadata['model']}'"
|
99
|
+
next
|
100
|
+
end
|
101
|
+
end
|
102
|
+
result_lines
|
103
|
+
end
|
104
|
+
|
105
|
+
def calculate_diff(old, new)
|
106
|
+
if old != new
|
107
|
+
Diffy::Diff.new(old, new, :include_diff_info => true).to_s(:color)
|
108
|
+
else
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_default_branch(repo)
|
114
|
+
branch_names = repo.branches.map(&:name).uniq
|
115
|
+
|
116
|
+
# Always use develop on Foreman-nightly, if present, or else relevant stable branch
|
117
|
+
target = SETTINGS[:version].tag == 'develop' ? 'develop' : "#{SETTINGS[:version].short}-stable"
|
118
|
+
return target if branch_names.include?(target)
|
119
|
+
|
120
|
+
# stay on default branch as fallback
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse_metadata(text)
|
125
|
+
# Pull out the first erb comment only - /m is for a multiline regex
|
126
|
+
extracted = text.match(/<%\#[\t a-z0-9=:]*(.+?).-?%>/m)
|
127
|
+
extracted.nil? ? {} : YAML.load(extracted[1])
|
128
|
+
end
|
129
|
+
|
130
|
+
def update_job_template(name, text)
|
131
|
+
file = name.gsub(/^#{@prefix}/, '')
|
132
|
+
puts 'Deprecation warning: JobTemplate support is moving to the Remote Execution plugin'
|
133
|
+
puts "- please add 'model: JobTemplate' to the metadata in '#{file}' to call the right method"
|
134
|
+
|
135
|
+
return {
|
136
|
+
:status => false,
|
137
|
+
:result => 'Skipping job template import, remote execution plugin is not installed.'
|
138
|
+
} unless defined?(JobTemplate)
|
139
|
+
template = JobTemplate.import(
|
140
|
+
text.sub(/^name: .*$/, "name: #{name}"),
|
141
|
+
:update => true
|
142
|
+
)
|
143
|
+
|
144
|
+
c_or_u = template.new_record? ? 'Created' : 'Updated'
|
145
|
+
id_string = ('id' + template.id) rescue ''
|
146
|
+
|
147
|
+
if template.template != template.template_was
|
148
|
+
diff = Diffy::Diff.new(
|
149
|
+
template.template_was,
|
150
|
+
template.template,
|
151
|
+
:include_diff_info => true
|
152
|
+
).to_s(:color)
|
153
|
+
end
|
154
|
+
|
155
|
+
result = " #{c_or_u} Template #{id_string}:#{name}"
|
156
|
+
{ :diff => diff, :status => template.save, :result => result }
|
157
|
+
end
|
158
|
+
|
159
|
+
def purge!
|
160
|
+
clause = "name #{@negate ? 'NOT ' : ''}LIKE ?"
|
161
|
+
ProvisioningTemplate.where(clause, "#{@prefix}%").each do |template|
|
162
|
+
puts template if @verbose
|
163
|
+
template.destroy
|
164
|
+
end
|
165
|
+
end # :purge
|
166
|
+
end
|
167
|
+
end
|
@@ -1,20 +1,28 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
1
3
|
require 'diffy'
|
2
4
|
require 'git'
|
3
5
|
|
4
6
|
module ForemanTemplates
|
5
|
-
#Inherit from the Rails module of the parent app (Foreman), not the plugin.
|
6
|
-
#Thus, inhereits from ::Rails::Engine and not from Rails::Engine
|
7
|
+
# Inherit from the Rails module of the parent app (Foreman), not the plugin.
|
8
|
+
# Thus, inhereits from ::Rails::Engine and not from Rails::Engine
|
7
9
|
class Engine < ::Rails::Engine
|
10
|
+
engine_name 'foreman_templates'
|
8
11
|
|
9
|
-
initializer 'foreman_templates.register_plugin', :
|
12
|
+
initializer 'foreman_templates.register_plugin', :before => :finisher_hook do
|
10
13
|
Foreman::Plugin.register :foreman_templates do
|
11
|
-
requires_foreman '>= 1.
|
14
|
+
requires_foreman '>= 1.12'
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
15
|
-
|
16
|
-
|
18
|
+
config.to_prepare do
|
19
|
+
begin
|
20
|
+
Template.send(:include, ForemanTemplates::TemplateImport)
|
21
|
+
Ptable.send(:include, ForemanTemplates::PtableImport)
|
22
|
+
ProvisioningTemplate.send(:include, ForemanTemplates::ProvisioningTemplateImport)
|
23
|
+
rescue => e
|
24
|
+
puts "#{ForemanTemplates::ENGINE_NAME}: skipping engine hook (#{e})"
|
25
|
+
end
|
17
26
|
end
|
18
|
-
|
19
27
|
end
|
20
28
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Tasks
|
2
|
+
namespace :templates do
|
3
|
+
desc 'Synchronize templates from a git repo'
|
4
|
+
task :sync => :environment do
|
5
|
+
# Available options:
|
6
|
+
#* verbose => Print extra information during the run [false]
|
7
|
+
#* repo => Sync templates from a different Git repo [https://github.com/theforeman/community-templates]
|
8
|
+
#* branch => Branch in Git repo [default branch]
|
9
|
+
#* prefix => The string all imported templates should begin with [Community ]
|
10
|
+
#* dirname => The directory within the git tree containing the templates [/]
|
11
|
+
#* filter => Import names matching this regex (case-insensitive; snippets are not filtered)
|
12
|
+
#* associate => Associate to OS, always, new or never [new]
|
13
|
+
|
14
|
+
results = ForemanTemplates::TemplateImporter.new({
|
15
|
+
verbose: ENV['verbose'],
|
16
|
+
repo: ENV['repo'],
|
17
|
+
branch: ENV['branch'],
|
18
|
+
prefix: ENV['prefix'],
|
19
|
+
dirname: ENV['dirname'],
|
20
|
+
filter: ENV['filter'],
|
21
|
+
associate: ENV['associate'],
|
22
|
+
}).import!
|
23
|
+
|
24
|
+
puts results.join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Purge unwanted templates from foreman'
|
28
|
+
task :purge => :environment do
|
29
|
+
ForemanTemplates::TemplateImporter.new({
|
30
|
+
#* negate => negate query [false]
|
31
|
+
#* prefix => The string all templates to purge should ( or not ) begin with [Community ]
|
32
|
+
#* verbose => Print extra information during the run [false]
|
33
|
+
negate: ENV['negate'],
|
34
|
+
prefix: ENV['prefix'],
|
35
|
+
verbose: ENV['verbose'],
|
36
|
+
}).purge!
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
# Tests
|
42
|
+
namespace :test do
|
43
|
+
desc "Test ForemanTemplates"
|
44
|
+
Rake::TestTask.new(:foreman_templates) do |t|
|
45
|
+
test_dir = File.join(File.dirname(__FILE__), '../..', 'test')
|
46
|
+
t.libs << ['test', test_dir]
|
47
|
+
t.pattern = "#{test_dir}/**/*_test.rb"
|
48
|
+
t.verbose = true
|
49
|
+
t.warning = false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
namespace :foreman_templates do
|
54
|
+
task :rubocop do
|
55
|
+
begin
|
56
|
+
require 'rubocop/rake_task'
|
57
|
+
RuboCop::RakeTask.new(:rubocop_foreman_templates) do |task|
|
58
|
+
task.patterns = ["#{ForemanTemplates::Engine.root}/app/**/*.rb",
|
59
|
+
"#{ForemanTemplates::Engine.root}/lib/**/*.rb",
|
60
|
+
"#{ForemanTemplates::Engine.root}/test/**/*.rb"]
|
61
|
+
end
|
62
|
+
rescue
|
63
|
+
puts 'Rubocop not loaded.'
|
64
|
+
end
|
65
|
+
|
66
|
+
Rake::Task['rubocop_foreman_templates'].invoke
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Rake::Task[:test].enhance ['test:foreman_templates']
|
71
|
+
|
72
|
+
load 'tasks/jenkins.rake'
|
73
|
+
if Rake::Task.task_defined?(:'jenkins:unit')
|
74
|
+
Rake::Task['jenkins:unit'].enhance ['test:foreman_templates', 'foreman_templates:rubocop']
|
75
|
+
end
|
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:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Sutcliffe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diffy
|
@@ -49,10 +49,14 @@ files:
|
|
49
49
|
- LICENSE
|
50
50
|
- README.md
|
51
51
|
- Rakefile
|
52
|
+
- app/models/concerns/foreman_templates/provisioning_template_import.rb
|
53
|
+
- app/models/concerns/foreman_templates/ptable_import.rb
|
54
|
+
- app/models/concerns/foreman_templates/template_import.rb
|
55
|
+
- app/services/foreman_templates/template_importer.rb
|
52
56
|
- lib/foreman_templates.rb
|
53
57
|
- lib/foreman_templates/engine.rb
|
54
58
|
- lib/foreman_templates/version.rb
|
55
|
-
- lib/
|
59
|
+
- lib/tasks/foreman_templates_tasks.rake
|
56
60
|
homepage: http://github.com/theforeman/foreman_templates
|
57
61
|
licenses:
|
58
62
|
- GPL-3
|
data/lib/templates.rake
DELETED
@@ -1,234 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'yaml'
|
3
|
-
require 'diffy'
|
4
|
-
require 'git'
|
5
|
-
|
6
|
-
class NoKindError < Exception ; end
|
7
|
-
|
8
|
-
def db_oses
|
9
|
-
@db_oses || Operatingsystem.all
|
10
|
-
end
|
11
|
-
|
12
|
-
def metadata(text)
|
13
|
-
# Pull out the first erb comment only - /m is for a multiline regex
|
14
|
-
extracted = text.match(/<%\#(.+?).-?%>/m)
|
15
|
-
extracted == nil ? {} : YAML.load(extracted[1])
|
16
|
-
end
|
17
|
-
|
18
|
-
def map_oses
|
19
|
-
oses = if @metadata['oses']
|
20
|
-
@metadata['oses'].map do |os|
|
21
|
-
db_oses.map { |db| db.to_label =~ /^#{os}/ ? db : nil}
|
22
|
-
end.flatten.compact
|
23
|
-
else
|
24
|
-
[]
|
25
|
-
end
|
26
|
-
return oses
|
27
|
-
end
|
28
|
-
|
29
|
-
def update_job_template
|
30
|
-
return {:status => false, :result => 'Skipping job template import, remote execution plugin is not installed.'} unless defined?(JobTemplate)
|
31
|
-
template = JobTemplate.import(@text.sub(/^name: .*$/, "name: #{@name}"), :update => true)
|
32
|
-
|
33
|
-
string = template.new_record? ? 'Created' : 'Updated'
|
34
|
-
|
35
|
-
if template.template != template.template_was
|
36
|
-
diff = Diffy::Diff.new(template.template_was, template.template, :include_diff_info => true).to_s(:color)
|
37
|
-
end
|
38
|
-
|
39
|
-
result = " #{string} Template #{ 'id' + template.id rescue ''}:#{@name}"
|
40
|
-
|
41
|
-
{:diff => diff, :status => template.save, :result => result}
|
42
|
-
end
|
43
|
-
|
44
|
-
def update_template
|
45
|
-
# Get template type
|
46
|
-
unless kind = TemplateKind.find_by_name(@metadata['kind'])
|
47
|
-
raise NoKindError
|
48
|
-
end
|
49
|
-
|
50
|
-
db_template = ProvisioningTemplate.where(:name => @name).first_or_initialize
|
51
|
-
data = {
|
52
|
-
:template => @text,
|
53
|
-
:snippet => false,
|
54
|
-
:template_kind_id => kind.id
|
55
|
-
}
|
56
|
-
string = db_template.new_record? ? "Created" : "Updated"
|
57
|
-
|
58
|
-
oses = map_oses
|
59
|
-
if (@associate == 'new' and db_template.new_record?) or (@associate == 'always')
|
60
|
-
data[:operatingsystem_ids] = oses.map(&:id)
|
61
|
-
end
|
62
|
-
|
63
|
-
if @text != db_template.template
|
64
|
-
diff = Diffy::Diff.new(db_template.template, @text, :include_diff_info => true).to_s(:color)
|
65
|
-
status = db_template.update_attributes(data)
|
66
|
-
result = " #{string} Template #{ 'id' + db_template.id rescue ''}:#{@name}"
|
67
|
-
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" if !oses.empty?
|
68
|
-
elsif data[:operatingsystem_ids]
|
69
|
-
diff = nil
|
70
|
-
status = db_template.update_attributes(data)
|
71
|
-
result = " #{string} Template Associations #{ 'id' + db_template.id rescue ''}:#{@name}"
|
72
|
-
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" if !oses.empty?
|
73
|
-
else
|
74
|
-
diff = nil
|
75
|
-
status = true
|
76
|
-
result = " No change to Template #{ ( 'id' + db_template.id ) rescue ''}:#{@name}"
|
77
|
-
end
|
78
|
-
{ :diff => diff, :status => status, :result => result }
|
79
|
-
end
|
80
|
-
|
81
|
-
def update_ptable
|
82
|
-
db_ptable = Ptable.where(:name => @name).first_or_initialize
|
83
|
-
data = { :layout => @text }
|
84
|
-
string = db_ptable.new_record? ? "Created" : "Updated"
|
85
|
-
|
86
|
-
oses = map_oses
|
87
|
-
if (@associate == 'new' and db_ptable.new_record?) or (@associate == 'always')
|
88
|
-
data[:os_family] = oses.map(&:family).uniq.first
|
89
|
-
end
|
90
|
-
|
91
|
-
if @text != db_ptable.layout
|
92
|
-
diff = Diffy::Diff.new(db_ptable.layout, @text, :include_diff_info => true).to_s(:color)
|
93
|
-
status = db_ptable.update_attributes(data)
|
94
|
-
result = " #{string} Ptable #{ ( 'id' + db_ptable.id ) rescue ''}:#{@name}"
|
95
|
-
elsif data[:os_family]
|
96
|
-
diff = nil
|
97
|
-
status = db_ptable.update_attributes(data)
|
98
|
-
result = " #{string} Ptable Associations #{ ( 'id' + db_ptable.id ) rescue ''}:#{@name}"
|
99
|
-
result += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" if !oses.empty?
|
100
|
-
else
|
101
|
-
diff = nil
|
102
|
-
status = true
|
103
|
-
result = " No change to Ptable #{ ( 'id' + db_ptable.id ) rescue ''}:#{@name}"
|
104
|
-
end
|
105
|
-
{ :diff => diff, :status => status, :result => result }
|
106
|
-
end
|
107
|
-
|
108
|
-
def update_snippet
|
109
|
-
db_snippet = ProvisioningTemplate.where(:name => @name).first_or_initialize
|
110
|
-
data = {
|
111
|
-
:template => @text,
|
112
|
-
:snippet => true
|
113
|
-
}
|
114
|
-
string = db_snippet.new_record? ? "Created" : "Updated"
|
115
|
-
|
116
|
-
if @text != db_snippet.template
|
117
|
-
diff = Diffy::Diff.new(db_snippet.template, @text, :include_diff_info => true).to_s(:color)
|
118
|
-
status = db_snippet.update_attributes(data)
|
119
|
-
result = " #{string} Snippet #{ ('id' + db_snippet.id) rescue ''}:#{@name}"
|
120
|
-
else
|
121
|
-
diff = nil
|
122
|
-
status = true
|
123
|
-
result = " No change to Snippet #{ 'id' + db_snippet.id rescue ''}:#{@name}"
|
124
|
-
end
|
125
|
-
{ :diff => diff, :status => status, :result => result }
|
126
|
-
end
|
127
|
-
|
128
|
-
def get_default_branch(repo)
|
129
|
-
branch_names = repo.branches.map(&:name).uniq
|
130
|
-
|
131
|
-
# Always use develop on Foreman-nightly, if present, or else relevant stable branch
|
132
|
-
target = SETTINGS[:version].tag == 'develop' ? 'develop' : "#{SETTINGS[:version].short}-stable"
|
133
|
-
return target if branch_names.include?(target)
|
134
|
-
|
135
|
-
# stay on default branch as fallback
|
136
|
-
return nil
|
137
|
-
end
|
138
|
-
|
139
|
-
desc <<-END_DESC
|
140
|
-
Synchronize templates from a git repo
|
141
|
-
END_DESC
|
142
|
-
namespace :templates do
|
143
|
-
task :sync => :environment do
|
144
|
-
# Available options:
|
145
|
-
#* verbose => Print extra information during the run [false]
|
146
|
-
#* repo => Sync templates from a different Git repo [https://github.com/theforeman/community-templates]
|
147
|
-
#* branch => Branch in Git repo [default branch]
|
148
|
-
#* prefix => The string all imported templates should begin with [Community ]
|
149
|
-
#* dirname => The directory within the git tree containing the templates [/]
|
150
|
-
#* filter => Import names matching this regex (case-insensitive; snippets are not filtered)
|
151
|
-
#* associate => Associate to OS, always, new or never [new]
|
152
|
-
|
153
|
-
@verbose = ( ENV['verbose'] and ENV['verbose'] != 'false' ) ? true : false
|
154
|
-
@associate = ENV['associate'] ? ENV['associate'] : 'new'
|
155
|
-
prefix = ENV['prefix'] ? ENV['prefix'] : 'Community '
|
156
|
-
dirname = ENV['dirname'] ? ENV['dirname'] : '/'
|
157
|
-
filter = ENV['filter'] ? ENV['filter'] : nil
|
158
|
-
repo = ENV['repo'] ? ENV['repo'] : "https://github.com/theforeman/community-templates.git"
|
159
|
-
|
160
|
-
# Check out the community templates to a temp location
|
161
|
-
dir = Dir.mktmpdir
|
162
|
-
begin
|
163
|
-
gitrepo = Git.clone(repo, dir)
|
164
|
-
branch = ENV['branch'] ? ENV['branch'] : get_default_branch(gitrepo)
|
165
|
-
gitrepo.checkout(branch) if branch
|
166
|
-
|
167
|
-
# Build a list of ERB files to parse
|
168
|
-
Dir["#{dir}#{dirname}/**/*.erb"].each do |template|
|
169
|
-
@text = File.read(template)
|
170
|
-
puts "Parsing: " + template.gsub(/#{dir}#{dirname}/,'') if @verbose
|
171
|
-
|
172
|
-
@metadata = metadata(@text)
|
173
|
-
|
174
|
-
# Get the name and filter
|
175
|
-
filename = template.split('/').last
|
176
|
-
title = filename.split('.').first
|
177
|
-
@name = @metadata['name'] || title
|
178
|
-
@name = [prefix, @name].compact.join()
|
179
|
-
next if filter and not @name.match(/#{filter}/i)
|
180
|
-
|
181
|
-
unless @metadata['kind']
|
182
|
-
puts " Error: Must specify template kind"
|
183
|
-
next
|
184
|
-
end
|
185
|
-
|
186
|
-
begin
|
187
|
-
case @metadata['kind']
|
188
|
-
when 'ptable'
|
189
|
-
data = update_ptable
|
190
|
-
when 'snippet'
|
191
|
-
data = update_snippet
|
192
|
-
when 'job_template'
|
193
|
-
data = update_job_template
|
194
|
-
else
|
195
|
-
data = update_template
|
196
|
-
end
|
197
|
-
|
198
|
-
if data[:status] == true && @verbose
|
199
|
-
puts data[:result]
|
200
|
-
puts data[:diff]
|
201
|
-
elsif data[:status] == false
|
202
|
-
puts "Error with #{@name}"
|
203
|
-
puts data[:result]
|
204
|
-
end
|
205
|
-
rescue NoKindError
|
206
|
-
puts " Error: Unknown template type '#{@metadata['kind']}'"
|
207
|
-
next
|
208
|
-
end
|
209
|
-
end
|
210
|
-
ensure
|
211
|
-
FileUtils.remove_entry_secure(dir) if File.exist?(dir)
|
212
|
-
end
|
213
|
-
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
# Setup Tests
|
218
|
-
namespace :test do
|
219
|
-
desc "Test ForemanTemplates plugin"
|
220
|
-
Rake::TestTask.new(:templates) do |t|
|
221
|
-
test_dir = File.join(File.dirname(__FILE__), '..', 'test')
|
222
|
-
t.libs << ["test",test_dir]
|
223
|
-
t.pattern = "#{test_dir}/**/*_test.rb"
|
224
|
-
end
|
225
|
-
end
|
226
|
-
Rake::Task[:test].enhance do
|
227
|
-
Rake::Task['test:templates'].invoke
|
228
|
-
end
|
229
|
-
load 'tasks/jenkins.rake'
|
230
|
-
if Rake::Task.task_defined?('jenkins:unit')
|
231
|
-
Rake::Task["jenkins:unit"].enhance do
|
232
|
-
Rake::Task['test:templates'].invoke
|
233
|
-
end
|
234
|
-
end
|