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