oscar 0.2.0alpha1
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/.gitignore +3 -0
- data/CHANGELOG +12 -0
- data/Gemfile +1 -0
- data/LICENSE +14 -0
- data/README.markdown +97 -0
- data/Vagrantfile +6 -0
- data/bootstrap/base/pre/set_hostname.sh +3 -0
- data/bootstrap/base/provision/install_puppet_enterprise.sh +6 -0
- data/bootstrap/master/post/relocate_puppet.sh +23 -0
- data/config/.gitignore +1 -0
- data/config/roles.yaml.dist +17 -0
- data/doc/answers/README.markdown +1 -0
- data/doc/answers/agent.txt +10 -0
- data/doc/answers/master-1.1.txt +22 -0
- data/doc/answers/master-2.0.0.txt +42 -0
- data/doc/answers/master-2.5.0.txt +36 -0
- data/doc/answers/master-db.txt +52 -0
- data/lib/oscar.rb +17 -0
- data/lib/oscar/config.rb +97 -0
- data/lib/oscar/environment.rb +28 -0
- data/lib/oscar/networking.rb +47 -0
- data/lib/oscar/node.rb +91 -0
- data/lib/oscar/schema.yaml +73 -0
- data/lib/oscar/version.rb +3 -0
- data/lib/pe_build.rb +13 -0
- data/lib/pe_build/action.rb +20 -0
- data/lib/pe_build/action/download.rb +56 -0
- data/lib/pe_build/action/unpackage.rb +70 -0
- data/lib/pe_build/command.rb +31 -0
- data/lib/pe_build/command/download.rb +8 -0
- data/lib/pe_build/command/list.rb +10 -0
- data/lib/pe_build/config.rb +30 -0
- data/lib/pe_build/provisioners.rb +9 -0
- data/lib/pe_build/provisioners/puppet_enterprise.rb +1 -0
- data/lib/pe_build/provisioners/puppet_enterprise_bootstrap.rb +138 -0
- data/lib/pe_build/version.rb +5 -0
- data/manifests/.gitignore +3 -0
- data/modules/.gitignore +2 -0
- data/oscar.gemspec +27 -0
- data/templates/answers/agent.txt.erb +10 -0
- data/templates/answers/master.txt.erb +52 -0
- metadata +175 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'oscar'
|
2
|
+
|
3
|
+
class Oscar::Environment
|
4
|
+
attr_reader :networking
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@config = Oscar::Config.new
|
9
|
+
@nodes = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def run!
|
13
|
+
@networking = Oscar::Networking.new(@config.data["networking"])
|
14
|
+
nodes = @config.all_node_configs
|
15
|
+
|
16
|
+
# TODO make sure that the master is provisioned before any agents.
|
17
|
+
nodes.each do |node_attrs|
|
18
|
+
node = Oscar::Node.new(node_attrs)
|
19
|
+
@networking.register(node)
|
20
|
+
|
21
|
+
@nodes << node
|
22
|
+
end
|
23
|
+
|
24
|
+
Vagrant::Config.run do |config|
|
25
|
+
@nodes.each { |node| node.define(config) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'oscar'
|
2
|
+
require 'ipaddress'
|
3
|
+
|
4
|
+
class Oscar::Networking
|
5
|
+
|
6
|
+
def initialize(yaml_config)
|
7
|
+
|
8
|
+
range = yaml_config["pool"]
|
9
|
+
@domainname = yaml_config["domainname"]
|
10
|
+
@network = IPAddress.parse(range)
|
11
|
+
|
12
|
+
@pool = []
|
13
|
+
@nodes = {}
|
14
|
+
|
15
|
+
@network.each_host { |h| @pool << h }
|
16
|
+
@iterator = @pool.each
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def register(node)
|
21
|
+
next_addr = @iterator.next.to_s
|
22
|
+
|
23
|
+
node.networking = self
|
24
|
+
node.address = next_addr
|
25
|
+
|
26
|
+
@nodes[next_addr] = node
|
27
|
+
end
|
28
|
+
|
29
|
+
def hosts_for(node)
|
30
|
+
arr = []
|
31
|
+
|
32
|
+
arr << ['127.0.0.1', ['localhost']]
|
33
|
+
arr << ['127.0.1.1', [node.name, "#{node.name}.#{@domainname}"]]
|
34
|
+
|
35
|
+
arr << ['::1', %w{ip6-localhost ip6-loopback}]
|
36
|
+
|
37
|
+
arr << ['fe00::0', ['ip6-localnet']]
|
38
|
+
arr << ['ff00::0', ['ip6-mcastprefix']]
|
39
|
+
arr << ['ff02::1', ['ip6-allnodes']]
|
40
|
+
arr << ['ff02::2', ['ip6-allrouters']]
|
41
|
+
|
42
|
+
@nodes.each_pair { |address, n| arr << [address, [n.name, "#{n.name}.#{@domainname}"]] }
|
43
|
+
|
44
|
+
|
45
|
+
arr
|
46
|
+
end
|
47
|
+
end
|
data/lib/oscar/node.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'oscar'
|
2
|
+
require 'vagrant'
|
3
|
+
|
4
|
+
require 'vagrant-hosts'
|
5
|
+
|
6
|
+
class Oscar::Node
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def addrole(name, &block)
|
10
|
+
@roles ||= {}
|
11
|
+
@roles[name] = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def getrole(name)
|
15
|
+
@roles[name] if @roles
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
addrole(:base) do |node|
|
20
|
+
node.vm.box = @boxname
|
21
|
+
|
22
|
+
if @forwards
|
23
|
+
@forwards.each { |h| node.vm.forward_port h["source"], h["dest"] }
|
24
|
+
end
|
25
|
+
|
26
|
+
node.vm.network :hostonly, @address
|
27
|
+
# Add in optional per-node configuration
|
28
|
+
node.vm.box_url = @boxurl if @boxurl
|
29
|
+
node.vm.boot_mode = @gui if @gui
|
30
|
+
|
31
|
+
host_entries = @networking.hosts_for(self)
|
32
|
+
#node.vm.provision :shell do |shell|
|
33
|
+
# shell.inline = %{grep "#{entry}" /etc/hosts || echo "#{entry}" >> /etc/hosts}
|
34
|
+
#end
|
35
|
+
|
36
|
+
node.vm.provision :hosts do |h|
|
37
|
+
host_entries.each { |(address, aliases)| h.add_host address, aliases }
|
38
|
+
end
|
39
|
+
|
40
|
+
node.vm.provision :puppet_enterprise_bootstrap do |pe|
|
41
|
+
pe.role = @role if @role
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
addrole(:master) do |node|
|
46
|
+
# Manifests and modules need to be mounted on the master via shared folders,
|
47
|
+
# but the default /vagrant mount has permissions and ownership that conflicts
|
48
|
+
# with the master and pe-puppet. We mount these folders separately with
|
49
|
+
# loosened permissions.
|
50
|
+
node.vm.share_folder 'manifests', '/manifests', './manifests', :extra => 'fmode=644,dmode=755,fmask=022,dmask=022'
|
51
|
+
node.vm.share_folder 'modules', '/modules', './modules', :extra => 'fmode=644,dmode=755,fmask=022,dmask=022'
|
52
|
+
|
53
|
+
# Boost RAM for the master so that activemq doesn't asplode
|
54
|
+
node.vm.customize([ "modifyvm", :id, "--memory", "1024" ])
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_accessor :address
|
58
|
+
attr_writer :networking # Callback attribute for retrieving hosts
|
59
|
+
|
60
|
+
attr_reader :name, :boxname, :boxurl, :role
|
61
|
+
attr_reader :forwards # really?
|
62
|
+
|
63
|
+
def initialize(yaml_attrs)
|
64
|
+
@attrs = yaml_attrs
|
65
|
+
|
66
|
+
@name = @attrs["name"]
|
67
|
+
@address = @attrs["address"]
|
68
|
+
@role = @attrs["role"].intern if @attrs["role"]
|
69
|
+
|
70
|
+
@boxurl = @attrs["boxurl"]
|
71
|
+
@boxname = @attrs["boxname"]
|
72
|
+
@forwards = @attrs["forwards"]
|
73
|
+
@gui = @attrs["gui"]
|
74
|
+
end
|
75
|
+
|
76
|
+
def define(config)
|
77
|
+
|
78
|
+
blk = lambda do |config|
|
79
|
+
config.vm.define @name do |node|
|
80
|
+
|
81
|
+
instance_exec(node, &(self.class.getrole(:base)))
|
82
|
+
|
83
|
+
if (blk = self.class.getrole(@role))
|
84
|
+
instance_exec(node, &blk)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
blk.call(config)
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
---
|
2
|
+
# Oscar kwalify schema
|
3
|
+
|
4
|
+
type: map
|
5
|
+
mapping:
|
6
|
+
|
7
|
+
"nodes":
|
8
|
+
type: seq
|
9
|
+
sequence:
|
10
|
+
-
|
11
|
+
type: map
|
12
|
+
mapping:
|
13
|
+
"name": { required: true }
|
14
|
+
"address": { required: false }
|
15
|
+
|
16
|
+
"boxname": &boxname
|
17
|
+
type: str
|
18
|
+
"boxurl": &boxurl
|
19
|
+
type: str
|
20
|
+
"role":
|
21
|
+
desc: "A node can have a role, that adds specific configuration for how it is going to be used."
|
22
|
+
type: str
|
23
|
+
enum: ["master", "console", "agent"]
|
24
|
+
default: "agent"
|
25
|
+
|
26
|
+
"profile":
|
27
|
+
desc: "A node can have a profile, which describes where the box came from."
|
28
|
+
type: str
|
29
|
+
"forwards": &forwards
|
30
|
+
type: seq
|
31
|
+
sequence:
|
32
|
+
-
|
33
|
+
type: map
|
34
|
+
mapping:
|
35
|
+
"source":
|
36
|
+
type: int
|
37
|
+
required: true
|
38
|
+
"dest":
|
39
|
+
type: int
|
40
|
+
required: true
|
41
|
+
|
42
|
+
"profiles":
|
43
|
+
desc: "A profile describes a box type, so what name it is and where to retrieve it from."
|
44
|
+
type: seq
|
45
|
+
sequence:
|
46
|
+
-
|
47
|
+
type: map
|
48
|
+
mapping:
|
49
|
+
"name":
|
50
|
+
required: true
|
51
|
+
"boxname": *boxname
|
52
|
+
"boxurl": *boxurl
|
53
|
+
|
54
|
+
|
55
|
+
"roles":
|
56
|
+
desc: "A role is the configuration needed for a node to do a specific role."
|
57
|
+
type: seq
|
58
|
+
sequence:
|
59
|
+
-
|
60
|
+
type: map
|
61
|
+
mapping:
|
62
|
+
name: { required: true }
|
63
|
+
"forwards": *forwards
|
64
|
+
|
65
|
+
# Network configuration for oscar, not applicable to any node.
|
66
|
+
"networking":
|
67
|
+
type: map
|
68
|
+
mapping:
|
69
|
+
"pool":
|
70
|
+
required: true
|
71
|
+
"domainname":
|
72
|
+
required: true
|
73
|
+
|
data/lib/pe_build.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
|
3
|
+
module PEBuild
|
4
|
+
def self.archive_directory
|
5
|
+
File.expand_path(File.join(ENV['HOME'], '.vagrant.d', 'pe_builds'))
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'pe_build/version'
|
10
|
+
require 'pe_build/action'
|
11
|
+
require 'pe_build/config'
|
12
|
+
require 'pe_build/command'
|
13
|
+
require 'pe_build/provisioners'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
require 'vagrant/action/builder'
|
3
|
+
require 'pe_build'
|
4
|
+
|
5
|
+
module PEBuild::Action
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'pe_build/action/download'
|
9
|
+
require 'pe_build/action/unpackage'
|
10
|
+
|
11
|
+
builder = Vagrant::Action::Builder.new do
|
12
|
+
use PEBuild::Action::Download
|
13
|
+
end
|
14
|
+
|
15
|
+
Vagrant.actions.register :download_pe_build, builder
|
16
|
+
|
17
|
+
Vagrant.actions.register(:prep_build, Vagrant::Action::Builder.new do
|
18
|
+
use PEBuild::Action::Download
|
19
|
+
use PEBuild::Action::Unpackage
|
20
|
+
end)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'pe_build'
|
2
|
+
require 'pe_build/action'
|
3
|
+
require 'vagrant'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
class PEBuild::Action::Download
|
7
|
+
# Downloads a PE build to a temp directory
|
8
|
+
|
9
|
+
def initialize(app, env)
|
10
|
+
@app, @env = app, env
|
11
|
+
load_variables
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
@env = env
|
16
|
+
perform_download
|
17
|
+
@app.call(@env)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Determine system state and download a PE build accordingly.
|
23
|
+
#
|
24
|
+
# If we are applying actions within the context of a single box, then we
|
25
|
+
# should try to prefer and box level configuration options first. If
|
26
|
+
# anything is unset then we should fall back to the global settings.
|
27
|
+
def load_variables
|
28
|
+
if @env[:box_name]
|
29
|
+
@root = @env[:vm].pe_build.download_root
|
30
|
+
@version = @env[:vm].pe_build.version
|
31
|
+
@filename = @env[:vm].pe_build.version
|
32
|
+
end
|
33
|
+
|
34
|
+
@root ||= @env[:global_config].pe_build.download_root
|
35
|
+
@version ||= @env[:global_config].pe_build.version
|
36
|
+
@filename ||= @env[:global_config].pe_build.filename
|
37
|
+
|
38
|
+
@archive_path = File.join(PEBuild.archive_directory, @filename)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [String] The full URL to download, based on the config
|
42
|
+
def url
|
43
|
+
[@root, @version, @filename].join('/')
|
44
|
+
end
|
45
|
+
|
46
|
+
def perform_download
|
47
|
+
if File.exist? @archive_path
|
48
|
+
@env[:ui].info "#{@filename} cached, skipping download."
|
49
|
+
else
|
50
|
+
FileUtils.mkdir_p PEBuild.archive_directory unless File.directory? PEBuild.archive_directory
|
51
|
+
cmd = %{curl -L -A "Vagrant/PEBuild (v#{PEBuild::VERSION})" -O #{url}}
|
52
|
+
@env[:ui].info "Executing '#{cmd}'"
|
53
|
+
Dir.chdir(PEBuild.archive_directory) { %x{#{cmd}} }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
require 'pe_build/action'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class PEBuild::Action::Unpackage
|
6
|
+
def initialize(app, env)
|
7
|
+
@app, @env = app, env
|
8
|
+
load_variables
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@env = env
|
13
|
+
@extracted_dir = File.join(@env[:unpack_directory], destination_directory)
|
14
|
+
extract_build
|
15
|
+
@app.call(@env)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def load_variables
|
21
|
+
if @env[:box_name]
|
22
|
+
@root = @env[:vm].pe_build.download_root
|
23
|
+
@version = @env[:vm].pe_build.version
|
24
|
+
@filename = @env[:vm].pe_build.version
|
25
|
+
end
|
26
|
+
|
27
|
+
@root ||= @env[:global_config].pe_build.download_root
|
28
|
+
@version ||= @env[:global_config].pe_build.version
|
29
|
+
@filename ||= @env[:global_config].pe_build.filename
|
30
|
+
|
31
|
+
@archive_path = File.join(PEBuild.archive_directory, @filename)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sadly, shelling out is more sane than trying to use the facilities
|
35
|
+
# provided.
|
36
|
+
def extract_build
|
37
|
+
if File.directory? @extracted_dir
|
38
|
+
@env[:ui].info "#{destination_directory} already present, skipping extraction."
|
39
|
+
else
|
40
|
+
cmd = %{tar xf #{@archive_path} -C #{@env[:unpack_directory]}}
|
41
|
+
@env[:ui].info "Extracting #{@archive_path} to #{@env[:unpack_directory]}"
|
42
|
+
%x{#{cmd}}
|
43
|
+
end
|
44
|
+
rescue => e
|
45
|
+
# If anything goes wrong while extracting, nuke the extracted directory
|
46
|
+
# as it could be incomplete. If we do this, then we can ensure that if
|
47
|
+
# the extracted directory already exists then it will be in a good state.
|
48
|
+
@env[:ui].info "Removing possibly damaged installer directory '#{@extracted_dir}'"
|
49
|
+
FileUtils.rm_r @extracted_dir
|
50
|
+
end
|
51
|
+
|
52
|
+
# Determine the name of the top level directory by peeking into the tarball
|
53
|
+
def destination_directory
|
54
|
+
raise Errno::ENOENT, "No such file \"#{@archive_path}\"" unless File.file? @archive_path
|
55
|
+
|
56
|
+
firstline = nil
|
57
|
+
dir = nil
|
58
|
+
dir_regex = %r[^(.*?)/]
|
59
|
+
IO.popen(%{tar -tf #{@archive_path}}) { |out| firstline = out.gets }
|
60
|
+
|
61
|
+
if firstline.nil? or firstline.empty?
|
62
|
+
raise "Could not get a directory listing from the installer tarfile #{File.basename @archive_path}"
|
63
|
+
elsif (match = firstline.match dir_regex)
|
64
|
+
dir = match[1]
|
65
|
+
else
|
66
|
+
raise "Could not determine the base directory name from #{firstline} - doesn't match #{dir_regex.to_s}"
|
67
|
+
end
|
68
|
+
dir
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'pe_build'
|
2
|
+
require 'vagrant'
|
3
|
+
|
4
|
+
class PEBuild::Command < Vagrant::Command::Base
|
5
|
+
def initialize(argv, env)
|
6
|
+
@argv, @env = argv, env
|
7
|
+
|
8
|
+
@main_args, @subcommand, @sub_args = split_main_and_subcommand(argv)
|
9
|
+
|
10
|
+
# Is this even remotely sane? Are we verging on cargo cult programming?
|
11
|
+
@subcommands = Vagrant::Registry.new
|
12
|
+
|
13
|
+
@subcommands.register('download') { PEBuild::Command::Download }
|
14
|
+
@subcommands.register('list') { PEBuild::Command::List }
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
if @subcommand and (klass = @subcommands.get(@subcommand))
|
19
|
+
klass.new(@argv, @env).execute
|
20
|
+
elsif @subcommand
|
21
|
+
raise "Unrecognized subcommand #{@subcommand}"
|
22
|
+
else
|
23
|
+
PEBuild::Command::List.new(@argv, @env).execute
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'pe_build/command/list'
|
29
|
+
require 'pe_build/command/download'
|
30
|
+
|
31
|
+
Vagrant.commands.register(:pe_build) { PEBuild::Command }
|