cloud-maker 0.1.2 → 0.2.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- cloud_maker = CloudMaker::Ec2.new(config,
53
- :aws_access_key_id => options.aws_access_key_id,
54
- :aws_secret_access_key => options.aws_secret_access_key
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
- if (!config.includes.empty?)
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
- config.includes.each do |url|
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
- print_table config.to_hash.map {|key, val| [key.dup.cyan, val]}
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
- if yes?("Launch a new EC2 instance with the options above? (y/n)")
74
- instance = cloud_maker.launch
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
@@ -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
+
@@ -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
- self.options.map {|key, properties| [key, properties["value"]]}
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), :config_path => full_path)
214
+ CloudMaker::Config.new(YAML::load(cloud_yaml), 'config_path' => full_path)
212
215
  end
213
216
  end
214
217
 
@@ -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
- # Public: Gets/Sets the CloudMaker::Config
10
- attr_accessor :config
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(cloud_maker_config, options)
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
- ec2 = RightAws::Ec2.new(self.aws_access_key_id, self.aws_secret_access_key)
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(config['ami'],
43
- :group_names => config['security_group'],
44
- :instance_type => config['instance_type'],
45
- :key_name => config['key_pair'],
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, self.config["tags"]) if self.config["tags"]
65
+ ec2.create_tags(instance_id, cloud_maker_config["tags"]) if cloud_maker_config["tags"]
52
66
 
53
- if (self.config["elastic_ip"])
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 => self.config["elastic_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 => self.config["s3_archive_bucket"]
83
+ :bucket_name => cloud_maker_config["tags"][BUCKET_TAG]
70
84
  )
71
- archiver.store_archive(self.config, instance)
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.key + "/user_data.cloud_config", userdata)
52
- self.bucket.put(self.key + "/instance.yaml", instance.to_yaml)
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
- self.bucket.get(self.key)
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
- class ShellExecutor
3
- YAML_DOMAIN = "!airbnb.com,2012-07-19"
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.1.2
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-01 00:00:00.000000000 Z
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: '0'
125
+ version: 1.3.1
110
126
  requirements: []
111
127
  rubyforge_project:
112
128
  rubygems_version: 1.8.24