roket 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,6 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .roketrc
19
+ \#*\#
20
+ .\#*
data/bin/roket ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # -*- mode: shell -*-
4
+
5
+ require_relative '../lib/roket'
6
+
7
+ require 'trollop'
8
+
9
+ options = Trollop::options do
10
+ version "roket #{Roket::VERSION} (c) 2013 Martin Rhoads"
11
+ banner <<END_OF_BANNER
12
+ welcome to roket
13
+ END_OF_BANNER
14
+
15
+ opt('aws_access_key',
16
+ "aws access key",
17
+ :type => String,
18
+ :default => ENV['AWS_ACCESS_KEY']
19
+ )
20
+
21
+ opt('aws_secret_key',
22
+ "aws secret key",
23
+ :type => String,
24
+ :default => ENV['AWS_SECRET_KEY']
25
+ )
26
+
27
+ opt('chef_validation_key_name',
28
+ 'chef validation key name',
29
+ :type => String,
30
+ :default => ENV['CHEF_VALIDATION_KEY_NAME'],
31
+ )
32
+
33
+ opt('chef_validation_key',
34
+ 'chef validation key path',
35
+ :type => String,
36
+ :default => ENV['CHEF_VALIDATION_KEY'],
37
+ )
38
+
39
+ opt('chef_data_bag_secret',
40
+ 'path to chef data bag encryption secret',
41
+ :type => String,
42
+ :default => ENV['CHEF_DATA_BAG_SECRET'],
43
+ )
44
+
45
+ opt('chef_role',
46
+ 'chef role of instance to be launched',
47
+ :type => String,
48
+ :default => ENV['CHEF_ROLE'],
49
+ )
50
+
51
+ opt('chef_environment',
52
+ 'chef environment of instance to be launched',
53
+ :type => String,
54
+ :default => ENV['CHEF_ENVIRONMENT'],
55
+ )
56
+
57
+ opt('key_name',
58
+ 'ssh key to launch instance with',
59
+ :type => String,
60
+ :default => ENV['KEY_NAME'],
61
+ )
62
+
63
+ opt('count',
64
+ 'number of instances to launch',
65
+ :type => String,
66
+ :default => ENV['COUNT'],
67
+ )
68
+
69
+ opt('region',
70
+ 'ec2 region to launch in',
71
+ :type => String,
72
+ :default => ENV['REGION'],
73
+ )
74
+
75
+ opt('image',
76
+ 'ami to use for launch',
77
+ :type => String,
78
+ :default => ENV['AMI'],
79
+ )
80
+
81
+ opt('security_group',
82
+ 'security group to launch instance with',
83
+ :type => String,
84
+ :default => ENV['SECURITY_GROUP'],
85
+ )
86
+
87
+ opt('machine_type',
88
+ 'instance type to launch',
89
+ :type => String,
90
+ :default => ENV['MACHINE_TYPE'],
91
+ )
92
+
93
+ end
94
+
95
+ required_parameters = [
96
+ 'aws_access_key',
97
+ 'aws_secret_key',
98
+ 'chef_validation_key_name',
99
+ 'chef_validation_key',
100
+ 'chef_role',
101
+ 'chef_environment',
102
+ 'key_name',
103
+ ]
104
+
105
+ required_parameters.each do |arg|
106
+ raise ArgumentError, "--#{arg} needs to be specified on the commandline or set \
107
+ by the #{arg.upcase.gsub('_','-')} environment variable" if
108
+ options[arg].nil? or ! options[arg]
109
+ end
110
+
111
+ roket = Roket::Roket.new(options)
112
+ roket.launch({'count' => options['count']})
113
+
data/lib/roket.rb CHANGED
@@ -1,5 +1,134 @@
1
- require "roket/version"
1
+ require 'logger'
2
+ require 'erb'
3
+ require 'aws-sdk'
4
+
5
+ require_relative "roket/version"
2
6
 
3
7
  module Roket
4
- # Your code goes here...
8
+ class Roket
9
+ def initialize(opts={})
10
+ @log = Logger.new(STDOUT)
11
+ @log.debug "opts are #{opts.inspect}"
12
+ ['aws_access_key',
13
+ 'aws_secret_key',
14
+ 'chef_validation_key_name',
15
+ 'chef_validation_key',
16
+ 'chef_role',
17
+ 'chef_environment',
18
+ 'key_name',
19
+ ].each do |req|
20
+ raise ArgumentError, "missing required param #{req}" unless opts[req]
21
+ instance_variable_set("@#{req}",opts[req])
22
+ end
23
+
24
+ @security_group = opts['security_group'] ? opts['security_group'] : 'default'
25
+ @image = opts['image'] ? opts['image'] : 'ami-d726abbe'
26
+ @machine_type = opts['machine_type'] ? opts['machine_type'] : 'm1.small'
27
+ @region = opts['region'] ? opts['region'] : 'us-east-1'
28
+ @ec2_url = "ec2.#{@region}.amazonaws.com"
29
+ @timeout = 120
30
+ @start_time = Time.new
31
+
32
+ begin
33
+ @chef_validation_key_value = File.read(@chef_validation_key)
34
+ rescue Object => e
35
+ raise "\ncould not open specified key #{@chef_validation_key}:\n#{e.inspect}#{e.backtrace}"
36
+ end
37
+
38
+ if opts['chef_data_bag_secret']
39
+ begin
40
+ @chef_data_bag_secret = File.read(opts['chef_data_bag_secret'])
41
+ rescue Object => e
42
+ raise "\ncould not open specified secret key file #{opts['chef_data_bag_secret']}:\n#{e.inspect}#{e.backtrace}"
43
+ end
44
+ else
45
+ @chef_data_bag_secret = ''
46
+ end
47
+
48
+ AWS.config({:access_key_id => @aws_access_key, :secret_access_key => @aws_secret_key})
49
+ @ec2 = AWS::EC2.new(:ec2_endpoint => @ec2_url)
50
+ @ec2_region = @ec2.regions[@region]
51
+
52
+ @user_data = render_template
53
+ end
54
+
55
+ def launch(opts={})
56
+ File.open('/tmp/user-data', 'w') {|f| f.write(@user_data) }
57
+ instances = do_launch(opts)
58
+ wait(instances)
59
+ print_run_info(instances)
60
+ print_config_info
61
+ return instances
62
+ end
63
+
64
+ private
65
+
66
+ def print_config_info
67
+ puts "install logs will be in /var/log/init and /var/log/init.err"
68
+ end
69
+
70
+ def print_run_info(instances)
71
+ puts "here is the info for what's launched:"
72
+ instances.each do |instance|
73
+ puts "\tinstance_id: #{instance.instance_id}"
74
+ puts "\tpublic ip: #{instance.public_ip_address}"
75
+ puts
76
+ end
77
+ end
78
+
79
+ def wait(instances)
80
+ sleep 3
81
+ while true
82
+ if Time.now - @start_time > @timeout
83
+ bail(instances)
84
+ raise TimeoutError, "exceded timeout of #{@timeout}"
85
+ end
86
+ puts "instances is #{instances.inspect}"
87
+ if instances.select{|i| i.status != :running }.empty?
88
+ @log.info "all instances in running state"
89
+ return
90
+ end
91
+ @log.info "instances not ready yet. sleeping..."
92
+ sleep 5
93
+ return wait(instances)
94
+ end
95
+ end
96
+
97
+ def do_launch(opts={})
98
+ options = {
99
+ :image_id => @image,
100
+ :security_groups => @security_group,
101
+ :user_data => @user_data,
102
+ :instance_type => @machine_type,
103
+ :key_name => @key_name,
104
+ }
105
+ options.merge!({:availability_zone => opts['avilibility_zone']}) if opts['availability_zone']
106
+ options.merge!({:count => opts['count']}) if opts['count']
107
+ puts "creating instance with options:\n#{options}"
108
+ instances = @ec2_region.instances.create(options)
109
+ instances = [instances] unless instances.class == Array
110
+ instances.each do |instance|
111
+ @log.info "launched instance #{instance.instance_id}"
112
+ end
113
+ return instances
114
+ end
115
+
116
+ def render_template
117
+ this_file = File.expand_path __FILE__
118
+ base_dir = File.dirname this_file
119
+ template_file_path = File.join(base_dir,'roket','templates','bootstrap.sh.erb')
120
+ template_file = File.read(template_file_path)
121
+ erb_template = ERB.new(template_file)
122
+ generated_template = erb_template.result(binding)
123
+ return generated_template
124
+ end
125
+
126
+ def bail(instances)
127
+ return if instances.nil?
128
+ instances.each do |instance|
129
+ instance.delete
130
+ end
131
+ end
132
+
133
+ end
5
134
  end
@@ -0,0 +1,130 @@
1
+ #!/bin/bash -ex
2
+ #
3
+ # This script will bootstrap and run chef
4
+ #
5
+ # You need to specify a run_list, environment, and validator key info below
6
+ #
7
+ # Martin Rhoads
8
+
9
+
10
+ set -o pipefail
11
+
12
+
13
+ # redirect stdout to /var/log/init
14
+ exec > /var/log/init
15
+
16
+ # redirect stderr to /var/log/init.err
17
+ exec 2> /var/log/init.err
18
+
19
+
20
+ ##
21
+ ## settings
22
+ ##
23
+
24
+
25
+ run_list=role[<%= @chef_role %>]
26
+ environment=<%= @chef_environment %>
27
+ validator_name=<%= @chef_validation_key_name %>
28
+ validator_value="<%= @chef_validation_key_value %>"
29
+ data_bag_secret="<%= @chef_data_bag_secret %>"
30
+
31
+ ##
32
+ ## common function
33
+ ##
34
+
35
+
36
+ update() {
37
+ echo updating apt repo
38
+ apt-get update 1>&2
39
+ }
40
+
41
+
42
+ install() {
43
+ if ! (dpkg -l | awk '{print $2}' | grep -q ^$1$ ) ; then
44
+ echo installing $1...
45
+ export DEBIAN_FRONTEND=noninteractive
46
+ apt-get install -y $1 1>&2
47
+ fi
48
+ }
49
+
50
+
51
+ get_instance_id() {
52
+ instance_id=`curl --retry 5 --retry-delay 5 169.254.169.254/latest/meta-data/instance-id` 1>&2
53
+ echo instance_id is $instance_id
54
+ }
55
+
56
+
57
+ configure_chef() {
58
+ echo configuring chef...
59
+ mkdir -p /etc/chef
60
+ echo -e "$validator_value" > /etc/chef/validation.pem
61
+ cat<<EOF>/etc/chef/client.rb
62
+ log_level :info
63
+ log_location STDOUT
64
+ chef_server_url "https://api.opscode.com/organizations/airbnb"
65
+ validation_client_name "$validator_name"
66
+ node_name "$instance_id"
67
+ EOF
68
+ echo -e "$data_bag_secret" > /etc/chef/encrypted_data_bag_secret
69
+ echo chef configured
70
+ }
71
+
72
+
73
+ install_chef() {
74
+ if ! which chef-client > /dev/null ; then
75
+ echo installing chef via omnibus...
76
+ curl -L --silent https://www.opscode.com/chef/install.sh | sudo bash 1>&2
77
+ curl -L --silent https://www.opscode.com/chef/install.sh | sudo bash -s -- -v 11.2.0 1>&2
78
+ else
79
+ echo chef is already installed
80
+ fi
81
+ }
82
+
83
+
84
+ configure_chef_daemon() {
85
+ cat<<EOF>/etc/init/chef-client.conf
86
+ description "chef-client"
87
+ author "Martin Rhoads"
88
+ start on networking
89
+ script
90
+ chef-client --interval 300 --splay 150 | logger -t chef-client 2>&1
91
+ end script
92
+ respawn
93
+ EOF
94
+ }
95
+
96
+
97
+ run_chef() {
98
+ echo running chef-client...
99
+ chef-client --environment $environment --override-runlist $run_list 1>&2
100
+ echo done running chef-client
101
+ }
102
+
103
+
104
+ start_chef_daemon() {
105
+ start chef-client
106
+ }
107
+
108
+
109
+ ##
110
+ ## main
111
+ ##
112
+
113
+
114
+ echo starting chef bootstrapping...
115
+ update
116
+ install curl
117
+ get_instance_id
118
+ configure_chef
119
+ install_chef
120
+ run_chef
121
+ configure_chef_daemon
122
+ start_chef_daemon
123
+
124
+
125
+ ##
126
+ ## exit
127
+ ##
128
+
129
+
130
+ echo "ran successfully:)"
data/lib/roket/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Roket
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/roket.gemspec CHANGED
@@ -16,4 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
+ gem.add_runtime_dependency 'trollop'
20
+ gem.add_runtime_dependency 'aws-sdk'
19
21
  end
22
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,45 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-09 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2013-02-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: trollop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: aws-sdk
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
14
46
  description: roket launches instances
15
47
  email:
16
48
  - martin.rhoads@airbnb.com
17
- executables: []
49
+ executables:
50
+ - roket
18
51
  extensions: []
19
52
  extra_rdoc_files: []
20
53
  files:
@@ -23,7 +56,9 @@ files:
23
56
  - LICENSE.txt
24
57
  - README.md
25
58
  - Rakefile
59
+ - bin/roket
26
60
  - lib/roket.rb
61
+ - lib/roket/templates/bootstrap.sh.erb
27
62
  - lib/roket/version.rb
28
63
  - roket.gemspec
29
64
  homepage: ''