cloudboot 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jan Zimmek
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = cloudboot
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 Jan Zimmek. See LICENSE for details.
data/bin/cloudboot ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'optparse'
4
+ require 'cloudboot'
5
+ require 'yaml'
6
+
7
+ options = {}
8
+ options[:config] = "default"
9
+
10
+ opts = OptionParser.new do |opts|
11
+ opts.banner = "Usage: cloudboot --help"
12
+
13
+ opts.on("--generate-config [CONFIG]", "Generate a default configuration file") do |value|
14
+ options[:action] = :generate_config
15
+ options[:config] = value if value
16
+ end
17
+
18
+ opts.on("--config CONFIG", "Use this configuration file") do |value|
19
+ options[:config] = value
20
+ end
21
+
22
+ opts.on("--cronjob", "Generates a sample cronjob") do
23
+ options[:action] = :cronjob
24
+ end
25
+
26
+ opts.on("--list-instances", "Show a list of instances") do
27
+ options[:action] = :list_instances
28
+ end
29
+
30
+ opts.on("-s", "--server", "Generates a server skeleton") do |name|
31
+ options[:action] = :server
32
+ end
33
+
34
+ opts.on("-c", "--client", "Generates a client skeleton") do |name|
35
+ options[:action] = :client
36
+ end
37
+
38
+ opts.on("--userdata", "Generates a userdata example") do |name|
39
+ options[:action] = :userdata
40
+ end
41
+
42
+ end
43
+
44
+ opts.parse!
45
+
46
+ module Cloudboot
47
+ class Bin
48
+
49
+ def initialize(options)
50
+ @options = options
51
+ end
52
+
53
+ def action
54
+ @options[:action]
55
+ end
56
+
57
+ def config
58
+ YAML.load_file(config_file)
59
+ end
60
+
61
+ def run
62
+
63
+ raise "call: cloudboot --generate-config #{@options[:config]}" if ![:generate_config, :client].include?(action) && !config_exist?
64
+
65
+ case action
66
+
67
+ when :generate_config
68
+ dir = "#{ENV['HOME']}/.cloudboot"
69
+
70
+ FileUtils.mkdir(dir) unless File.directory?(dir)
71
+
72
+ raise "config #{config_file} already exist" if File.exist?(config_file)
73
+
74
+ File.open(config_file, "w") do |f|
75
+
76
+ cfg = {}
77
+ cfg["database"] = {
78
+ "adapter" => "mysql",
79
+ "host" => "localhost",
80
+ "port" => 3306,
81
+ "username" => "root",
82
+ "password" => "root",
83
+ "database" => "cloudboot",
84
+ "encoding" => "utf8"
85
+ }
86
+ cfg["server"] = {
87
+ "host" => `hostname`.strip,
88
+ "port" => 4567,
89
+ "domain" => "mydomain"
90
+ }
91
+
92
+ f.puts(cfg.to_yaml)
93
+ end
94
+
95
+ when :list_instances
96
+ require 'activerecord'
97
+ ActiveRecord::Base.establish_connection(config["database"])
98
+
99
+ Domain.find_by_name(config["server"]["domain"]).instances.each do |instance|
100
+ puts "#{instance.name},#{instance.fqdn},#{instance.public_ip},#{instance.private_ip},#{instance.refresh.strftime('%Y-%m-%d %H:%m:%S')}"
101
+ end
102
+
103
+ when :userdata
104
+ puts config["server"].to_yaml
105
+
106
+ when :server
107
+ File.open("cloudboot-server.rb", "w") do |f|
108
+ f.puts Cloudboot::Generator.server_script(config)
109
+ end
110
+
111
+ when :client
112
+ File.open("cloudboot-client.rb", "w") do |f|
113
+ f.puts Cloudboot::Generator.bootstrap_script
114
+ end
115
+
116
+ when :cronjob
117
+ ruby = `which ruby`.strip
118
+ ruby = "/path/to/ruby" if ruby =~ /^which: no ruby/
119
+
120
+ puts "3 * * * * #{ruby} #{ENV['HOME']}/cloudboot-client.rb"
121
+
122
+ else
123
+ puts opts.banner
124
+ end
125
+ end
126
+
127
+ def config_file
128
+ "#{ENV['HOME']}/.cloudboot/#{@options[:config]}"
129
+ end
130
+
131
+ def config_exist?
132
+ File.exist?(config_file)
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+ Cloudboot::Bin.new(options).run
data/lib/cloudboot.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'cloudboot/bootstrap'
2
+ require 'cloudboot/generator'
3
+ require 'cloudboot/domain'
@@ -0,0 +1,127 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'yaml'
4
+ require 'fileutils'
5
+
6
+ module Cloudboot
7
+
8
+ class Callback
9
+ def self.success(options)
10
+ end
11
+ def self.failure(options)
12
+ end
13
+ end
14
+
15
+ class Bootstrap
16
+ def read(url, options={})
17
+
18
+ options = {
19
+ :params => ""
20
+ }.merge(options)
21
+
22
+ url = URI.parse(url)
23
+
24
+ http = Net::HTTP.new(url.host, url.port)
25
+ http.open_timeout = 10
26
+ http.read_timeout = 10
27
+
28
+ params = options[:params]
29
+
30
+ puts "options: #{options.inspect}"
31
+
32
+ res = if params.empty?
33
+ puts "req: GET #{url}"
34
+ http.get(url.path)
35
+ else
36
+ puts "req: POST #{url} - #{params}"
37
+ http.post(url.path, params)
38
+ end
39
+
40
+ s = if res.is_a?(Net::HTTPOK)
41
+ r = res.body.strip
42
+ r.empty? ? nil : r
43
+ else
44
+ nil
45
+ end
46
+
47
+ puts "res: #{s.nil? ? 'nil' : s}"
48
+
49
+ s
50
+ end
51
+
52
+ def hostname
53
+ `hostname`.strip
54
+ end
55
+
56
+ def fqdn
57
+ `hostname -f`.strip
58
+ end
59
+
60
+ def mutex(lock_file, &block)
61
+
62
+ unless File.exist?(lock_file)
63
+ begin
64
+ FileUtils.touch(lock_file)
65
+ block.call
66
+ ensure
67
+ FileUtils.rm(lock_file)
68
+ end
69
+ else
70
+ puts "skipping - lock_file #{lock_file} exist"
71
+ end
72
+
73
+ end
74
+
75
+ def success(options)
76
+ end
77
+
78
+ def failure(options)
79
+ end
80
+
81
+ def bootstrap
82
+
83
+ mutex "/tmp/cloudboot.lock" do
84
+
85
+ domain = nil
86
+ hostname = nil
87
+ error = nil
88
+
89
+ begin
90
+ user_data = read('http://169.254.169.254/1.0/user-data')
91
+
92
+ if user_data
93
+ bootstrap = YAML.load(user_data)
94
+
95
+ domain = bootstrap['domain']
96
+
97
+ public_ipv4 = read("http://169.254.169.254/latest/meta-data/public-ipv4")
98
+ local_ipv4 = read("http://169.254.169.254/latest/meta-data/local-ipv4")
99
+
100
+ puts bootstrap.inspect
101
+
102
+ url = "http://#{bootstrap['host']}:#{bootstrap['port']}/register"
103
+
104
+ params = "public_ip=#{public_ipv4}&private_ip=#{local_ipv4}&domain=#{domain}&host="
105
+ params << hostname()
106
+
107
+ hostname = read(url, {:params => params})
108
+ end
109
+
110
+ rescue Exception => e
111
+ error = e
112
+ ensure
113
+ options = {:hostname => hostname, :domain => domain, :error => error}
114
+
115
+ if hostname && domain
116
+ Callback.success(options)
117
+ else
118
+ Callback.failure(options)
119
+ end
120
+
121
+ end
122
+ end
123
+
124
+ end
125
+ end
126
+
127
+ end
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'activerecord'
3
+ require 'cloudboot/instance'
4
+
5
+ module Cloudboot
6
+ class Domain < ActiveRecord::Base
7
+ has_many :instances, :class_name => "Cloudboot::Instance"
8
+
9
+ def self.ensure(name, instance_prefix, num)
10
+ ActiveRecord::Base.transaction do
11
+
12
+ domain = find_or_create_by_name(name) do |d|
13
+ d.instance_prefix = instance_prefix
14
+ end
15
+
16
+ raise "domain '#{name}' has a different instance_prefix '#{domain.instance_prefix}' <=> '#{instance_prefix}'" if domain.instance_prefix != instance_prefix
17
+
18
+ num.times do |i|
19
+ hostname = "#{domain.instance_prefix}#{(i+1).to_s.rjust(3, '0')}"
20
+ now = DateTime.now
21
+
22
+ domain.instances.find_or_create_by_name(hostname) do |instance|
23
+ instance.public_ip = "0.0.0.0"
24
+ instance.private_ip = "0.0.0.0"
25
+ instance.refresh = now
26
+ instance.created_at = now
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+
34
+ def self.register(domainname, hostname, public_ip, private_ip)
35
+
36
+ puts "registering:"
37
+ puts " domain: #{domainname}"
38
+ puts " hostname: #{hostname}"
39
+ puts " public_ip: #{public_ip}"
40
+ puts " private_ip: #{private_ip}"
41
+
42
+ domain = find_by_name(domainname)
43
+ raise "unknown domain: #{domainname}" unless domain
44
+
45
+ instance = domain.instances.find_by_name_and_public_ip_and_private_ip(hostname, public_ip, private_ip)
46
+
47
+ now = DateTime.now
48
+
49
+ unless instance
50
+ instance = domain.instances.find(:first, :conditions => ["refresh < ? or refresh = created_at", now - 60.seconds], :order => "name asc")
51
+ end
52
+
53
+ raise "no available instance in domain: #{domainname}" unless instance
54
+
55
+ ActiveRecord::Base.transaction do
56
+ instance.public_ip = public_ip
57
+ instance.private_ip = private_ip
58
+ instance.refresh = now
59
+ instance.save
60
+ end
61
+
62
+ instance
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,127 @@
1
+ require 'yaml'
2
+
3
+ module Cloudboot
4
+ class Generator
5
+
6
+ def self.bootstrap_script
7
+ <<-EOF
8
+ #!/usr/bin/ruby
9
+
10
+ require 'rubygems'
11
+ require 'cloudboot'
12
+
13
+ module Cloudboot
14
+ class Callback
15
+
16
+ def self.failure(options)
17
+ puts "cloudboot failure"
18
+ puts options[:error].inspect if options[:error]
19
+ end
20
+
21
+ def self.success(options)
22
+
23
+ hostname = options[:hostname]
24
+ domain = options[:domain]
25
+
26
+ puts "setup - hostname: \#{hostname}, domain: \#{domain}"
27
+
28
+ `echo "127.0.0.1 \#{hostname}.\#{domain} localhost \#{hostname}" > /etc/hosts`
29
+ `sed -i 's/HOSTNAME=.*/HOSTNAME=\"\#{hostname}\"/g' /etc/rc.conf`
30
+
31
+ `hostname \#{hostname}`
32
+ end
33
+ end
34
+ end
35
+
36
+ Cloudboot::Bootstrap.new.bootstrap
37
+ EOF
38
+ end
39
+
40
+ def self.server_script(options)
41
+
42
+ database = options["database"]
43
+ server = options["server"]
44
+
45
+ <<-EOF
46
+ #!/usr/bin/ruby
47
+
48
+ require 'rubygems'
49
+ require 'activerecord'
50
+ require 'sinatra'
51
+ require 'cloudboot'
52
+
53
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
54
+
55
+ ActiveRecord::Base.establish_connection({
56
+ :adapter => '#{database["adapter"]}',
57
+ :encoding => '#{database["encoding"]}',
58
+ :database => '#{database["database"]}',
59
+ :host => '#{database["host"]}',
60
+ :port => #{database["port"]},
61
+ :username => '#{database["username"]}',
62
+ :password => '#{database["password"]}'
63
+ })
64
+
65
+ Cloudboot::Generator.setup_tables
66
+
67
+ Cloudboot::Domain.ensure("#{server["domain"]}", "srv", 3)
68
+
69
+ set :server, ["webrick"]
70
+ set :environment, :production
71
+ set :lock, true
72
+
73
+ post "/register" do
74
+
75
+ public_ip = request.params['public_ip']
76
+ private_ip = request.params['private_ip']
77
+ domain = request.params['domain']
78
+ hostname = request.params['host']
79
+
80
+ instance = Cloudboot::Domain.register(domain, hostname, public_ip, private_ip)
81
+ instance.name
82
+ end
83
+ EOF
84
+ end
85
+
86
+ def self.table_exist?(table_name)
87
+ ActiveRecord::Base.connection.select_value("show tables like '#{table_name}'") != nil
88
+ end
89
+
90
+ def self.setup_tables
91
+
92
+ unless table_exist?("domains")
93
+ ActiveRecord::Base.connection.execute <<-SQL
94
+ create
95
+ table domains
96
+ (
97
+ id bigint not null AUTO_INCREMENT,
98
+ name varchar(255) not null,
99
+ instance_prefix varchar(255) not null,
100
+ primary key (id)
101
+ )
102
+ ENGINE=InnoDB default CHARSET=utf8
103
+ SQL
104
+ end
105
+
106
+ unless table_exist?("instances")
107
+ ActiveRecord::Base.connection.execute <<-SQL
108
+ create
109
+ table instances
110
+ (
111
+ id bigint not null AUTO_INCREMENT,
112
+ name varchar(255) not null,
113
+ domain_id bigint not null,
114
+ public_ip varchar(15) not null,
115
+ private_ip varchar(15) not null,
116
+ refresh datetime not null,
117
+ created_at datetime not null,
118
+ primary key (id)
119
+ )
120
+ ENGINE=InnoDB default CHARSET=utf8
121
+ SQL
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+ end
@@ -0,0 +1,9 @@
1
+ module Cloudboot
2
+ class Instance < ActiveRecord::Base
3
+ belongs_to :domain, :class_name => "Cloudboot::Domain"
4
+
5
+ def fqdn
6
+ "#{name}.#{domain.name}"
7
+ end
8
+ end
9
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'cloudboot'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,6 @@
1
+ require 'helper'
2
+
3
+ class TestCloudboot < Test::Unit::TestCase
4
+ should "success" do
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudboot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Zimmek
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-06 00:00:00 +01:00
13
+ default_executable: cloudboot
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: cloudboot is the missing glue between your bare bone ec2 instance and a configuration management tool like cfengine, puppet or chef.
36
+ email: jan.zimmek@web.de
37
+ executables:
38
+ - cloudboot
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - bin/cloudboot
46
+ - lib/cloudboot.rb
47
+ - lib/cloudboot/bootstrap.rb
48
+ - lib/cloudboot/domain.rb
49
+ - lib/cloudboot/generator.rb
50
+ - lib/cloudboot/instance.rb
51
+ - LICENSE
52
+ - README.rdoc
53
+ has_rdoc: true
54
+ homepage: http://github.com/jzimmek/cloudboot
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --charset=UTF-8
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: simplify the bootstrapping of your amazone ec2 instances
81
+ test_files:
82
+ - test/helper.rb
83
+ - test/test_cloudboot.rb