oscar 0.2.0alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG +12 -0
  3. data/Gemfile +1 -0
  4. data/LICENSE +14 -0
  5. data/README.markdown +97 -0
  6. data/Vagrantfile +6 -0
  7. data/bootstrap/base/pre/set_hostname.sh +3 -0
  8. data/bootstrap/base/provision/install_puppet_enterprise.sh +6 -0
  9. data/bootstrap/master/post/relocate_puppet.sh +23 -0
  10. data/config/.gitignore +1 -0
  11. data/config/roles.yaml.dist +17 -0
  12. data/doc/answers/README.markdown +1 -0
  13. data/doc/answers/agent.txt +10 -0
  14. data/doc/answers/master-1.1.txt +22 -0
  15. data/doc/answers/master-2.0.0.txt +42 -0
  16. data/doc/answers/master-2.5.0.txt +36 -0
  17. data/doc/answers/master-db.txt +52 -0
  18. data/lib/oscar.rb +17 -0
  19. data/lib/oscar/config.rb +97 -0
  20. data/lib/oscar/environment.rb +28 -0
  21. data/lib/oscar/networking.rb +47 -0
  22. data/lib/oscar/node.rb +91 -0
  23. data/lib/oscar/schema.yaml +73 -0
  24. data/lib/oscar/version.rb +3 -0
  25. data/lib/pe_build.rb +13 -0
  26. data/lib/pe_build/action.rb +20 -0
  27. data/lib/pe_build/action/download.rb +56 -0
  28. data/lib/pe_build/action/unpackage.rb +70 -0
  29. data/lib/pe_build/command.rb +31 -0
  30. data/lib/pe_build/command/download.rb +8 -0
  31. data/lib/pe_build/command/list.rb +10 -0
  32. data/lib/pe_build/config.rb +30 -0
  33. data/lib/pe_build/provisioners.rb +9 -0
  34. data/lib/pe_build/provisioners/puppet_enterprise.rb +1 -0
  35. data/lib/pe_build/provisioners/puppet_enterprise_bootstrap.rb +138 -0
  36. data/lib/pe_build/version.rb +5 -0
  37. data/manifests/.gitignore +3 -0
  38. data/modules/.gitignore +2 -0
  39. data/oscar.gemspec +27 -0
  40. data/templates/answers/agent.txt.erb +10 -0
  41. data/templates/answers/master.txt.erb +52 -0
  42. 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
+
@@ -0,0 +1,3 @@
1
+ module Oscar
2
+ VERSION = '0.2.0alpha1'
3
+ end
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 }