inception 0.1.0

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 (74) hide show
  1. data/.chef/knife.rb +4 -0
  2. data/.gitignore +20 -0
  3. data/.kitchen.yml +42 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +23 -0
  6. data/Berksfile +8 -0
  7. data/Berksfile.lock +9 -0
  8. data/Gemfile +18 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +139 -0
  11. data/Rakefile +66 -0
  12. data/TODO.md +26 -0
  13. data/bin/bosh-inception +8 -0
  14. data/config/ssh/kitchen-aws +23 -0
  15. data/cookbooks/bosh_inception/README.md +15 -0
  16. data/cookbooks/bosh_inception/attributes/default.rb +20 -0
  17. data/cookbooks/bosh_inception/files/default/Gemfile.cf +4 -0
  18. data/cookbooks/bosh_inception/files/default/Gemfile.micro +5 -0
  19. data/cookbooks/bosh_inception/metadata.rb +32 -0
  20. data/cookbooks/bosh_inception/recipes/default.rb +15 -0
  21. data/cookbooks/bosh_inception/recipes/install_bosh.rb +37 -0
  22. data/cookbooks/bosh_inception/recipes/install_ruby.rb +10 -0
  23. data/cookbooks/bosh_inception/recipes/mount_store_volume.rb +24 -0
  24. data/cookbooks/bosh_inception/recipes/packages.rb +23 -0
  25. data/cookbooks/bosh_inception/recipes/setup_git.rb +34 -0
  26. data/cookbooks/bosh_inception/recipes/useful_dirs.rb +13 -0
  27. data/inception.gemspec +42 -0
  28. data/lib/bosh/providers.rb +41 -0
  29. data/lib/bosh/providers/README.md +5 -0
  30. data/lib/bosh/providers/cli/aws_provider_cli.rb +58 -0
  31. data/lib/bosh/providers/cli/openstack_provider_cli.rb +47 -0
  32. data/lib/bosh/providers/cli/provider_cli.rb +17 -0
  33. data/lib/bosh/providers/clients/aws_provider_client.rb +168 -0
  34. data/lib/bosh/providers/clients/fog_provider_client.rb +161 -0
  35. data/lib/bosh/providers/clients/openstack_provider_client.rb +65 -0
  36. data/lib/bosh/providers/constants/aws_constants.rb +25 -0
  37. data/lib/bosh/providers/constants/openstack_constants.rb +12 -0
  38. data/lib/inception.rb +9 -0
  39. data/lib/inception/cli.rb +136 -0
  40. data/lib/inception/cli_helpers/display.rb +26 -0
  41. data/lib/inception/cli_helpers/infrastructure.rb +157 -0
  42. data/lib/inception/cli_helpers/interactions.rb +15 -0
  43. data/lib/inception/cli_helpers/prepare_deploy_settings.rb +89 -0
  44. data/lib/inception/cli_helpers/provider.rb +14 -0
  45. data/lib/inception/cli_helpers/settings.rb +47 -0
  46. data/lib/inception/inception_server.rb +305 -0
  47. data/lib/inception/inception_server_cookbook.rb +89 -0
  48. data/lib/inception/next_deploy_actions.rb +20 -0
  49. data/lib/inception/version.rb +3 -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 +39 -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/bosh/providers/aws_spec.rb +199 -0
  63. data/spec/unit/cli_delete_spec.rb +39 -0
  64. data/spec/unit/cli_deploy_aws_spec.rb +84 -0
  65. data/spec/unit/cli_ssh_spec.rb +82 -0
  66. data/spec/unit/inception_server_cookbook_spec.rb +61 -0
  67. data/spec/unit/inception_server_spec.rb +58 -0
  68. data/test/integration/default/bats/discover_user.bash +2 -0
  69. data/test/integration/default/bats/install_ruby.bats +8 -0
  70. data/test/integration/default/bats/useful_dirs.bats +8 -0
  71. data/test/integration/default/bats/user.bats +9 -0
  72. data/test/integration/default/bats/verify_bosh.bats +13 -0
  73. data/test/integration/default/bats/verify_git.bats +18 -0
  74. metadata +342 -0
@@ -0,0 +1,168 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module Providers; module Clients; end; end; end
4
+
5
+ require "bosh/providers/clients/fog_provider_client"
6
+ require "bosh/providers/constants/aws_constants"
7
+
8
+ class Bosh::Providers::Clients::AwsProviderClient < Bosh::Providers::Clients::FogProviderClient
9
+ include Bosh::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 raring_image_id(region=nil)
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
+ def bootstrap(new_attributes = {})
124
+ new_attributes[:image_id] ||= raring_image_id(fog_compute.region)
125
+ vpc = new_attributes[:subnet_id]
126
+
127
+ server = fog_compute.servers.new(new_attributes)
128
+
129
+ unless new_attributes[:key_name]
130
+ raise "please provide :key_name attribute"
131
+ end
132
+ unless private_key_path = new_attributes.delete(:private_key_path)
133
+ raise "please provide :private_key_path attribute"
134
+ end
135
+
136
+ if vpc
137
+ # TODO setup security group on new server
138
+ else
139
+ # make sure port 22 is open in the first security group
140
+ security_group = fog_compute.security_groups.get(server.groups.first)
141
+ authorized = security_group.ip_permissions.detect do |ip_permission|
142
+ ip_permission['ipRanges'].first && ip_permission['ipRanges'].first['cidrIp'] == '0.0.0.0/0' &&
143
+ ip_permission['fromPort'] == 22 &&
144
+ ip_permission['ipProtocol'] == 'tcp' &&
145
+ ip_permission['toPort'] == 22
146
+ end
147
+ unless authorized
148
+ security_group.authorize_port_range(22..22)
149
+ end
150
+ end
151
+
152
+ server.save
153
+ unless Fog.mocking?
154
+ server.wait_for { ready? }
155
+ server.setup(:keys => [private_key_path])
156
+ end
157
+ server
158
+ end
159
+
160
+ # Construct a Fog::Compute object
161
+ # Uses +attributes+ which normally originates from +settings.provider+
162
+ def setup_fog_connection
163
+ configuration = Fog.symbolize_credentials(attributes.credentials)
164
+ configuration[:provider] = "AWS"
165
+ configuration[:region] = attributes.region
166
+ @fog_compute = Fog::Compute.new(configuration)
167
+ end
168
+ end
@@ -0,0 +1,161 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ require "fog"
4
+ module Bosh; module Providers; module Clients; end; end; end
5
+
6
+ class Bosh::Providers::Clients::FogProviderClient
7
+ attr_reader :fog_compute
8
+ attr_reader :attributes
9
+
10
+ def initialize(attributes)
11
+ @attributes = attributes.is_a?(Hash) ? Settingslogic.new(attributes) : attributes
12
+ raise "@attributes must be Settingslogic (or Hash)" unless @attributes.is_a?(Settingslogic)
13
+ setup_fog_connection
14
+ end
15
+
16
+ def setup_fog_connection
17
+ raise "must implement"
18
+ end
19
+
20
+ def create_key_pair(key_pair_name)
21
+ fog_compute.key_pairs.create(:name => key_pair_name)
22
+ end
23
+
24
+ # set_resource_name(fog_server, "inception")
25
+ # set_resource_name(volume, "inception-root")
26
+ # set_resource_name(volume, "inception-store")
27
+ def set_resource_name(resource, name)
28
+ fog_compute.tags.create :key => "Name", :value => name, :resource_id => resource.id
29
+ end
30
+
31
+ def delete_key_pair_if_exists(key_pair_name)
32
+ if fog_key_pair = fog_compute.key_pairs.get(key_pair_name)
33
+ fog_key_pair.destroy
34
+ end
35
+ end
36
+
37
+ def delete_servers_with_name(name)
38
+ fog_compute.servers.select {|s| s.tags["Name"].downcase == name.downcase }.each do |server|
39
+ puts "Destroying server #{server.id}..."
40
+ server.destroy
41
+ end
42
+ end
43
+
44
+ def delete_volumes_with_name(name)
45
+ fog_compute.volumes.select do |v|
46
+ volume_name = v.tags["Name"]
47
+ volume_name && volume_name.downcase == name.downcase
48
+ end.each do |volume|
49
+ puts "Destroying volume #{volume.id}..."
50
+ volume.destroy
51
+ end
52
+ end
53
+
54
+ # Destroy all IP addresses that aren't bound to a server
55
+ def cleanup_unused_ip_addresses
56
+ fog_compute.addresses.each do |a|
57
+ unless a.server
58
+ puts "Deleting unused IP address #{a.public_ip}..."
59
+ a.destroy
60
+ end
61
+ end
62
+ end
63
+
64
+ # Creates or reuses an security group and opens ports.
65
+ #
66
+ # +security_group_name+ is the name to be created or reused
67
+ # +ports+ is a hash of name/port for ports to open, for example:
68
+ # {
69
+ # ssh: 22,
70
+ # http: 80,
71
+ # https: 443
72
+ # }
73
+ # protocol defaults to TCP
74
+ # You can also use a more verbose +ports+ using the format:
75
+ # {
76
+ # ssh: 22,
77
+ # http: { ports: (80..82) },
78
+ # mosh: { protocol: "udp", ports: (60000..60050) }
79
+ # mosh: { protocol: "rdp", ports: (3398..3398), ip_ranges: [ { cidrIp: "196.212.12.34/32" } ] }
80
+ # }
81
+ # In this example,
82
+ # * TCP 22 will be opened for ssh from any ip_range,
83
+ # * TCP ports 80, 81, 82 for http from any ip_range,
84
+ # * UDP 60000 -> 60050 for mosh from any ip_range and
85
+ # * TCP 3398 for RDP from ip range: 96.212.12.34/32
86
+ def create_security_group(security_group_name, description, ports)
87
+ security_groups = fog_compute.security_groups
88
+ unless sg = security_groups.find { |s| s.name == security_group_name }
89
+ sg = fog_compute.security_groups.create(name: security_group_name, description: description)
90
+ puts "Created security group #{security_group_name}"
91
+ else
92
+ puts "Reusing security group #{security_group_name}"
93
+ end
94
+ ip_permissions = ip_permissions(sg)
95
+ ports_opened = 0
96
+ ports.each do |name, port_defn|
97
+ (protocol, port_range, ip_range) = extract_port_definition(port_defn)
98
+ unless port_open?(ip_permissions, port_range, protocol, ip_range)
99
+ authorize_port_range(sg, port_range, protocol, ip_range)
100
+ puts " -> opened #{name} ports #{protocol.upcase} #{port_range.min}..#{port_range.max} from IP range #{ip_range}"
101
+ ports_opened += 1
102
+ end
103
+ end
104
+ puts " -> no additional ports opened" if ports_opened == 0
105
+ true
106
+ end
107
+
108
+ def port_open?(ip_permissions, port_range, protocol, ip_range)
109
+ ip_permissions && ip_permissions.find do |ip|
110
+ ip["ipProtocol"] == protocol \
111
+ && ip["ipRanges"].detect { |range| range["cidrIp"] == ip_range } \
112
+ && ip["fromPort"] <= port_range.min \
113
+ && ip["toPort"] >= port_range.max
114
+ end
115
+ end
116
+
117
+ def authorize_port_range(sg, port_range, protocol, ip_range)
118
+ sg.authorize_port_range(port_range, {:ip_protocol => protocol, :cidr_ip => ip_range})
119
+ end
120
+
121
+ def ip_permissions(sg)
122
+ sg.ip_permissions
123
+ end
124
+
125
+ # Any of the following +port_defn+ can be used:
126
+ # {
127
+ # ssh: 22,
128
+ # http: { ports: (80..82) },
129
+ # mosh: { protocol: "udp", ports: (60000..60050) }
130
+ # mosh: { protocol: "rdp", ports: (3398..3398), ip_range: "196.212.12.34/32" }
131
+ # }
132
+ # In this example,
133
+ # * TCP 22 will be opened for ssh from any ip_range,
134
+ # * TCP ports 80, 81, 82 for http from any ip_range,
135
+ # * UDP 60000 -> 60050 for mosh from any ip_range and
136
+ # * TCP 3398 for RDP from ip range: 96.212.12.34/32
137
+ def extract_port_definition(port_defn)
138
+ protocol = "tcp"
139
+ ip_range = "0.0.0.0/0"
140
+ if port_defn.is_a? Integer
141
+ port_range = (port_defn..port_defn)
142
+ elsif port_defn.is_a? Range
143
+ port_range = port_defn
144
+ elsif port_defn.is_a? Hash
145
+ protocol = port_defn[:protocol] if port_defn[:protocol]
146
+ port_range = port_defn[:ports] if port_defn[:ports]
147
+ ip_range = port_defn[:ip_range] if port_defn[:ip_range]
148
+ end
149
+ [protocol, port_range, ip_range]
150
+ end
151
+
152
+ def provision_or_reuse_public_ip_address(options={})
153
+ provision_public_ip_address(options) || find_unused_public_ip_address(options)
154
+ end
155
+
156
+ def find_unused_public_ip_address(options={})
157
+ if address = fog_compute.addresses.find { |s| s.server_id.nil? }
158
+ address.public_ip
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module Providers; module Clients; end; end; end
4
+
5
+ require "bosh/providers/clients/fog_provider_client"
6
+ require "bosh/providers/constants/openstack_constants"
7
+
8
+ class Bosh::Providers::Clients::OpenStackProviderClient < Bosh::Providers::Clients::FogProviderClient
9
+ # @return [String] provisions a new public IP address in target region
10
+ # TODO nil if none available
11
+ def provision_public_ip_address(options={})
12
+ address = fog_compute.addresses.create
13
+ address.ip
14
+ # TODO catch error and return nil
15
+ end
16
+
17
+ def associate_ip_address_with_server(ip_address, server)
18
+ address = fog_compute.addresses.find { |a| a.ip == ip_address }
19
+ address.server = server
20
+ end
21
+
22
+ # Hook method for FogProviderClient#create_security_group
23
+ def ip_permissions(sg)
24
+ sg.rules
25
+ end
26
+
27
+ # Hook method for FogProviderClient#create_security_group
28
+ def authorize_port_range(sg, port_range, protocol, ip_range)
29
+ sg.create_security_group_rule(port_range.min, port_range.max, protocol, ip_range)
30
+ end
31
+
32
+ def find_server_device(server, device)
33
+ va = fog_compute.get_server_volumes(server.id).body['volumeAttachments']
34
+ va.find { |v| v["device"] == device }
35
+ end
36
+
37
+ def create_and_attach_volume(name, disk_size, server, device)
38
+ volume = fog_compute.volumes.create(:name => name,
39
+ :description => "",
40
+ :size => disk_size,
41
+ :availability_zone => server.availability_zone)
42
+ volume.wait_for { volume.status == 'available' }
43
+ volume.attach(server.id, device)
44
+ volume.wait_for { volume.status == 'in-use' }
45
+ end
46
+
47
+ def delete_security_group_and_servers(sg_name)
48
+ raise "not implemented yet"
49
+ end
50
+
51
+ # Construct a Fog::Compute object
52
+ # Uses +attributes+ which normally originates from +settings.provider+
53
+ def setup_fog_connection
54
+ configuration = Fog.symbolize_credentials(attributes.credentials)
55
+ configuration[:provider] = "OpenStack"
56
+ unless attributes.region == openstack_constants.no_region_code
57
+ configuration[:openstack_region] = attributes.region
58
+ end
59
+ @fog_compute = Fog::Compute.new(configuration)
60
+ end
61
+
62
+ def openstack_constants
63
+ Bosh::Providers::Constants::OpenStackConstants
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module Providers; module Constants; end; end; end
4
+
5
+ module Bosh::Providers::Constants::AwsConstants
6
+ extend self
7
+
8
+ # http://docs.aws.amazon.com/general/latest/gr/rande.html#region
9
+ def region_labels
10
+ [
11
+ { label: "US East (Northern Virginia) Region", code: "us-east-1" },
12
+ { label: "US West (Oregon) Region", code: "us-west-2" },
13
+ { label: "US West (Northern California) Region", code: "us-west-1" },
14
+ { label: "EU (Ireland) Region", code: "eu-west-1" },
15
+ { label: "Asia Pacific (Singapore) Region", code: "ap-southeast-1" },
16
+ { label: "Asia Pacific (Sydney) Region", code: "ap-southeast-2" },
17
+ { label: "Asia Pacific (Tokyo) Region", code: "ap-northeast-1" },
18
+ { label: "South America (Sao Paulo) Region", code: "sa-east-1" },
19
+ ]
20
+ end
21
+
22
+ def default_region_code
23
+ "us-east-1"
24
+ end
25
+ end
@@ -0,0 +1,12 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module Providers; module Constants; end; end; end
4
+
5
+ module Bosh::Providers::Constants::OpenStackConstants
6
+ extend self
7
+
8
+ # explicit value representing "no region requested"
9
+ def no_region_code
10
+ "no-region-requested"
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ require "inception/version"
2
+
3
+ module Inception
4
+ end
5
+
6
+ require "bosh/providers"
7
+ require "inception/inception_server"
8
+ require "inception/inception_server_cookbook"
9
+ require "inception/next_deploy_actions"
@@ -0,0 +1,136 @@
1
+ require "thor"
2
+ require "highline"
3
+ require "fileutils"
4
+ require "json"
5
+
6
+ # for the #sh helper
7
+ require "rake"
8
+ require "rake/file_utils"
9
+
10
+ require "escape"
11
+ require "inception/cli_helpers/display"
12
+ require "inception/cli_helpers/infrastructure"
13
+ require "inception/cli_helpers/interactions"
14
+ require "inception/cli_helpers/provider"
15
+ require "inception/cli_helpers/settings"
16
+ require "inception/cli_helpers/prepare_deploy_settings"
17
+
18
+ module Inception
19
+ class Cli < Thor
20
+ include FileUtils
21
+ include Inception::CliHelpers::Display
22
+ include Inception::CliHelpers::Infrastructure
23
+ include Inception::CliHelpers::Interactions
24
+ include Inception::CliHelpers::Provider
25
+ include Inception::CliHelpers::Settings
26
+ include Inception::CliHelpers::PrepareDeploySettings
27
+
28
+ desc "deploy", "Create/upgrade a Bosh inception server"
29
+ def deploy
30
+ migrate_old_settings
31
+ configure_provider
32
+ prepare_deploy_settings
33
+ perform_deploy
34
+ converge_cookbooks
35
+ end
36
+
37
+ desc "delete", "Destroy target Bosh inception server, volumes & release the IP address"
38
+ method_option :"non-interactive", aliases: ["-n"], type: :boolean, desc: "Don't ask questions, just get crankin'"
39
+ def delete
40
+ migrate_old_settings
41
+ perform_delete(options[:"non-interactive"])
42
+ end
43
+
44
+ desc "ssh [COMMAND]", "Open an ssh session to the inception server [do nothing if local machine is the inception server]"
45
+ long_desc <<-DESC
46
+ If a command is supplied, it will be run, otherwise a session will be opened.
47
+ DESC
48
+ def ssh(cmd=nil)
49
+ migrate_old_settings
50
+ run_ssh_command_or_open_tunnel(cmd)
51
+ end
52
+
53
+ desc "tmux", "Open an ssh (with tmux) session to the inception server [do nothing if local machine is inception server]"
54
+ long_desc <<-DESC
55
+ Opens a connection using ssh and attaches to the most recent tmux session;
56
+ giving you persistance across disconnects.
57
+ DESC
58
+ def tmux
59
+ migrate_old_settings
60
+ run_ssh_command_or_open_tunnel(["-t", "tmux attach || tmux new-session"])
61
+ end
62
+
63
+ no_tasks do
64
+ # update settings.git.name/git.email from local ~/.gitconfig if available
65
+ # provision public IP address for inception server if not allocated one
66
+ # Note: helper methods are in inception/cli_helpers/prepare_deploy_settings.rb
67
+ def prepare_deploy_settings
68
+ header "Preparing deployment settings"
69
+ update_git_config
70
+ provision_or_reuse_public_ip_address_for_inception unless settings.exists?("inception.provisioned.ip_address")
71
+ recreate_key_pair_for_inception unless settings.exists?("inception.key_pair.private_key")
72
+ recreate_private_key_file_for_inception
73
+ validate_deploy_settings
74
+ setup_next_deploy_actions
75
+ end
76
+
77
+ def perform_deploy
78
+ header "Provision inception server"
79
+ server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
80
+ server.create
81
+ ensure
82
+ # after any error handling, still save the current InceptionServer state back into settings.inception
83
+ settings["inception"] = server.export_attributes
84
+ save_settings!
85
+ end
86
+
87
+ def setup_next_deploy_actions
88
+ settings["next_deploy_actions"] ||= {}
89
+ @next_deploy_actions = NextDeployActions.new(settings.next_deploy_actions, options)
90
+ end
91
+
92
+ # Perform converge chef cookbooks upon inception server
93
+ # Does not update settings
94
+ def converge_cookbooks
95
+ if @next_deploy_actions.skip_chef_converge?
96
+ header "Prepare inception server", skip: "Requested to be skipped on this deploy."
97
+ else
98
+ header "Prepare inception server"
99
+ server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
100
+ cookbook = InceptionServerCookbook.new(server, settings, settings_dir)
101
+ cookbook.prepare
102
+ settings.set("cookbook.prepared", true)
103
+ save_settings!
104
+ cookbook.converge
105
+ end
106
+ end
107
+
108
+ def perform_delete(non_interactive)
109
+ server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
110
+ if non_interactive
111
+ header "Deleting inception server, volumes and releasing IP address"
112
+ server.delete_all
113
+ else
114
+ raise "Interactive delete not implemented yet"
115
+ end
116
+ ensure
117
+ # after any error handling, still save the current InceptionServer state back into settings.inception
118
+ settings["inception"] = server.export_attributes
119
+ save_settings!
120
+ end
121
+
122
+ def run_ssh_command_or_open_tunnel(cmd)
123
+ recreate_private_key_file_for_inception
124
+ unless settings.exists?("inception.provisioned.host")
125
+ exit "inception server has not finished launching; run to complete: inception deploy"
126
+ end
127
+
128
+ server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
129
+ username = settings.inception.provisioned.username
130
+ host = settings.inception.provisioned.host
131
+ result = system Escape.shell_command(["ssh", "-i", server.private_key_path, "#{username}@#{host}", cmd].flatten.compact)
132
+ exit result
133
+ end
134
+ end
135
+ end
136
+ end