roket 0.0.1 → 0.0.2

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 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: ''