inception-server 0.2.1

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.
Files changed (75) hide show
  1. data/.chef/knife.rb +4 -0
  2. data/.gitignore +21 -0
  3. data/.kitchen.yml +47 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +18 -0
  6. data/Berksfile +8 -0
  7. data/Berksfile.lock +9 -0
  8. data/ChangeLog.md +20 -0
  9. data/Gemfile +27 -0
  10. data/Guardfile +6 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +126 -0
  13. data/Rakefile +66 -0
  14. data/TODO.md +25 -0
  15. data/bin/inception +8 -0
  16. data/bin/inception-server +8 -0
  17. data/config/ssh/kitchen-aws +23 -0
  18. data/cookbooks/bosh_inception/README.md +15 -0
  19. data/cookbooks/bosh_inception/attributes/default.rb +25 -0
  20. data/cookbooks/bosh_inception/files/default/Gemfile.cf +5 -0
  21. data/cookbooks/bosh_inception/files/default/Gemfile.micro +5 -0
  22. data/cookbooks/bosh_inception/metadata.rb +32 -0
  23. data/cookbooks/bosh_inception/recipes/default.rb +16 -0
  24. data/cookbooks/bosh_inception/recipes/install_bosh.rb +37 -0
  25. data/cookbooks/bosh_inception/recipes/install_ruby.rb +10 -0
  26. data/cookbooks/bosh_inception/recipes/mount_store_volume.rb +24 -0
  27. data/cookbooks/bosh_inception/recipes/packages.rb +23 -0
  28. data/cookbooks/bosh_inception/recipes/setup_dotfog.rb +29 -0
  29. data/cookbooks/bosh_inception/recipes/setup_git.rb +34 -0
  30. data/cookbooks/bosh_inception/recipes/useful_dirs.rb +13 -0
  31. data/inception-server.gemspec +43 -0
  32. data/lib/inception/cli.rb +141 -0
  33. data/lib/inception/cli_helpers/display.rb +26 -0
  34. data/lib/inception/cli_helpers/interactions.rb +15 -0
  35. data/lib/inception/cli_helpers/prepare_deploy_settings.rb +89 -0
  36. data/lib/inception/cli_helpers/provider.rb +14 -0
  37. data/lib/inception/cli_helpers/settings.rb +53 -0
  38. data/lib/inception/inception_server.rb +304 -0
  39. data/lib/inception/inception_server_cookbook.rb +90 -0
  40. data/lib/inception/next_deploy_actions.rb +20 -0
  41. data/lib/inception/providers/README.md +5 -0
  42. data/lib/inception/providers/clients/aws_provider_client.rb +144 -0
  43. data/lib/inception/providers/clients/fog_provider_client.rb +185 -0
  44. data/lib/inception/providers/clients/openstack_provider_client.rb +84 -0
  45. data/lib/inception/providers/constants/aws_constants.rb +25 -0
  46. data/lib/inception/providers/constants/openstack_constants.rb +12 -0
  47. data/lib/inception/providers.rb +28 -0
  48. data/lib/inception/version.rb +3 -0
  49. data/lib/inception.rb +9 -0
  50. data/nodes/.gitkeep +0 -0
  51. data/spec/assets/.gitkeep +0 -0
  52. data/spec/assets/gitconfig +5 -0
  53. data/spec/assets/settings/aws-before-server.yml +14 -0
  54. data/spec/assets/settings/aws-created-server.yml +31 -0
  55. data/spec/integration/.gitkeep +0 -0
  56. data/spec/integration/aws/aws_basic_spec.rb +38 -0
  57. data/spec/spec_helper.rb +50 -0
  58. data/spec/support/aws/aws_helpers.rb +73 -0
  59. data/spec/support/settings_helper.rb +20 -0
  60. data/spec/support/stdout_capture.rb +17 -0
  61. data/spec/unit/.gitkeep +0 -0
  62. data/spec/unit/cli_delete_spec.rb +39 -0
  63. data/spec/unit/cli_deploy_aws_spec.rb +83 -0
  64. data/spec/unit/cli_ssh_spec.rb +80 -0
  65. data/spec/unit/inception_server_cookbook_spec.rb +62 -0
  66. data/spec/unit/inception_server_spec.rb +58 -0
  67. data/spec/unit/providers/aws_spec.rb +198 -0
  68. data/test/integration/default/bats/discover_user.bash +2 -0
  69. data/test/integration/default/bats/dotfog.bats +11 -0
  70. data/test/integration/default/bats/install_ruby.bats +8 -0
  71. data/test/integration/default/bats/useful_dirs.bats +8 -0
  72. data/test/integration/default/bats/user.bats +9 -0
  73. data/test/integration/default/bats/verify_bosh.bats +18 -0
  74. data/test/integration/default/bats/verify_git.bats +18 -0
  75. metadata +361 -0
@@ -0,0 +1,304 @@
1
+ require "fog"
2
+
3
+ module Inception
4
+ class InceptionServer
5
+
6
+ DEFAULT_SERVER_NAME = "inception"
7
+ DEFAULT_FLAVOR = "m1.small"
8
+ DEFAULT_DISK_SIZE = 16
9
+ DEFAULT_SECURITY_GROUPS = ["ssh"]
10
+
11
+ attr_reader :attributes
12
+
13
+ # @provider_client [Inception::Providers::FogProvider] - interact with IaaS
14
+ # @attributes [ReadWriteSettings]
15
+ #
16
+ # Required @attributes:
17
+ # {
18
+ # "name" => "inception",
19
+ # "ip_address" => "54.214.15.178",
20
+ # "key_pair" => {
21
+ # "name" => "inception",
22
+ # "private_key" => "private_key",
23
+ # "public_key" => "public_key"
24
+ # }
25
+ # }
26
+ #
27
+ # Including optional @attributes and default values:
28
+ # {
29
+ # "name" => "inception",
30
+ # "ip_address" => "54.214.15.178",
31
+ # "security_groups" => ["ssh"],
32
+ # "flavor" => "m1.small",
33
+ # "key_pair" => {
34
+ # "name" => "inception",
35
+ # "private_key" => "private_key",
36
+ # "public_key" => "public_key"
37
+ # }
38
+ # }
39
+ def initialize(provider_client, attributes, ssh_dir)
40
+ @provider_client = provider_client
41
+ @ssh_dir = ssh_dir
42
+ @attributes = attributes.is_a?(Hash) ? ReadWriteSettings.new(attributes) : attributes
43
+ raise "@attributes must be ReadWriteSettings (or Hash)" unless @attributes.is_a?(ReadWriteSettings)
44
+ end
45
+
46
+ # Create the underlying server, with key pair & security groups, unless it is already created
47
+ #
48
+ # The @attributes hash is updated with a `provisioned` key during/after creation.
49
+ # When saved as YAML it might look like:
50
+ # inception:
51
+ # provisioned:
52
+ # image_id: ami-123456
53
+ # server_id: i-e7f005d2
54
+ # security_groups:
55
+ # - ssh
56
+ # - mosh
57
+ # username: ubuntu
58
+ # disk_device: /dev/sdi
59
+ # host: ec2-54-214-15-178.us-west-2.compute.amazonaws.com
60
+ # validated: true
61
+ # converged: true
62
+ def create
63
+ validate_attributes_for_bootstrap
64
+ ensure_required_security_groups
65
+ create_missing_default_security_groups
66
+ bootstrap_vm
67
+ attach_persistent_disk
68
+ end
69
+
70
+ # Delete the server, volume and release the IP address
71
+ def delete_all
72
+ delete_server
73
+ delete_volume
74
+ delete_key_pair
75
+ release_ip_address
76
+ end
77
+
78
+ def delete_server
79
+ @fog_server = nil # force reload of fog_server model
80
+ if fog_server
81
+ print "Deleting server... "
82
+ fog_server.destroy
83
+ wait_for_termination(fog_server) unless Fog.mocking?
84
+ puts "done."
85
+ else
86
+ puts "Server already destroyed"
87
+ end
88
+ provisioned.delete("host")
89
+ provisioned.delete("server_id")
90
+ provisioned.delete("username")
91
+ end
92
+
93
+ def delete_volume
94
+ volume_id = provisioned.exists?("disk_device.volume_id")
95
+ if volume_id && (volume = fog_compute.volumes.get(volume_id)) && volume.ready?
96
+ print "Deleting volume... "
97
+ volume.destroy
98
+ wait_for_termination(volume, "deleting")
99
+ puts ""
100
+ else
101
+ puts "Volume already destroyed"
102
+ end
103
+ provisioned.delete("disk_device")
104
+ end
105
+
106
+ def delete_key_pair
107
+ key_pair_name = attributes.exists?("key_pair.name")
108
+ if key_pair_name && key_pair = fog_compute.key_pairs.get(key_pair_name)
109
+ puts "Deleting key pair '#{key_pair_name}'"
110
+ key_pair.destroy
111
+ else
112
+ puts "Keypair already destroyed"
113
+ end
114
+ attributes.delete("key_pair")
115
+ end
116
+
117
+
118
+ def release_ip_address
119
+ public_ip = provisioned.exists?("ip_address")
120
+ if public_ip && ip_address = fog_compute.addresses.get(public_ip)
121
+ puts "Releasing IP address #{public_ip}"
122
+ ip_address.destroy
123
+ else
124
+ puts "IP address already released"
125
+ end
126
+ provisioned.delete("ip_address")
127
+ end
128
+
129
+ def security_groups
130
+ @attributes.security_groups
131
+ end
132
+
133
+ def server_name
134
+ @attributes["name"] ||= DEFAULT_SERVER_NAME
135
+ @attributes.name
136
+ end
137
+
138
+ def key_name
139
+ @attributes.key_pair.name
140
+ end
141
+
142
+ def private_key_path
143
+ @private_key_path ||= File.join(@ssh_dir, key_name)
144
+ end
145
+
146
+ def public_key
147
+ @attributes.exists?("key_pair.public_key")
148
+ end
149
+
150
+ # Flavor/instance type of the server to be provisioned
151
+ # TODO: DEFAULT_FLAVOR should become IaaS/provider specific
152
+ def flavor
153
+ @attributes["flavor"] ||= DEFAULT_FLAVOR
154
+ end
155
+
156
+ # Size of attached persistent disk for the inception server
157
+ def disk_size
158
+ @attributes["disk_size"] ||= DEFAULT_DISK_SIZE
159
+ end
160
+
161
+ def ip_address
162
+ provisioned.ip_address
163
+ end
164
+
165
+ def image_id
166
+ @attributes["image_id"] ||= @provider_client.image_id
167
+ end
168
+
169
+ # The progresive/final attributes of the provisioned Inception server &
170
+ # persistent disk.
171
+ def provisioned
172
+ @attributes["provisioned"] = {} unless @attributes["provisioned"]
173
+ @attributes.provisioned
174
+ end
175
+
176
+ # Because @attributes["provisioned"] is not the same as @attributes.provisioned
177
+ # we need a helper to export the complete nested attributes.
178
+ def export_attributes
179
+ attrs = attributes.to_nested_hash
180
+ attrs["provisioned"] = provisioned.to_nested_hash
181
+ attrs
182
+ end
183
+
184
+ def disk_devices
185
+ provisioned["disk_device"] ||= default_disk_device
186
+ end
187
+
188
+ def external_disk_device
189
+ disk_devices["external"]
190
+ end
191
+
192
+ def default_disk_device
193
+ case @provider_client
194
+ when Inception::Providers::Clients::AwsProviderClient
195
+ { "external" => "/dev/sdf", "internal" => "/dev/xvdf" }
196
+ when Inception::Providers::Clients::OpenStackProviderClient
197
+ { "external" => "/dev/vdc", "internal" => "/dev/vdc" }
198
+ else
199
+ raise "Please implement InceptionServer#default_disk_device for #{@provider_client.class}"
200
+ end
201
+ end
202
+
203
+ def user_host
204
+ "#{provisioned.username}@#{provisioned.host}"
205
+ end
206
+
207
+ def fog_server
208
+ @fog_server ||= begin
209
+ if server_id = provisioned["server_id"]
210
+ fog_compute.servers.get(server_id)
211
+ end
212
+ end
213
+ end
214
+
215
+ def fog_compute
216
+ @provider_client.fog_compute
217
+ end
218
+
219
+ protected
220
+ # set_resource_name(fog_server, "inception")
221
+ # set_resource_name(volume, "inception-root")
222
+ # set_resource_name(volume, "inception-store")
223
+ def set_resource_name(resource, name)
224
+ @provider_client.set_resource_name(resource, name)
225
+ end
226
+
227
+ def fog_attributes
228
+ @provider_client.fog_attributes(self)
229
+ end
230
+
231
+ def validate_attributes_for_bootstrap
232
+ missing_attributes = []
233
+ missing_attributes << "provisioned.ip_address" unless @attributes.exists?("provisioned.ip_address")
234
+ missing_attributes << "key_pair.private_key" unless @attributes.exists?("key_pair.private_key")
235
+ if missing_attributes.size > 0
236
+ raise "Missing InceptionServer attributes: #{missing_attributes.join(', ')}"
237
+ end
238
+ end
239
+
240
+ # ssh group must be first (bootstrap method looks for port 22 in first group)
241
+ def ensure_required_security_groups
242
+ if @attributes["security_groups"] && @attributes["security_groups"].is_a?(Array)
243
+ unless @attributes["security_groups"].include?("ssh")
244
+ @attributes["security_groups"] = ["ssh", *@attributes["security_groups"]]
245
+ end
246
+ else
247
+ @attributes["security_groups"] = ["ssh"]
248
+ end
249
+ end
250
+
251
+ def create_missing_default_security_groups
252
+ # provider method only creates group if missing
253
+ @provider_client.create_security_group("ssh", "ssh", {ssh: 22})
254
+ end
255
+
256
+ def bootstrap_vm
257
+ unless fog_server
258
+ print "Booting #{flavor} inception server... "
259
+ @fog_server = @provider_client.bootstrap(fog_attributes)
260
+ provisioned["server_id"] = fog_server.id
261
+ provisioned["host"] = fog_server.dns_name || fog_server.public_ip_address
262
+ provisioned["username"] = fog_attributes[:username]
263
+ puts provisioned.server_id
264
+ end
265
+ set_resource_name(fog_server, server_name)
266
+ end
267
+
268
+ def attach_persistent_disk
269
+ unless Fog.mocking?
270
+ print "Confirming ssh access to server... "
271
+ Fog.wait_for(60) { fog_server.sshable?(ssh_options) }
272
+ puts "done"
273
+ end
274
+
275
+ unless volume = @provider_client.find_server_device(fog_server, external_disk_device)
276
+ print "Provisioning #{disk_size}Gb persistent disk for inception server... "
277
+ volume = @provider_client.create_and_attach_volume("Inception Disk", disk_size, fog_server, external_disk_device)
278
+ disk_devices["volume_id"] = volume.id
279
+ puts disk_devices.volume_id
280
+ end
281
+ set_resource_name(volume, server_name)
282
+ end
283
+
284
+ def ssh_options
285
+ {
286
+ keys: [private_key_path]
287
+ }
288
+ end
289
+
290
+ # Poll a fog model until it terminates; print . each second
291
+ def wait_for_termination(fog_model, state_to_wait_for="terminated")
292
+ fog_model.wait_for do
293
+ print "."
294
+ state == state_to_wait_for
295
+ end
296
+ end
297
+
298
+ protected
299
+ # TODO emit events rather than writing directly to STDOUT
300
+ def say(*args)
301
+ puts(*args)
302
+ end
303
+ end
304
+ end
@@ -0,0 +1,90 @@
1
+ module Inception
2
+ # Perform converge chef cookbooks upon inception server
3
+ class InceptionServerCookbook
4
+ include FileUtils
5
+
6
+ attr_reader :server, :settings, :project_dir
7
+
8
+ class InvalidTarget < StandardError; end
9
+
10
+ def initialize(inception_server, settings, project_dir)
11
+ @server = inception_server
12
+ @settings = settings
13
+ @project_dir = project_dir
14
+ end
15
+
16
+ def prepare
17
+ FileUtils.chdir(project_dir) do
18
+ prepare_project_dir
19
+ knife_solo :prepare unless ignore_chef_preparations?
20
+ end
21
+ end
22
+
23
+ # To be invoked within the settings_dir
24
+ def converge
25
+ FileUtils.chdir(project_dir) do
26
+ knife_solo :cook
27
+ end
28
+ end
29
+
30
+ def ignore_chef_preparations?
31
+ @settings.exists?("cookbook.prepared")
32
+ end
33
+
34
+ def user_host; server.user_host; end
35
+ def key_path; server.private_key_path; end
36
+
37
+ def knife_solo(command)
38
+ attributes = cookbook_attributes_for_inception.to_json
39
+ sh %Q{knife solo #{command} #{user_host} -i #{key_path} -j '#{attributes}' -r 'bosh_inception'}
40
+ end
41
+
42
+ protected
43
+ def prepare_project_dir
44
+ prepare_cookbook
45
+ prepare_knife_config
46
+ prepare_berksfile
47
+ end
48
+
49
+ def prepare_cookbook
50
+ mkdir_p("cookbooks")
51
+ rm_rf("cookbooks/bosh_inception")
52
+ cp_r(inception_cookbook_path, "cookbooks/")
53
+ end
54
+
55
+ def prepare_knife_config
56
+ mkdir_p("nodes") # needed for knife solo
57
+ end
58
+
59
+ def prepare_berksfile
60
+ unless File.exists?("Berksfile")
61
+ cp_r(File.join(gem_root_path, "Berksfile"), "Berksfile")
62
+ end
63
+ end
64
+
65
+ def cookbook_attributes_for_inception
66
+ {
67
+ "disk" => {
68
+ "mounted" => true,
69
+ "device" => settings.inception.provisioned.disk_device.internal
70
+ },
71
+ "git" => {
72
+ "name" => settings.git.name,
73
+ "email" => settings.git.email
74
+ },
75
+ "user" => {
76
+ "username" => settings.inception.provisioned.username
77
+ },
78
+ "fog" => settings.provider.credentials
79
+ }
80
+ end
81
+
82
+ def gem_root_path
83
+ File.expand_path("../../..", __FILE__)
84
+ end
85
+
86
+ def inception_cookbook_path
87
+ File.join(gem_root_path, "cookbooks/bosh_inception")
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,20 @@
1
+ module Inception
2
+
3
+ class NextDeployActions
4
+ def initialize(attributes, cli_options)
5
+ @attributes = attributes.is_a?(Hash) ? ReadWriteSettings.new(attributes) : attributes
6
+ raise "@attributes must be ReadWriteSettings (or Hash)" unless @attributes.is_a?(ReadWriteSettings)
7
+ raise "@cli_options must be Hash" unless cli_options.is_a?(Hash)
8
+ apply_cli_options(cli_options)
9
+ end
10
+
11
+ def skip_chef_converge?
12
+ @attributes["no_converge"] || @attributes["no-converge"] || @attributes["skip_chef_converge"]
13
+ end
14
+
15
+ protected
16
+ def apply_cli_options(cli_options)
17
+ @attributes.merge(cli_options)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ # Providers
2
+
3
+ Currently these files are kept in sync with bosh-cloudfoundry project.
4
+
5
+ TODO - extract into shared library.
@@ -0,0 +1,144 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Inception; module Providers; module Clients; end; end; end
4
+
5
+ require "inception/providers/clients/fog_provider_client"
6
+ require "inception/providers/constants/aws_constants"
7
+
8
+ class Inception::Providers::Clients::AwsProviderClient < Inception::Providers::Clients::FogProviderClient
9
+ include Inception::Providers::Constants::AwsConstants
10
+
11
+ # @return [Integer] megabytes of RAM for requested flavor of server
12
+ def ram_for_server_flavor(server_flavor_id)
13
+ if flavor = fog_compute_flavor(server_flavor_id)
14
+ flavor[:ram]
15
+ else
16
+ raise "Unknown AWS flavor '#{server_flavor_id}'"
17
+ end
18
+ end
19
+
20
+ # @return [Hash] e.g. { :bits => 0, :cores => 2, :disk => 0,
21
+ # :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
22
+ # or nil if +server_flavor_id+ is not a supported flavor ID
23
+ def fog_compute_flavor(server_flavor_id)
24
+ aws_compute_flavors.find { |fl| fl[:id] == server_flavor_id }
25
+ end
26
+
27
+ # @return [Array] of [Hash] for each supported compute flavor
28
+ # Example [Hash] { :bits => 0, :cores => 2, :disk => 0,
29
+ # :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
30
+ def aws_compute_flavors
31
+ Fog::Compute::AWS::FLAVORS
32
+ end
33
+
34
+ def aws_compute_flavor_ids
35
+ aws_compute_flavors.map { |fl| fl[:id] }
36
+ end
37
+
38
+ # Provision an EC2 or VPC elastic IP addess.
39
+ # * VPC - provision_public_ip_address(vpc: true)
40
+ # * EC2 - provision_public_ip_address
41
+ # @return [String] provisions a new public IP address in target region
42
+ # TODO nil if none available
43
+ def provision_public_ip_address(options={})
44
+ if options.delete(:vpc)
45
+ options[:domain] = "vpc"
46
+ else
47
+ options[:domain] = options.delete(:domain) || "standard"
48
+ end
49
+ address = fog_compute.addresses.create(options)
50
+ address.public_ip
51
+ # TODO catch error and return nil
52
+ end
53
+
54
+ def associate_ip_address_with_server(ip_address, server)
55
+ address = fog_compute.addresses.get(ip_address)
56
+ address.server = server
57
+ end
58
+
59
+ def create_vpc(name, cidr_block)
60
+ vpc = fog_compute.vpcs.create(name: name, cidr_block: cidr_block)
61
+ vpc.id
62
+ end
63
+
64
+ # Creates a VPC subnet
65
+ # @return [String] the subnet_id
66
+ def create_subnet(vpc_id, cidr_block)
67
+ subnet = fog_compute.subnets.create(vpc_id: vpc_id, cidr_block: cidr_block)
68
+ subnet.subnet_id
69
+ end
70
+
71
+ def create_internet_gateway(vpc_id)
72
+ gateway = fog_compute.internet_gateways.create(vpc_id: vpc_id)
73
+ gateway.id
74
+ end
75
+
76
+ def find_server_device(server, device)
77
+ server.volumes.all.find {|v| v.device == device}
78
+ end
79
+
80
+ def create_and_attach_volume(name, disk_size, server, device)
81
+ volume = fog_compute.volumes.create(
82
+ size: disk_size,
83
+ name: name,
84
+ description: '',
85
+ device: device,
86
+ availability_zone: server.availability_zone)
87
+ # TODO: the following works in fog 1.9.0+ (but which has a bug in bootstrap)
88
+ # https://github.com/fog/fog/issues/1516
89
+ #
90
+ # volume.wait_for { volume.status == 'available' }
91
+ # volume.attach(server.id, "/dev/vdc")
92
+ # volume.wait_for { volume.status == 'in-use' }
93
+ #
94
+ # Instead, using:
95
+ volume.server = server
96
+ end
97
+
98
+ # Ubuntu 13.04
99
+ def image_id
100
+ region = fog_compute.region
101
+ # http://cloud-images.ubuntu.com/locator/ec2/
102
+ image_id = case region.to_s
103
+ when "ap-northeast-1"
104
+ "ami-6b26ab6a"
105
+ when "ap-southeast-1"
106
+ "ami-2b511e79"
107
+ when "eu-west-1"
108
+ "ami-3d160149"
109
+ when "sa-east-1"
110
+ "ami-28e43e35"
111
+ when "us-east-1"
112
+ "ami-c30360aa"
113
+ when "us-west-1"
114
+ "ami-d383af96"
115
+ when "ap-southeast-2"
116
+ "ami-84a333be"
117
+ when "us-west-2"
118
+ "ami-bf1d8a8f"
119
+ end
120
+ image_id || raise("Please add Ubuntu 13.04 64bit (EBS) AMI image id to aws.rb#raring_image_id method for region '#{region}'")
121
+ end
122
+
123
+ # Construct a Fog::Compute object
124
+ # Uses +attributes+ which normally originates from +settings.provider+
125
+ def setup_fog_connection
126
+ configuration = Fog.symbolize_credentials(attributes.credentials)
127
+ configuration[:provider] = "AWS"
128
+ configuration[:region] = attributes.region
129
+ @fog_compute = Fog::Compute.new(configuration)
130
+ end
131
+
132
+ def fog_attributes(inception_server)
133
+ {
134
+ image_id: inception_server.image_id,
135
+ groups: inception_server.security_groups,
136
+ key_name: inception_server.key_name,
137
+ private_key_path: inception_server.private_key_path,
138
+ flavor_id: inception_server.flavor,
139
+ public_ip_address: inception_server.ip_address,
140
+ bits: 64,
141
+ username: "ubuntu",
142
+ }
143
+ end
144
+ end