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.
@@ -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