cloud-maker 0.1.2 → 0.2.0.pre2
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/bin/cloud-maker +65 -25
- data/lib/cloud-maker.rb +8 -0
- data/lib/cloud_maker/config.rb +8 -5
- data/lib/cloud_maker/ec2.rb +31 -17
- data/lib/cloud_maker/s3_archiver.rb +30 -5
- data/lib/cloud_maker/shell_executor.rb +3 -2
- metadata +21 -5
data/bin/cloud-maker
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require 'thor'
|
3
|
-
require 'colorize'
|
4
2
|
require 'cloud-maker'
|
5
3
|
|
6
4
|
class CloudMakerCLI < Thor
|
@@ -23,6 +21,34 @@ class CloudMakerCLI < Thor
|
|
23
21
|
puts config.to_user_data
|
24
22
|
end
|
25
23
|
|
24
|
+
desc "terminate [AWS_INSTANCE_ID]", "Terminate the specified AWS instance"
|
25
|
+
def terminate(aws_instance_id)
|
26
|
+
say "Not implemented.".red
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "info [AWS_INSTANCE_ID]", "Display config info about the specified AWS instance"
|
30
|
+
method_option :aws_access_key_id,
|
31
|
+
:desc => "Your AWS access key id",
|
32
|
+
:default => ENV['AWS_ACCESS_KEY_ID'],
|
33
|
+
:required => true
|
34
|
+
method_option :aws_secret_access_key,
|
35
|
+
:desc => "Your AWS secret access key",
|
36
|
+
:default => ENV['AWS_SECRET_ACCESS_KEY'],
|
37
|
+
:required => true
|
38
|
+
def info(aws_instance_id)
|
39
|
+
cloud_maker = CloudMaker::Ec2.new(
|
40
|
+
:aws_access_key_id => options.aws_access_key_id,
|
41
|
+
:aws_secret_access_key => options.aws_secret_access_key
|
42
|
+
)
|
43
|
+
|
44
|
+
info = cloud_maker.info(aws_instance_id)
|
45
|
+
|
46
|
+
print_config_hash(info[:cloud_config])
|
47
|
+
puts
|
48
|
+
say "Original instance state:".green
|
49
|
+
print_colored_hash(info[:instance])
|
50
|
+
end
|
51
|
+
|
26
52
|
desc "launch [INSTANCE_CONFIG_YAML]", "Launch a new EC2 instance as described by INSTANCE_CONFIG_YAML"
|
27
53
|
method_option :aws_access_key_id,
|
28
54
|
:desc => "Your AWS access key id",
|
@@ -49,41 +75,57 @@ class CloudMakerCLI < Thor
|
|
49
75
|
|
50
76
|
config = build_config(instance_config_yaml, options)
|
51
77
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
78
|
+
print_config_hash(config.to_hash)
|
79
|
+
|
80
|
+
if yes?("Launch a new EC2 instance with the options above? (y/n)")
|
81
|
+
cloud_maker = CloudMaker::Ec2.new(
|
82
|
+
:aws_access_key_id => options.aws_access_key_id,
|
83
|
+
:aws_secret_access_key => options.aws_secret_access_key
|
84
|
+
)
|
56
85
|
|
57
|
-
|
86
|
+
instance = cloud_maker.launch(config)
|
87
|
+
puts
|
88
|
+
say "Successfully launched new EC2 instance: ".green + instance[:aws_instance_id].magenta
|
89
|
+
puts
|
90
|
+
print_colored_hash(instance)
|
91
|
+
else
|
92
|
+
say "Launch aborted!".red
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def print_colored_hash(hash, color=:cyan)
|
98
|
+
print_table hash.map {|key, val|
|
99
|
+
[key.to_s.dup.send(color), val.to_s]
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def print_config_hash(hash)
|
104
|
+
puts
|
105
|
+
say "CloudInit configuration:".green
|
106
|
+
print_colored_hash hash['cloud-init']
|
107
|
+
puts
|
108
|
+
|
109
|
+
if (!hash['include'].empty?)
|
58
110
|
puts
|
59
111
|
say 'Include URLs:'.green
|
60
|
-
|
112
|
+
hash['include'].each do |url|
|
61
113
|
puts url
|
62
114
|
end
|
63
115
|
end
|
64
116
|
|
65
117
|
puts
|
66
118
|
say "CloudMaker configuration:".green
|
67
|
-
|
68
|
-
|
69
|
-
puts
|
70
|
-
say "Configuration file:".green + " " + config.extra_options[:config_path]
|
119
|
+
print_colored_hash hash['cloud-maker']
|
71
120
|
puts
|
72
121
|
|
73
|
-
|
74
|
-
|
75
|
-
puts
|
76
|
-
say "Successfully launched new EC2 instance: ".green + instance[:aws_instance_id].magenta
|
77
|
-
puts
|
78
|
-
print_table instance.map {|key, val|
|
79
|
-
[key.to_s.cyan, val.to_s]
|
80
|
-
}
|
81
|
-
else
|
82
|
-
say "Launch aborted!".red
|
122
|
+
say "Configuration files:".green
|
123
|
+
[hash['extra-options']['config_path'], *hash['import']].each do |file|
|
124
|
+
puts file
|
83
125
|
end
|
126
|
+
puts
|
84
127
|
end
|
85
128
|
|
86
|
-
private
|
87
129
|
def build_config(instance_config_yaml, options)
|
88
130
|
config = CloudMaker::Config.from_yaml(instance_config_yaml)
|
89
131
|
options.set.each_pair {|key, val| config[key] = val}
|
@@ -112,6 +154,4 @@ class CloudMakerCLI < Thor
|
|
112
154
|
end
|
113
155
|
end
|
114
156
|
|
115
|
-
|
116
|
-
|
117
157
|
CloudMakerCLI.start
|
data/lib/cloud-maker.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
+
Bundler.require(ENV['CLOUDMAKER_ENV'].to_sym) if ENV['CLOUDMAKER_ENV']
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'colorize'
|
5
|
+
require 'deep_merge'
|
6
|
+
require 'right_aws'
|
7
|
+
|
1
8
|
require 'cloud_maker/config'
|
2
9
|
require 'cloud_maker/ec2'
|
3
10
|
require 'cloud_maker/s3_archiver'
|
4
11
|
require 'cloud_maker/shell_executor'
|
12
|
+
|
data/lib/cloud_maker/config.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'deep_merge'
|
3
|
-
|
4
1
|
module CloudMaker
|
5
2
|
class Config
|
6
3
|
# Public: Gets/Sets the CloudMaker specific properties. The options hash
|
@@ -104,7 +101,13 @@ module CloudMaker
|
|
104
101
|
|
105
102
|
# Returns a Hash of all of the CloudMaker specific properties in the configuration.
|
106
103
|
def to_hash
|
107
|
-
|
104
|
+
{
|
105
|
+
'cloud-maker' => self.options.map {|key, properties| [key, properties["value"]]},
|
106
|
+
'cloud-init' => self.cloud_config,
|
107
|
+
'include' => self.includes,
|
108
|
+
'import' => self.imports,
|
109
|
+
'extra-options' => self.extra_options
|
110
|
+
}
|
108
111
|
end
|
109
112
|
|
110
113
|
# Public: Generates a multipart userdata string suitable for use with Cloud Init on EC2
|
@@ -208,7 +211,7 @@ module CloudMaker
|
|
208
211
|
raise "ERROR: The path to the CloudMaker config is incorrect"
|
209
212
|
end
|
210
213
|
|
211
|
-
CloudMaker::Config.new(YAML::load(cloud_yaml),
|
214
|
+
CloudMaker::Config.new(YAML::load(cloud_yaml), 'config_path' => full_path)
|
212
215
|
end
|
213
216
|
end
|
214
217
|
|
data/lib/cloud_maker/ec2.rb
CHANGED
@@ -6,8 +6,11 @@ module CloudMaker
|
|
6
6
|
attr_accessor :aws_secret_access_key
|
7
7
|
# Public: Gets/Sets the AWS secret.
|
8
8
|
attr_accessor :aws_access_key_id
|
9
|
-
#
|
10
|
-
attr_accessor :
|
9
|
+
# Internal: Gets/Sets the RightAws::Ec2 instance.
|
10
|
+
attr_accessor :ec2
|
11
|
+
|
12
|
+
# Public: The name of the tag that will be used to find the name of an s3 bucket for archiving/information retrieval
|
13
|
+
BUCKET_TAG = 's3_archive_bucket'
|
11
14
|
|
12
15
|
# Public: Creates a new Ec2 instance
|
13
16
|
#
|
@@ -19,45 +22,56 @@ module CloudMaker
|
|
19
22
|
#
|
20
23
|
# Returns a new CloudMaker::Ec2 instance
|
21
24
|
# Raises RuntimeError if any of the required options are not specified
|
22
|
-
def initialize(
|
25
|
+
def initialize(options)
|
23
26
|
required_keys = [:aws_access_key_id, :aws_secret_access_key]
|
24
27
|
unless (required_keys - options.keys).empty?
|
25
28
|
raise RuntimeError.new("Instantiated #{self.class} without required attributes: #{required_keys - options.keys}.")
|
26
29
|
end
|
27
30
|
|
28
|
-
self.config = cloud_maker_config
|
29
31
|
self.aws_access_key_id = options[:aws_access_key_id]
|
30
32
|
self.aws_secret_access_key = options[:aws_secret_access_key]
|
33
|
+
|
34
|
+
self.ec2 = RightAws::Ec2.new(self.aws_access_key_id, self.aws_secret_access_key)
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def info(instance_id)
|
39
|
+
bucket = self.ec2.describe_tags(:filters => {'resource-id' => instance_id, 'key' => BUCKET_TAG}).first[:value]
|
40
|
+
archiver = S3Archiver.new(
|
41
|
+
:instance_id => instance_id,
|
42
|
+
:aws_access_key_id => self.aws_access_key_id,
|
43
|
+
:aws_secret_access_key => self.aws_secret_access_key,
|
44
|
+
:bucket_name => bucket
|
45
|
+
)
|
46
|
+
archiver.load_archive
|
31
47
|
end
|
32
48
|
|
33
49
|
# Public: Launches a new EC2 instance, associates any specified elastic IPS
|
34
50
|
# with it, adds any specified tags, and archives the launch details to S3.
|
35
51
|
#
|
36
52
|
# Returns a RightAws supplied Hash describing the launched instance.
|
37
|
-
def launch
|
38
|
-
|
39
|
-
|
40
|
-
user_data = self.config.to_user_data
|
53
|
+
def launch(cloud_maker_config)
|
54
|
+
user_data = cloud_maker_config.to_user_data
|
41
55
|
|
42
|
-
instance = ec2.launch_instances(
|
43
|
-
:group_names =>
|
44
|
-
:instance_type =>
|
45
|
-
:key_name =>
|
56
|
+
instance = ec2.launch_instances(cloud_maker_config['ami'],
|
57
|
+
:group_names => cloud_maker_config['security_group'],
|
58
|
+
:instance_type => cloud_maker_config['instance_type'],
|
59
|
+
:key_name => cloud_maker_config['key_pair'],
|
46
60
|
:user_data => user_data
|
47
61
|
).first
|
48
62
|
|
49
63
|
instance_id = instance[:aws_instance_id]
|
50
64
|
|
51
|
-
ec2.create_tags(instance_id,
|
65
|
+
ec2.create_tags(instance_id, cloud_maker_config["tags"]) if cloud_maker_config["tags"]
|
52
66
|
|
53
|
-
if (
|
67
|
+
if (cloud_maker_config["elastic_ip"])
|
54
68
|
#we can't associate IPs while the state is pending
|
55
69
|
while instance[:aws_state] == 'pending'
|
56
70
|
#this is going to hammer EC2 a bit, it might be necessary to add some delay in here
|
57
71
|
instance = ec2.describe_instances([instance_id]).first
|
58
72
|
end
|
59
73
|
|
60
|
-
ec2.associate_address(instance_id, :public_ip =>
|
74
|
+
ec2.associate_address(instance_id, :public_ip => cloud_maker_config["elastic_ip"])
|
61
75
|
|
62
76
|
instance = ec2.describe_instances([instance_id]).first # So we get the correct IP address
|
63
77
|
end
|
@@ -66,9 +80,9 @@ module CloudMaker
|
|
66
80
|
:instance_id => instance_id,
|
67
81
|
:aws_access_key_id => self.aws_access_key_id,
|
68
82
|
:aws_secret_access_key => self.aws_secret_access_key,
|
69
|
-
:bucket_name =>
|
83
|
+
:bucket_name => cloud_maker_config["tags"][BUCKET_TAG]
|
70
84
|
)
|
71
|
-
archiver.store_archive(
|
85
|
+
archiver.store_archive(cloud_maker_config, instance)
|
72
86
|
|
73
87
|
instance
|
74
88
|
end
|
@@ -15,6 +15,10 @@ module CloudMaker
|
|
15
15
|
# Public: All archive keys will be prefixed with KEY_PREFIX/
|
16
16
|
KEY_PREFIX = "cloud-maker"
|
17
17
|
|
18
|
+
INSTANCE_YAML = 'instance.yaml'
|
19
|
+
CLOUD_CONFIG_YAML = 'cloud_config.yaml'
|
20
|
+
|
21
|
+
|
18
22
|
# Public: Creates a new S3 Archiver instance
|
19
23
|
#
|
20
24
|
# options - S3 configuration options
|
@@ -48,8 +52,9 @@ module CloudMaker
|
|
48
52
|
# Returns nothing.
|
49
53
|
def store_archive(cloud_maker_config, instance)
|
50
54
|
userdata = cloud_maker_config.to_user_data
|
51
|
-
self.bucket.put(self.
|
52
|
-
self.bucket.put(self.
|
55
|
+
self.bucket.put(self.user_data_key, userdata)
|
56
|
+
self.bucket.put(self.instance_yaml_key, instance.to_yaml)
|
57
|
+
self.bucket.put(self.cloud_config_yaml_key, cloud_maker_config.to_hash.to_yaml)
|
53
58
|
true
|
54
59
|
end
|
55
60
|
|
@@ -57,13 +62,33 @@ module CloudMaker
|
|
57
62
|
#
|
58
63
|
# Returns the content of the archive.
|
59
64
|
def load_archive
|
60
|
-
|
65
|
+
{
|
66
|
+
:user_data => self.bucket.get(self.user_data_key),
|
67
|
+
:cloud_config => YAML::load(self.bucket.get(self.cloud_config_yaml_key)),
|
68
|
+
:instance => YAML::load(self.bucket.get(self.instance_yaml_key))
|
69
|
+
}
|
61
70
|
end
|
62
71
|
|
72
|
+
# Internal: Returns the key for the user_data file
|
73
|
+
def user_data_key
|
74
|
+
self.prefix_key('user_data')
|
75
|
+
end
|
76
|
+
|
77
|
+
# Internal: Returns the key for the instance yaml file
|
78
|
+
def instance_yaml_key
|
79
|
+
self.prefix_key('instance.yaml')
|
80
|
+
end
|
81
|
+
|
82
|
+
# Internal: Returns the key for the cloud config yaml file
|
83
|
+
def cloud_config_yaml_key
|
84
|
+
self.prefix_key('cloud_config.yaml')
|
85
|
+
end
|
86
|
+
|
87
|
+
|
63
88
|
# Public: Returns the key that the archive will be stored under
|
64
|
-
def key
|
89
|
+
def prefix_key(key)
|
65
90
|
if self.instance_id
|
66
|
-
[KEY_PREFIX, self.instance_id].join('/')
|
91
|
+
[KEY_PREFIX, self.instance_id, key].join('/')
|
67
92
|
else
|
68
93
|
raise RuntimeError.new("Attempted to generate a key name without an instance id.")
|
69
94
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module CloudMaker
|
2
|
-
|
3
|
-
|
2
|
+
class ShellExecutor
|
3
|
+
#If we don't use yaml.org,2002 some YAML implementations don't pickup our !shell-script nodes properly.
|
4
|
+
YAML_DOMAIN = "yaml.org,2002"
|
4
5
|
YAML_TYPE = "shell-script"
|
5
6
|
|
6
7
|
attr_accessor :script
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloud-maker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0.pre2
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Nathan Baxter
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-08-
|
13
|
+
date: 2012-08-03 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: colorize
|
@@ -76,6 +76,22 @@ dependencies:
|
|
76
76
|
- - ~>
|
77
77
|
- !ruby/object:Gem::Version
|
78
78
|
version: '1.0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: pry
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
79
95
|
description:
|
80
96
|
email: nathan.baxter@airbnb.cloud-maker
|
81
97
|
executables:
|
@@ -104,9 +120,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
121
|
none: false
|
106
122
|
requirements:
|
107
|
-
- - ! '
|
123
|
+
- - ! '>'
|
108
124
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
125
|
+
version: 1.3.1
|
110
126
|
requirements: []
|
111
127
|
rubyforge_project:
|
112
128
|
rubygems_version: 1.8.24
|