vagrant-pe_build 0.0.3 → 0.1.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.
- data/Gemfile +2 -0
- data/README.markdown +10 -10
- data/lib/pe_build.rb +15 -7
- data/lib/pe_build/archive.rb +112 -0
- data/lib/pe_build/archive_collection.rb +43 -0
- data/lib/pe_build/command.rb +48 -11
- data/lib/pe_build/command/copy.rb +38 -0
- data/lib/pe_build/command/download.rb +34 -3
- data/lib/pe_build/command/list.rb +16 -4
- data/lib/pe_build/config/global.rb +69 -0
- data/lib/pe_build/config/pe_bootstrap.rb +110 -0
- data/lib/pe_build/config_default.rb +16 -0
- data/lib/pe_build/idempotent.rb +16 -0
- data/lib/pe_build/plugin.rb +38 -0
- data/lib/pe_build/provisioner/pe_bootstrap.rb +202 -0
- data/lib/pe_build/transfer/file.rb +18 -0
- data/lib/pe_build/transfer/uri.rb +53 -0
- data/lib/pe_build/unpack/tar.rb +38 -0
- data/lib/pe_build/util/config.rb +29 -0
- data/lib/pe_build/version.rb +1 -1
- data/lib/{vagrant_init.rb → vagrant-pe_build.rb} +0 -0
- data/templates/answers/agent.txt.erb +1 -1
- data/templates/answers/master-existing-db.txt.erb +2 -2
- data/templates/answers/master.txt.erb +2 -2
- data/templates/locales/en.yml +7 -0
- data/{bootstrap/master/post/relocate_puppet.sh → templates/scripts/relocate_installation.sh} +0 -0
- data/vagrant-pe_build.gemspec +2 -1
- metadata +40 -17
- data/bootstrap/base/provision/install_puppet_enterprise.sh +0 -10
- data/lib/pe_build/action.rb +0 -20
- data/lib/pe_build/action/download.rb +0 -56
- data/lib/pe_build/action/unpackage.rb +0 -70
- data/lib/pe_build/config.rb +0 -35
- data/lib/pe_build/provisioners.rb +0 -9
- data/lib/pe_build/provisioners/puppet_enterprise.rb +0 -1
- data/lib/pe_build/provisioners/puppet_enterprise_bootstrap.rb +0 -171
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
|
3
|
+
require 'pe_build/config/global'
|
4
|
+
require 'pe_build/config_default'
|
5
|
+
|
6
|
+
module PEBuild
|
7
|
+
module Config
|
8
|
+
|
9
|
+
class PEBootstrap < PEBuild::Config::Global
|
10
|
+
|
11
|
+
# @!attribute master
|
12
|
+
# @return The DNS hostname of the Puppet master for this node.
|
13
|
+
attr_accessor :master
|
14
|
+
|
15
|
+
# @!attribute answer_file
|
16
|
+
# @return [String] The path to a user specified answer_file file (Optional)
|
17
|
+
attr_accessor :answer_file
|
18
|
+
|
19
|
+
# @!attribute verbose
|
20
|
+
# @return [TrueClass, FalseClass] if stdout will be displayed when installing
|
21
|
+
attr_accessor :verbose
|
22
|
+
|
23
|
+
# @!attribute role
|
24
|
+
# @return [Symbol] The type of the PE installation role. One of [:master, :agent]
|
25
|
+
attr_accessor :role
|
26
|
+
VALID_ROLES = [:agent, :master]
|
27
|
+
|
28
|
+
# @!attribute step
|
29
|
+
# @return [Hash<Symbol, String>] a hash whose keys are step levels, and whose
|
30
|
+
# keys are directories to optional steps.
|
31
|
+
attr_accessor :step
|
32
|
+
|
33
|
+
attr_accessor :relocate_manifests
|
34
|
+
# @!attribute relocate_manifests
|
35
|
+
# @return [TrueClass, FalseClass] if the puppet master should use manifests
|
36
|
+
# out of the vagrant directory.
|
37
|
+
|
38
|
+
# @todo config option for autosigning.
|
39
|
+
#attr_accessor :autosign
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
super
|
43
|
+
@role = UNSET_VALUE
|
44
|
+
@verbose = UNSET_VALUE
|
45
|
+
@master = UNSET_VALUE
|
46
|
+
@answer_file = UNSET_VALUE
|
47
|
+
|
48
|
+
@relocate_manifests = UNSET_VALUE
|
49
|
+
|
50
|
+
#@autosign = UNSET_VALUE
|
51
|
+
|
52
|
+
@step = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
include PEBuild::ConfigDefault
|
56
|
+
|
57
|
+
def finalize!
|
58
|
+
# This does _not_ set default values for config options inherited from the
|
59
|
+
# global configuration. If configuration is not set for a value on the
|
60
|
+
# global config or here it will be passed through as `UNSET_VALUE`, which
|
61
|
+
# is not ideal.
|
62
|
+
|
63
|
+
set_default :@role, :agent
|
64
|
+
set_default :@verbose, true
|
65
|
+
set_default :@master, 'master'
|
66
|
+
set_default :@answer_file, nil
|
67
|
+
|
68
|
+
set_default :@relocate_manifests, (@role == :master)
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_step(name, script_path)
|
72
|
+
name = (name.is_a?(Symbol)) ? name : name.intern
|
73
|
+
step[name] = script_path
|
74
|
+
end
|
75
|
+
|
76
|
+
# @todo Convert error strings to I18n
|
77
|
+
def validate(machine)
|
78
|
+
h = super
|
79
|
+
|
80
|
+
errors = []
|
81
|
+
if @version == UNSET_VALUE and machine.config.pe_build.version == UNSET_VALUE
|
82
|
+
errors << "Version must be set on provisioner when unset globally"
|
83
|
+
end
|
84
|
+
|
85
|
+
unless VALID_ROLES.any? {|sym| @role == sym}
|
86
|
+
errors << "Role must be one of #{VALID_ROLES.inspect}, was #{@role.inspect}"
|
87
|
+
end
|
88
|
+
|
89
|
+
unless @verbose == !!@verbose
|
90
|
+
errors << "'verbose' must be a boolean, got #{@verbose.class}"
|
91
|
+
end
|
92
|
+
|
93
|
+
unless @master.is_a? String
|
94
|
+
errors << "'master' must be a string containing the address of the master, got a #{@master.class}"
|
95
|
+
end
|
96
|
+
|
97
|
+
if @answer_file and !File.readable? @answer_file
|
98
|
+
errors << "'answers_file' must be a readable file"
|
99
|
+
end
|
100
|
+
|
101
|
+
if @relocate_manifests and not @role == :master
|
102
|
+
errors << "'relocate_manifests' can only be applied to a master"
|
103
|
+
end
|
104
|
+
|
105
|
+
errors |= h.values.flatten
|
106
|
+
{"PE Bootstrap" => errors}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
|
3
|
+
module PEBuild
|
4
|
+
module ConfigDefault
|
5
|
+
|
6
|
+
# @param [Symbol] iv The instance variable to set the default value
|
7
|
+
# @param [Object] default The default value
|
8
|
+
def set_default(iv, default)
|
9
|
+
iv_val = instance_variable_get(iv)
|
10
|
+
if iv_val == Vagrant::Plugin::V2::Config::UNSET_VALUE
|
11
|
+
instance_variable_set(iv, default)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
private :set_default
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module PEBuild
|
2
|
+
module Idempotent
|
3
|
+
|
4
|
+
# @param fpath [String]
|
5
|
+
# @param desc [String, nil]
|
6
|
+
def idempotent(fpath, desc = nil, &block)
|
7
|
+
desc ||= fpath
|
8
|
+
|
9
|
+
if File.exist? fpath
|
10
|
+
@env.ui.warn "#{desc} is already present.", :prefix => true
|
11
|
+
else
|
12
|
+
yield
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
require 'pe_build/version'
|
3
|
+
|
4
|
+
if Vagrant::VERSION < "1.1.0"
|
5
|
+
raise "vagrant-pe_build version #{PEBuild::VERSION} requires Vagrant 1.1 or later"
|
6
|
+
end
|
7
|
+
|
8
|
+
module PEBuild
|
9
|
+
class Plugin < Vagrant.plugin('2')
|
10
|
+
|
11
|
+
name 'pe_build'
|
12
|
+
|
13
|
+
description <<-DESC
|
14
|
+
This plugin adds commands and provisioners to automatically install Puppet
|
15
|
+
Enterprise on Vagrant guests.
|
16
|
+
DESC
|
17
|
+
|
18
|
+
config(:pe_bootstrap, :provisioner) do
|
19
|
+
require_relative 'config/pe_bootstrap'
|
20
|
+
PEBuild::Config::PEBootstrap
|
21
|
+
end
|
22
|
+
|
23
|
+
config(:pe_build) do
|
24
|
+
require_relative 'config/global'
|
25
|
+
PEBuild::Config::Global
|
26
|
+
end
|
27
|
+
|
28
|
+
provisioner(:pe_bootstrap) do
|
29
|
+
require_relative 'provisioner/pe_bootstrap'
|
30
|
+
PEBuild::Provisioner::PEBootstrap
|
31
|
+
end
|
32
|
+
|
33
|
+
command(:'pe-build') do
|
34
|
+
require_relative 'command'
|
35
|
+
PEBuild::Command
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
|
3
|
+
require 'pe_build/archive'
|
4
|
+
require 'pe_build/util/config'
|
5
|
+
|
6
|
+
require 'log4r'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'erb'
|
9
|
+
|
10
|
+
module PEBuild
|
11
|
+
module Provisioner
|
12
|
+
|
13
|
+
class PEBootstrap < Vagrant.plugin('2', :provisioner)
|
14
|
+
|
15
|
+
# @!attribute [r] work_dir
|
16
|
+
# @return [String] The path to the machine pe_build working directory
|
17
|
+
|
18
|
+
attr_reader :work_dir
|
19
|
+
|
20
|
+
# @!attribute [r] answer_dir
|
21
|
+
# @return [String] The path to the default answer file template dir
|
22
|
+
attr_reader :answer_dir
|
23
|
+
|
24
|
+
# @!attribute [r] answer_file
|
25
|
+
# @return [String] The path to the answer file for this machine.
|
26
|
+
attr_reader :answer_file
|
27
|
+
|
28
|
+
def initialize(machine, config)
|
29
|
+
super
|
30
|
+
|
31
|
+
@logger = Log4r::Logger.new('vagrant::provisioners::pe_bootstrap')
|
32
|
+
|
33
|
+
@work_dir = File.join(@machine.env.root_path, '.pe_build')
|
34
|
+
@answer_dir = File.join(work_dir, 'answers')
|
35
|
+
end
|
36
|
+
|
37
|
+
# Instantiate all working directory content and stage the PE installer.
|
38
|
+
#
|
39
|
+
# @param root_config [Object] ???
|
40
|
+
# @return [void]
|
41
|
+
def configure(root_config)
|
42
|
+
late_config_merge(root_config)
|
43
|
+
|
44
|
+
unless File.directory? work_dir
|
45
|
+
FileUtils.mkdir_p work_dir
|
46
|
+
end
|
47
|
+
|
48
|
+
unless File.directory? answer_dir
|
49
|
+
FileUtils.mkdir_p answer_dir
|
50
|
+
end
|
51
|
+
|
52
|
+
archive = PEBuild::Archive.new(@config.filename, @machine.env)
|
53
|
+
archive.version = @config.version
|
54
|
+
|
55
|
+
archive.download_from(@config.download_root)
|
56
|
+
archive.unpack_to(@work_dir)
|
57
|
+
end
|
58
|
+
|
59
|
+
def provision
|
60
|
+
prepare_answers_file
|
61
|
+
|
62
|
+
[:base, @config.role].each do |rolename|
|
63
|
+
process_step rolename, :pre
|
64
|
+
end
|
65
|
+
|
66
|
+
perform_installation
|
67
|
+
|
68
|
+
if @config.relocate_manifests
|
69
|
+
relocate_installation
|
70
|
+
end
|
71
|
+
|
72
|
+
[:base, @config.role].each do |rolename|
|
73
|
+
process_step rolename, :post
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def late_config_merge(root_config)
|
80
|
+
provision = @config
|
81
|
+
global = root_config.pe_build
|
82
|
+
|
83
|
+
# We don't necessarily know if the configs have been merged. If a config
|
84
|
+
# is being used for default values and was never directly touched then it
|
85
|
+
# may have bad values, so we re-finalize everything. This may not be
|
86
|
+
# generally safe but inside of this plugin it should be ok.
|
87
|
+
provision.finalize!
|
88
|
+
global.finalize!
|
89
|
+
|
90
|
+
merged = PEBuild::Util::Config.local_merge(provision, global)
|
91
|
+
|
92
|
+
@config = merged
|
93
|
+
end
|
94
|
+
|
95
|
+
def generate_answers
|
96
|
+
if @config.answer_file
|
97
|
+
template_path = @config.answer_file
|
98
|
+
else
|
99
|
+
default_template_path = File.join(PEBuild.template_dir, 'answers', "#{@config.role}.txt.erb")
|
100
|
+
template_path = default_template_path
|
101
|
+
end
|
102
|
+
@machine.ui.info "Using #{template_path} as answers template"
|
103
|
+
template = File.read(template_path)
|
104
|
+
str = ERB.new(template).result(binding)
|
105
|
+
end
|
106
|
+
|
107
|
+
def prepare_answers_file
|
108
|
+
str = generate_answers
|
109
|
+
|
110
|
+
dest_file = File.join(@answer_dir, "#{@machine.name}.txt")
|
111
|
+
|
112
|
+
@machine.ui.info "Writing answers file to #{dest_file}"
|
113
|
+
File.open(dest_file, "w") do |file|
|
114
|
+
file.write(str)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_step(role, stepname)
|
119
|
+
|
120
|
+
if role != :base && config.step[stepname]
|
121
|
+
if File.file? config.step[stepname]
|
122
|
+
script_list = [*config.step[stepname]]
|
123
|
+
else
|
124
|
+
script_list = []
|
125
|
+
@machine.ui.warn "Cannot find defined step for #{role}/#{stepname.to_s} at \'#{config.step[stepname]}\'"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
# We do not have a user defined step for this role or we're processing the :base step
|
129
|
+
script_dir = File.join(PEBuild.source_root, 'bootstrap', role.to_s, stepname.to_s)
|
130
|
+
script_list = Dir.glob("#{script_dir}/*")
|
131
|
+
end
|
132
|
+
|
133
|
+
if script_list.empty?
|
134
|
+
@logger.info "No steps for #{role}/#{stepname}", :color => :cyan
|
135
|
+
end
|
136
|
+
|
137
|
+
script_list.each do |template_path|
|
138
|
+
# A message to show which step's action is running
|
139
|
+
@machine.ui.info "Running action for #{role}/#{stepname}"
|
140
|
+
template = File.read(template_path)
|
141
|
+
contents = ERB.new(template).result(binding)
|
142
|
+
|
143
|
+
on_remote contents
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Determine the proper invocation of the PE installer
|
148
|
+
def installer_cmd
|
149
|
+
root = "/vagrant/.pe_build"
|
150
|
+
|
151
|
+
installer_dir = "puppet-enterprise-#{@config.version}-#{@config.suffix}"
|
152
|
+
installer = "puppet-enterprise-installer"
|
153
|
+
|
154
|
+
answers = "#{root}/answers/#{@machine.name}.txt"
|
155
|
+
log_file = "/root/puppet-enterprise-installer-#{Time.now.strftime('%s')}.log"
|
156
|
+
|
157
|
+
cmd = File.join(root, installer_dir, installer)
|
158
|
+
|
159
|
+
@installer_cmd = "#{cmd} -a #{answers} -l #{log_file}"
|
160
|
+
end
|
161
|
+
|
162
|
+
def perform_installation
|
163
|
+
if @machine.communicate.test('test -f /opt/puppet/pe_version')
|
164
|
+
@machine.ui.warn "Puppet Enterprise is already installed, skipping installation.",
|
165
|
+
:name => @machine.name
|
166
|
+
else
|
167
|
+
on_remote installer_cmd
|
168
|
+
@machine.ui.info "Scheduling puppet run to prime pe_mcollective"
|
169
|
+
on_remote "echo '/opt/puppet/bin/puppet agent -t' | at next minute"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Modify the PE puppet master config to use alternate /manifests and /modules
|
174
|
+
#
|
175
|
+
# Manifests and modules need to be mounted on the master via shared folders,
|
176
|
+
# but the default /vagrant mount has permissions and ownership that conflicts
|
177
|
+
# with the puppet master process and the pe-puppet user. Those directories
|
178
|
+
# need to be mounted with permissions like 'fmode=644,dmode=755,fmask=022,dmask=022'
|
179
|
+
#
|
180
|
+
def relocate_installation
|
181
|
+
script_path = File.join(PEBuild.template_dir, 'scripts', 'relocate_installation.sh')
|
182
|
+
script = File.read script_path
|
183
|
+
on_remote script
|
184
|
+
end
|
185
|
+
|
186
|
+
def on_remote(cmd)
|
187
|
+
@machine.communicate.sudo(cmd) do |type, data|
|
188
|
+
if type == :stdout
|
189
|
+
if @config.verbose
|
190
|
+
@machine.ui.info(data.chomp, :color => :green, :prefix => true)
|
191
|
+
else
|
192
|
+
@machine.ui.info('.', :color => :green)
|
193
|
+
end
|
194
|
+
else
|
195
|
+
@machine.ui.info(data.chomp, :color => :red, :prefix => true)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module PEBuild
|
4
|
+
module Transfer
|
5
|
+
class File
|
6
|
+
|
7
|
+
# @param src [String] The path to the file to copy
|
8
|
+
# @param dst [String] The path to destination of the copied file
|
9
|
+
def initialize(src, dst)
|
10
|
+
@src, @dst = src, dst
|
11
|
+
end
|
12
|
+
|
13
|
+
def copy
|
14
|
+
FileUtils.cp @src, @dst
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'pe_build/version'
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
require 'progressbar'
|
5
|
+
|
6
|
+
|
7
|
+
module PEBuild
|
8
|
+
module Transfer
|
9
|
+
class URI
|
10
|
+
|
11
|
+
# @param src [String] The URL to the file to copy
|
12
|
+
# @param dst [String] The path to destination of the copied file
|
13
|
+
def initialize(src, dst)
|
14
|
+
@src, @dst = src, dst
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy
|
18
|
+
tmpfile = open_uri(@src)
|
19
|
+
FileUtils.mv(tmpfile, @dst)
|
20
|
+
end
|
21
|
+
|
22
|
+
HEADERS = {'User-Agent' => "Vagrant/PEBuild (v#{PEBuild::VERSION})"}
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Open a open-uri file handle for the given URL
|
27
|
+
#
|
28
|
+
# @return [IO]
|
29
|
+
def open_uri(path)
|
30
|
+
uri = ::URI.parse(path)
|
31
|
+
progress = nil
|
32
|
+
|
33
|
+
content_length_proc = lambda do |length|
|
34
|
+
if length and length > 0
|
35
|
+
progress = ProgressBar.new('Fetching', length)
|
36
|
+
progress.file_transfer_mode
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
progress_proc = lambda do |size|
|
41
|
+
progress.set(size) if progress
|
42
|
+
end
|
43
|
+
|
44
|
+
options = HEADERS.merge({
|
45
|
+
:content_length_proc => content_length_proc,
|
46
|
+
:progress_proc => progress_proc,
|
47
|
+
})
|
48
|
+
|
49
|
+
uri.open(options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|