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.
Files changed (36) hide show
  1. data/Gemfile +2 -0
  2. data/README.markdown +10 -10
  3. data/lib/pe_build.rb +15 -7
  4. data/lib/pe_build/archive.rb +112 -0
  5. data/lib/pe_build/archive_collection.rb +43 -0
  6. data/lib/pe_build/command.rb +48 -11
  7. data/lib/pe_build/command/copy.rb +38 -0
  8. data/lib/pe_build/command/download.rb +34 -3
  9. data/lib/pe_build/command/list.rb +16 -4
  10. data/lib/pe_build/config/global.rb +69 -0
  11. data/lib/pe_build/config/pe_bootstrap.rb +110 -0
  12. data/lib/pe_build/config_default.rb +16 -0
  13. data/lib/pe_build/idempotent.rb +16 -0
  14. data/lib/pe_build/plugin.rb +38 -0
  15. data/lib/pe_build/provisioner/pe_bootstrap.rb +202 -0
  16. data/lib/pe_build/transfer/file.rb +18 -0
  17. data/lib/pe_build/transfer/uri.rb +53 -0
  18. data/lib/pe_build/unpack/tar.rb +38 -0
  19. data/lib/pe_build/util/config.rb +29 -0
  20. data/lib/pe_build/version.rb +1 -1
  21. data/lib/{vagrant_init.rb → vagrant-pe_build.rb} +0 -0
  22. data/templates/answers/agent.txt.erb +1 -1
  23. data/templates/answers/master-existing-db.txt.erb +2 -2
  24. data/templates/answers/master.txt.erb +2 -2
  25. data/templates/locales/en.yml +7 -0
  26. data/{bootstrap/master/post/relocate_puppet.sh → templates/scripts/relocate_installation.sh} +0 -0
  27. data/vagrant-pe_build.gemspec +2 -1
  28. metadata +40 -17
  29. data/bootstrap/base/provision/install_puppet_enterprise.sh +0 -10
  30. data/lib/pe_build/action.rb +0 -20
  31. data/lib/pe_build/action/download.rb +0 -56
  32. data/lib/pe_build/action/unpackage.rb +0 -70
  33. data/lib/pe_build/config.rb +0 -35
  34. data/lib/pe_build/provisioners.rb +0 -9
  35. data/lib/pe_build/provisioners/puppet_enterprise.rb +0 -1
  36. 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